• 标 题:ArtCursors 3.03 ASPR壳软件脱壳后修整记 (10千字)
  • 作 者:草原猎豹 
  • 时 间:2003-11-24 15:33:36
  • 链 接:http://bbs.pediy.com

首先声明:本文写的是脱壳后的修整,而不是脱壳。用Stripper脱aspr的壳还是挺方便的:P

目标软件:鼠标光标编辑软件ArtCursors 3.03,可制作32位色的cur、ani文件,并可以导入、导出多种图象格式
相关网址: http://www.aha-soft.com/artcursors/index.htm
编写语言:Delphi编写,Aspr1.22壳。
版本:3.03

工具:Stripper,DEDE,OllyDBg,Trw2000,superBPM,TOPO,LordPE,ResHack,WinHex

---------------------------------------------------------------------------------------------
脱完壳,软件一运行就退出,用trw2000+SuperBPM跟踪版比较,发现

005363E0   . FF15 F0CB5300  CALL DWORD PTR DS:[53CBF0]

有问题,原版call进去后来到535dc0,而脱壳版call进去后来到5363d1。试着把这一句改成

005363E0   . E8 DBF9FFFF    CALL UNPACKED_.00535DC0
005363E5   . 90             NOP

正常运行,但为未注册版,运行前弹出NAG,说30天期限已经用了1天。。。
不对啊,我明明用了不止一天啊?莫非。。。?我把时间调后了一年,运行,他还是说只用了一天。。。
原来,时间限制已经解除!只要去掉NAG,改掉"未注册"的字样,不就搞定了?然而,事情没那么简单。。。

先用DEDE分析,点击 File -> load symbo file 载入调试符号vcl60.dsf、vclx60.dsf、rtl60.dsf
(可以用dumpers -> dsf Builder来创建,但必须先安装delphi),
在主窗口的create事件里发现

* Reference to: dmu.Proc_00534C60

00517B40   E81BD10100             call    00534C60
00517B45   837DF400               cmp     dword ptr [ebp-$0C], +$00
00517B49   7505                   jnz     00517B50  <----关键跳

* Reference to: about.Proc_005084E4

00517B4B   E89409FFFF             call    005084E4 <---显示NAG

* Reference to: kernel32.GetTickCount()

00517B50   E8F3F0EEFF             call    00406C48

用原版跟踪,发现517B49在原版跳,在脱壳版不跳,于是把脱壳版中的改成强行跳,
NAG没了,但是主窗口竟不能完整显示,整天异常。。。。。。
天啊,难道他NAG里对主窗口进行了一些初始化的工作(猜测)?怎么办。。。。连个NAG都搞不定?
把我这只菜鸟惹急了,我什么土办法都做的出来的。。。

我决定,用最土的办法干掉NAG:用TOPO插入代码,在合适的时机给NAG发一个WM_CLOSE消息。

首先要找到相关API的调用方法:
用LordPE的PE Editor打开脱壳版程序,点击Directories,再点击Import Table一栏的"..."按钮,
在上面的列表里找到User32.dll(好几个),在下面的列表里找到PostMessageA,
看看他的ThunkRVA的值(最左边),是13f6b4,那么PostMessageA的调用方法就是call [53f6b4]。
(53f6b4=13f6b4+400000)

然后Topo插入大概200字节空间,记下空间首地址:544019,然后在在DEDE里,找到NAG所在的单元:
TfmAbout,看到有FormShow事件,恩,好,就在这里动手脚!

接下来,要找到得到NAG的窗口句柄的办法,在DEDE里,我看到

00508BD5   8B00                   mov     eax, [eax]

* Reference to: Controls.TWinControl.GetHandle()  <---DSF文件的妙用

00508BD7   E8B42DF3FF             call    0043B990  <---可以得到窗口句柄
00508BDC   50                     push    eax       <---返回值,窗口句柄

Controls.TWinControl.GetHandle()是得到特定窗口句柄的方法,
也就是说,在TfmAbout的窗口事件里,只要调用call 0043B990,
EAX里返回的就是TfmAbout的句柄!哈哈哈!这就是DSF文件的妙用,他可以认出不少VCL类的方法!
开始做手脚!

FormShow的首地址是508ad4,先记下开始的5个字节的指令(其对应的机器码是55,8b,ec,6a,00):

00508AD4   55                     push    ebp        <-- 记下
00508AD5   8BEC                   mov     ebpesp   <-- 这三个
00508AD7   6A00                   push    $00    <-- 指令

00508AD9   6A00                   push    $00   <-- 手脚做完从这里执行

然后打开OllyDBG,(我不习惯用HIEW)载入脱壳版程序,把508AD4处代码改成

00508AD4   E940B50300             jmp     00544019 <---跳到我们插入的代码空间

然后按Ctrl-G来到544019处,开始写入做手脚的代码:
                                          _____________________
00544019   55               PUSH EBP     |  这里先执行508AD4处 |
0054401A   8BEC             MOV EBP,ESP  |  被改掉的代码       |
0054401C   6A 00            PUSH 0       |_____________________|
                                       
0054401E   50               PUSH EAX  <---保护EAX寄存器
0054401F   E8 6C79EFFF      CALL UNPACKED_.0043B990   <----Controls.TWinControl.GetHandle()
00544024   6A 00            PUSH 0      <---lParam
00544026   6A 00            PUSH 0      <---wParam
00544028   6A 10            PUSH 10     <---WM_CLOSE
0054402A   50               PUSH EAX    <---NAG的窗口句柄
0054402B   FF15 B4F65300    CALL DWORD PTR DS:[53F6B4] <---PostMessageA
00544031   58               POP EAX    <---恢复EAX寄存器
00544032   E9 A24AFCFF      JMP UNPACKED_.00508AD9   <---手脚做完,回到原处

然后选中被修改过的指令,单击右键,选"Copy to executable file"(别忘了508AD4处),
再单击右键,选"Save File",保存成新文件,运行,NAG窗口一闪而过,成功了。

虽然成功了,但又出来了新的麻烦:没办法显示"关于"对话筐了,因为那正是NAG窗口!
怎么办?把我这只菜鸟逼急了。。。(省略)
我酝酿了一个更土的计划:用ResHack插入一个对话筐资源,并插入代码来显示他!

我找了个用Vc写的有着不错的"关于"对话筐的程序,用ResHack打开,把"关于"对话筐另存为.res文件,
在用ResHack打开这个.res文件,把这个对话筐的资源名称改个象样的名字,叫什么好呢?
既然原来的"关于"对话筐的RCDate的名称叫TfmAbout,就把新对话筐的资源名称也改成"TFMABOUT"吧:P
在用ResHack打开动过手脚后的程序,插入res文件里的对话筐,保存,接下来就是写代码显示他了。

要显示对话筐,需要DialogBoxParamA、EndDialog、GetModuleHandleA三个API函数,
但是DialogBoxParamA、EndDialog两个是原程序里没有的,
不过不要紧,用LordPE可以在输入表里增加API函数。用LordPE打开动过手脚的程序,
点击Directories,再点击Import Table一栏的"..."按钮,在上面的列表里单击右键,选"add import",
在Dll文本筐里写入User32.dll,在API文本筐里写入EndDialog,单击"+" 号,
在API文本筐里再写入DialogBoxParamA,再单击"+" 号,这样就加入了这两个API函数。
记下他们的ThunkRVA:

EndDialog : 1A800B
DialogBoxParamA : 1A8017
GetModuleHandleA : 13F210
那么,
EndDialog的调用方法就是 call [5A800B]
DialogBoxParamA的调用方法就是 call [5A8017]
同理,
GetModuleHandleA的调用方法就是 call [53F210]
记得保存修改。

显示"关于"对话筐的事件是主窗体TfmMain的aHelpAboutExecute事件,这是一个TAction对象的事件,
不能象以前那样用call 43B990得到主窗口的句柄了,只能另寻他法。

在DEDE里,TfmAbout的FormActive事件里我找到了这么一段代码:

* Reference to : TfmMain._PROC_0051A9BC()  <---在NAG里调用主窗口的过程

00508BC4   E8F31D0100             call    0051A9BC
00508BC9   E856AB0200             call    00533724
00508BCE   6AFF                   push    $FF
00508BD0   A108CF5300             mov     eaxdword ptr [$0053CF08]
00508BD5   8B00                   mov     eax, [eax]

* Reference to: Controls.TWinControl.GetHandle()  

00508BD7   E8B42DF3FF             call    0043B990  <--取主窗口句柄?
00508BDC   50                     push    eax       <--主窗口句柄?

* Reference to : TApplication._PROC_00455A04()

00508BDD   E822CEF4FF             call    00455A04  <--改成 jmp 0054403B

00508BE2   5F                     pop     edi
00508BE3   5E                     pop     esi
00508BE4   5B                     pop     ebx
00508BE5   5D                     pop     ebp
00508BE6   C3                     ret


猜测与实践证明,在508BDC一处,eax保存的是主窗口的句柄,我们干脆在这里也动点手脚,
让他把主窗口句柄保存起来算了,就保存在504437处吧,那里处于用topo增加的空间。

用OllyDBG载入修改后的程序
先把508BDD一处改成jmp 0054403B,
然后到54403b处写入如下代码,保存主窗口的句柄:

00544037   0000             ADD BYTE PTR DS:[EAX],AL
00544039   0000             ADD BYTE PTR DS:[EAX],AL

;------------- 544037 ~ 544039 的空间是用来保存主窗口句柄的---------------

0054403B   A3 37405400      MOV DWORD PTR DS:[544037],EAX <--保存住窗口句柄
00544040   E8 BF19F1FF      CALL 00455A04                 <--执行508bdd处原来的代码
00544045   E9 984BFCFF      JMP UNPACKED1_.00508BE2        <--手脚做完,返回原处

现在可以写入代码显示新的对话筐了,嘿嘿。。。
以下是主窗体TfmMain的aHelpAboutExecute事件的代码:

005163E8   E8F720FFFF             call    005084E4 <--改成 jmp 00544053
005163ED   C3                     ret

把5163E8处改成jmp 00544053以后,来到54404a处,写入以下代码:

0054404a   <--此处写入 "TFMABOUT" 的ASCII码,是新插入的对话筐的资源名称

00544053   E8 8C44FCFF      CALL UNPACKED_.005084E4  <---执行5163e8处原有代码
00544058   50               PUSH EAX                <--保护EAX寄存器
00544059   6A 00            PUSH 0                  <---dwInitParam
0054405B   68 7C405400      PUSH UNPACKED_.0054407C  <---对话筐过程
00544060   FF35 37405400    PUSH DWORD PTR DS:[544037]  <--父窗口句柄
00544066   68 4A405400      PUSH UNPACKED_.0054404A <--对话筐资源名称"TFMABOUT"
0054406B   6A 00            PUSH 0
0054406D   FF15 10F25300    CALL DWORD PTR DS:[53F210] <--GetModuleHandleA
00544073   50               PUSH EAX                   <--进程实例句柄 hInstance
00544074   FF15 2D805A00    CALL DWORD PTR DS:[5A802D] <--DialogBoxParamA
0054407A   58               POP EAX                    <--恢复EAX值
0054407B   C3               RETN                       <--直接返回,不必jmp回原处了

紧接着写对话筐过程,直接写有点困难,我是用MASM32写了一个小程序,反汇编以后照抄的:

0054407C   55               PUSH EBP
0054407D   8BEC             MOV EBP,ESP
0054407F   8B45 0C          MOV EAX,DWORD PTR SS:[EBP+C]  <--把消息保存在 EAX
00544082   3D 11010000      CMP EAX,111                   <--WM_COMMAND
00544087   74 0B            JE SHORT UNPACKED_.00544094
00544089   83F8 10          CMP EAX,10                    <--WM_CLOSE
0054408C   74 06            JE SHORT UNPACKED_.00544094
0054408E   33C0             XOR EAX,EAX
00544090   C9               LEAVE
00544091   C2 1000          RETN 10

00544094   6A 00            PUSH 0
00544096   FF75 08          PUSH DWORD PTR SS:[EBP+8]
00544099   FF15 29805A00    CALL DWORD PTR DS:[5A8029] <--EndDialog
0054409F   33C0             XOR EAX,EAX
005440A1   C9               LEAVE
005440A2   C2 1000          RETN 10

----------------------------------------------------------------------------------------------
这是我写的对话筐过程的原代码:

DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

mov eax,uMsg

cmp eax,WM_COMMAND
jz quit
cmp eax,WM_CLOSE
jz quit

xor eax,eax
ret
quit:
invoke EndDialog, hWnd,NULL
xor eax,eax
ret

DlgProc endp
----------------------------------------------------------------------------------------------
别忘了保存修改结果。
试试看?点击help -> About,啊,成功了,显示的是我们自己做的About对话筐!!!

还有个恼人的地方:主窗口标题上有个可恨的"EXPIRED!"字样,在资源里找不到。
于是动用winHex寻找该字串,竟找不到!天啊,难道又要逼我用土办法。。。?
还是他把字母拆开了?绝望中,我在winHex里寻找"D!"字串,还真找到了!
在文件F565c处,有个"D!PRDIXE"字串,果然,他把"EXPIRED!"这个字串进行了变换(加密?),实在晕菜~
这下简单了,改成空格好了。

在工具栏上有个红红的"REGISTER"大字,很恶心,不过很好办,在RCDate资源里找的到,把大小改成0就好了。
大功告成,再修改资源,把NAG窗口的大小改成0,把插入的关于对话筐改的好看一点,哈哈,天衣无缝的说~~~~~~~~~~~~