軟體解密技術研究 (轉)

gugu99發表於2007-08-16
軟體解密技術研究 (轉)[@more@]

ideograph; MARGIN: 9pt 0cm; TEXT-ALIGN: justify">技術研究:namespace prefix = o ns = "urn:schemas--com::office" />

---- PE 脫殼

透過設定Hook,動態跟蹤堆疊,對加過殼的可檔案進行解密還原。

 

當然,要解密,至少先要----得到一個被過的“正版” 軟體。

然後----編寫簡單的Deger。

 

僅需要攔截LoadLibray/GetModuleHandle/GetProcAddress,在這三個Hook中執行記錄Library檔名,Procedure(匯入)名。自己按ImportTable結構偽造一個ImportTable,並額外的相關資訊。

為每個匯入函式生成一個Hook函式體,該函式(我們的Hook) 從堆疊中取得返回地址(即該函式的call指令的下一條指令的地址) ,對返回地址的前一條指令(即呼叫該函式的call指令)進行譯碼,一般是如下可能的形式:

  call  jump_thunk

  call  [thunk_fun]

  call  reg    ;為簡單,先忽略此形式

  call  [reg]   ;為簡單,先忽略此形式

  call  [reg + thunk_offset] ;為簡單,先忽略此形式

其它奇奇怪怪的形式完全忽略不考慮,如

  push  offset ret_address

push  fun_address

ret

ret_address:

或:

  push  offset ret_address

jmp   fun_address

ret_address:

關鍵的難點在這裡,很有可能在此處出現問題,因為反向譯碼有可能把一個地址錯誤的當成指令譯碼了(不過這種情況很少) 。如果忽略這些問題,則這一個難點就OK了。

稱返回地址的前一條指令,即呼叫該函式的call指令,為caller_entry。

  caller_entry指令的bin code為:op XXXXXXXX,其中XXXXXXXX為轉向地址。我們的hook code都做些什么工作呢?

1.  讀出caller_entry及其附近一些bin code並寫入我們的檔案,為其後進一步分析用,當然該指令中XXXXXXXX會被記錄下來。

2.  由於動態自動跟蹤不可能跟蹤到所有呼叫Import Function的地方,但幾乎可以跟蹤到對至少一個Import Function的一次呼叫。所以改動XXXXXXXX(即jump_thunk或fun_thunk的值)可能不太實際,於是只有----

3.  對 jump jump_thunk,我們走到jump_thunk,這裡一般是
”call [fun_thunk]” 的形式。

4.  把fun_thunk寫入我們的Crack檔案,為進一步分析使用。

5.  在這樣的過程中我們需要儘量遍歷“該正版軟體”所有的操作路徑(如點檔案>儲存/另存,編輯>複製/貼上...

6.  使用者點選Debugger的“遍歷結束” 按鈕,就可以把整個PE Image Dump出來。

然後,點選Debugger的“分析&” ,即可分析我們生成的Crack檔案。

分析過程中的主要操作是:

1.  修改 Dump出來的,主要是PE Header和Section Table。

2.  走到fun_thunk,把[fun_thunk] 改為我們的hook地址。

該脫殼軟體的特點(區別與其它脫殼軟體)基本上只有一點:其它脫殼軟體都是想辦法取得殘留的ImportAddressTable或ImportNameTable,從其中做文章。

而該軟體是從的執行中獲得實際的thunk地址,並自己構造ImportTable。

其實一切都很簡單!

後記:

本人一年前作“軟體加密技術及實現” 為畢業論文,工作一年來,因專注於工作,幾乎已經廢棄了加密的研究。

不久前有朋友說:“你就會加密,你怎知你加密的不能被別人破解?” 於是又老毛病大發,連做夢都想:用什么辦法可以使別人不能破解?想來想去,總不能找到完全之策。最後終於想到了一個:“地址加密” 方法,即把ImportFunction的地址加密,呼叫ImportFunciton時,先解密地址再跳轉到ImportFunciton,具體實現如下:

寫一個Hook Code,把ImportAddressTable中的地址填為該Hook Code的地址,於是對應與每一個ImportAddressTableEntry,都有一個Hook Code的副本,該Hook Code對加密過的地址進行解密,然後再跳轉過去。該Hook Code讓它(cracker) 總不能從 PE Image中得到任何ImportFunction的地址。

我自以為此方法完美無缺,然而不久我又想到了破解方法,即此文所描述的。

於是,很快,我又找到了另外一種方法來對付這種破解----

在HookCode開始,把返回地址(即文中所說的call_entry) 加密,然後為被調函式(即該ImportFunction) 建立一個新的StackFrame,再把call_entry之前傳來的引數複製到新的這個StackFrame中。然後呼叫ImportFuncion。

然而此方法也有問題----我實際上不知道被調函式有幾個引數。

所以我為它建立StackFrame時,只能把該StackFrame建得足夠大,可以容納引數最多的函式----如我假設它至少有100個引數,於是這會浪費較多的堆疊區,幸好這裡不會有遞迴呼叫,否則堆疊有可能。這個問題暫時得到了解決。

然而又出現了另外一個問題----當被調函式返回,我不知道它到底跳過了幾個引數(ImportFunction一般是stdcall,跳過引數區的責任由被調者完成) 。即被調函式返回後,ESP的值與呼叫前不同。

幸好,函式呼叫不會改變ESI,EDI,EBX,EBP的值,我只要選用這其中的一個暫存器,如EBP,然後用他儲存呼叫前的ESP(當然要先把它存入堆疊而後在恢復)。再呼叫ImportFunction,待它返回後,
ParamSize =(StackFrameSize – (EBP – ESP)) 就是被調者跳過的引數區的尺寸。

然而又有問題:此刻怎么返回?我必須跳過引數區呀! 如果我可以生成一條指令ret ParamSize----(ParamSize必須是立即數) ,就可以了。

但又有問題----這樣的程式碼是不可重入的!

沒辦法了嗎?當然有,這樣的程式碼可以完成ret ParamSize的功能:

  add   ESP, ParamSize

  jmp   [ESP – ParamSize] ;此刻,該返回地址已經解密.

一切OK!!

此加密方法,徵求破解!!

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-960915/,如需轉載,請註明出處,否則將追究法律責任。

相關文章