首頁 > web前端 > js教程 > 以下是如何在 JavaScript 中混淆而無需耗費大量時間:AST、Babel、插件。

以下是如何在 JavaScript 中混淆而無需耗費大量時間:AST、Babel、插件。

DDD
發布: 2025-01-10 12:34:44
原創
146 人瀏覽過

介紹

嘿,你有沒有想過你的演算法有多酷、多獨特? ?許多程式設計師和公司都這樣做,這就是為什麼他們可能不願意與所有人分享他們的工作。如果將部分程式碼移至伺服器(對於客戶端-伺服器應用程式),這個問題會稍微好一點,但這種方法並不總是可行。有時,我們必須將敏感的程式碼部分直接公開。

在本文中,我們將研究 JavaScript 中的混淆,創建隱藏演算法並使學習程式碼變得更加困難的方法。我們還將探索 AST 是什麼,並提供可用於與其互動以實現混淆的工具。

這是怎麼回事?

這是一個愚蠢的例子。讓我們想像一下這種情況:

  1. 鮑伯去了一個贈送電腦顯示器的網站(這裡是 -> ?)。鮑伯的顯示器更好,但免費的東西總是好的!
  2. 當 Bob 造訪網站時,JavaScript 在瀏覽器中執行,收集有關使用者裝置的資料並將其傳送到伺服器。比方說,就是這樣:
let w = screen.width, h = screen.height;
// Let's say there's a logic with some check. 
console.info(w, h);
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
  1. 不幸的是,鮑伯無法造訪贈品頁面,他對此感到非常沮喪。他不明白為什麼。然後他在贈品規則中了解到,不允許使用者擁有大而好的顯示器。

  2. 幸運的是,鮑伯在高中時參加了一些電腦科學課程。他果斷按下F12打開開發者控制台,研究了一下腳本,發現主辦單位正在檢查螢幕解析度。然後他決定透過手機參與並成功通過測試。

一個美好結局的虛構故事 - 但如果主角看到的是這個而不是之前的代碼,它就不會這麼好:

l=~[];l={___:++l,$$$$:(![]+"")[l],__$:++l,$_$_:(![]+"")[l],_$_:++l,$_$$:({}+"")[l],$$_$:(l[l]+"")[l],_$$:++l,$$$_:(!""+"")[l],$__:++l,$_$:++l,$$__:({}+"")[l],$$_:++l,$$$:++l,$___:++l,$__$:++l};l.$_=(l.$_=l+"")[l.$_$]+(l._$=l.$_[l.__$])+(l.$$=(l.$+"")[l.__$])+((!l)+"")[l._$$]+(l.__=l.$_[l.$$_])+(l.$=(!""+"")[l.__$])+(l._=(!""+"")[l._$_])+l.$_[l.$_$]+l.__+l._$+l.$;l.$$=l.$+(!""+"")[l._$$]+l.__+l._+l.$+l.$$;l.$=(l.___)[l.$_][l.$_];l.$(l.$(l.$$+"\""+(![]+"")[l._$_]+l.$$$_+l.__+"\"+l.$__+l.___+"\"+l.__$+l.$$_+l.$$$+"\"+l.$__+l.___+"=\"+l.$__+l.___+"\"+l.__$+l.$$_+l._$$+l.$$__+"\"+l.__$+l.$$_+l._$_+l.$$$_+l.$$$_+"\"+l.__$+l.$_$+l.$$_+".\"+l.__$+l.$$_+l.$$$+"\"+l.__$+l.$_$+l.__$+l.$$_$+l.__+"\"+l.__$+l.$_$+l.___+",\"+l.$__+l.___+"\"+l.__$+l.$_$+l.___+"\"+l.$__+l.___+"=\"+l.$__+l.___+"\"+l.__$+l.$$_+l._$$+l.$$__+"\"+l.__$+l.$$_+l._$_+l.$$$_+l.$$$_+"\"+l.__$+l.$_$+l.$$_+".\"+l.__$+l.$_$+l.___+l.$$$_+"\"+l.__$+l.$_$+l.__$+"\"+l.__$+l.$__+l.$$$+"\"+l.__$+l.$_$+l.___+l.__+";\"+l.__$+l._$_+l.$$__+l._$+"\"+l.__$+l.$_$+l.$$_+"\"+l.__$+l.$$_+l._$$+l._$+(![]+"")[l._$_]+l.$$$_+".\"+l.__$+l.$_$+l.__$+"\"+l.__$+l.$_$+l.$$_+l.$$$$+l._$+"(\"+l.__$+l.$$_+l.$$$+",\"+l.$__+l.___+"\"+l.__$+l.$_$+l.___+");"+"\"")())();
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

Here
我向你保證,這不是亂碼,而是 JavaScript!並且它執行相同的操作。您可以嘗試在此處的控制台中執行程式碼。

我想在這種情況下,我們的英雄就會認命,不參加有獎活動,主辦單位也會保留他們的計畫。

那這裡有什麼意義呢?恭喜 - 您已經了解了 jjencode 工具以及混淆是什麼以及它可以發揮什麼作用。

總之,混淆是將程式碼或資料轉換為人類難以理解但仍適用於機器或程式的形式的過程。

隱藏秘密。快速方式

理論已經夠多了,讓我們繼續看更多實際例子? ‍? 。現在,讓我們嘗試使用您更可能在網路上找到的混淆來轉換程式碼。讓我們來看一個更有趣的程式碼,其中包含我們的「專有技術」操作。而且非常不希望每個不懶得達到 F12 的人都可以了解它們:

let w = screen.width, h = screen.height;
// Let's say there's a logic with some check. 
console.info(w, h);
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

例如,此程式碼收集裝置和瀏覽器資料並將結果輸出到控制台(我們將使用輸出作為程式碼效能的指標):

l=~[];l={___:++l,$$$$:(![]+"")[l],__$:++l,$_$_:(![]+"")[l],_$_:++l,$_$$:({}+"")[l],$$_$:(l[l]+"")[l],_$$:++l,$$$_:(!""+"")[l],$__:++l,$_$:++l,$$__:({}+"")[l],$$_:++l,$$$:++l,$___:++l,$__$:++l};l.$_=(l.$_=l+"")[l.$_$]+(l._$=l.$_[l.__$])+(l.$$=(l.$+"")[l.__$])+((!l)+"")[l._$$]+(l.__=l.$_[l.$$_])+(l.$=(!""+"")[l.__$])+(l._=(!""+"")[l._$_])+l.$_[l.$_$]+l.__+l._$+l.$;l.$$=l.$+(!""+"")[l._$$]+l.__+l._+l.$+l.$$;l.$=(l.___)[l.$_][l.$_];l.$(l.$(l.$$+"\""+(![]+"")[l._$_]+l.$$$_+l.__+"\"+l.$__+l.___+"\"+l.__$+l.$$_+l.$$$+"\"+l.$__+l.___+"=\"+l.$__+l.___+"\"+l.__$+l.$$_+l._$$+l.$$__+"\"+l.__$+l.$$_+l._$_+l.$$$_+l.$$$_+"\"+l.__$+l.$_$+l.$$_+".\"+l.__$+l.$$_+l.$$$+"\"+l.__$+l.$_$+l.__$+l.$$_$+l.__+"\"+l.__$+l.$_$+l.___+",\"+l.$__+l.___+"\"+l.__$+l.$_$+l.___+"\"+l.$__+l.___+"=\"+l.$__+l.___+"\"+l.__$+l.$$_+l._$$+l.$$__+"\"+l.__$+l.$$_+l._$_+l.$$$_+l.$$$_+"\"+l.__$+l.$_$+l.$$_+".\"+l.__$+l.$_$+l.___+l.$$$_+"\"+l.__$+l.$_$+l.__$+"\"+l.__$+l.$__+l.$$$+"\"+l.__$+l.$_$+l.___+l.__+";\"+l.__$+l._$_+l.$$__+l._$+"\"+l.__$+l.$_$+l.$$_+"\"+l.__$+l.$$_+l._$$+l._$+(![]+"")[l._$_]+l.$$$_+".\"+l.__$+l.$_$+l.__$+"\"+l.__$+l.$_$+l.$$_+l.$$$$+l._$+"(\"+l.__$+l.$$_+l.$$$+",\"+l.$__+l.___+"\"+l.__$+l.$_$+l.___+");"+"\"")())();
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

現在讓我們使用流行的 JS 混淆器 - obfuscator.io 來修改上面的程式碼。結果,我們會得到這樣的程式碼:

function getGpuData(){
  let cnv = document.createElement("canvas");
  let ctx = cnv.getContext("webgl");
  const rendererInfo = ctx.getParameter(ctx.RENDERER);
  const vendorInfo = ctx.getParameter(ctx.VENDOR);

  return [rendererInfo, vendorInfo]
}

function getLanguages(){
  return window.navigator.languages;
}

let data = {};
data.gpu = getGpuData();
data.langs = getLanguages();
console.log(JSON.stringify(data))
登入後複製
登入後複製

瞧!現在,只有機器會樂意解析這段程式碼(你和我可能不在其中?)。儘管如此,它仍然有效並產生相同的結果。注意變化:

  1. 換行符和多餘空格消失了。
  2. 變數名稱已被替換為無資訊的名稱,例如 _0x587f42。
  3. 字串和物件屬性已轉換為函數調用,透過索引從數組返回其值。例如,document.createElement(“canvas”) 變成了 document[_0x12260c(0x197)](_0x12260c(0x191))。這是透過使用計算屬性來實現的。

在這種情況下,最後一種技術可能是最令人討厭的,因為它增加了靜態程式碼分析的負擔。

好吧,看來所有的秘密都被隱藏了。我們可以將程式碼部署到生產中嗎?

等等...如果有程式碼混淆服務,也許有些可以把這些東西拉回來?絕對嗎?而且不只一個!讓我們嘗試使用其中之一 - webcrack。看看我們是否可以獲得原始的、可讀的程式碼。以下是使用此反混淆器的結果:

{"gpu":["ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar","Mozilla"],"langs":["en-US","en"]}
登入後複製
登入後複製

哎呀? 。 當然,它沒有回傳變數的名稱,但謝謝你。

所以事實證明,在這種情況下冷靜研究我們的程式碼的唯一障礙是研究人員使用反混淆器的意志力。毫無疑問,也可以使用其他解決方案和定制,但對於任何流行的混淆,我們很可能應該期待流行的反混淆。

我們應該絕望並不戰而屈人之兵嗎?當然不是!讓我們看看還能做些什麼...

Here

你如何成為一個混淆者

混淆器 - 聽起來像是來自奇幻宇宙的某種法師,不是嗎? ??‍♂️
當然,有人可以在編寫程式碼時混淆程式碼,並且是天生的魔術師。你甚至可能無意中自己已經能夠施展這樣的咒語一段時間了。但是,如果這些技能由於「高級程式設計師」的批評而消失,並且您有一個可能使您的程式難以調查的想法,那麼您現在該怎麼辦?在這種情況下,轉向與程式碼結構本身互動並允許您修改它的工具是有意義的。讓我們來看看它們。

AS工具

也可以嘗試透過像與文字互動一樣簡單地與程式碼互動來修改程式碼,用正規表示式取代某些結構等等。但我想說,按照這種方式,你有更多的機會毀掉你的程式碼和時間,而不是混淆它。

為了更可靠和受控的修改,將其引入抽象結構、樹(AST - 抽象語法樹)是有意義的,透過它我們可以更改我們感興趣的元素和結構.

處理 JS 程式碼有不同的解決方案,最終的 AST 也有所不同。在本文中,我們將使用 babel 來實現此目的。你不需要安裝任何東西,你可以在astexplorer這樣的資源上嘗試一切。

(如果你不想搞亂babel,請查看shift-refactor。它允許你使用**CSS 選擇器與AST 互動。非常簡約且方便的學習方法並修改程式碼。但它使用特定版本的AST,與babel 不同,您可以在shift-query 互動式簡報中測試此工具的CSS 查詢)。

0. 使用 AST

現在讓我們透過一個簡單的範例來看看如何在不離開瀏覽器的情況下輕鬆使用這些工具。假設我們需要將同名函數中的測試變數的名稱改為changed:

let w = screen.width, h = screen.height;
// Let's say there's a logic with some check. 
console.info(w, h);
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

將此程式碼貼到astexplorer中(從上方選擇JavaScript@babel/parser),它應該在那裡顯示為AST。您可以單擊測試變數以在右側視窗中查看此程式碼部分的語法:
Here

為了解決我們的問題,我們可以編寫以下 babel 插件,它將解析我們的程式碼並查找其中的所有名稱標識符,並在滿足某些條件時重命名它們。讓我們將其貼到 astexplorer 的左下視窗中(打開 transform 滑桿並選擇 babelv7 使其出現):

let w = screen.width, h = screen.height;
// Let's say there's a logic with some check. 
console.info(w, h);
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

控制台輸出包含在這個插件中是有原因的。這允許我們透過檢查瀏覽器控制台中的輸出來調試我們的插件。在這種情況下,我們輸出有關 Identifier 類型的所有節點的資訊。這些資訊包含有關節點本身(node)、父節點(parent)和環境(範圍-包含在當前上下文中創建的變數以及對它們的引用)的資料:
Here

因此,在右下視窗中,我們可以注意到原始程式碼中的變數已成功更改,而沒有影響其他標識符:

l=~[];l={___:++l,$$$$:(![]+"")[l],__$:++l,$_$_:(![]+"")[l],_$_:++l,$_$$:({}+"")[l],$$_$:(l[l]+"")[l],_$$:++l,$$$_:(!""+"")[l],$__:++l,$_$:++l,$$__:({}+"")[l],$$_:++l,$$$:++l,$___:++l,$__$:++l};l.$_=(l.$_=l+"")[l.$_$]+(l._$=l.$_[l.__$])+(l.$$=(l.$+"")[l.__$])+((!l)+"")[l._$$]+(l.__=l.$_[l.$$_])+(l.$=(!""+"")[l.__$])+(l._=(!""+"")[l._$_])+l.$_[l.$_$]+l.__+l._$+l.$;l.$$=l.$+(!""+"")[l._$$]+l.__+l._+l.$+l.$$;l.$=(l.___)[l.$_][l.$_];l.$(l.$(l.$$+"\""+(![]+"")[l._$_]+l.$$$_+l.__+"\"+l.$__+l.___+"\"+l.__$+l.$$_+l.$$$+"\"+l.$__+l.___+"=\"+l.$__+l.___+"\"+l.__$+l.$$_+l._$$+l.$$__+"\"+l.__$+l.$$_+l._$_+l.$$$_+l.$$$_+"\"+l.__$+l.$_$+l.$$_+".\"+l.__$+l.$$_+l.$$$+"\"+l.__$+l.$_$+l.__$+l.$$_$+l.__+"\"+l.__$+l.$_$+l.___+",\"+l.$__+l.___+"\"+l.__$+l.$_$+l.___+"\"+l.$__+l.___+"=\"+l.$__+l.___+"\"+l.__$+l.$$_+l._$$+l.$$__+"\"+l.__$+l.$$_+l._$_+l.$$$_+l.$$$_+"\"+l.__$+l.$_$+l.$$_+".\"+l.__$+l.$_$+l.___+l.$$$_+"\"+l.__$+l.$_$+l.__$+"\"+l.__$+l.$__+l.$$$+"\"+l.__$+l.$_$+l.___+l.__+";\"+l.__$+l._$_+l.$$__+l._$+"\"+l.__$+l.$_$+l.$$_+"\"+l.__$+l.$$_+l._$$+l._$+(![]+"")[l._$_]+l.$$$_+".\"+l.__$+l.$_$+l.__$+"\"+l.__$+l.$_$+l.$$_+l.$$$$+l._$+"(\"+l.__$+l.$$_+l.$$$+",\"+l.$__+l.___+"\"+l.__$+l.$_$+l.___+");"+"\"")())();
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

我希望,基於這個例子,我們可以更清楚地了解如何解析和修改程式碼。無論如何,讓我總結一下所做的工作:

  1. 我們透過 astexplorer 使用 babel 將程式碼轉換為 AST。
  2. 透過檢查 AST,我們看到測試變數被標記為 Identifier 類型,其名稱可以使用 name 屬性定義。
  3. 接下來,使用我們的 babel 插件,我們繞過了所有標識符,並更改了函數中名稱為 test 的標識符。

1.隱藏函數和變數名

現在很清楚如何進行程式碼修改了。讓我們嘗試一些更有用的東西,我們可以將其稱為混淆:)我們將採用我們在上一節中嘗試混淆的更複雜的程式碼。現在我們將其中所有變數和函數的名稱更改為隨機名稱。因此,潛在的逆向工程師對某些程式碼元素的用途了解較少。

此外,請隨意使用任何 JS 程式碼來偵錯問題。 正如他們所說,沒有比痛苦更好的老師? .

以下外掛程式將幫助我們完成工作:

function getGpuData(){
  let cnv = document.createElement("canvas");
  let ctx = cnv.getContext("webgl");
  const rendererInfo = ctx.getParameter(ctx.RENDERER);
  const vendorInfo = ctx.getParameter(ctx.VENDOR);

  return [rendererInfo, vendorInfo]
}

function getLanguages(){
  return window.navigator.languages;
}

let data = {};
data.gpu = getGpuData();
data.langs = getLanguages();
console.log(JSON.stringify(data))
登入後複製
登入後複製

這段程式碼有什麼作用?與前面的範例幾乎相同:

  1. 我們遍歷所有Identifier類型的AST節點;
  2. 這次,我們使用generateRndName函數無條件隨機產生識別碼的名稱;
  3. 產生唯一的名稱可以保證我們不會隨機得到可能破壞程式邏輯的重複名稱。

作為插件執行的結果,我們得到以下帶有隨機變數名稱和函數的程式碼:

{"gpu":["ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar","Mozilla"],"langs":["en-US","en"]}
登入後複製
登入後複製

您可以透過在控制台中執行程式碼來檢查它 - 經過我們的操作,它仍然有效!這就是一個好的混淆器的主要品質✨。

但是我們的混淆品質怎麼樣?對我來說 - 邪惡還不太強大:即使透過替換名稱,經驗豐富的程式設計師也很容易理解這段程式碼的目的。如果任何 JS 壓縮器都可以處理這個任務,那還有什麼意義呢?現在是否可以為逆向者做一些更實際、更麻煩的事情?還有一個咒語...

Here

2.隱藏?一切!

當我寫「一切」時,我可能有點自信,但我們現在要做的將最大程度地隱藏我們程式碼的操作。在本節中,我們將隱藏字串和各種物件屬性,以使靜態分析複雜化,並可能阻止「客戶端」深入我們的程式碼!

讓我們使用上一階段獲得的隱藏名稱的程式碼,並對其應用以下插件:

let w = screen.width, h = screen.height;
// Let's say there's a logic with some check. 
console.info(w, h);
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

我已經在程式碼註解中描述了這個外掛程式的一些工作,但讓我們一步一步簡單描述一下它的作用:

  1. 我們建立一個陣列 data,其中將儲存程式碼中要替換的所有屬性和字串。此陣列將在傳回資料的 getData 函數中使用;
  2. 接下來,我們遍歷 AST 並找到根節點 Program,使用程式將 getData 函數(傳回給定索引處的屬性和字串)插入到程式碼的開頭;
  3. 然後我們繞過MemberExpression類型的節點。我們用對 getData 函數的呼叫來取代屬性。在這種情況下,像 document.createElement 這樣的構造將被轉換為 document[getData(0)],這要歸功於計算屬性。一路上,我們將屬性的名稱放入資料數組中;
  4. 最後,我們繞過 StringLiteral 類型的節點,其中我們還透過呼叫具有所需索引的 getData 來替換字串。

值得一提的是,解析操作不是順序執行的,而是在 AST 處理過程中找到必要的節點。

執行此外掛程式的結果,我們將得到以下程式碼:

l=~[];l={___:++l,$$$$:(![]+"")[l],__$:++l,$_$_:(![]+"")[l],_$_:++l,$_$$:({}+"")[l],$$_$:(l[l]+"")[l],_$$:++l,$$$_:(!""+"")[l],$__:++l,$_$:++l,$$__:({}+"")[l],$$_:++l,$$$:++l,$___:++l,$__$:++l};l.$_=(l.$_=l+"")[l.$_$]+(l._$=l.$_[l.__$])+(l.$$=(l.$+"")[l.__$])+((!l)+"")[l._$$]+(l.__=l.$_[l.$$_])+(l.$=(!""+"")[l.__$])+(l._=(!""+"")[l._$_])+l.$_[l.$_$]+l.__+l._$+l.$;l.$$=l.$+(!""+"")[l._$$]+l.__+l._+l.$+l.$$;l.$=(l.___)[l.$_][l.$_];l.$(l.$(l.$$+"\""+(![]+"")[l._$_]+l.$$$_+l.__+"\"+l.$__+l.___+"\"+l.__$+l.$$_+l.$$$+"\"+l.$__+l.___+"=\"+l.$__+l.___+"\"+l.__$+l.$$_+l._$$+l.$$__+"\"+l.__$+l.$$_+l._$_+l.$$$_+l.$$$_+"\"+l.__$+l.$_$+l.$$_+".\"+l.__$+l.$$_+l.$$$+"\"+l.__$+l.$_$+l.__$+l.$$_$+l.__+"\"+l.__$+l.$_$+l.___+",\"+l.$__+l.___+"\"+l.__$+l.$_$+l.___+"\"+l.$__+l.___+"=\"+l.$__+l.___+"\"+l.__$+l.$$_+l._$$+l.$$__+"\"+l.__$+l.$$_+l._$_+l.$$$_+l.$$$_+"\"+l.__$+l.$_$+l.$$_+".\"+l.__$+l.$_$+l.___+l.$$$_+"\"+l.__$+l.$_$+l.__$+"\"+l.__$+l.$__+l.$$$+"\"+l.__$+l.$_$+l.___+l.__+";\"+l.__$+l._$_+l.$$__+l._$+"\"+l.__$+l.$_$+l.$$_+"\"+l.__$+l.$$_+l._$$+l._$+(![]+"")[l._$_]+l.$$$_+".\"+l.__$+l.$_$+l.__$+"\"+l.__$+l.$_$+l.$$_+l.$$$$+l._$+"(\"+l.__$+l.$$_+l.$$$+",\"+l.$__+l.___+"\"+l.__$+l.$_$+l.___+");"+"\"")())();
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

從結果程式碼中可以看到,所有屬性都已被具有給定索引的 getData 函數呼叫取代。我們對字串做了同樣的事情,並開始透過函數呼叫來獲取它們。屬性名稱和字串本身使用 base64 進行編碼,使它們更難以被注意到...

Here

我想您已經注意到了 - 這個外掛程式以及一般的程式碼在這個階段有缺陷。例如,可以修正以下問題:

  • 傳回我們的屬性和字串的函數尖叫著它們的目的 - getData。但好處是,這一點可以透過應用我們的第一個插件來修正,該插件會重新命名標識符。
  • getData 函數內部的字串本身沒有受到可靠的保護,很容易找到它們的初始值,因為它只是 base64。解決這個問題更具挑戰性,例如,您可以重新製作 getData 函數並套用加密而不是眾所周知的編碼。
  • getData 函數是唯一的,編寫一個腳本並不難,透過拉取並執行該函數本身,將其所有呼叫替換為原始值。

儘管有這些簡單性和缺點,我認為它已經可以稱為混淆。但話又說回來,我們與開源混淆器有何不同,因為它們做類似的事情?

我們必須記住最初的問題—這些混淆對於公共反混淆器來說是小菜一碟。現在,讓我們使用得到的程式碼並在 webcrack 中對其進行反混淆! (希望它仍然無法解決我們的咒語?)。我想你可能會說實際重要性已經實現 - 我們的“受保護”代碼不再可以通過公共反混淆器一鍵拉回

獎金。反混淆

現在讓我們學習一個全新的咒語。儘管公共反混淆器無法處理我們的插件,但是,在研究了混淆的實際概念後,我們可以注意到一些可用於恢復原始程式碼的模式。

讓我們開始吧,並特別利用:

  • 有一個儲存我們的屬性和字串的呼叫函數;
  • 對此函數的呼叫不會被屏蔽;

鑑於這些缺點,我們可以實現以下插件:

let w = screen.width, h = screen.height;
// Let's say there's a logic with some check. 
console.info(w, h);
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

我們來描述一下這個反混淆插件的功能:

  1. 我們從混淆程式碼複製了 getData 函數,透過使用所需的參數(索引)執行它,我們可以獲得所需的字串;
  2. 我們檢查了所有 getData 函數調用,並用其執行結果替換它們;
  3. 最後,我們在 AST 中找到了 getData 函數,並將其從程式碼中刪除,因為那裡不再需要它。

結果,我們得到以下程式碼:

l=~[];l={___:++l,$$$$:(![]+"")[l],__$:++l,$_$_:(![]+"")[l],_$_:++l,$_$$:({}+"")[l],$$_$:(l[l]+"")[l],_$$:++l,$$$_:(!""+"")[l],$__:++l,$_$:++l,$$__:({}+"")[l],$$_:++l,$$$:++l,$___:++l,$__$:++l};l.$_=(l.$_=l+"")[l.$_$]+(l._$=l.$_[l.__$])+(l.$$=(l.$+"")[l.__$])+((!l)+"")[l._$$]+(l.__=l.$_[l.$$_])+(l.$=(!""+"")[l.__$])+(l._=(!""+"")[l._$_])+l.$_[l.$_$]+l.__+l._$+l.$;l.$$=l.$+(!""+"")[l._$$]+l.__+l._+l.$+l.$$;l.$=(l.___)[l.$_][l.$_];l.$(l.$(l.$$+"\""+(![]+"")[l._$_]+l.$$$_+l.__+"\"+l.$__+l.___+"\"+l.__$+l.$$_+l.$$$+"\"+l.$__+l.___+"=\"+l.$__+l.___+"\"+l.__$+l.$$_+l._$$+l.$$__+"\"+l.__$+l.$$_+l._$_+l.$$$_+l.$$$_+"\"+l.__$+l.$_$+l.$$_+".\"+l.__$+l.$$_+l.$$$+"\"+l.__$+l.$_$+l.__$+l.$$_$+l.__+"\"+l.__$+l.$_$+l.___+",\"+l.$__+l.___+"\"+l.__$+l.$_$+l.___+"\"+l.$__+l.___+"=\"+l.$__+l.___+"\"+l.__$+l.$$_+l._$$+l.$$__+"\"+l.__$+l.$$_+l._$_+l.$$$_+l.$$$_+"\"+l.__$+l.$_$+l.$$_+".\"+l.__$+l.$_$+l.___+l.$$$_+"\"+l.__$+l.$_$+l.__$+"\"+l.__$+l.$__+l.$$$+"\"+l.__$+l.$_$+l.___+l.__+";\"+l.__$+l._$_+l.$$__+l._$+"\"+l.__$+l.$_$+l.$$_+"\"+l.__$+l.$$_+l._$$+l._$+(![]+"")[l._$_]+l.$$$_+".\"+l.__$+l.$_$+l.__$+"\"+l.__$+l.$_$+l.$$_+l.$$$$+l._$+"(\"+l.__$+l.$$_+l.$$$+",\"+l.$__+l.___+"\"+l.__$+l.$_$+l.___+");"+"\"")())();
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

Here

因此,我們能夠透過使用所示的缺點為 babel 編寫一個簡單的插件來擺脫隱藏屬性和字串的混淆。

我希望這個小例子能夠解釋你如何在babel的幫助下對抗這些滋擾。使用這些方法,你還可以解決更複雜的混淆問題 - 主要是找到程式碼中的模式並熟練地使用 AST 進行操作。

結論

我們了解了混淆,一種使程式碼逆向工程複雜化的技術,以及實現它的工具。儘管有一些公共解決方案可以混淆 JavaScript 程式碼,但也有同樣多的公共解決方案可以立即消除這種保護。

因此,您需要編寫自己的解決方案來保護公共反混淆器無法刪除的程式碼。在 JS 中實現混淆的一種可靠方法是編寫自訂 babel 插件,與所需程式碼的 AST 交互,將其轉換為可讀性較差的形式。

當然,這個領域已經有已知的混淆技術和方法,但仍然對創造力和新「技巧」持開放態度,這可能會使學習程式碼變得更加困難。儘管此類技術數量眾多,但它們根本無法保證演算法的保密性,因為程式碼始終「掌握在客戶端手中」。此外,還有調試的可能性,這可以使研究程式碼變得更容易。混淆可以讓您拒絕動機不良的研究人員,從而增加逆向工程的成本。

有一些高階方法,例如,混淆中的一種是程式碼虛擬化,或者簡單地說,在JS中建立一個虛擬機器來執行自訂字節碼。這種方法幾乎完全消除了靜態分析的機會,並使調試變得盡可能困難。然而,這是一個單獨的主題來討論? ....

我希望這對您獲取有關此主題的資訊有用,並且您不會再因為最初混淆的程式碼而責怪自己或您的程式設計師。欣賞這些奇才??‍♀️!我很高興在這裡與您討論魔法的最新趨勢?

以上是以下是如何在 JavaScript 中混淆而無需耗費大量時間:AST、Babel、插件。的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板