鐵人賽 Day 11 Object.defineProperty、Proxy 在 JavaScript 逆向中的應用

鐵人賽 Day 11 Object.defineProperty、Proxy 在 JavaScript 逆向中的應用
鐵人賽 Day 11 Object.defineProperty、Proxy 在 JavaScript 逆向中的應用

本系列文章所討論的 JavaScript 資安與逆向工程技術,旨在分享知識、探討防禦之道,並促進技術交流。

所有內容僅供學術研究與學習,請勿用於任何非法或不道德的行為。

讀者應對自己的行為負完全責任。尊重法律與道德規範是所有技術人員應共同遵守的準則。

 

在前端資安與逆向工程中,若我們想觀察某個對象的行為(例如:監控某函式是否被呼叫、攔截特定 API 的回傳值),Object.defineProperty 與 Proxy 是最常見的手法。

這兩者不僅能幫助逆向工程師理解程式邏輯,同時也常被惡意攻擊者利用來竄改行為。

假設我們已經知道 token 是登入成功後儲存憑證的關鍵變數,那麼接下來就需要追蹤 token 在程式執行的哪個階段被賦值。

這時候就能透過 Object.defineProperty 來攔截並觀察 token 的變化。

需要注意的是Object.defineProperty 必須在目標變數被定義之前就執行,這樣才能成功攔截。

例如:可以在網頁剛開始載入 Script 時透過斷點暫停並執行Object.defineProperty,確保能捕捉到完整的變更過程。

若不方便手動操作,也可以利用 Tampermonkey 這類瀏覽器腳本工具,自動在頁面載入初期注入攔截程式碼,達到相同效果。

var __token = "";
Object.defineProperty(window, "token", {
  get() {
    console.log("有人讀取了 token");
    return __token;
  },
  set(value) {
    console.log("有人修改了 token:", value);
    __token = value;
  }
});
1.jpg

透過上圖可以看到只要是對token進行getter或是setter都可以在console中看到訊息提示。

Proxy 是 ES6 引入的強大 API,可以攔截物件的操作包含讀取、修改、呼叫、刪除...等等。

相比 Object.defineProperty 只能針對單一屬性Proxy能做到全域監控。

let user = { token: "" };

user = new Proxy(user, {
	get(target, prop, receiver) {
		console.log("有人讀取了:", prop);
		return Reflect.get(target, prop, receiver);
  },
	set(target, prop, value, receiver) {
		console.log("有人修改了:", prop, "=", value);
		return Reflect.set(target, prop, value, receiver);
	}
});
2.jpg

精準控制:可以針對物件的特定屬性進行精確的讀取和寫入監控,非常適合只需要關注一兩個關鍵屬性的情境。

向下相容性:作為 ES5 的標準在較舊的JavaScript環境中也能運作,這對於需要支援舊版瀏覽器的專案來說是個優勢。

無法全面監控:必須為每一個需要監控的屬性單獨呼叫 Object.defineProperty,對於像 window 這樣擁有大量屬性的物件來說操作起來會非常繁瑣。

無法監控動態新增的屬性:如果有腳本在執行時動態新增了屬性,Object.defineProperty 無法自動監控到這些新屬性。

全面代理:可以代理整個物件監控所有對其屬性的讀取、寫入、刪除,甚至是函式呼叫,這大大簡化了程式碼。

監控動態新增的屬性:它能自然地處理新增加到物件中的屬性,無需額外設定。

更多的攔截能力:除了讀取與寫入之外還能攔截其他操作,例如deleteProperty或apply,使其在監控上更具彈性。

新標準:是ES6的功能現代瀏覽器都已支援,但在一些老舊環境中可能無法使用。

性能考量:在絕大多數情況下差異不明顯,但在高頻率操作中性能開銷可能會比直接操作物件來得更高一些。

作者頭像
Nick

擅長從前端的互動設計到後端的資料處理,都能親自規劃與實作。對我來說開發網站不只是完成功能,而是打造一個能被真正使用、體驗順暢的平台。我喜歡把複雜的技術轉化成簡單好懂的成果,並在這個過程中持續學習與挑戰自己。