首页
社区
课程
招聘
[原创]菜鸟啄硬壳(之三)――借壳自动修复ITA表和IID表
发表于: 2007-1-26 12:43 15107

[原创]菜鸟啄硬壳(之三)――借壳自动修复ITA表和IID表

wulje 活跃值
14
2007-1-26 12:43
15107

[摘要]   
        1.摸拟壳的装载过程,自动修复ITA表和IID表,脱壳变得如此轻松愉快。
        2.在OD中修改PE头,脱壳后程序可以立即运行。
[正文]
        现在市面上有很多脱壳工具,但大多功能有限,只能脱一些非常标准的已知壳。若壳稍加变化它们就无能为力了,正象杀毒软件一样,只能杀已知的病毒。壳是可以千变万化的,万能脱壳工具好象是没有的?有没有十分容易的脱壳方法呢?答案是看你有没有一个很好的脱壳思路。下面以脱Spy.exe 的壳为例谈点体会。
        我很喜欢窗口侦测的Spy.exe软件(见附件),搞软件破解的没有人不知道它的。它用UPX加壳,我说过LoadLibrary和GetProcAddress函数是壳的软肋,这正好来验证一下这个结论是否管用?
        
        1.对Spy.exe壳的认识
        用壳侦查软件测试Spy.exe,都报告说它是UPX壳。但用UPX脱壳工具(一大堆)上阵,要么没反映,要么windows要发“错误报告”,UpxShell更搞笑,说它是一个陈旧的版本不能解壳?没有一个能生成(那怕是不能运行的)脱壳文件,只有ProcDump稍好点,脱出了一个不能运行的Unpacked.exe。Spy.exe的壳肯定是一个老版本了,老版本尚且如此,新版本谈何容易?
        我在《菜鸟啄硬壳(之一)》中有一句话说过头了。我说:“无论是windows或者自己调用GetProcAddress装载API函数,都必须要有IAT表”。这句话错了(资历太浅)!凡事都有例外!这个Spy.exe壳装载API函数就没有现存的IAT表!抢先抓取IAT表的思路受阻了。
        
        2.使用对付壳的杀手锏――拦截“GetProcAddress”函数
        由于有了前面乱说的教训,我不敢说拦截LoadLibrary和GetProcAddress函数一定可以做到什么。拦截的目的是分析代码是如何装载API函数的?从分析中尽量做到下面四点。这四点是脱壳思路的源泉!
        1)获取API函数名(脱壳后)地址;
        2)抓取IAT表或获取IAT表地址(如果没有IAT表,那么装载第1个API的地址就是IAT表地址);
        3)抓取IID表或获取IID表的地址(可能在LoadLibrary访问的地址附近,但不一定有IID表);
        4)获取OEP地址。
        如果你能做到以上4点(无论你用什么方法),那么让程序自动修复ITA表和IID表且脱壳后可以立即运行的程序就离你不远了。下面说具体的对Spy.exe脱壳的操作方法:
        
        (1)用OD打开Spy,用右键打开弹出式菜单找“搜索―全部模块中的名称”找到LoadLibrary,记下地址:4B7154。在OD命令栏键入“HR 4B7154”回车,运行。(按ctrl+F9)来到下面代码:
004BA123      8B07................mov eax,dword ptr ds:[edi]
004BA125      09C0................or eax,eax
004BA127      743D................je short 004BA166                       
004BA129      8B5F04..............mov ebx,dword ptr ds:[edi+4]
004BA12C      8D8430 00600B00.....lea eax,dword ptr ds:[eax+esi+B6000]  ;获取了库函数名
004BA133      01F3................add ebx,esi                           ;取得了IAT表地址
004BA135      50..................push eax
004BA136      83C708..............add edi,8                             ;指向了库中第1个API
004BA139      FF96 54610B00.......call dword ptr ds:[esi+B6154]         ;调用LoadLibrary
004BA13F      92..................xchg eax,edx
004BA140      8A07................mov al,byte ptr ds:[edi]              ;获取API函数名
004BA142      47..................inc edi
004BA143      08C0................or al,al
004BA145      74DC................je short 004BA123                     ;是库函数返回,是API往下                    
004BA147      52..................push edx
004BA148      89F9................mov ecx,edi
004BA14A      7907................jns short 004BA153                    ;必跳,14C―152是花指令
004BA14C      0FB707..............movzx eax,word ptr ds:[edi]
004BA14F      47..................inc edi
004BA150      50..................push eax
004BA151      47..................inc edi
004BA152      B9 5748F2AE.........mov ecx,AEF24857                      ;花指令,153开始才是真指令
004BA157      52..................push edx
004BA158      FF96 58610B00.......call dword ptr ds:[esi+B6158]         ;调用GetProcAddress
004BA15E      5A..................pop edx
004BA15F      8903................mov dword ptr ds:[ebx],eax            ;装载IAT表
004BA161      83C304..............add ebx,4
004BA164      EBDA................jmp short 004BA140                     
         单步走几下,到4BA148,状态栏显示一个函数名,地址是4B1009。切换内存到4B1000一下就看到一连串的函数名,显然API函数名称表地址在4B1000。继续走,在4BA158调用GetProcAddress,eax中返回了API地址,在4BA15F将把该值存入[ebx],状态栏显示ebx=480140,内存切换到这,发现是全空白,没有装载前的IAT表,但该地址就是装载后的IAT表的位置。4BA139是调用LoadLibrary,当某个.dll中的API装完后,自然就会来访问它。当我们来到4BA12C时,显示user32.dll,地址是4B719A,内存切换过去就发现了一堆.dll函数,原来Spy.exe将库函数和普通API是分开放置的。稍往回走就发现了IID表,地址是4B7000。最后我们发现,程序开始的入口处就是一个pushad,显然出口就在popad(满足pushad、popad成对规律)。来到popad处立即发现jmp 47A6C0,该地址为OEP无疑。这样,前面四个问题就圆满解决了。
        
        (2)修复或创建IAT表,IID表的思路:
        ITA表的实质就是其值是指向API函数名字串地址的,当我们知道函数名表开始的地址,并在其中搜索到某函数时将其所在的“地址”依次填入IAT表,则该表就修复或创建好了。(IID表的实质就自己去看有关书籍吧)弄懂后思路就清晰了。现在关键是搞清楚函数名表中函数排列的规律:我们发现每个函数名是以00 01开始的,每个库函数是以00 00开始的。有了这个就够了(其实在每个00 00后面的数字中暗藏着装载API的地址)。这样就有下面的方法了:
        将4BA198的jmp 47A6C0(出口),改为jmp 4B6B30(跳到空白处),OD中键入下列代码:
004B6B30    BD 00004000...........mov ebp,400000                       
004B6B35    BF 3C010800...........mov edi,8013C                 ;80140是IAT表地址
004B6B3A    BE FE0F0B00...........mov esi,0B0FFE                ;B1000是函数名表
004B6B3F    BB 24704B00...........mov ebx,4B7024                ;4B7014是IID表地址
004B6B44    8A042E................mov al,byte ptr ds:[esi+ebp]  ;搜索函数名
004B6B47    46....................inc esi
004B6B48    81FE 90280B00.........cmp esi,0B2890
004B6B4E    7202..................jb short 004B6B52                     
004B6B50    C3....................retn                          ;此处设断
004B6B51    90....................nop
004B6B52    3C00..................cmp al,0
004B6B54    75EE..................jnz short 004B6B44                     
004B6B56    8A042E................mov al,byte ptr ds:[esi+ebp]
004B6B59    3C00..................cmp al,0
004B6B5B    740A..................je short 004B6B67                       
004B6B5D    4E....................dec esi
004B6B5E    89342F................mov dword ptr ds:[edi+ebp],esi   ;填写IAT表
004B6B61    83C704................add edi,4
004B6B64    46....................inc esi
004B6B65    EBDD..................jmp short 004B6B44                     
004B6B67    83C608................add esi,8
004B6B6A    83C704................add edi,4
004B6B6D    893B..................mov dword ptr ds:[ebx],edi       ;填写IID表
004B6B6F    83C314................add ebx,14
004B6B72    EBD0..................jmp short 004B6B44                     
        运行在4B6B50断下后,因为PE头在运行中是禁止写入的,输入表,导入函数表的“登记”只能手工写入了:OD中内存切换到400000,修改400180:为14700B00 40010000;修改4001D8:为40010800 64060000。最后把第2块节表名由:UPX1改名为:.rsrc(这样eXeScope才可以编辑资源)。一切就绪后,dump当前进程,并将“重建输入表”选项去掉,入口依然是7A6C0。怎么样,脱壳出来的程序是不是运行得很正常?
        
        (3)摸拟壳写IAT表的过程,直接写入函数名地址:
        照你这么说,何苦先让壳把IAT表写成了 API的调用地址,再自己来改成函数名地址,不如让壳直接写函数名地址?还省得我小心地去找函数名字串排列的规律,自编程序。这话有理,但可能难度较大,因为壳写IAT表的空间只有41h字节,且壳也没有写IID表。小心地设计程序,调整原可用代码的位置(因为空间太小了)也能实现,在OD中汇编如下代码(对照那些是原有的,那些是添加的):
004BA123      BA 24704B00.........mov edx,4B7024                ;B7014是IID表地址
004BA128      BD 00004000.........mov ebp,400000               
004BA12D      8B07................mov eax,dword ptr ds:[edi]    ;edi初值指向函数名表
004BA12F      09C0................or eax,eax
004BA131      7433................je short 004BA166              
004BA133      8B5F04..............mov ebx,dword ptr ds:[edi+4]
004BA136      03DE................add ebx,esi
004BA138      33DD................xor ebx,ebp
004BA13A      891A................mov dword ptr ds:[edx],ebx    ;填写IID表
004BA13C      33DD................xor ebx,ebp
004BA13E      83C214..............add edx,14
004BA141      83C708..............add edi,8
004BA144      8A07................mov al,byte ptr ds:[edi]      ;查找函数名
004BA146      47..................inc edi
004BA147      08C0................or al,al
004BA149      74E2................je short 004BA12D                     
004BA14B      8BCF................mov ecx,edi
004BA14D      57..................push edi
004BA14E      48..................dec eax
004BA14F      F2:AE...............repne scas byte ptr es:[edi]  ;指向下个函数名
004BA151      58..................pop eax
004BA152      48..................dec eax
004BA153      48..................dec eax
004BA154      33C5................xor eax,ebp
004BA156      8903................mov dword ptr ds:[ebx],eax    ;填写IAT表
004BA158      83C304..............add ebx,4
004BA15B      EBE7................jmp short 004BA144                     
004BA15D      90..................nop
        在47A6C0设断(壳已经修复好了两个表了),断下后最后的操作同(2),得到的也是一个可以立即运行的程序。
        
        后记:
        将脱壳后的程序用eXeScope打开,发现它是用Delhpi写成的,怪说不得装载了6000多个API函数(绝大多数是不参与运行的,也是垃圾代码,但不装载还不行!)若是用手工来修改IAT表,这几乎是不可能完成的!
        因为PE头的禁止写入,使脱壳还不能实现全自动化。若另写一程序来打开它,并摸拟它的解压并修改PE头,这不就是脱壳机吗?我没有学习过任何脱壳机的写法,不知是不是这个原理,即便如此,写个脱壳机后,它的适应范围会有多宽多久呢?
        谢谢你认真读完!请菜鸟共勉,请高手指教。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (32)
雪    币: 47147
活跃值: (20460)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
最初由 wulje 发布
不如让壳直接写函数名地址?还省得我小心地去找函数名字串排列的规律,自编程序。


可以用ImportREC重建输入表,只要一张正确的IAT表,剩下的工作ImportREC帮你完成。

wulje学习方法不错,可以加强对PE结构的了解,另附一张输入表结构图,可能更容易理解此文:

2007-1-26 12:58
0
雪    币: 898
活跃值: (4039)
能力值: ( LV9,RANK:3410 )
在线值:
发帖
回帖
粉丝
3
支持wulje写个UPX的完美脱壳机
2007-1-26 13:11
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
历害!以我等菜鸟的水平都不好意思顶贴子了~~~~
以后多多发表!谢谢了。
2007-1-26 13:21
0
雪    币: 305
活跃值: (10)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
5
谢谢坛主、版主的器重,支持。
我其实也是个菜鸟,甚至有点不务正业。四楼此话差矣,学习软件思想是会大步前进的!
2007-1-26 13:36
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
莫非阁下是名震江湖,神来杀神,鬼来杀鬼,逢山开路,遇水搭桥,千里独行,前无古人,后无来者,独一无二,万人敬仰的 中原一点红?
2007-1-26 14:59
0
雪    币: 304
活跃值: (82)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
7
学习,
btw:我只知道IAT表,谁能告诉我楼主说的ITA和IID是啥子?
2007-1-26 15:14
0
雪    币: 146
活跃值: (33)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
8
这些文章正是我们菜菜所需要的
2007-1-26 17:22
0
雪    币: 305
活跃值: (10)
能力值: ( LV9,RANK:570 )
在线值:
发帖
回帖
粉丝
9
不好意思,牙齿有时还要咬住舌头。我不过是才长了几皮还未干透羽毛的菜鸟。
2007-1-26 18:13
0
雪    币: 304
活跃值: (82)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
10
最初由 wulje 发布
不好意思,牙齿有时还要咬住舌头。我不过是才长了几皮还未干透羽毛的菜鸟。

我更菜。
我没有恶意,诚心请教。
2007-1-26 18:59
0
雪    币: 211
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
你写的方法是很好。

但我下载后,用PEID查壳是:UPX 0.71 - 0.72 -> Markus & Laszlo壳,OEP是0047A6C0   用OD载入spy.exe,OD选项/调试设置/事件设为“主模块入口点”
重新载入,停在:004BA000 > $  60            PUSHAD
F8一次,ESP为:0012FFA4  在命令行打入hr 0012FFA4后回车(设硬件断点),运行一次,来到:
004BA198   .- E9 2305FCFF   JMP Spy.0047A6C0(跳到OEP处)
把硬件断点删掉,F8一次,就到:
0047A6C0    55              PUSH EBP  (真正的OEP地址)
这时就可以Dump了,脱后直接就可以运行。不知你怎么要用这么麻烦的事来脱壳。
2007-1-27 15:16
0
雪    币: 707
活跃值: (1301)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
12
最初由 laohai 发布
你写的方法是很好。

但我下载后,用PEID查壳是:UPX 0.71 - 0.72 -> Markus & Laszlo壳,OEP是0047A6C0 用OD载入spy.exe,OD选项/调试设置/事件设为“主模块入口点”
重新载入,停在:004BA000 > $ 60 PUSHAD
F8一次,ESP为:0012FFA4 在命令行打入hr 0012FFA4后回车(设硬件断点),运行一次,来到:
........


人家是分析,
2007-1-27 17:14
0
雪    币: 191
活跃值: (345)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
13
最初由 laohai 发布
你写的方法是很好。

但我下载后,用PEID查壳是:UPX 0.71 - 0.72 -> Markus & Laszlo壳,OEP是0047A6C0 用OD载入spy.exe,OD选项/调试设置/事件设为“主模块入口点”
重新载入,停在:004BA000 > $ 60 PUSHAD
F8一次,ESP为:0012FFA4 在命令行打入hr 0012FFA4后回车(设硬件断点),运行一次,来到:
........


是压缩壳,只能建议LZ把标题改掉了,有误导嫌疑
2007-1-27 18:01
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
14
我也需要静态un upx:)

支持
2007-1-27 22:20
0
雪    币: 451
活跃值: (78)
能力值: ( LV12,RANK:470 )
在线值:
发帖
回帖
粉丝
15
支持
静态分析文章太难得了。。。不支持不行
2007-1-27 22:28
0
雪    币: 304
活跃值: (82)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
16
期待upx-static-unapcker,
2007-1-27 22:30
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
17
顺便问一下,代码里面那些..........是怎么搞得看起来很爽
2007-1-27 23:10
0
雪    币: 91
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
顶一下.
学习中...
2007-1-29 08:37
0
雪    币: 304
活跃值: (82)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
19
想仿照楼主的思路做个UPX的脱壳机
发现做出来的东西通用性不强,最后发现是有个地方弄错乐:
004BA14A      7907................jns short 004BA153                    ;必跳,14C―152是花指令
004BA14C      0FB707..............movzx eax,word ptr ds:[edi]
004BA14F      47..................inc edi
004BA150      50..................push eax
004BA151      47..................inc edi
004BA152      B9 5748F2AE.........mov ecx,AEF24857                      ;花指令,153开始才是真指令

14c-152那段不是花指令,当导入是by 序号的时候就会走这个分支,所以不能把它当作花指令(由于这个壳导入都是 by name,所以不走这个分支)
2007-1-30 10:39
0
雪    币: 225
活跃值: (142)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
20
LZ写的真是不错。。。

以前脱UPX都是,用OD载入,ctrl-f找popad,下断点,一下F9,两下F8,DUMP,搞定。从来没想过这么多。。。。
2007-2-4 23:29
0
雪    币: 2871
活跃值: (2340)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
学习一下!!!!
2007-2-5 11:33
0
雪    币: 264
活跃值: (30)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
22
嘿嘿,把壳解压IAT讲具体点!!!
004BA122   .  5F            pop     edi                          ;  edi指向函数名区块
004BA123   >  8B07          mov     eax, dword ptr [edi]         ;  共8个字节,前四个字节是库名的偏移,后四个字节为IAT表的偏移
004BA125   .  09C0          or      eax, eax
004BA127   .  74 3D         je      short 004BA166               ;  偏移为0,表示结束,调处加载IAT表
004BA129   .  8B5F 04       mov     ebx, dword ptr [edi+4]       ;  读后四个字节
004BA12C   .  8D8430 00600B>lea     eax, dword ptr [eax+esi+B600>;  esi为00401000加上B6000再加上偏移,得到函数名字符串地址
004BA133   .  01F3          add     ebx, esi                     ;  ebx为后四个字节的偏移,加上esi,即指向IAT表中的偏移!!!
004BA135   .  50            push    eax                          ;  库名入栈
004BA136   .  83C7 08       add     edi, 8                       ;  edi加8(跳过那8个字节)指向下一个地址
004BA139   .  FF96 54610B00 call    dword ptr [esi+B6154]        ;  调用LoadLibrary函数
004BA13F   .  92            xchg    eax, edx                     ;  将句柄保存入edx
004BA140   >  8A07          mov     al, byte ptr [edi]           ;  读取1个字节
004BA142   .  47            inc     edi                          ;  指向下一个字节
004BA143   .  08C0          or      al, al                       ;  判断是否为函数,如果为函数值为1,0就是库名
004BA145   .^ 74 DC         je      short 004BA123               ;  为0(库名)跳上去,
004BA147   .  52            push    edx                          ;  kernel32.7C800000
004BA148   .  89F9          mov     ecx, edi
004BA14A   .  79 07         jns     short 004BA153
004BA14C   .  0FB707        movzx   eax, word ptr [edi]
004BA14F   .  47            inc     edi
004BA150   .  50            push    eax
004BA151   .  47            inc     edi
004BA152      B9            db      B9
004BA153   .  57            push    edi
004BA154   .  48            dec     eax
004BA155   .  F2:AE         repne   scas byte ptr es:[edi]       ;  将edi指向下一个函数名
004BA157   .  52            push    edx
004BA158   .  FF96 58610B00 call    dword ptr [esi+B6158]
004BA15E   .  5A            pop     edx
004BA15F   .  8903          mov     dword ptr [ebx], eax
004BA161   .  83C3 04       add     ebx, 4
004BA164   .^ EB DA         jmp     short 004BA140

楼主方法不错值得敬仰,呵呵,基本上脱离了工具,全手工搞定!!!

上传的附件:
2007-2-5 13:03
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
我是菜鸟 看不懂
2007-2-8 23:37
0
雪    币: 400
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
没太看懂,俺慢慢学吧
2007-2-9 22:16
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
谢谢,楼主了,楼主的文章果然很好
2007-2-9 23:29
0
游客
登录 | 注册 方可回帖
返回
//