[逆向知識] AST抽象語法樹:混淆與反混淆的邏輯互換(一)

博客配套代碼發布于github:半自動化cookie更新(歡迎順手Star一下?)

相關逆向知識:

[逆向知識] AST抽象語法樹:混淆與反混淆的邏輯互換(二)-CSDN博客

相關爬蟲專欄:JS逆向爬蟲實戰??爬蟲知識點合集??爬蟲實戰案例?逆向知識點合集


前言:

AST作為逆向知識中一個非常重要的知識點,在面對難度稍大的反爬時必須知道其概念與處理的方式。本文將全面講解AST抽象語法樹的相關知識以及處理方法,先對其有個相對清晰的認知。

而在AST(二)的下一篇文章中,我們將進一步插入大量圖片與實際演示代碼輔助理解,盡可能幫助讀者掌握混淆代碼的應對方式。

一、 什么是抽象語法樹(AST)?

在我們深入探討混淆技術之前,必須先理解編譯器(或解釋器)是如何“閱讀”我們的代碼的。當你寫下一行代碼時,例如:

var result = 10 + (20 / 2);

計算機并不能直接理解這個字符串。它需要經過幾個階段的處理:

  • 詞法分析: 將代碼字符串分解成一個個有意義的最小單元,稱為“Token”。例如,var, result, =, 10, +, (, 20, /, 2, ), ;

  • 語法分析: 根據編程語言的語法規則,將這些Token組合成一個樹形結構,這個結構就是AST

AST精確地描繪了代碼的語法結構。對于上面那行代碼,其AST(簡化后)長這個樣子:

- VariableDeclaration (聲明一個變量)- id: Identifier { name: 'result' } (變量名)- init: BinaryExpression (初始值為一個二元表達式)- operator: '+' (運算符)- left: Literal { value: 10 } (左操作數)- right: BinaryExpression (右操作數是另一個二元表達式)- operator: '/'- left: Literal { value: 20 }- right: Literal { value: 2 }

核心要點:AST拋棄了代碼中的空格、注釋和括號等非結構性元素,只保留了代碼的骨架和邏輯。這使得我們可以通過程序來分析、修改和生成代碼,而這正是混淆與反混淆的技術基石。

二、 利用AST進行代碼混淆:把清晰變模糊

代碼混淆的目的是在不改變代碼執行結果的前提下,使其邏輯變得難以閱讀和分析。有了AST,我們不再需要用復雜的正則表達式去替換字符串,而是可以直接操作代碼的“骨架”。

以下是幾種常見的基于AST的混淆技術:

  1. 標識符重命名

    • 邏輯:遍歷AST中的所有 Identifier(標識符) 節點,將有意義的變量名(如 resultuserName)替換成無意義的短字符(如 _0x1a2b_0x5c8f)。

    • AST操作:找到所有定義變量/函數的作用域,記錄下其中的標識符,然后將它們和所有引用到它們的地方統一重命名。

    • 示例

      // 原始代碼
      function calculateSum(a, b) {var total = a + b;return total;
      }// 混淆后代碼
      function _0xabc1(a, b) {var _0xdef2 = a + b;return _0xdef2;
      }
      
  2. 常量替換與表達式轉換

    • 邏輯:將代碼中的常量(如數字、字符串)替換成一個等價的、但更復雜的表達式。

    • AST操作:找到 Literal(字面量) 節點,并將其替換為一個 BinaryExpression(二元表達式) 或其他更復雜的結構。

    • 示例

      // 原始代碼
      var key = "secret";
      var value = 1000;// 混淆后代碼
      var key = "\x73\x65\x63\x72\x65\x74"; // 字符串拆分為十六進制表示
      var value = 500 + 500; // 數字拆分為表達式
      
  3. 控制流平坦化

    • 邏輯:將代碼塊(如 if/elsefor 循環)打散,放進一個巨大的 switch 語句中,由一個狀態變量來控制執行順序。這使得代碼的執行流程不再是線性的,而是跳躍式的,極大地干擾了靜態分析。

    • AST操作:識別出代碼中的基本邏輯塊,將它們包裹成 case 語句。然后用一個 while 循環和 switch 語句替換掉原始的控制流結構(如 IfStatementForStatement 等)。

    • 示例

      // 原始代碼
      function checkAccess(level) {let message;if (level > 5) {message = "Allowed";} else {message = "Denied";}return message;
      }// 混淆后代碼 (簡化版)
      function checkAccess(level) {let message;var state = '1|0|2'.split('|');var i = 0;while (true) {switch (state[i++]) {case '0':message = "Denied";continue;case '1':if (level > 5) {// 如果滿足條件,下一個狀態是'2'state[i-1] = '2';} else {// 否則,下一個狀態是'0'state[i-1] = '0';}continue;case '2':message = "Allowed";continue;}return message;}
      }
      
  4. 僵尸代碼注入 (Dead Code Injection)

    • 邏輯:在代碼中插入一些永遠不會執行或不影響最終結果的“垃圾”代碼。

    • AST操作:在AST的任意合法位置(如 BlockStatement 中)插入新的、無用的節點,例如一個永遠為 falseif 語句塊。

    • 示例

      // 原始代碼
      function getResult(a) {return a * 10;
      }// 混淆后代碼
      function getResult(a) {// 注入一個永遠不會執行的if分支if ("" === "abc") {var x = 1 + 2;console.log(x);return x - 3;} else {// 原始邏輯被放在這里return a * 10;}
      }
      

三、 利用AST進行代碼反混淆:從模糊到清晰的實踐

反混淆的本質,就是混淆的逆向過程。既然混淆是通過修改AST來增加復雜性,那么反混淆就是通過修改AST來消除這些復雜性。

二者的工具和原理是完全一致的:解析 (Parse) -> 遍歷 (Traverse) -> 修改 (Modify) -> 生成 (Generate)。下面我們將結合實際操作,看看如何處理常見的混淆技術。

1. 表達式化簡與常量折疊

這是反混淆最基礎、最有效的步驟之一。

  • 處理思路:遍歷AST,找到所有能立即計算出結果的表達式,如 500 + 500"a" + "b",以及像 "\x73\x65\x63\x72\x65\x74" 這樣的十六進制字符串。

  • AST操作

    1. 遍歷:使用工具(如 JavaScript 的 babel/traverse)遍歷 AST,尋找 BinaryExpression(二元表達式)和 StringLiteral(字符串字面量)節點。

    2. 識別:如果一個 BinaryExpression 的左右操作數都是 Literal(常量),或者一個 StringLiteral 包含轉義字符,就可以進行處理。

    3. 修改:在遍歷函數中,執行表達式計算(例如 eval() 或手動實現計算邏輯),然后用一個新的 Literal 節點替換掉整個表達式節點。

  • 示例

    // 待反混淆代碼
    var value = 500 + 500;
    var key = "\x73\x65\x63\x72\x65\x74";// 反混淆后代碼
    var value = 1000;
    var key = "secret";
    

2. 字符串解密

許多混淆器會將所有字符串加密,并用一個解密函數在運行時還原。

  • 處理思路:找到這個解密函數及其調用點,在靜態分析時提前執行它,將加密的字符串還原為明文。

  • AST操作

    1. 識別:首先,需要識別出解密函數的定義。它通常是一個接收加密字符串作為參數并返回明文字符串的函數。

    2. 定位:遍歷AST,找到所有對這個解密函數的CallExpression(函數調用)節點。

    3. 沙箱執行:創建一個安全的沙箱環境(例如,使用 Node.js 的 vm 模塊),將解密函數的代碼和它的參數傳入,執行后獲取返回值。

    4. 修改:用一個新的 StringLiteral 節點替換掉整個函數調用節點。

  • 示例

    // 待反混淆代碼
    function _decrypt(str) {// ...復雜的解密邏輯return decodedStr;
    }
    var url = _decrypt("加密的字符串1");
    var msg = _decrypt("加密的字符串2");// 反混淆后代碼
    // (解密函數可以被移除,或者保持原樣)
    var url = "http://example.com";
    var msg = "Hello, World!";
    

3. 控制流反平坦化

這是反混淆中最具挑戰性的任務,需要復雜的靜態分析。

  • 處理思路:分析 while-switch 結構中的狀態變量case 之間的跳轉關系,從而重建出原始的 if/else 和循環結構。這通常需要模擬程序執行,跟蹤狀態變量的值。

  • AST操作

    1. 識別:找到包含 while(true)switch 語句的結構。識別出狀態變量(控制 switch 的變量)和分發函數(通常是一個數組,例如 _0xabc.split('|'))。

    2. 分析:通過污點分析符號執行等技術,跟蹤狀態變量的流向。記錄每個 case 代碼塊以及它們是如何通過 continue 或變量賦值跳轉到下一個 case 的。

    3. 重建:根據分析結果,將這些 case 塊重新組織成更高級的結構。例如,如果 case '1' 后的下一個狀態取決于一個 if 條件,那么就可以將這兩個 case 重新組合成一個 IfStatement。這個過程通常非常復雜,需要自定義的分析算法。

四、 混淆與反混淆的邏輯互換:同一場游戲,不同方向

現在,我們可以清晰地看到這兩者之間的對稱關系:

混淆操作 (增加復雜度)反混淆操作 (降低復雜度)
AST層面

常量 -> 復雜表達式

替換 LiteralExpression

復雜表達式 -> 常量

替換 ExpressionLiteral

有意義變量名 -> 無意義符號

修改 Identifier 節點的 name 屬性

作用域分析與重命名

修改 Identifier 節點的 name 屬性

線性控制流 -> while-switch平坦化

替換 If/ForWhile/Switch

while-switch -> 還原高級結構

重新組合 case 塊為 If/For

插入無用代碼塊

插入或刪除特定的AST節點

移除無用或不可達代碼塊

刪除不可達的AST節點

結論就是:無論是混淆還是反混淆,其核心都是在 AST 這個層面上,對代碼的結構進行程序化的、大規模的增熵(使其更混亂)或減熵(使其更有序)操作。

五、小結

AST為我們提供了一個上帝視角來審視和操作代碼。它不僅僅是編譯器工作的中間產物,更是代碼自動化處理的利器。對于軟件開發者而言,了解AST可以幫助你編寫更強大的代碼轉換工具(如 Babel 插件、代碼格式化工具)。而對于安全研究人員來說,掌握基于AST的分析技術,則是深入理解、破解和防御復雜代碼混淆攻擊的必備技能。

在下篇文章[逆向知識] AST抽象語法樹:混淆與反混淆的邏輯互換(二)-CSDN博客中,我們將進一步,了解如何實際運用各種工具來真正理解并借助AST破解掉混淆的代碼。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/93472.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/93472.shtml
英文地址,請注明出處:http://en.pswp.cn/web/93472.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網絡安全合規6--服務器安全檢測和防御技術

一、服務器安全風險主要威脅:不必要的服務暴露(如僅需HTTP卻開放多余端口)。外網掃描(IP/端口掃描)、DDoS攻擊。系統漏洞攻擊(操作系統、軟件版本已知漏洞)。Web攻擊(SQL注入、XSS、…

Mutually aided uncertainty

cycle loss calculation in order to regularize the two aux-decoders 輔助信息 作者未提供代碼

go基礎學習筆記

思維導圖變量 聲明形式為var 變量名 變量類型 賦值形式為變量名變量值 聲明和賦值同時形式為變量名:變量值 多個變量同時聲明使用形式為 var (x intb bool )當有多個變量類型一樣時,可以放在一行,形式為var x,y int,當類型一樣,并且需要賦值同…

C++析構函數和線程退出1

線程作為程序在操作系統中的執行單元,它是活動對象,有生命周期狀態,它是有始有終的。有啟動就有結束,在上篇文章中討論了線程作為數據成員啟動時的順序問題,如何避免構造函數在初始化對象時對線程啟動的負面影響&#…

【語法】JSON格式與基礎語法

文章目錄JSON 簡介JSON 語法規則JSON 名稱/值對JSON 值類型JSON文件存儲JSON示例數據示例Python解析JSON代碼JSON 簡介 JSON 語法是 JavaScript 語法的子集。JSON 是存儲和交換文本信息的語法。JSON: JavaScript Object Notation(JavaScript 對象表示法)。 JSON 語法規則 數…

GitHub 熱榜項目 - 日榜(2025-08-16)

GitHub 熱榜項目 - 日榜(2025-08-16) 生成于:2025-08-16 統計摘要 共發現熱門項目:13 個 榜單類型:日榜 本期熱點趨勢總結 本期GitHub熱榜呈現三大技術熱點:1) AI應用深入垂直領域,SpatialLM將大語言模型應用于空間…

什么是EDA(Exploratory Data Analysis,探索性數據分析)

EDA(Exploratory Data Analysis,探索性數據分析)是一種在正式建模前,通過統計量和可視化方法來理解數據特征、發現模式與異常、并提出假設的過程。 這張圖里你會看到: 直方圖:展示單變量的分布,…

計算機畢業設計java的小天鵝酒店月子會所管理小天鵝酒店母嬰護理中心管理系統設計小天鵝酒店產后護理會所信息化管理平臺

計算機畢業設計java的小天鵝酒店月子會所管理9zl079(配套有源碼 程序 mysql數據庫 論文) 本套源碼可以在文本聯xi,先看具體系統功能演示視頻領取,可分享源碼參考。在當今數字化時代,隨著人們對產后護理需求的不斷增加,…

Docker-14.項目部署-DockerCompose

一.DockerCompose大家可以看到,我們部署一個簡單的java項目,其中包含3個容器:MySQLNginxJava項目而稍微復雜的項目,其中還會有各種各樣的其它中間件,需要部署的東西遠不止3個。如果還像之前那樣手動的逐一部署&#xf…

Vue組件基礎解析

一、組件的核心意義 組件是Vue中實現UI復用與邏輯封裝的基礎單元,能將復雜UI拆分為獨立、可重用的部分,最終組織成嵌套的樹狀結構(類似HTML元素嵌套)。Vue組件模型支持自定義內容與邏輯封裝,也能兼容原生Web Component。 二、組件的定義方式 根據是否使用構建步驟,Vue…

第5問 對于數據分析領域,統計學要學到什么程度?

1. ?統計學在數據分析中的定位??核心作用?:統計學是數據分析的底層方法論,涵蓋數據描述、推斷預測和模型構建。?兩大分支?:?描述統計?(EDA階段):數據清洗、特征工程的基礎(如均值/分布/…

[go] 橋接模式

橋接模式 是一種結構型設計模式, 可將一個大類或一系列緊密相關的類拆分為抽象和實現兩個獨立的層次結構, 從而能在開發時分別使用。 模型說明抽象部分(Abstraction)提供高層控制邏輯,依賴于完成底層實際工作的實現對象…

GitHub的使用教程

第一章:準備工作 1.1:安裝Git并設置你的GitHub賬戶 1.1.1:注冊 GitHub 賬號: 訪問 https://github.com/ 并注冊一個新賬號。 可以使用qq郵箱進行注冊 輸入郵箱后點擊sign up for GitHub,設置密碼后進行注冊,輸入驗…

Day56 Java面向對象10 方法重寫

Day56 Java面向對象10 方法重寫 1.為什么要方法重寫 當子類不需要父類方法的全部內容 或 父類的方法無法滿足子類的需求時,就需要在子類重寫父類的方法 2.如何方法重寫 重寫必須發生在繼承關系中,只能是子類重寫父類子類重寫的方法名必須和父類方法一致,方法體可以不同子類重寫…

【C++】標準庫中用于組合多個值的數據結構pair、tuple、array...

在 C 標準庫中,有多種數據結構可用于組合多個值,每種結構都有其特定的設計目的和適用場景。以下是主要組合數據結構的分類解析: 一、核心組合數據結構 1. std::pair (C98) 用途:存儲兩個相關值(鍵值對、坐標點等&#…

深入解析C++ STL鏈表(List)模擬實現

目錄 一、需要實現的三個類及其成員函數接口 二、結點類的模擬實現 構造函數 三、迭代器類的模擬實現 1、迭代器類的作用 2、迭代器類模板參數說明 3、構造函數 4、前置運算符重載 5、后置運算符重載 6、前置 -- 運算符重載 7、后置 -- 運算符重載 8、運算符重載 …

將mysql數據庫表結構導出成DBML格式

前言 DBML(數據庫標記語言)是一種簡單易讀的 DSL 語言,用于定義數據庫結構。 因為需要分析商品模塊的表設計是否合理,所以需要圖形化表,并顯示表之前的關系。 想來想去,找到了DBML。所以就需要將數據庫結構…

玩轉tokenizer

🌟 案例 1:加載現成的 BERT 分詞器from tokenizers import Tokenizer# 加載一個預訓練的 BERT tokenizer(文件需要提前下載,比如bert-base-uncased) tokenizer Tokenizer.from_file("bert-base-uncased-tokenize…

Day53--圖論--106. 島嶼的周長(卡碼網),110. 字符串接龍(卡碼網),105. 有向圖的完全聯通(卡碼網)

Day53–圖論–106. 島嶼的周長(卡碼網),110. 字符串接龍(卡碼網),105. 有向圖的完全聯通(卡碼網) 106. 島嶼的周長(卡碼網) 方法:深搜 思路&am…

Elasticsearch 數據建模與映射(Mapping)詳解

在 Elasticsearch 中,數據建模與映射(Mapping) 是決定搜索性能、存儲效率和功能支持的核心環節。合理的映射設計能讓搜索更精準、聚合更高效、存儲更節省。 本文將全面詳解 Elasticsearch 的 數據建模原則、字段類型、動態映射、自定義分析器…