c++實現的一種程式碼膨脹變形殼

Editor發表於2018-01-10

主要實現3個功能:

1.程式碼膨脹

2.程式碼變形

3.對地址常量包括字串常量進行加密


程式碼膨脹


程式碼膨脹無非就是指令等價替換,不過這裡沒條指令最好能有多個膨脹規則,這樣會加大還原難度。


原始程式大小為6kb,經過5次混淆後,大小變為66kb.


c++實現的一種程式碼膨脹變形殼


混淆前程式碼:


c++實現的一種程式碼膨脹變形殼


混淆後程式碼:


c++實現的一種程式碼膨脹變形殼

c++實現的一種程式碼膨脹變形殼


從上圖會發現有大量的垃圾程式碼生成。


說一下程式碼變形:

1.有分支跳轉改成無分支跳轉。


在逆向分析時主要是通過條件跳轉來確定函式的流程,根據跳轉的目標地址來確定這是一個分支還是一個迴圈。如果將所有條件跳轉改成無分支跳轉,那麼程式只有在動態執行時,才能確定流程。這大大提高了逆向分析的難度。另外,ida在進行還原偽c程式碼時,遇到無分支跳轉,會進行截斷,導致後面指令無法還原偽c程式碼。這對靠f5吃飯的人說就gameover了。因為此時已經控制了程式碼流程, 如果在後面加一些垃圾指令,會導致後面的程式碼全部識別錯亂,只有執行時才確定要跳到哪個位置。


另外對 jmp imm, call imm和call [imm](一般這種間接call都是api呼叫) 也改成無分支跳轉。


本來沒打算對這類指令進行處理,但是發現效果還挺好,乾脆就加上吧。因為jmp imm也可以作為判斷分支或者迴圈的依據,而call也是一個比較關鍵的指令,尤其是call api,在逆向分析時,通過觀察api的前後程式碼來確定程式碼到底在幹嘛。這樣一處理,你靜態分析時看不到任何條件跳轉,看不到任何call呼叫(除了call reg,當然了,靜態分析你也看不出來call reg)。


另外對條件跳轉和call這類指令的下一條指令加一些垃圾程式碼,會導致後面的整段指令識別錯誤。也會給別人留下個坑,比如一般人想步過這個函式呼叫,會在函式的下一條指令地址處下斷點,但這條指令實際上永遠不會執行。


實現思路:


1.將跳轉目標地址儲存到一個地址陣列表裡面,在建一個索引表記錄地址表的索引,在對索引進行隨機加密。執行時根據條件碼得到正確的索引表地址,在對索引進行解密,得到正確的目標地址偏移。


程式碼變形前:


c++實現的一種程式碼膨脹變形殼


程式碼變形後:


c++實現的一種程式碼膨脹變形殼


兩幅圖對比發現call memset 變成了一大串指令,最後以ret結尾。這一大串做了一個事情,去索引表找到地址表的索引,在根據這個索引去地址表找到正確偏移值,加上基地址,得到目標地址。同時會把返回地址壓棧,實際上返回地址是可以隨意更改的,我是按照程式碼塊進行處理的,隨意加一些垃圾指令, 把後面的程式碼塊往下移動一點就好了,這個自己改,另外索引加密這個我註釋掉了,去掉就可以。大家發現這裡先push了2個暫存器,在這裡偷了點懶,實際上最好應該是用反彙編引擎掃一下當前哪些暫存器是被佔用的,再從沒被佔用的暫存器中隨機分配2個暫存器。如果沒有可利用的暫存器,在這麼做。


call memset是一個立即數,再來看下scanf (call [imm])


程式碼變形前:


c++實現的一種程式碼膨脹變形殼


程式碼變形後:


c++實現的一種程式碼膨脹變形殼


再來看下f5後的程式碼:


程式碼變形前:


c++實現的一種程式碼膨脹變形殼


程式碼變形後:


c++實現的一種程式碼膨脹變形殼

從上圖可以發現變形後,ida根本就沒有識別完整個函式,只識別了一點點。ida遇到無條件跳轉會截斷當前函式。


對地址常量包括字串常量進行加密 :


在進行逆向分析時,很多人第一反應會去看看有沒有一些敏感字串,根據ida的交叉引用來確定目標地址。


另外一些地址值可能也比較關鍵,比如回撥函式。而這類常量賦值一般都是mov指令和push指令,對 mov imm,push imm這類指令的立即數做隨機加密,就可以起到一個保護的作用。


加密前:


c++實現的一種程式碼膨脹變形殼


加密後:


c++實現的一種程式碼膨脹變形殼


本文由看雪論壇 淡淡的熒光 原創  轉載請註明來自看雪社群

相關文章