• 标 题:破解DebugApiSpy v3.2.1.56
  • 作 者:pyzpyz
  • 时 间:2004年3月31日 09:21
  • 链 接:http://bbs.pediy.com

破解DebugApiSpy v3.2.1.56 

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

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

这是一个双层壳,比较简单。虽然可以一步一步跟,但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 ESIEAX
00409BEA   CMP ESIEDI
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 EAXEAX
0040542F       CMP DWORD PTR SS:[EBP-8], EAX
00405432       JE SHORT DebugApi.00405449
00405434       PUSH DWORD PTR SS:[EBP+8]
00405437       MOV ECXDWORD 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 CLBYTE 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 ECXDWORD 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 ESIEAX
00405469       ADD EDIEAX
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 ECXEBX
00424CDB      PUSH ESI
00424CDC      PUSH DWORD PTR SS:[EBP-10]
00424CDF      PUSH EDI
00424CE0      CALL DebugApi.004053F9               //解码
00424CE5      TEST EBXEBX
00424CE7      JE SHORT DebugApi.00424CF7
00424CE9      MOV ECXEBX
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 EAXDWORD 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 EAXDWORD 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 ECXEDX
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 EAXDWORD 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 EAXDWORD 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 EAXEAX                      //=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 ECXDWORD PTR DS:[ESI+90]           ; |
004244CC   POP EDI                                  ; |
004244CD   LEA EAXDWORD 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   


pyzpyz
2004.3.31