找回密码
 注册
搜索
热搜: 回贴

脱壳技术,Armadillo v3.x脱壳完全篇――下篇 完成引入表、修复 IAT,Armadillo

2010-1-30 18:18| 发布者: admin| 查看: 123| 评论: 0|原作者: 小寳寳


脱壳技术,Armadillo v3.x脱壳完全篇――下篇 完成引入表、修复 IAT,Armadillo
2008年06月23日 星期一 下午 03:37
这是一篇脱Armadillo加壳软件的个人的经历过程,本人特别声明:这个过程是参照了Bighead[DFCG][YCG]翻译的Ricardo Narvaja的“Getright 5 脱壳和重建”文章操作的,可以说是个照葫芦画瓢做的。感谢Bighead[DFCG][YCG]和相关的翻译者,感谢Ricardo Narvaja的文章。

这次拿来练习的软件是FCG的peterdocter老大提供的软件,下载地址:http://peter.88vip.com/armadillo3.rar

工具:

Ollydbg v1.09d中文版

PuPe 2002版 感谢yesky1兄提供

LordPE Deluxe-1.4 by yoda

Import Reconstructor 16f

准备好笔和纸(不能少喔!),一杯茶和好是心情^_^.让我们开始一个艰苦和漫长的脱壳过程吧(因为我菜!)。Armadillo是当今猛壳之一啦。其CopyMem-II Debug-Blocker的加壳方式是非常强劲的,好在有牛人在前面带路,虽艰难倒也不迷失方向。

在上篇中完成了查找OEP和代码修复的工作,但是用LordPE dump下的程序不能运行。这是因为Armadillo的壳破坏了引入表,下面我们将要设法查找 magic 跳转,消除破坏引入表的代码。

开始吧!~

用Ollydbg加载dump下来的程序dump.exe用F7运行他,在下面的代码处就会出错了,因为程序需要的函数地址指向了一个不正确的地方:

0040128C - FF25 D0804200 JMP NEAR DWORD PTR DS:[4280D0]

DS:[4280D0] 的内存地址指向了00BF919B,这个地址在程序的代码外,所以运行到这里后就会出现异常问题。原来的程序在这里肯定指向一个有效的地址才能运行下去。现在来到OD的转存窗口中。用Ctrl G命令输入4280D0到窗口中来到:

004280D0 9B 91 BF 00 A5 5B F3 77 洃? 体 //看这里

004280D8 9F 9C BF 00 9C 76 BF 00 煖?渧?

004280E0 CC 76 BF 00 B2 E3 E2 77 蘶?层鈝

004280E8 5F 8E F5 77 66 74 BF 00 _庻wft?

004280F0 8B 49 E3 77 D7 E7 E1 77 娅鉾诅醱

这是程序的引入表的一部分,来看看他开始于什么地方,向上找到这里:

004280A0 B7 8B F3 77 C6 20 F3 77 穻体?体

004280A8 00 20 F3 77 03 2D E1 77 . 体 -醱

004280B0 66 63 E1 77 49 25 E1 77 fc醱I%醱

因为再向上没有了这个结构的地方,所以可以认定引入表的开始地址是004280A0,再向下看看什么地方是表的结束地址:

004284D0 AF 9B BC 70 00 00 00 00 瘘紁....

004284D8 6B 65 72 6E 65 6C 33 32 kernel32

004284E0 2E 64 6C 6C 00 00 00 00 .dll....

004284E8 00 00 00 00 00 00 00 00 ........

后面再也没有了。所以可以认定表的结束地址是004284E4 ,看看表的长度=004284E4-004280A0=444用纸记下这些信息。以后用ImportREC修复IAT时需要手工填入这些信息。

Ricardo Narvaja的原文有这样的一段话:

《这里说明一点:我们已经知道程序的父进程和他的子进程是相同文件,但要用不同的句柄装入两次,所以他们变成两个不同的进程。由其中一个进程掌管着父进程的 OEP从这里开始运行。

我们知道当它被转储时,另一进程是掌管着子进程的 OEP,但我也要告诉你,并不是所有子进程都以相同父进程的入口点来开始运行的。顺便一提:我是设法告诉你,它不是从父进程那复制 IAT 到它的子进程,却是从子进程那解出自己的 IAT。这应该是一大麻烦,因为我们不能在他从父进程脱离前用 OllyDbg 进入子进程。》

现在的问题是怎么用OD进入子进程中,让我跟着Ricardo Narvaja向前走。

现在用OD加载没有脱壳的程序armadillo3.exe按老规矩记住要将 IsDebuggerPresent 的值设为零,清除所有以前的 BPX、BP WaitForDebugEvent 然后单击运行。等armadillo的注册提示框出来后,打开PUPE选择进程中2个armadillo3.exe中的上面一个(因为他是子进程,是我们需要的)。右击进入parcheando窗口选择4字节,在地址里填4280D0 (这是第一个不正确的地址)Buscar一下信息窗口中就出现了4个字节的值00 00 00 00 然后不断的在OD中运行、在PUPE中检查字节窗口中的值,直到字节窗口中的值变成 9B 91 BF 00 ,这个值就是程序写入到内存地址004280D0处的。那么反过来想想,如果能在写入这个值前进入到子进程中并跟踪什么地方的代码写入了这个值就能跟踪到那个magic的跳转。那么出现9B 91 BF 00这个值前出现过CA 85 02 00 这个值。重新做上面的一切直到字节窗口中出现CA 85 02 00这个值时停下。现在是进入子进程的时候了:

在OD中打开模块窗口选择一个API生成死循环,然后进入到子进程中。我模仿Ricardo Narvaja选择了KERNEL32.dll 中的GetProcAddress这个API。在名称窗口中查找这个函数的地址是77E12DFB(这个地址可能与你的不同)。又是用到PUPE这个工具的时候了,在字节数中选择2个字节。地址中填77E12DFB再"Buscar"一下,就会在字节信息窗口中看到 55 8B 这个值,修改为EB FE并按 "Parchear",看看字节信息窗口中的值变成了EB FE ,子进程就会在这个API中停下了。或者说是休眠在这个API中直到你来唤醒他。

会到OD中选择一段代码,比如:

0042F000 55 PUSH EBP

0042F001 8BEC MOV EBP,ESP

0042F003 83EC 0C SUB ESP,0C

0042F006 8B45 10 MOV EAX,DWORD PTR SS:[EBP 10]

0042F009 50 PUSH EAX

0042F00A E8 18CE0000 CALL armadill.0043BE27

用鼠标在0042F000行右击选择〔新建起源〕(呵呵,程序会到这里运行我们写的代码),打开附加窗口查看那个没有变色的主程序的句柄是0BAC(这个值每次加载会不同,注意了)这个就是子进程的句柄。回到代码中把上面的代码修改为:

0042F000 68 AC0B0000 PUSH 0BAC //子进程句柄

0042F005 E8 AD16A377 CALL kernel32.DebugActiveProcessStop

0042F00A 90 NOP //在这里下中断

在0042F00A 下中断,然后运行一下停在0042F00A 处。看看寄存器窗口的EAX值,如果这个值=01就表示子进程和父进程分离了。如果=00那么可能是子进程的句柄填错了,你可以在下面重新写入代码再次运行,直到EAX=01时就可以关闭OD了(杀掉父进程!)然后就可以进入子进程了。

重新运行Ollydbg(不要加载程序)附加上进程中的那个唯一的armadillo3.exe (附加上了吗?成功了吗?反正我是成功了)F9运行一下然后F12暂停程序就会来到休眠的地方:

77E12DFB >- EB FE JMP SHORT kernel32.GetProcAddress //看在循环呢

77E12DFD EC IN AL,DX

77E12DFE 51 PUSH ECX

再次在PUPE中恢复原来的代码 55 8B并按 "Parchear" 再看看程序中的代码又还原成:

77E12DFB > 55 PUSH EBP

77E12DFC 8BEC MOV EBP,ESP

77E12DFE 51 PUSH ECX

这个程序重新被我唤醒了。

先来到出错的地址看看,在转存窗口中G 004280D0 :

004280D0 CA 85 02 00 DE 85 02 00 蕝 .迏 .

004280D8 EE 85 02 00 00 86 02 00 颛 ..?.

004280E0 0C 86 02 00 1E 86 02 00 .?. ?.

004280E8 2E 86 02 00 3A 86 02 00 .?..

看看引入表还没有写入呢,说明我们停在解密引入表的代码前面,上面说过这个引入表是子进程自己解密的,那么只要找到解密的地方。然后把破坏的代码修改就可以得到完整的引入表了。

好了,第一个目的达到了-进入到子进程中并且停在解密引入表的前面。成功了一半了!下面要做的就是找那个magic了。

Ctrl F9再F7一次就来到:

00BF545E PUSH DWORD PTR SS:[EBP C]

00BF5461 PUSH ECX

00BF5462 CALL NEAR DWORD PTR DS:[C130C4] ; kernel32.GetProcAddress

00BF5468 JMP SHORT 00BF54BE //返回的地方。

我们知道程序的第一个出错的地方就是:004280D0地址指向的代码 00BF919B 现在在转存窗口中的004280D0开始的4个字节上下内存写入中断-跟踪是什么地方写入了00BF919B这个值,运行被OD中断在:

00C0942F MOV EAX,DWORD PTR SS:[EBP-4A4]

00C09435 MOV ECX,DWORD PTR SS:[EBP-6B8]

00C0943B MOV DWORD PTR DS:[EAX],ECX ; ECX=00BF919B

00C0943D MOV EAX,DWORD PTR SS:[EBP-4A4]

00C09443 ADD EAX,4

00C09446 MOV DWORD PTR SS:[EBP-4A4],EAX

00C0944C JMP 00C092C9

可以看出00BF919B这个值是EBP-6B8这个地址里的。

在转存窗口中G EBP-6B8 来到这里:

0012ED60 9B 91 BF 00 B3 73 F3 77 ....硈体

0012ED68 78 01 14 00 00 00 00 00 x .....

在上面的9B 91 BF 00 这4个字节上下内存访问断点然后Run一下就来到:

00C093D1 8985 F4FDFFF>MOV DWORD PTR SS:[EBP-20C],EAX

00C093D7 FFB5 40F9FFF>PUSH DWORD PTR SS:[EBP-6C0]

00C093DD FFB5 70FBFFF>PUSH DWORD PTR SS:[EBP-490]

00C093E3 E8 F8C0FEFF CALL 00BF54E0

00C093E8 8985 48F9FFF>MOV DWORD PTR SS:[EBP-6B8],EAX //停在这里 ; ntdll.RtlGetLastWin32Error

00C093EE 83BD 48F9FFF>CMP DWORD PTR SS:[EBP-6B8],0

00C093F5 75 38 JNZ SHORT 00C0942F

看看停下的地方,发现是EAX赋了这个内存地址的值。而这个EAX是上面的CALL 00BF54E0函数计算的值,到这个函数中看看:

00BF54E0 PUSH EBP //在这里下中断

00BF54E1 MOV EBP,ESP

00BF54E3 PUSH EBX

00BF54E4 PUSH ESI

00BF54E5 PUSH EDI

00BF54E6 XOR EDI,EDI

00BF54E8 XOR EBX,EBX

00BF54EA TEST WORD PTR SS:[EBP E],0FFFF

00BF54F0 JNZ SHORT 00BF54F5 // 第一个跳

00BF54F2 MOV EBX,DWORD PTR SS:[EBP C]

00BF54F5 PUSH EDI

00BF54F6 CALL NEAR DWORD PTR DS:[C130A4] ; kernel32.GetModuleHandleA

00BF54FC CMP DWORD PTR SS:[EBP 8],EAX

00BF54FF JNZ SHORT 00BF5508 //第二个跳

00BF5501 MOV ESI,0C153C0

00BF5506 JMP SHORT 00BF5568

00BF5508 CMP DWORD PTR DS:[C15998],EDI

00BF550E MOV ECX,0C15998

00BF5513 JE SHORT 00BF5551 // 第三个跳 改为 JMP(magic跳转)

00BF5515 MOV ESI,DWORD PTR DS:[C1B1B8]

00BF551B MOV EAX,DWORD PTR DS:[C1EECC]

00BF5520 TEST BYTE PTR DS:[ECX 8],1

00BF5524 JE SHORT 00BF5534 //第4个跳

00BF5526 MOV EDX,DWORD PTR DS:[EAX 70]

00BF5529 XOR EDX,DWORD PTR DS:[EAX 60]

00BF552C XOR EDX,DWORD PTR DS:[EAX 3C]

00BF552F TEST DL,80

00BF5532 JNZ SHORT 00BF5547 //第5个跳

00BF5534 MOV EDX,DWORD PTR DS:[EAX 70]

00BF5537 XOR EDX,DWORD PTR DS:[EAX 64]

00BF553A XOR EDX,DWORD PTR DS:[EAX 58]

00BF553D XOR EDX,DWORD PTR DS:[EAX 20]

00BF5540 XOR EDX,DWORD PTR DS:[ESI]

00BF5542 CMP DWORD PTR SS:[EBP 8],EDX

00BF5545 JE SHORT 00BF5565 //第6个跳

00BF5547 ADD ECX,0C

00BF554A ADD ESI,4

00BF554D CMP DWORD PTR DS:[ECX],EDI

00BF554F JNZ SHORT 00BF5520 //第7个跳

00BF5551 PUSH DWORD PTR SS:[EBP C]

00BF5554 PUSH DWORD PTR SS:[EBP 8]

00BF5557 CALL 00BF5397

00BF555C POP ECX

00BF555D POP ECX

00BF555E POP EDI

00BF555F POP ESI

00BF5560 POP EBX

00BF5561 POP EBP

00BF5562 RETN 8



上面有7个跳转,跟踪看看是那个跳转跳到破坏引入表的地方。跟踪发现第3个跳转如果不跳就会到破坏引入表的代码处。现在只要把这个跳转改成JMP就可以避开破坏引入表的代码。这个就是magic跳转了。好像在Armadillos 是相同的。

好了找到了这个关键的magic跳转就能修复全部的引入表了。

重新做上面的工作,用magic的地址做休眠的地址,附加上程序后改magic 的跳转然后运行就会停在子进程的OEP处,用Import REConstructor修复dump的程序,注意要点:

在Import REConstructor中填:

OEP=入口地址-400000

RVA=引入表的开始地址(上面说过了)-400000

SIZE=引入表的结束地址-开始地址

比如我的信息:

OEP=000251D0

RVA=000280A0

SIZE=00000444

注意!注意!注意!――不要按IAT Autosearch 直接按Get Import获得引入表,如果有错误的就直接Cut他 Fix dump 修复dump下的程序,运行一下是不是还正常了。

由于时间关系,我写得很仓促,可能存在许多的错误和表述不正确的地方。请大虾们指正了!

虽是依葫芦画的瓢,且画的不好。但也是我的心得,我想学习其实也是不断的在画别人已画好的瓢,如果画的好了就是自己的了。

文章虽粗糙,转载请保证他的完整性。

感谢二点兄弟收集的资料!



fxyang

2003.11.27晚


最新评论

QQ|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )

GMT+8, 2024-9-29 23:37 , Processed in 0.215052 second(s), 13 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

返回顶部