本系列文章所討論的 JavaScript 資安與逆向工程技術,旨在分享知識、探討防禦之道,並促進技術交流。
所有內容僅供學術研究與學習,請勿用於任何非法或不道德的行為。
讀者應對自己的行為負完全責任。尊重法律與道德規範是所有技術人員應共同遵守的準則。
本文同步發佈:https://nicklabs.cc/webpack-bundled-code-how-it-works
在前端資安逆向工程中遇到 Webpack 打包編譯過的程式碼是非常常見的,所以理解 Webpack 的內部機制,對於資安逆向工程來說,從打包編譯過的程式碼找到關鍵邏輯是非常之重要的。
Webpack 是前端模組打包工具,在 Webpack 出現之前可能會使用 多個標籤引入 JavaScript 檔案。
這會導致全域變數污染、載入順序問題以及請求過多等眾多問題。
Webpack 作為一個模組打包工具將這些分散的 JavaScript 模組打包成一個或多個檔案。
Webpack 的核心概念是把專案中所有的資源(JavaScript、CSS、圖片、字體等)都視為模組,透過分析依賴關係,把它們打包成一個或多個「bundle 檔案」,方便在瀏覽器中載入與使用。
這是一個簡單的範例
(() => {
var n = {
123: (module, exports, t) => {
const Dog = t(999);
const d = new Dog();
d.eat();
},
153: (module, exports, t) => {
class Person{
constructor(n){
this.name = n;
}
sayHello(){
console.log(`Hello, ${this.name}!`);
}
}
module.exports = new Person("Nick")
},
999: (module, exports, t) => {
class Dog {
eat() {
console.log(`The dog is eating food`);
}
}
module.exports = Dog;
}
},
r = {};
function t(u) {
var e = r[u];
if (void 0 !== e)
return e.exports;
var i = r[u] = { exports: {} };
n[u].call(i.exports, i, i.exports, t);
return i.exports;
}
t.g = function() {
if ("object" == typeof globalThis)
return globalThis;
try {
return this || new Function("return this")()
} catch (n) {
if ("object" == typeof window)
return window
}
}(),
t.o = (n, r) => Object.prototype.hasOwnProperty.call(n, r),
t(123)
})();
整包程式的本體使用立即執行函式包裝起來,起到一個封閉的作用域執行程式,也避免全域污染。
這裡的 n 物件就像是 Webpack 打包後的模組表。
key 是模組 ID(通常是壓縮過或編號的數字)。
value 是一個函式,代表模組的程式碼。
var n = {
123: (module, exports, t) => {
const Dog = t(999);
const d = new Dog();
d.eat();
},
153: (module, exports, t) => {
class Person{
constructor(n){
this.name = n;
}
sayHello(){
console.log(`Hello, ${this.name}!`);
}
}
module.exports = new Person("Nick")
},
999: (module, exports, t) => {
class Dog {
eat() {
console.log(`The dog is eating food`);
}
}
module.exports = Dog;
}
}
r = {};
這是快取物件,避免同一個模組重複執行。
function t(u) {
var e = r[u];
if(void 0 !== e)
return e.exports;
var i = r[u] = { exports: {} };
n[u].call(i.exports, i, i.exports, t);
return i.exports
}
t.g = function() {
if("object" == typeof globalThis)
return globalThis;
try{
return this || new Function("return this")()
}catch(n){
if("object" == typeof window)
return window
}
}(),
t.o = (n, r) => Object.prototype.hasOwnProperty.call(n, r),
t(123)
我們已經知道t函式是載入器,n是所有的模組,那我們可以將t外拋給window或是global這時我們可以隨心所欲的調用所有的模組。
實際的執行結果如下
圖片中「1
」為原先t(123)的執行結果。
圖片中「2
」為外部調用t(999)的執行結果。
這時可以方便的調用所有模組對於逆向分析來說有極大的幫助。