睡不著,帖一個:aspr程式的脫殼修復體會 (8千字)

看雪資料發表於2015-11-15

aspr程式的脫殼修復體會
作者:Fpc/CCG

  工具:softice, fs0's aspr Loader, imprec 1.42, icedump, superbpm, fi2.45
 
  我們的目標是.....呵呵,不要條件反射,是脫殼。Mp3 TagClinic V2.5 Build 7。跟過以前版本的註冊碼,現在是用了aspr的Key註冊方式,註冊碼是不用想了:( 只能脫殼,暴破。
 
  目標軟體可在華軍下到,8月1、2號更新的,1.8M。用fi245檢查是Asprotect 1.2x的殼,誰知道呢。限制方式是一定的使用次數,好象是20次,註冊碼驗證整合在aspr中。
 
  說說我明白的地方:
  找入口點用fs0寫的loader,太牛了~~,dump可用softice + superbpm + icedump或trw2000 + superbpm,接下來是修復 ImportTable,用現成的工具ImpRec 1.42+(Revirgin不熟~) 這一版本的aspr無法全部修復,外掛看來需要升級了。還有8個函式需要手工修復(不同機器上的偏移地址不同):

1.  0130E8DC
2.  0130E930
3.  0130E93C
4.  0130E8F8
5.  0130E94C
6.  0130E95C
7.  01321A0C
8.  01321A1C



  下面是修復過程,首先簡單看一下aspr對一些函式的優待:

019F:0131253B  PUSH      0131290D
019F:01312540  PUSH      DWORD PTR FS:[EAX]
019F:01312543  MOV      FS:[EAX],ESP
019F:01312546  CALL      0130E70C        <- 特殊函式的處理

>>>>>>>>

019F:0130E70C  PUSHAD
019F:0130E70D  PUSH      01316716
019F:0130E712  PUSH      00
019F:0130E714  PUSH      0130E723
019F:0130E719  MOV      EAX,[013050B2]
019F:0130E71F  JMP      [EAX]
019F:0130E721  JMP      0130E748
019F:0130E723  PUSH      0130E72E
019F:0130E728  JMP      0130E7B2

... ....

<<<<<<<<


019F:0131254B  CALL      0131258A
019F:01312550  PUSH      01312559
019F:01312555  INC      DWORD PTR [ESP]
019F:01312558  RET                                                             
019F:01312559  MOV      ESP,0C24448B
019F:0131255E  JMP      01312561

  上面這個call不長,裡面有花指令,它對幾個特殊的函式作處理。舉例來說,對於向GetVersion這類函式來說,返回值為一個DWORD,是在eax中。aspr在殼中執行GetVersion,將返回的eax放到記憶體的某個位置;而後程式再呼叫GetVersion時,由於Import Address Table作了處理,並不是再次執行這個函式,只是簡單的從記憶體中賦值給eax,返回,算是反脫殼的一種方式。對付此種加密方式,對記憶體保留返回值的地方下bpm,倒追,就可以找到究竟是呼叫哪一個函式來的。

(1)來看第一個 0130E8DC :

019F:0040700A  RET
019F:0040700B  NOP
019F:0040700C  PUSH      EAX
019F:0040700D  PUSH      00                 <- 引數**
019F:0040700F  CALL      00406F0C            <- 指向 0130E8DC
019F:00407014  MOV      EDX,005D6108
019F:00407019  PUSH      EDX
019F:0040701A  MOV      [005DB4DC],EAX 


call的內容:

019F:0130E8DC  PUSH      EBP
019F:0130E8DD  MOV      EBP,ESP 
019F:0130E8DF  MOV      EAX,[EBP+08]            <- 引數**
019F:0130E8E2  TEST      EAX,EAX
019F:0130E8E4  JNZ      0130E8ED             <- 跟蹤可知,eax不為0時,指向dll檔名
019F:0130E8E6  MOV      EAX,[01316624]            <- 是基址:400000,就是程式的Handle
019F:0130E8EB  JMP      0130E8F3

019F:0130E8ED  PUSH      EAX
019F:0130E8EE  CALL      KERNEL32!GetModuleHandleA

019F:0130E8F3  POP      EBP
019F:0130E8F4  RET      0004

[0130E8DC]=GetModuleHandleA

  注意最後多彈出4位元組的堆疊,原因是:無論是否執行到 0130E8EE,由於在前面 0040700D 處的引數入棧是多餘的,必須要保持平衡。

  根據上面程式看出,第一個函式是?GetModuleHandleA,下面是它的原型:

The GetModuleHandle function returns a module handle for the specified module if the file has been mapped into the address space of the calling process.

HMODULE GetModuleHandle(

    LPCTSTR lpModuleName     // address of module name to return handle for 
  );    

Parameters

lpModuleName

Points to a null-terminated string that names a Win32 module (either a .DLL or .EXE file). If the filename extension is omitted, the default library extension .DLL is appended. The filename string can include a trailing point character (.) to indicate that the module name has no extension. The string does not have to specify a path. The name is compared (case independently) to the names of modules currently mapped into the address space of the calling process.
If this parameter is NULL, GetModuleHandle returns a handle of the file used to create the calling process.


Return Values

If the function succeeds, the return value is a handle to the specified module.
If the function fails, the return value is NULL. To get extended error information, call GetLastError. ?              ?                  ?                                      ?                                                                              ?                                                                                                                            019F:0130E930  CALL      KERNEL32!GetVersion 


(2)函式2、3、4,aspr的誘惑??

019F:0130E930  CALL      KERNEL32!GetVersion        <- do nothing...
019F:0130E935  MOV      EAX,[01316710]         <- 倒追上面的 CALL 0130E70C,可知此處是GetCurrentProcessID
019F:0130E93A  RET 
019F:0130E93B  NOP

[0130E930]=GetCurrentProcessID

019F:0130E93C  PUSH      00
019F:0130E93E  CALL      KERNEL32!GetModuleHandleA    <- do nothing...
019F:0130E943  PUSH      DWORD PTR [01316714]        <- 倒追法,此處是 GetVersion
019F:0130E949  POP      EAX
019F:0130E94A  RET
019F:0130E94B  RET

[0130E93C]=GetVersion

同樣的方法可得到:
[0130E8F8]=GetVersion

(3)函式5、6,什麼都不作:
 
019F:0130E94C  PUSH      EBP
019F:0130E94D  MOV      EBP,ESP
019F:0130E94F  CALL      KERNEL32!GetCurrentProcess
019F:0130E954  MOV      EAX,[EBP+08]
019F:0130E957  POP      EBP                                                   
019F:0130E958  RET      0004

簡化為:
mov eax, dword ptr [esp+4]
ret 04
整個函式相當於彈出壓棧的引數到eax,即pop eax
解決辦法:在 ImpRec 中修復時選一個特殊一點的函式,比如 MakeCriticalSectionGlobal 之類的。修復完成後用 wdasm 反彙編,將對這個函式的呼叫改為 pop eax 和幾個 nop 。

[0130E94C]=pop eax

下面這個也一樣:
                                                 
019F:0130E95C  PUSH      EBP                                                   
019F:0130E95D  MOV      EBP,ESP                                               
019F:0130E95F  CALL      KERNEL32!GetVersion                                   
019F:0130E964  POP      EBP                                                   
019F:0130E965  RET      0004

簡化為:
call GetVersion
ret 04

注意它與GetVersion並不等價,原因在於 ret 04 != ret。跟蹤對此處的呼叫發現,返回的 eax 值立刻被 mov eax, esi 覆蓋掉,所以這個也同樣處理為 pop eax + Nop

[0130E95C]=pop eax

(4)函式7、8,aspr的乾坤挪移??

我是第一次見到這種方式~~~先執行函式的幾條指令,然後跳進函式體中繼續~:

019F:01321A0C  PUSH      EBP 
019F:01321A0D  MOV      EBP,ESP
019F:01321A0F  PUSH      EBX
019F:01321A10  JMP      BFF9369            <- 跳走

跟下去會發現是到了這裡:

KERNEL32!MulDiv
019F:BFF9368F  PUSH      EBP 
019F:BFF93690  MOV      EBP,ESP                                               
019F:BFF93692  PUSH      EBX
                                                 
019F:BFF93693  MOV      EBX,[EBP+10]            <- 在這裡繼續                                         
019F:BFF93696  MOV      ECX,EBX                                               
019F:BFF93698  OR        EBX,EBX                                               
019F:BFF9369A  JNS      BFF9369E                                             
019F:BFF9369C  NEG      EBX                                                   
019F:BFF9369E  MOV      EAX,[EBP+08]                                         
019F:BFF936A1  XOR      ECX,EAX                                               
019F:BFF936A3  OR        EAX,EAX                                               
019F:BFF936A5  JNS      BFF936A9                                             
019F:BFF936A7  NEG      EAX     
019F:BFF936A9  MOV      EDX,[EBP+0C]

...

[01321A0C]=MulDiv

同樣:

019F:01321A1C  PUSH      EBP 
019F:01321A1D  MOV      EBP,ESP
019F:01321A1F  PUSH      EBX                                                   
019F:01321A20  JMP      BFF93693

[01321A1C]=MulDiv

  這種方式修復起來沒有任何難度,而且也降低了加殼後程式執行的相容性,是作者作出的一種嘗試。

  上述修復完畢,脫後的檔案變成未註冊版,可正常執行,沒有次數限制,但每次有NAG,軟體也不想用,懶得跟下去。誰知道使用次數是存在哪的,半天也找不到:(
 
  希望能對大家水平提高有所幫助:)

編者注:此殼可以用Import REConstructor1.42+修復IAT。本文提供瞭如用Import REConstructor1.42+不能發現正確的函式時,手動修復的方法。

相關文章