• 标 题:IconEdit2 脱壳
  • 作 者:chilly200  
  • 时 间:2002/03/28 12:33pm
  • 链 接:http://bbs.pediy.com

IconEdit2 脱壳,Asprotect的脱壳高手们已经写过很过了,我菜鸟也凑个
热闹,多少给和我一样菜的同志们借鉴一下,也希望高手们多多指教。

IconEdit2(v2.3)的壳很有意思,用FI245查出是Asprotect1.3的,用FI249查
出是Asprotect1.21的,估计FI249可能会准确一点吧(可是我觉得FI249没有
FI245好用,所以很少用,只有245查不到的时候才用,^_^)
IconEdit2(v2.4)的壳是Asprotect1.22的,大家都差不多,就那v2.3开刀吧。

因为以前跟过1.2的壳,这次碰到个1.3的(也不知道真的假的),也跟一跟,
我是从头到尾一个劲的傻跟了一遍,没设过API断点(其实也不知具体该设什
么),最后终于到达OEP,感觉和1.2版的壳差别不大。具体跟踪细节我这里
就不说了,高手们有很多文章了。而且这个程序是Delphi写的,可以用快速
找OEP的方法快速脱壳。我来说说dump出来以后的事情。

从OEP处dump出iEditu.exe,由于Asprotect对import table做了破坏,所以
程序不能运行,必须修复IAT,ImportREC142是个好东东,可以很快很方便的
修复IAT,就用它来做。在Process下拉栏选择运行的进程(运行脱壳前的程序)
然后在OEP栏输入我们得到的OEP值(0x00491D80),注意不要加base,即491D80
接着就IAT Autosearch,提示说找到,跟着Get Imports,窗口里看到了很多
找到的引入函数,用Show Invalid看到一些未解析到的,在其上面点鼠标右键
选Trace Level1,哇,又解析出一大堆来,还剩10个左右,再用右键的Plugin
Tracer,嘿嘿,Congratulation! 全部解析成功。暗暗窃喜... 最后就是 Fix
Dump,当然最好选中Add new section。生成修复过的文件iEditu_.exe

满怀热情的双击运行,咦,什么反应都没有?考,是不是鼠标坏了,咚咚咚,
帮帮帮......还好没真把我的鼠标敲坏了。冷静下来开始分析,好像记得哪位
大侠说过“一运行就退出,一看就是CRC”。唉,看来这回是遇到棘手的了,
没办法,跟一跟了。入口如下:

018F:00491D80  PUSH     EBP          <-- OEP
018F:00491D81  MOV      EBP,ESP
018F:00491D83  ADD      ESP,BYTE -0C
018F:00491D86  PUSH     EBX
018F:00491D87  PUSH     ESI
018F:00491D88  PUSH     EDI
018F:00491D89  MOV      EAX,00491B30
018F:00491D8E  CALL     00406AD4
018F:00491D93  XOR      EAX,EAX
018F:00491D95  PUSH     EBP
018F:00491D96  PUSH     DWORD 00491E14
018F:00491D9B  PUSH     DWORD [FS:EAX]
018F:00491D9E  MOV      [FS:EAX],ESP
018F:00491DA1  CALL     0048A390
018F:00491DA6  MOV      EAX,[004938C4]
018F:00491DAB  MOV      EAX,[EAX]
018F:00491DAD  CALL     00448FC8
018F:00491DB2  CALL     0048A410
018F:00491DB7  MOV      EAX,[004938C4]
018F:00491DBC  MOV      EAX,[EAX]
018F:00491DBE  MOV      EDX,00491E30
018F:00491DC3  CALL     00448BCC
018F:00491DC8  CALL     0048A438       <-- F10带过就没了(1)
018F:00491DCD  MOV      EAX,[004938C4]
018F:00491DD2  MOV      EAX,[EAX]
018F:00491DD4  ADD      EAX,BYTE +40
018F:00491DD7  MOV      EDX,00491E44
018F:00491DDC  CALL     00403B8C
018F:00491DE1  CALL     0048A480        <-- F10带过也没了(2)
018F:00491DE6  MOV      ECX,[00493714]
018F:00491DEC  MOV      EAX,[004938C4]
018F:00491DF1  MOV      EAX,[EAX]
018F:00491DF3  MOV      EDX,[0048BA8C]
018F:00491DF9  CALL     00448FE0        <-- F10带过还是没了(3)
018F:00491DFE  MOV      EAX,[004938C4]
018F:00491E03  MOV      EAX,[EAX]
018F:00491E05  CALL     00449060        <-- 这里主程序开始运行
018F:00491E0A  XOR      EAX,EAX

F8进到第(1)个CALL看看,如下:

018F:0048A438  PUSH     EBP
018F:0048A439  MOV      EBP,ESP
018F:0048A43B  PUSH     ECX
018F:0048A43C  MOV      BYTE [EBP-01],00
018F:0048A440  MOV      EAX,[00493624]
018F:0048A445  CMP      BYTE [EAX],00
018F:0048A448  JNZ      0048A463
018F:0048A44A  PUSH     ESI
018F:0048A44B  MOV      ESI,[004944D8]     <--这里取基址400000
018F:0048A451  MOV      EAX,[ESI+3C]
018F:0048A454  MOV      EAX,[ESI+EAX+28]   <--EAX为OEP
018F:0048A458  CMP      AX,1000            <--OEP不为1000就退出
018F:0048A45C  JZ       0048A462           <--改这里强行跳走。
018F:0048A45E  OR       BYTE [EBP-01],01
018F:0048A462  POP      ESI
018F:0048A463  CMP      BYTE [EBP-01],00
018F:0048A467  JZ       0048A47C
018F:0048A469  XOR      ECX,ECX
018F:0048A46B  MOV      DL,01
018F:0048A46D  MOV      EAX,[00407B60]
018F:0048A472  CALL     0040BA4C
018F:0048A477  CALL     00403608
018F:0048A47C  POP      ECX
018F:0048A47D  POP      EBP
018F:0048A47E  RET    

再进到第(2)个CALL看看,如下:

018F:0048A480  PUSH     EBP
018F:0048A481  MOV      EBP,ESP
018F:0048A483  PUSH     ECX
018F:0048A484  PUSH     EBX
018F:0048A485  MOV      EAX,[00406C4A]
018F:0048A480  PUSH     EBP
018F:0048A481  MOV      EBP,ESP
018F:0048A483  PUSH     ECX
018F:0048A484  PUSH     EBX
018F:0048A485  MOV      EAX,[00406C4A]
018F:0048A48B  MOV      EBX,[EAX]
018F:0048A48D  PUSH     DWORD [EBX]   \ 这个push和pop没什么意义,
018F:0048A48F  MOV      [EBP-04],EBX  | 我估计也是用来检查脱壳的,
018F:0048A492  POP      DWORD [EBX]   / 这时EBX=BFFxxxxx,异常。
018F:0048A494  MOV      EAX,[EBP-04]    把Push和Pop都NOP掉。
018F:0048A497  POP      EBX
018F:0048A498  POP      ECX
018F:0048A499  POP      EBP
018F:0048A49A  RET    

改了这两个地方后,觉得该行了吧,没想到到(3)的CALL又死悄悄了。真让人
上火,它到底要设多少陷阱呀!这个CALL可是个很大的呀,用原版带过也要
等一阵才过来的,进去以后简直像进了一个无底洞,半天出不来,而且也看
不到什么可疑的类似CRC的校验地方。这可真是大海捞针呀!用脱壳后的程序
跟进去,一不留神就退出了,发现都是产生异常而退出的,基本排除有CRC校
验的保护。

一筹莫展之际,dede反编译,发现这个CALL似乎是来建立资源的,难道是资源
在脱壳中有什么不测?然后用一些资源编译器试着编辑一下,果然有很多异常
现象。初步认为是ASPR对资源做了特殊压缩,这样可麻烦了。用procdump查知
.rsrc段RVA是4A4000,size是7D000,载入原版在OEP中断,看看资源段,一切
正常,干脆把它都dump出来,随即dump出个rsrc.bin准备替换脱壳版的.rsrc段
可是一看原来一模一样,那还换个什么劲呀。再载入脱壳后的程序停在入口,
看资源段,咦?发现只有4A4000到4A6000有东西,后面的都是????,难道是段
的size有问题,翻来覆去的查,可是什么问题也看不出来。现在我还不知道那
些????????是怎么回事,有知道的请教教我。

穷途末路了,还是用TRW跟吧,硬着头皮一层层的跟,设了无数断点,记录了我
无数辛苦的脚步,最后总算把错误定位在了一条语句上。对比原版,发现好像
就是和上面的那些????????有关,可是那些????????我又搞不定,难道就这么
宣告失败了?

最后最后,我又想到是不是我当初dump出来的程序就有问题呀(是用Procdump),
我再用TRW直接pedump一次,然后一样用ImportREC修复IAT,咦,这次居然有
10个函数没有解析出来,真是见了鬼了。没办法,手动来吧,还好没有太难的,
全部找出来,发现有两个是空函数,想在import list中删掉,可是一不小心
竟然连整个Trunk都给删掉了,晕倒(是不是ImportREC没有删函数的功能呀?)
只好重新再来抓,哇,这回居然又全部解析成功了,真不知怎么搞的。成功就
成功吧,刚才也不能白忙活呀,对比对比吧,发现和我手动找的基本一致,只
有一个函数不一样,还有就是那两个空函数都被FreeResource代替了。
“FreeResource”,看到这个字眼,我的眼睛突然一亮,我上面定位的出错点
反推回去好像就是在一个CALL FreeResource 附近呀,还好断点还在,马上来
到这里:

018F:00413305  PUSH     EAX
018F:00413306  PUSH     ESI
018F:00413307  CALL     `KERNEL32!FindResourceA`
018F:0041330C  MOV      EDI,EAX
018F:0041330E  MOV      [EBX+10],EDI
018F:00413311  TEST     EDI,EDI
018F:00413313  JNZ      0041331C
018F:00413315  PUSH     EBP
018F:00413316  CALL     00413280
018F:0041331B  POP      ECX
018F:0041331C  MOV      EAX,[EBX+10]
018F:0041331F  PUSH     EAX
018F:00413320  PUSH     ESI
018F:00413321  CALL     `KERNEL32!LoadResource`
018F:00413326  MOV      EDI,EAX
018F:00413328  MOV      [EBX+14],EDI
018F:0041332B  TEST     EDI,EDI
018F:0041332D  JNZ      00413336
018F:0041332F  PUSH     EBP
018F:00413330  CALL     00413280
018F:00413335  POP      ECX
018F:00413336  MOV      EAX,[EBX+10]
018F:00413339  PUSH     EAX
018F:0041333A  PUSH     ESI
018F:0041333B  CALL     `KERNEL32!SizeofResource`
018F:00413340  PUSH     EAX
018F:00413341  MOV      EAX,[EBX+14]       <-- EAX=0x4AF088(就是????)
018F:00413344  PUSH     EAX                    这个值很关键,后面有用
018F:00413345  CALL     `KERNEL32!FreeResource`
018F:0041334A  MOV      EDX,EAX            <-- 到这EAX=0,导致后面的
018F:0041334C  MOV      EAX,EBX                异常错误产生。
018F:0041334E  POP      ECX
018F:0041334F  CALL     00413058
018F:00413354  POP      EDI
018F:00413355  POP      ESI
018F:00413356  POP      EBX
018F:00413357  POP      ECX
018F:00413358  POP      EBP
018F:00413359  RET      04

看来这里来一个FreeResource很不合适,既然原来是空函数,那就NOP掉它吧,
这样保持EAX的值不变,至少后面的那个出错点不会再有问题了,改过后F5跑,
有好多次都会来到这里,全部跳过去后,程序画面总算出来了,啊,老天有眼
可算是搞定了。既然有两个空函数,找到另一个,也NOP掉,另外一个不一样
的函数好像没什么影响,不管了吧。改完以后终于可以正常运行了,^_^

总结一下,这次脱壳成功,运气成分不可少,要不是不留神将Trunk删了,可能
还发现不了ImportREC自动解析的错误,那样还不知道什么时候才能搞定这个壳
呢。由此看出,ImportREC自动解析的引入函数也不能保证都正确,最好在使用
TraceLavel1后还剩余的函数上留意一下,如有什么问题可以在这里找找原因,
嘿嘿,这是我得出的一点经验,与大家分享。有不对的地方请帮我指出来。

最后说一下,IconEdit2(v2.4)脱壳后,只需要修正IAT,并改一下(1)(2)两个
陷阱,即可正常运行,没那么麻烦了。

  • 标 题:补充
  • 作 者:chilly200  
  • 时 间:2002/04/01 10:59am 
  • 链 接:http://bbs.pediy.com

上次脱了IconEdit2 v2.3的壳,发现有个重大问题,就是无法存盘。

v2.3的pj倒不难,很容易抓出注册码来,用
Name: chilly200
Code: 8kHXkiJ8qGfk9ig7hwy7bhMT
注册成功后,存盘时不会弹出什么对话框,好像一切正常似的,可实际上却没有
存到,在硬盘上找不到存盘的文件。而原版则可以。

再来找BUG,载入原版,存盘的时候下bpx createfilea,拦住以后F12几次返回
程序空间,如下:

018F:0048D780  CALL     00403B8C
018F:0048D785  CALL     00491098
018F:0048D78A  TEST     AL,AL
018F:0048D78C  JNZ      NEAR 0048D9A2
018F:0048D792  PUSH     EBP
018F:0048D793  CALL     0048D1D0      <-- 这个CALL是关键。
018F:0048D798  POP      ECX           <-- 停在这
018F:0048D799  JMP      0048D9A2

第二次,用F8进入上面的CALL,一直F10跟到这里:

018F:0048D1F5  PUSH     DWORD 0048D5DB
018F:0048D1FA  PUSH     DWORD [FS:EDX]
018F:0048D1FD  MOV      [FS:EDX],ESP
018F:0048D200  CALL     0048A2EC       <-- 这个CALL关键,要进去
018F:0048D205  JMP      0048D5CC
018F:0048D20A  SUB      [EDI+9C849801],EBP
018F:0048D210  MOV      CH,E9
018F:0048D212  JS       0048D1F0
018F:0048D214  STOSB  
018F:0048D215  DB       82
018F:0048D216  PUSH     DWORD 1D8D0E8D
018F:0048D21B  MOV      ESP,57A1446B
018F:0048D220  SHL      DWORD [ESI+9B724265],CL
018F:0048D226  MOV      DH,1D
018F:0048D228  OUTSB  

上面这个CALL里面肯定有SMC,因为带过后代码变了:

018F:0048D1F5  PUSH     DWORD 0048D5DB
018F:0048D1FA  PUSH     DWORD [FS:EDX]
018F:0048D1FD  MOV      [FS:EDX],ESP
018F:0048D200  CALL     0048A2EC
018F:0048D205  JMP      0048D20B    <-- 变了!上面的CALL好厉害呀
018F:0048D20A  NOP                  <-- 这里原本是花指令,呵呵
018F:0048D20B  MOV      EAX,[EBP+08]
018F:0048D20E  MOV      EAX,[EAX-04]
018F:0048D211  MOV      EAX,[EAX+02E0]
018F:0048D217  MOV      AX,[EAX+0246]
018F:0048D21E  SUB      AX,BYTE +10
018F:0048D222  JZ       NEAR 0048D2C1
018F:0048D228  SUB      AX,BYTE +10
018F:0048D22C  JNZ      NEAR 0048D35C
018F:0048D232  LEA      EAX,[EBP+FFFFF342]
018F:0048D238  MOV      ECX,FF
018F:0048D23D  MOV      EDX,0CBE
018F:0048D242  CALL     00402B70

因此跟进这个关键CALL去追踪,一步步耐心的跟,最终发现,这段代码用到了原
来壳中的一些代码和数据,一共有三段(当然是一段一段来的,不是一下子全找
到的,呵呵),全部dump出来,分别是:
 file      start      end     lenth
add1.bin:  0530000   0535000   5000
add2.bin:  11B1000   11C8000   17000
add3.bin:  11D0000   11D9000   9000
注意,这三段不是在OEP处dump的,而要在
cs: 00491DF9 CALL 00448FE0 的CALL过后再dump(因为这里面好像也有些SMC)

不巧的是importREC加的段.mackt的RAV和add1的有冲突,将.mackt的RAV改为
140000,当然也要改文件中相关的IT和IAT的RAV值(用查找替换就行了),最后
再改一下180H处的00001300为00001400即可。
然后把上面dump出的三个文件内容添加到原脱壳后的文件中,用PEditor1.7分别
添加三个段.add1;.add2;.add3,并改好相应的数值。
添第二个段的时候,PEditor说PE头没有足够的空间添加段了,不知道是怎么回
事,诚请高手指点一下!我看到有几个段的size是0,估计删掉应该可以吧,可
是PEditor中却不能删,用Procdump的PE editor删掉了。然后就可以继续添加段
了。最后用rebuilder只选中make PE Header win nt/2000 compatible进行PE头
文件重建,就OK了。

不幸,还是不能成功存盘。继续跟踪知道,dump出来的数据在使用的时候已经改
变了,是有什么程序又用了SMC把我们千辛万苦抓出来的数据和代码给改了,真是
可恶呀!我也尝试了把这几个段改成只读,没成功。只好继续耐心的追踪,终于
找到了这段SMC的代码在这里:

018F:0048A1EC  MOV      BYTE [004935DC],01 <-- 这里必须置1,存盘判断
018F:0048A1F3  PUSH     EDX
018F:0048A1F4  PUSH     EAX
018F:0048A1F5  CALL     NEAR [004935E0]    <-- CALL壳里的一段SMC
018F:0048A1FB  RET    

就是这里改变了我们dump出来的东西,把从push开始的全NOP掉吧。

存盘试一下,大功告成了!