邪恶八进制信息安全团队技术讨论组's Archiver

EvilOctal 2005-6-2 14:59

[转载]破解DebugApiSpy v3.2.1.56

文章作者:pyzpyz

上次trw2000兄的贴子里提到这个软件,我研究了一阵,现把结果呈给大家,请大家指正。我先扔一块石头,有玉的尽管朝我砸来。

原贴:[url]http://bbs2.pediy.com/viewtopic.php?t=5531&highlight=%CB%AB%B2%E3%BF%C7[/url]

这是一个双层壳,比较简单。虽然可以一步一步跟,但trw2000兄的方法简洁实用,所以这儿用他的方法(谢谢trw2000兄)


【转】

一、脱壳

   一运行,要注册。很久没搞破解啦,就拿它开刀温习一下功课吧。  
   因为在od中找不到可用的中文字符串,所以考虑用w32dasm反汇编,但要求先脱壳。  
   用FI2.5查看,是用AsPack2.1加的壳,很久没有手工跟啦,手工跟一把吧。用od刚一载入,停在:  

00469001 PUSHAD  

   Ctrl-F查找popad指令,去掉“整个区段”选项,找到这里:  

004694F3  POPAD  
004694F4  JNZ SHORT 004694FE  
004694F6  MOV EAX,1  
004694FB  RETN 0C  
004694FE  PUSH 00464000  
00469503  RETN      

   让光标停在上面一段的最后一行,按F4一次,再F8一次,即可到达第一层壳的OEP。这时如果转存进程的话,用fi查看还有一层PeCompact的壳,并且不能运行(用工具自动脱也不能运行,不知怎样修改)。因此继续脱第二层壳,起始一段如下,有特征命令pushfd和pushad,是壳的开始:  

00464000  JMP SHORT 00464008  
00464002  PUSH 2E9E8    这就是程序的原始OEP的RVA地址  
00464007  RETN  
00464008  PUSHFD  
00464009  PUSHAD  
0046400A  CALL 00464011  

   跟到下面一段,有特征命令popad和popfd,是壳的结束:  

0046554E  POPAD  
0046554F  POPFD  
00465550  PUSH EAX  
00465551  PUSH 0042E9E8  
00465556  RETN 4  

   到达00465556一行,再F8一次,返回到真正入口点:  

0042E9E8  PUSH EBP  
0042E9E9  MOV EBP,ESP  

   光标停在入口点后转存该进程,再用fi查看无壳,是用Visual C++ 6.0编写的。但是用od直接转存的程序不能运行。重跟到入口点,用别的方法转存。重跟就没有必要细跟啦,只要在下面几个关键点设断即可:  

1. 停在入口点时,在00469503处设断,中断后单步一下,即到达第二层壳的入口点。  

2. 在第二层壳的入口点时,壳的结尾00465556处还没有代码,因此无法把断点设在这里,要分几步到才能到达这里。第一步可以断点可以设在0046407B,执行到这儿后,向结尾处写代码的代码才出现:  

00464077  REP MOVS DWORD PTR ES:[EDI],DWORD PTR [ESI]  
00464079  MOV EDI,EBX  
0046407B  RETN  

3. 第二步,在0046407B处中断后,断点可以设在004651A7,执行到这儿以后壳结尾代码才形成:  

004651A5  REP MOVS BYTE PTR ES:[EDI],BYTE PTR [ESI]  
004651A7  JMP SHORT 004651CF  

4. 第三步,在004651A7处中断后,在壳结尾代码00465556处设断,中断后单步走到真正的OEP。  

   用PEditor来dump进程,并修正入口点,测试,程序不能运行,用别的方法dump,也不能运行。


【结束】


当然在最后需要用ImportREC来修复输入表(未加密)。



下面我们来修复dump出来的程序。


二、修复

1.去掉ZwSetInformationThread

00409BD1  PUSH DebugApi.0044DD34             ; ASCII "ZwSetInformationThread"
00409BD6  PUSH DebugApi.0044DD28             ; ASCII "ntdll.dll"
00409BDB  CALL DWORD PTR DS:[4361D8]          ; kernel32.GetModuleHandleA
00409BE1  PUSH EAX
00409BE2  CALL DWORD PTR DS:[4361E8]          ; kernel32.GetProcAddress
00409BE8  MOV ESI, EAX
00409BEA  CMP ESI, EDI
00409BEC  JE SHORT DebugApi.00409BFB
00409BEE  PUSH EDI
00409BEF  PUSH EDI
00409BF0  PUSH 11
00409BF2  CALL DWORD PTR DS:[436150]          ; kernel32.GetCurrentThread
00409BF8  PUSH EAX                //nop
00409BF9  CALL ESI                //nop


   脱壳程序在00409BF9处调用ZwSetInformationThread函数来反调试,这个函数除了反调试以外没其他用途,因此可以把00409BF8和00409BF9处nop掉。


2.解决动态解码

运行程序到424d0f处出错,上一句为CALL 0042407C,进去看看,发现代码显然不对,这是程序动态自解码,脱壳后解码出错,因此运行也出错。

在OD中打开原程序在00409BF8处下一个硬件执行断点,运行停住,在00409BFB处new origin here。在0042407C处下硬件执行断点,运行停住,0042407C处已正确解码,0042407C~0042447B。可以直接用OD的Binary copy和Binary paste拷贝到脱壳程序中。但要注意,这样拷贝过去后,程序还是要对该段代码进行解码,因此要对解码函数处理。

下面寻找解码函数,运行到00409BFB后,在0042407C处下一硬件访问断点,再运行,中断:

0042CEA3    REP MOVS DWORD PTR ES:[EDI], DWORD PTR DS:[ESI]  //断下

这是把0042407C开始处的代码复制到一内存空间,准备解码。假设内存空间为0fc0570,则在0fc0570处再下一硬件访问断点,运行。

0040542D     XOR EAX, EAX
0040542F     CMP DWORD PTR SS:[EBP-8], EAX
00405432     JE SHORT DebugApi.00405449
00405434     PUSH DWORD PTR SS:[EBP+8]
00405437     MOV ECX, DWORD PTR SS:[EBP-4]
0040543A     PUSH ESI
0040543B     CALL DebugApi.00404E68        //解码函数
00405440     ADD ESI, 8
00405443     ADD DWORD PTR SS:[EBP+8], 8
00405447     JMP SHORT DebugApi.0040546E
00405449     MOV CL, BYTE PTR DS:[EAX+ESI]
0040544C     MOV BYTE PTR DS:[EAX+EDI], CL    //断在这
0040544F     INC EAX
00405450     CMP EAX, 8
00405453     JL SHORT DebugApi.00405449
00405455     PUSH DWORD PTR SS:[EBP+C]
00405458     MOV ECX, DWORD PTR SS:[EBP-4]
0040545B     PUSH EDI
0040545C     CALL DebugApi.00404E68        //解码函数
00405461     PUSH 8
00405463     POP EAX
00405464     ADD DWORD PTR SS:[EBP+8], EAX
00405467     ADD ESI, EAX
00405469     ADD EDI, EAX
0040546B     ADD DWORD PTR SS:[EBP+C], EAX
0040546E     DEC EBX
0040546F     JNZ SHORT DebugApi.0040542D


进入CALL DebugApi.00404E68看看,好长一段代码,没去研究解码算法。改成在00404E68直接返回,即改成:
00404E68  RETN 8

修改完成。我们回到调用0042CEA3复制语句的上一层函数看看:


00424CCC    PUSH ESI
00424CCD    PUSH DWORD PTR SS:[EBP+C]
00424CD0    PUSH EDI
00424CD1    CALL DebugApi.0042CE70          //复制
00424CD6    ADD ESP, 0C
00424CD9    MOV ECX, EBX
00424CDB    PUSH ESI
00424CDC    PUSH DWORD PTR SS:[EBP-10]
00424CDF    PUSH EDI
00424CE0    CALL DebugApi.004053F9          //解码
00424CE5    TEST EBX, EBX
00424CE7    JE SHORT DebugApi.00424CF7
00424CE9    MOV ECX, EBX
00424CEB    CALL DebugApi.00404AE1
00424CF0    PUSH EBX
00424CF1    CALL DebugApi.0042D1A5
00424CF6    POP ECX
00424CF7    PUSH ESI
00424CF8    PUSH DWORD PTR SS:[EBP-10]
00424CFB    PUSH DWORD PTR SS:[EBP+C]
00424CFE    CALL DebugApi.0042CE70          //把正确代码复制到42407c
00424D03    PUSH DWORD PTR SS:[EBP-10]
00424D06    PUSH EDI
00424D07    PUSH DWORD PTR SS:[EBP+10]
00424D0A    CALL DebugApi.0042407C          //运行42407c
00424D0F    PUSH ESI
00424D10    PUSH EDI
00424D11    PUSH DWORD PTR SS:[EBP+C]
00424D14    CALL DebugApi.0042CE70          //运行完后,把错误代码复制回42407c
00424D19    PUSH DWORD PTR SS:[EBP-10]


另外4144e2~4158e1也是类似的动态解码。程序中有两处解码函数:
  
0042429E   CALL dumped_6.004053F9
00424CE0   CALL dumped_6.004053F9

就是这两个地方的解码。也有可能还有其他地方是动态解码,我没有再找。



3.解决自效验

这个问题一开始我的方法是正面去找,即运行脱壳程序一处一处找,找到了一些地方,但太麻烦,后来看到程序中有这样的语句:

CALL DWORD PTR DS:[<&kernel32.ExitProces>; ExitProcess

这不就是退出函数吗,因此查找所有命令:CALL DWORD PTR DS:[4361F4],然后在每处都设断,运行,停住:

(1)
00414589   CMP EAX, DWORD PTR SS:[ESP+14]
0041458D   JE SHORT dumped_4.00414597    //JE改成JMP
0041458F   PUSH 0                        ; /ExitCode = 0
00414591   CALL DWORD PTR DS:[<&kernel32.ExitProces>; ExitProcess   //停在这

00414589处就是一个自效验,把0041458D处的je改成jmp。好,其他的也类似修改:

(2)
004167E3   CMP EAX, DWORD PTR SS:[ESP+14]
004167E7   JE SHORT dumped_4.004167F1    //JE改成JMP
004167E9   PUSH 0                        ; /ExitCode = 0
004167EB   CALL DWORD PTR DS:[<&kernel32.ExitProces>; ExitProcess


修改好这两处后,程序可以运行了,但运行一段时间后还会退出,因此还有自效验:

(3)
004168D3   CMP ECX, EDX
004168D5   MOV DWORD PTR SS:[EBP+8], ECX
004168D8   JE SHORT dumped_4.004168E2    //JE改成JMP
004168DA   PUSH 0                        ; /ExitCode = 0
004168DC   CALL DWORD PTR DS:[<&kernel32.ExitProces>; ExitProcess

(4)
00416907   CMP EAX, DWORD PTR SS:[EBP-4]
0041690A   MOV DWORD PTR SS:[EBP+8], EAX
0041690D   JE SHORT dumped_4.0041691A    //JE改成JMP
0041690F   PUSH 0                        ; /ExitCode = 0
00416911   CALL DWORD PTR DS:[<&kernel32.ExitProces>; ExitProcess

(5)
00416928   CMP EAX, DWORD PTR DS:[ECX+8]
0041692B   JE SHORT dumped_4.00416935    //JE改成JMP
0041692D   PUSH 0                        ; /ExitCode = 0
0041692F   CALL DWORD PTR DS:[<&kernel32.ExitProces>; ExitProcess


修改好这些地方以后,程序正常运行。如果使用时还会退出,用同样的方法解决。



三、破解

(1)爆破:

从注册表中取假注册码:

0042497C    PUSH EAX            //注册码字符长度保存地址
0042497D    PUSH EBX            //注册码保存地址
0042497E    PUSH dumped_6.00452818             ;  ASCII "RegInfoBin"
00424983    CALL dumped_6.00424A5E

........

假注册码计算:

004249B1    PUSH EAX                      ; /Arg6
004249B2    PUSH 0                        ; |Arg5 = 00000000
004249B4    PUSH 10                       ; |Arg4 = 00000010
004249B6    PUSH DWORD PTR DS:[ESI+A4]          ; |Arg3
004249BC    PUSH 10                       ; |Arg2 = 00000010
004249BE    PUSH EBX                      ; |Arg1
004249BF    CALL dumped_6.0041EF76             ; dumped_6.0041EF76


比较:

004249CE    E8 58000000  CALL dumped_6.00424A2B         //比较call
004249D3    85C0       TEST EAX, EAX               //=0错,=1对  
004249D5    74 1B      JE SHORT dumped_6.004249F2


因此要爆破,只要把 004249D5处改成JMP SHORT dumped_6.004249E2即可。 这段代码中没出现真注册码的明码,是计算后的结果比较。



(1)寻找注册码:

这个软件注册算法很复杂,我没看明白,也不清楚找到的是不是真的注册码,但好像可以用,有兴趣的可以自己试试:

004244BE  PUSH EBX                      ; /Arg6
004244BF  PUSH ECX                      ; |Arg5
004244C0  OR DWORD PTR SS:[EBP-4], FFFFFFFF      ; |
004244C4  PUSH 10                       ; |
004244C6  LEA ECX, DWORD PTR DS:[ESI+90]        ; |
004244CC  POP EDI                       ; |
004244CD  LEA EAX, DWORD PTR DS:[ESI+24]        ; |
004244D0  PUSH EDI                      ; |Arg4 => 00000010
004244D1  PUSH ECX                      ; |Arg3
004244D2  PUSH 8                        ; |Arg2 = 00000008
004244D4  PUSH EAX                      ; |Arg1
004244D5  CALL dumped_6.0041F14C             ; dumped_6.0041F14C


关键call:04244D5  CALL dumped_6.0041F14C,Arg5为保存保存注册码的地址。  


运行过这个call,得到注册码:AACBF24A6CE25E89AE6643DDC496502B

对应的注册ID:A7E4EB4B4A683C9E10ED6CF866F5188E

页: [1]
© 1999-2008 EvilOctal Security Team