Python 的控制流程式碼混淆

王平發表於2020-04-13

聊下 Python 的程式碼混淆,對 Python 的程式碼做混淆感覺是不倫不類,但是對於外包專案交付型的,又有一些需要。
混淆的目的就是加大別人分析你程式碼邏輯和流程的難度,讓程式碼看上去雜亂,邏輯混亂。但是程式要能正常執行。

一般混淆

對 Python 程式碼做簡單點混淆的就是變數名/類名/字串/常量做混淆,把名稱變成很長或者近似。

這類的混淆庫很多,比如 Intensio-Obfuscator 這個庫,這個庫分簡單和複雜混淆,來看下用它的簡單模式來混淆 Python 程式碼:

Python 的控制流程式碼混淆

左邊是混淆前,右邊是混淆後,只是把變數名方法名混淆並且加長了。

這種簡單混淆的意義不大,字串和常量都一目瞭然,程式碼結構,就靠靜態分析,程式碼的脈絡也看得還是清楚。

再複雜一點的混淆就是把關鍵程式碼藏起來,和在程式碼里加一些無效程式碼。
還是 Intensio-Obfuscator 這個庫的複雜混淆模式,我們來看看:

Python 的控制流程式碼混淆

右邊初看,貌似不像是 Python 程式碼,實際上右邊那串字串就是左邊的 Python 程式碼,只不過是 unicode 碼。  因為 Python 有個內建函式 exec() 可以執行字串程式,像這樣:

> exec("1+1")
>2

我們把這個字串裡的內容列印成 utf8 看看裡面的內容:

Python 的控制流程式碼混淆

如上圖,它的混淆一是把變數名做得更長,二是程式碼里加了些干擾程式碼,看標紅處,原始程式碼本來沒有 for 和 if 語句,混淆後的程式碼有了。看上去如果要靜態分析這個程式碼很困難了,實際如果把變數名重名命和變短後,這部分多餘的 for 和 if 通過靜態分析,還是較容易跳過去。

總結下  Intensio-Obfuscator 庫複雜混淆模式,先是把程式碼變數函式名弄得很長,然後是在程式碼里加入了無效程式碼,最後是把原始碼壓縮當成一個字串,用 exec 來執行。

 

抽象語法樹混淆

上面的混淆方式相對簡單,通過靜態分析就能反混淆出來。更復雜一點的混淆就是控制流混淆。通常程式的執行流程都是很有條理的,控制流混淆就是把程式的執行流程混淆。
比如程式碼裡多了很多 while for if 乃至 lamdb 語句,把賦值,加減操作,變成位運算等等。讓你通過靜態分析的方式,很難看出程式碼的目的和邏輯是什麼。

怎麼做到控制流混淆,要通過抽象語法樹 (AST),通過抽象語法樹,可以做到用程式來修改程式。通過抽象語法樹,可以很精確的知道程式在做什麼操作,這樣就能很精準的修改程式碼。

先看一下簡單的通過抽象語法樹來混淆程式的例子,還是拿上面的程式來舉例。

Python 的控制流程式碼混淆

左邊是混淆前的程式碼,後面是混淆後的。這個例子也是把變數名混淆了,然後是把字串和常量,還有 import 也混淆了。反混淆的難度比上面大了一點,要通過動態除錯才知道程式在幹嘛。

 

什麼是抽象語法樹

見名知意就是把程式抽象成一棵樹,程式碼裡的語句被拆成了樹上的一個個節點。Python 裡有個 AST 模組就是用來幹這個的,還是上面的原始碼,看下被 AST 拆成節點後是什麼樣。

Python 的控制流程式碼混淆
Python 的控制流程式碼混淆

第二張圖就是把第一張圖建立為了抽象語法樹,並且把原始碼按樹的節點列印出來了。

紅箭頭標註了,有 Import 節點,Assign 節點, 函式節點, 加法節點等等。這顆樹可以完全表達上述程式。我們可以通過訪問這顆樹,來用程式修改程式。

Python 的控制流程式碼混淆

自定義一個類,繼承 ast.NodeTransformer ,比如你想訪問字串,就實現visit_Str這個方法,想訪問 Import 就實現 visit_ImportFrom 這個方法。在實現的方法裡,你可以用一些混淆演算法去混淆,(注意只能是混淆,不能改變結果)。這樣就能做到精細化和更復雜的混淆。
有一個 ASTObfuscate 第三方混淆庫就是通過操作 AST 來混淆程式碼,不過對程式邏輯流的混淆沒有,要實現更復雜的控制流混淆,要完整實現這顆解析樹。

當然 Python 的程式碼混淆更難的話,應該是通過混淆位元組碼,或者把關鍵程式碼做成 so 檔案,這樣的混淆難度更大。 位元組碼和 so 檔案都是彙編指令。

猿人學banner宣傳圖

我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。

***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***

相關文章