实战Armadillo3.60 Original CopyMem-ll +Debug-Blocker -----UltraEdit
大家好,上次我已写了Armadillo3.60的标准壳的脱壳方法,相信大家一定会了吧。呵呵,小妹今天要带领大家一起来脱掉Original CopyMem-ll +Debug-Blocker的方式壳。不要害怕,一起来吧。
在这里,我首选感谢以前写过AM教程的人,虽然都是3.0之类的,都这些都是基础啊,特别是FTBirthday FLY等人,尤其是FTBirthday翻译的Ricardo Narvaja的文章,可谓非常经典,小妹用的方法也是参照他的方法,我试图找到一种不一样的方法,但都失败了,因为这种方式的加壳实在是太难了,当然他的有些做法在3.6中已不适用,但我都改变了一下,并且在修复时,我使用了自己的一些做法,这样可以更快地完成,并且更简单,更保险,一句话,为广大CRACKER着想,确保大家都能学会。呵呵。好的,我们的教程马上要开始了,这次小妹仍然使用本本来调试,呵呵,我把本本放在被子上,自己躺在床上调试,因为,我们面对的是一个复杂的壳,需要一个好的环境,旁边放上一杯果真,OK,让我们开始吧。
调试环境:本本迅弛1.4G 256MDDR 1M的L2二级缓存,调试起来非常快。如果你的机上有问题请和我联系,因为本本调试可能会有问题。还有请在XP以上系统下调试,因为其中用到的DebugActiveProcessStop只有XP以上系统才有,
在教程最后,我将总结一些经验给大家。
小妹不想在开头讲AM的一些理论知识,我想放在中间分开来讲解比较好,毕竟这是一门有别于其它的学科,呵呵,我们开始了。
这次我脱壳的对象是Ultraedit.exe 10.00
首选我用Armadillo3.60专业版为Ultraedit.exe加上Original CopyMem-ll +Debug-Blocker的壳。为了方便,我去掉了AM提醒框,这会稍微增加一点难度,后面小妹会提到的。
后来,打开OD1.1b 载入它,忽略所有异常事件。如下:
005EB000 > 60 PUSHAD
005EB001 E8 00000000 CALL Uedit32.005EB006
005EB006 5D POP EBP
005EB007 50 PUSH EAX
005EB008 51 PUSH ECX
005EB009 EB 0F JMP SHORT Uedit32.005EB01A
说明一下,不能再用标准壳的脱壳方法来脱Original CopyMem-ll +Debug-Blocker的壳,并且断点也不一样,因为这是一种完全不一样的加壳方式,armadillo自己生成2个进程,父进程做调试器,子进程做被调试者,父进程负责对子进程的代码进行解码,而对脱壳来说很重要的IAT部分却是子进程负责进行解密并hook。Ollydbg现在却没办法attach上子进程。
载入之后,隐藏OD吧。这一步是必须的。
好的,接着在命令行里下断 BP WaitForDebugEvent 下面我会讲解为什么要用这个函数来下断。我们先运行一下,让它断下来。按SHIFT+F9两次忽略两次异常后断下,如下:这是堆栈窗口:
0012DA98 005D0D67 CALL 到 WaitForDebugEvent 来自 Uedit32.005D0D61
0012DA9C 0012EB5C |pDebugEvent = 0012EB5C
0012DAA0 000003E8 Timeout = 1000. ms
0012DAA4 0012FF04
0012DAA8 00000000
0012DAAC 005E0999 Uedit32.005E0999
上面的0012EB5C就是发生调试事件时具体的内容存放地址,每次调用WaitForDebugEvent时都会使用这个地址。因为AM的两个进程会用到这个函数,下面马上要讲到了。
在0012EB5C上右击选择转存中跟随,在左下角内存窗口如下:
留意这个窗口,一会儿我们的OEP将出现在这里面,呵呵,小妹一会儿告诉你为什么。
清除掉上面的断点,在命令行下断 Bp WriteProcessMemory (注意,类似这样的断点需要注意大小写)F9一下,稍等一下,因为它要创建一个新线程,马上中断下来。观察堆栈窗口如下:
0012D938 005D4C6E /CALL 到 WriteProcessMemory 来自 Uedit32.005D4C68
0012D93C 0000004C |hProcess = 0000004C (window)
0012D940 00504000 |Address = 504000
0012D944 003A7FB8 |Buffer = 003A7FB8
0012D948 00001000 |BytesToWrite = 1000 (4096.)
0012D94C 0012DA54 pBytesWritten = 0012DA54
0012D950 00000000
0012D954 00001030
好了,小妹在这里要讲解一些理论知识。
父进程的一个缓冲区从003A7FB8开始,然后它会复制给子进程一个从504000开始的 1000 bytes的块,很明显啊,子进程这时的第一个section是完全空的,所以当执行到OEP时,它当然要报告一个错误,父进程得到这个信息后会停止自身运行,而去复制另一个数据块,然后再自己运行,这样循环下去,被复制的数据块的大小是1000 bytes,所以当程序试图执行任何超过这个大小的块外的指令时, 另一个错误将会发生,然后这个错误会被通知给父进程, 父进程会复制另外1000 bytes的块然后继续。大家都可以想到上面的第一个错误很有可能是因为它的子进程调用了OEP,所以父进程会为了解决这个错误为复制1000bytes的数据块,呵呵,那么子进程的OEP就应该在里面了。确切地说OEP应该是在00504000+1000-1=00504FFF内。好的,我们看一下内存窗口:
看见上面的三个005045AD了吗,这就是OEP。
0012EB5C 01 00 00 00 DebugEventCode 01表示EXCEPTION_DEBUG_EVENT
0012EB60 F0 06 00 00 ProcessId 发生调试事件的进程id
0012EB64 A8030000 ThreadId 发生调试时间的线程id
0012EB68 01 00 00 00 因为12eb5C处为1,所以这里代表的意思是ExceptionCode
WriteProcessMemory 函数是内存复制函数,大家知道Armadillo的copymem-II方式会在内存中生成同名的2个进程,其中一个是父进程,一个是子进程。那么子进程是怎么生成的呢,就是从父进程中用WriteProcessMemory函数复制来的。(这二个进程的窗口句柄不同,并且互相掌握着对方的OEP)S因为复制是按1000的块进行的,所以第一个复制的块中必定包含了子进程的OEP地址。这个地址的值在第一块的值之间。所以上面的转存窗口中的那个005045AD就是真正的OEP了。
虽然OEP找到了,但DUMP出来的还是加密的,因为还有一个加密call用来重新加密或者破坏已使用的块来避免被dump,OK,我们来找到这个加密CALL吧。
先清除掉上面的断点,以住的方法是查看调用堆栈,找有关信息,但这一方法在3.6中不再适用,怎么办呢,根据以往的经验,我找到了一个办法 ,可以正确并快速地找到这个地方。原理吗,就是3.6中也用了相关指令,我们可以通过这一途径来找到。OK,先返回到005D4C68这个调用WriteProcessMemory的下方,然后右击 查找所有命令:填入 xor al,al 回车,
如下图:
上图中的红色部分是当前指令 往上数5行,也就是现在鼠标停留在的005D38E8处,好双击那个地方如下:
005D38D2 51 PUSH ECX
005D38D3 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8]
005D38D6 52 PUSH EDX
005D38D7 E8 48030000 CALL Uedit32.005D3C24
005D38DC 83C4 0C ADD ESP,0C
005D38DF 25 FF000000 AND EAX,0FF
005D38E4 85C0 TEST EAX,EAX
005D38E6 75 07 JNZ SHORT Uedit32.005D38EF
005D38E8 32C0 XOR AL,AL
005D38EA E9 2E030000 JMP Uedit32.005D3C1D
005D38EF 50 PUSH EAX
看一下上面005D38D7有个CALL 005D3C24 这就是一个用来解密的CALL,对应的还有一个用来加密的以CALL 右击 查找全部命令 填入:CALL 005D3C24 回车 OK,终于找到了另外一个地方也调用了这个CALL:
005D3BAC E8 73000000 CALL Uedit32.005D3C24
005D3BB1 83C4 0C ADD ESP,0C
005D3BB4 50 PUSH EAX
005D3BB5 F7D0 NOT EAX
005D3BB7 0FC8 BSWAP EAX
005D3BB9 58 POP EAX
005D3BBA 73 00 JNB SHORT Uedit32.005D3BBC
在005D3BAC处也调用了它,但在那里,那个CALL的作用是加密块,好的我们NOP它,空格,然后NOP,现在我们要得到不加密的块,我们要执行WriteProcessMemory,返回到刚才那个005D4C68处,
0012D938 005D4C6E /CALL 到 WriteProcessMemory 来自 Uedit32.005D4C68
0012D93C 0000004C |hProcess = 0000004C (window)
0012D940 00504000 |Address = 504000
0012D944 003A7FB8 |Buffer = 003A7FB8
0012D948 00001000 |BytesToWrite = 1000 (4096.)
0012D94C 0012DA54 pBytesWritten = 0012DA54
快速返回的方法在上面堆栈窗口第一行上,右击选择反汇编跟随就可以了,
005D4C68 FF15 64B15F00 CALL NEAR DWORD PTR DS:[<&KERNEL32.Write>; kernel32.WriteProcessMemory
005D4C6E 85C0 TEST EAX,EAX
005D4C70 75 4B JNZ SHORT Uedit32.005D4CBD
005D4C72 50 PUSH EAX
005D4C73 F7D0 NOT EAX
005D4C75 0FC8 BSWAP EAX
005D4C77 58 POP EAX
005D4C78 73 00 JNB SHORT Uedit32.005D4C7A
在005D4C6E处下个断,F9一下,会断下来。OK,清除断点,现在我们必须把父进程杀死,然后再调试子进程,首先我们要把子进程进入死循环,OK,拿出PUPE吧,最新版本是2002,我从西班牙的一个站点上搞来的,运行PUPE:
可以看到有两个uedit32.exe 进程,上面那个也就是句柄是6F0的那个,是子进程,我们在它上面右击,选择第一个命令,
然后按上图的方式填好,005045AD是子进程OEP,EBFE是让它死循环,Buscar按钮是相当于回车键,在输入地上之后,在右边的灰色框中显示具体值是多少一,Parchear是在下面的白色框中输入新值之后让它马上生效,Salir是关闭。
实就是用PUPE修改入口的代码,让他变成跳转到EIP地址的代码。这样程序在入口点就会停下。跳向自己的地址,程序会向下执行吗?
因为父进程和子进程之间有调试关联,所以我们下断BP WaitForDebugEvent 断下:
0012DA98 005D0D67 /CALL 到 WaitForDebugEvent 来自 Uedit32.005D0D61
0012DA9C 0012EB5C |pDebugEvent = 0012EB5C
0012DAA0 000003E8 Timeout = 1000. ms
0012DAA4 0012FF04
0012DAA8 00000000
0012DAAC 005E0999 Uedit32.005E0999
0012DAB0 00000001
0012DAB4 80000004
上面是堆栈窗口 ,好,小妹和大家一起到 005D0D61 处去吧。
005D0D52 A0 336168E8 MOV AL,BYTE PTR DS:[E8686133] 这里看出是 3.6程序
005D0D57 0300 ADD EAX,DWORD PTR DS:[EAX]
005D0D59 008B 95E0F5FF ADD BYTE PTR DS:[EBX+FFF5E095],CL
005D0D5F FF52 FF CALL NEAR DWORD PTR DS:[EDX-1]
005D0D62 15 A0B05F00 ADC EAX,<&KERNEL32.WaitForDebugEvent>
005D0D67 85C0 TEST EAX,EAX
005D0D69 0F84 9A260000 JE Uedit32.005D3409
005D0D6F 60 PUSHAD
005D0D70 33C0 XOR EAX,EAX
005D0D72 75 02 JNZ SHORT Uedit32.005D0D76
005D0D74 EB 15 JMP SHORT Uedit32.005D0D8B
我们返回到005D0D67处,好,现在我们要把父进程杀死,在这里,当然不能运行任何命令,所以我们在005D0D67上右击,选择此处新建EIP,因为我们的目的是想让程序运行我们当前自己的命令,呵呵。
然后,把005D0D52到005D0D62 这里都NOP,因为我们不想运行这个函数,不要让父进程再调试子进程。改好之后,如下:
005D0D52 90 NOP
005D0D53 90 NOP
005D0D54 90 NOP
005D0D55 90 NOP
005D0D56 90 NOP
005D0D57 90 NOP
005D0D58 90 NOP
005D0D59 90 NOP
005D0D5A 90 NOP
005D0D5B 90 NOP
005D0D5C 90 NOP
005D0D5D 90 NOP
005D0D5E 90 NOP
005D0D5F 90 NOP
005D0D60 90 NOP
005D0D61 90 NOP
005D0D62 90 NOP
005D0D63 90 NOP
005D0D64 90 NOP
005D0D65 90 NOP
005D0D66 90 NOP
005D0D67 85C0 TEST EAX,EAX
005D0D69 0F84 9A260000 JE Uedit32.005D3409
005D0D6F 60 PUSHAD
005D0D70 33C0 XOR EAX,EAX
005D0D72 75 02 JNZ SHORT Uedit32.005D0D76
好,现在我们做杀死父进程的最后步骤――――打上补丁。
把上面的005D0D69处的命令 改成 jmp 00401000
因为父进程的偏移是00401000 ,如下:
005D0D67 85C0 TEST EAX,EAX
005D0D69 - E9 9202E3FF JMP Uedit32.00401000
005D0D6E 90 NOP
因为我们是在005D0D67建立了EIP,所以,下面当然是jmp,让程序跳到我们的补丁处去执行啊,呵呵,好,现在我们跳到00401000处去打补丁吧。
00401000 0000 ADD BYTE PTR DS:[EAX],AL
00401002 0000 ADD BYTE PTR DS:[EAX],AL
00401004 0000 ADD BYTE PTR DS:[EAX],AL
00401006 0000 ADD BYTE PTR DS:[EAX],AL
00401008 0000 ADD BYTE PTR DS:[EAX],AL
0040100A 0000 ADD BYTE PTR DS:[EAX],AL
0040100C 0000 ADD BYTE PTR DS:[EAX],AL
0040100E 0000 ADD BYTE PTR DS:[EAX],AL
00401010 0000 ADD BYTE PTR DS:[EAX],AL
00401012 0000 ADD BYTE PTR DS:[EAX],AL
00401014 0000 ADD BYTE PTR DS:[EAX],AL
00401016 0000 ADD BYTE PTR DS:[EAX],AL
00401018 0000 ADD BYTE PTR DS:[EAX],AL
0040101A 0000 ADD BYTE PTR DS:[EAX],AL
还是空白的,准备写命令进去。Alt+M 打开内存镜象窗口:
程序的text从00401000到0054CFFF,我们需要修改OEP,好的,来到内存窗口吧。
把上面的三个005045AD都改成00400000
我们将要欺骗父进程,使他相信从第一个section开始的所有块都存在错误, 一个接一个所有的块都会被正确的复制进来。
开始从00401000 处 打补丁。
00401000 8105 74EB1200 0>ADD DWORD PTR DS:[12EB74],1000
0040100A 8105 80EB1200 0>ADD DWORD PTR DS:[12EB80],1000
00401014 8105 80EB1200 0>ADD DWORD PTR DS:[12EB84],1000
0040101E 813D 80EB1200 0>CMP DWORD PTR DS:[12EB84],Uedit32.0054D00>
00401028 - 0F85 40FD1C00 JNZ Uedit32.005D0D6E
0040102E 90 NOP
0040102F 90 NOP
00401030 0000 ADD BYTE PTR DS:[EAX],AL
00401032 0000 ADD BYTE PTR DS:[EAX],AL
00401034 0000 ADD BYTE PTR DS:[EAX],AL
按照上面的从00401000一直打到0040102F
上面的jnz是比较是否解压块结束,后面的NOP是为了我们在那儿中断,在0040102E上下断,OK,F9一下,被断下来了。大家看一下内存窗口:
可以看到数据被完全的复制过来了。好的,下面我们要把父进程杀死。
00401000 8105 74EB1200 0>ADD DWORD PTR DS:[12EB74],1000
0040100A 8105 80EB1200 0>ADD DWORD PTR DS:[12EB80],1000
00401014 8105 80EB1200 0>ADD DWORD PTR DS:[12EB84],1000
0040101E 813D 80EB1200 0>CMP DWORD PTR DS:[12EB84],Uedit32.0054D00>
00401028 - 0F85 40FD1C00 JNZ Uedit32.005D0D6E
0040102E 90 NOP
0040102F 90 NOP
00401030 0000 ADD BYTE PTR DS:[EAX],AL
00401032 0000 ADD BYTE PTR DS:[EAX],AL
00401034 0000 ADD BYTE PTR DS:[EAX],AL
现在断点断在0040102E处,我们在这里用CALL kernel32.DebugActiveProcessStop这个API,把它们分离出来。首选我们用知道子进程的句柄,可以用PUPE看一下,刚才我们也看到了,但现在我重新载入程序了,(是个意外,不好意思)所以句柄发生了变化,现在查看一下是49C,注意如果第一个是字母,比如B95 那么修改的时候用0B95在前面增加一个0,好,我们修改如下:
00401000 8105 74EB1200 0>ADD DWORD PTR DS:[12EB74],1000
0040100A 8105 80EB1200 0>ADD DWORD PTR DS:[12EB80],1000
00401014 8105 84EB1200 0>ADD DWORD PTR DS:[12EB84],1000
0040101E 813D 84EB1200 0>CMP DWORD PTR DS:[12EB84],Uedit32.0054D000
00401028 - 0F85 40FD1C00 JNZ Uedit32.005D0D6E
0040102E 68 9C040000 PUSH 49C
00401033 E8 FBE1A877 CALL kernel32.DebugActiveProcessStop
00401038 90 NOP
00401039 90 NOP
我们在00401038处,下个断点,这样的话就正好运行到把父进程杀死,呵呵。F9一下,好,被断下来了,并且看一下EAX的值为1,可以确定子进程与他的父进程已经分离,现在我们可以关闭 OllyDbg了。而如果 EAX = 0,那是因为你写入有点不对劲(可能是句柄),则你必须要从头到尾核对那些行。你可以重新写入代码,再来一次。反正小妹是第一次就成功了,大家一定要非常小心。
OK,可以关掉OD了,这样父进程就被杀死了,而子进程还在死循环中,呵呵。
关掉OD,重新打开OD。选择文件菜单中的附加命令,可以看到只有一个够本为49C的子进程了,呵呵把它附加上吧。OK,成功附加上,运行一下,程序就会在死循环运行,因此按 F12 来暂停程序,然后在PUPE中把OEP的代码还原成55 8B ,再“Parchear”一下就会还原成原来的代码了。呵呵,我们成功了。
好的,现在打开 LordPE 然后搜索armadillo3.exe这个进程。选择这进程以及选择〔active dump engine〕| 〔IntelliDump〕|〔select〕,然后点击 Dump full ,并且编辑一下,把OEP改成1045AD,OK成功了。
运行一下这个dump.exe,当然不可以运行,呵呵,让我们来修复它吧。这里,我会采用一些小妹自已的方法,因为原来的一些方法在3.6中显得复杂并且有些不能用,请大家留意,并且我尽量,加快速度,包括简单化。
用OD载入这个dump.exe,
用F8走下。发现走到005045D3 FF15 98D45400 CALL NEAR DWORD PTR DS:[54D498]
再F8一下就飞了,所以留意这一行时的信息
005045AD > 55 PUSH EBP
005045AE 8BEC MOV EBP,ESP
005045B0 6A FF PUSH -1
005045B2 68 B06F5600 PUSH dumped.00566FB0
005045B7 68 907F5000 PUSH dumped.00507F90
005045BC 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
005045C2 50 PUSH EAX
005045C3 64:8925 0000000>MOV DWORD PTR FS:[0],ESP
005045CA 83EC 58 SUB ESP,58
005045CD 53 PUSH EBX
005045CE 56 PUSH ESI
005045CF 57 PUSH EDI
005045D0 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
005045D3 FF15 98D45400 CALL NEAR DWORD PTR DS:[54D498]
也就是005045D3,我们运行到这里,
可以看到一些东西:
00F1C3BB??明显是错误的,OK,我们到0054D498中去,注意是内存窗口,
OK,鼠标滚轮向上走,一直到顶:
可以看出IAT从0054D0000开始,(为什么,因为可以看到很多77之类的东西,呵呵) 现在滚轮向下走,
现在IAT结束了,也就是在0054DAD8,因为没有77了,呵呵,归类一下,OEP=0010ad45 IAT 起始应该是14D000 大小为 AD8 。好记下这些重要内容,一会儿要用。
和标准壳一样,也有一个magicjump,我们要找到它,现在难就难在,这些加解密都是子进程自己完成的,我们又要重新再次杀死父进程,而且我们要走在magicjump之前,不要害怕麻烦,小妹和大家再来吧。
用OD重新打开加壳的uledit.exe,隐藏OD,然后去掉所有异常选项的勾,因为我前面说过了,我加壳时去掉了AM注册提醒框,而现在我们要有个合适断点在合适时候停下来,所有如果不去掉勾的话,走两步就运行了。呵呵。把上面的工作做好。F9运行,按SHIFT+F9两次忽略掉两次开头的异常。然后单步事件而停下来。如下:
005D0E75 C745 FC 0000000>MOV DWORD PTR SS:[EBP-4],0――――>停在这。
005D0E7C EB 33 JMP SHORT Uedit32.005D0EB1
005D0E7E 8B55 EC MOV EDX,DWORD PTR SS:[EBP-14]
005D0E81 8B02 MOV EAX,DWORD PTR DS:[EDX]
005D0E83 8B08 MOV ECX,DWORD PTR DS:[EAX]
005D0E85 898D 2CE7FFFF MOV DWORD PTR SS:[EBP-18D4],ECX
005D0E8B 8B85 2CE7FFFF MOV EAX,DWORD PTR SS:[EBP-18D4]
005D0E91 2D 04000080 SUB EAX,80000004
005D0E96 F7D8 NEG EAX
005D0E98 1BC0 SBB EAX,EAX
005D0E9A 24 FE AND AL,0FE
005D0E9C 83C0 01 ADD EAX,1
005D0E9F C3 RETN
005D0EA0 8B65 E8 MOV ESP,DWORD PTR SS:[EBP-18]
005D0EA3 C685 44F6FFFF 0>MOV BYTE PTR SS:[EBP-9BC],1
005D0EAA C745 FC 0000000>MOV DWORD PTR SS:[EBP-4],0
005D0EB1 8B95 E0F5FFFF MOV EDX,DWORD PTR SS:[EBP-A20]
005D0EB7 A1 D8156000 MOV EAX,DWORD PTR DS:[6015D8]
下面要格外小心了。
先重新打开PUPE,因为我们随时要用到它,我们的原理是刚才那个地址
0054D498,被赋了一个错误值,从而跟出magicjump的地方,我们利用PUPE,来观察0054D498被赋值的情况,呵呵,现在当然是0了:
好,开始按SHIFT+F9一次一次往下走吧,我的经验至少要60次后,才会发生变化,60次后,我按一次SHIFT+F9,再回到PUPE中,按一次Buscar按钮,看看目前值是多少,62次后,如下:
这是它第一次被赋值,对我们来说太重要了。赶在magicjump之前了,好,现在小妹又要和大家来杀死父进程,我们又要制造一个死循环了,我们随意选取一个API就可以,在OD中查看模块,来到kernel32.dll,在窗口中右击,查找当前模块指令,然后再下面这图:
GetProcAddress 这个函数的地址是77E5B332
好,现在利用PUPE,来让这个函数死循环,按下面的图做:
原指令是 558B 我们改成 EBFE .
好,在OD中回到模块uedit32.exe中,
我们来到:
005BB000 55 PUSH EBP
005BB001 8BEC MOV EBP,ESP
005BB003 83EC 0C SUB ESP,0C
005BB006 8B45 10 MOV EAX,DWORD PTR SS:[EBP+10]
005BB009 50 PUSH EAX
005BB00A E8 A0480200 CALL Uedit32.005DF8AF
005BB00F 83C4 04 ADD ESP,4
005BB012 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX
005BB015 8B4D F8 MOV ECX,DWORD PTR SS:[EBP-8]
005BB018 894D FC MOV DWORD PTR SS:[EBP-4],ECX
005BB01B 8B55 0C MOV EDX,DWORD PTR SS:[EBP+C]
005BB01E 52 PUSH EDX
005BB01F 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
005BB022 50 PUSH EAX
005BB023 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
005BB026 51 PUSH ECX
我们在005BB000处右击新建EIP,是为了让程序马上执行我们这里的命令。用PUPU查看一下当前子进程的句柄,我的是13C 改代码如下:
005BB000 68 3C010000 PUSH 13C
005BB005 E8 29428D77 CALL kernel32.DebugActiveProcessStop
005BB00A 90 NOP ――――>下断。
005BB00B 90 NOP
005BB00C 90 NOP
005BB00D 90 NOP
005BB00E 90 NOP
005BB00F 83C4 04 ADD ESP,4
005BB012 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX
再次利用kernel32.DebugActiveProcessStop杀死父进程,下面的NOP当然是用来下断的。
F9一下,OK,被断下,EAX=1,说明父进程和子进程之间已分离,好,关闭OD,重新打开OD,附加这个子进程,运行一下,然后暂停,在PUPE中,把558B再写回去,不要让它再死循环了,呵呵。
下面我用自己的方法
下BP VirtualProtect,F9中断,看一下堆栈窗口:
0012BEF0 00F318F8 /CALL 到 VirtualProtect 来自 00F318F2
0012BEF4 0054D9EC |Address = Uedit32.0054D9EC
0012BEF8 00000058 |Size = 58 (88.)
0012BEFC 00000002 |NewProtect = PAGE_READONLY
0012BF00 0012D5F0 pOldProtect = 0012D5F0
0012BF04 00000001
0012BF08 003CC008
0012BF0C 00000000
0012BF10 00000039
0012BF14 00000000
好的,我们跳到00F318F2,现在我们鼠标滚轮向上走,直到发现了有这样的API:
; kernel32.GetModuleHandleA
好,我们很快找到了。
00F31349 FF15 C480F300 CALL NEAR DWORD PTR DS:[F380C4] ; kernel32.GetModuleHandleA
00F3134F 3985 BCE8FFFF CMP DWORD PTR SS:[EBP-1744],EAX
00F31355 75 0F JNZ SHORT 00F31366
00F31357 C785 B8E8FFFF 3>MOV DWORD PTR SS:[EBP-1748],0F3C530
00F31361 E9 C4000000 JMP 00F3142A
00F31366 83A5 94E6FFFF 0>AND DWORD PTR SS:[EBP-196C],0
00F3136D C785 90E6FFFF 4>MOV DWORD PTR SS:[EBP-1970],0F3CB48
00F31377 EB 1C JMP SHORT 00F31395
00F31379 8B85 90E6FFFF MOV EAX,DWORD PTR SS:[EBP-1970]
00F3137F 83C0 0C ADD EAX,0C
00F31382 8985 90E6FFFF MOV DWORD PTR SS:[EBP-1970],EAX
00F31388 8B85 94E6FFFF MOV EAX,DWORD PTR SS:[EBP-196C]
00F3138E 40 INC EAX
00F3138F 8985 94E6FFFF MOV DWORD PTR SS:[EBP-196C],EAX
00F31395 8B85 90E6FFFF MOV EAX,DWORD PTR SS:[EBP-1970]
00F3139B 8338 00 CMP DWORD PTR DS:[EAX],0
00F3139E 0F84 86000000 JE 00F3142A
00F313A4 8B85 90E6FFFF MOV EAX,DWORD PTR SS:[EBP-1970]
00F313AA 8B40 08 MOV EAX,DWORD PTR DS:[EAX+8]
00F313AD 83E0 01 AND EAX,1
00F313B0 85C0 TEST EAX,EAX
00F313B2 74 25 JE SHORT 00F313D9
00F313B4 A1 9455F400 MOV EAX,DWORD PTR DS:[F45594]
00F313B9 8B0D 9455F400 MOV ECX,DWORD PTR DS:[F45594] ; Uedit32.005FB260
00F313BF 8B40 58 MOV EAX,DWORD PTR DS:[EAX+58]
00F313C2 3341 6C XOR EAX,DWORD PTR DS:[ECX+6C]
00F313C5 8B0D 9455F400 MOV ECX,DWORD PTR DS:[F45594] ;
清掉原来的断点,上面的00F3139E就是magicjump一定要让它跳,我们在那里下断,F9后断下来。修改标志位,使Z=1,让它强行跳。依次循环做,我改了17次,后来运行一下便提示无法处理异常了,不能再按任何键,不能就飞了,可异没有走到OEP,小妹想通过标准壳中的方法强行下断走到OEP,也不行,断点都断不下来,不关也没关系,可以看到内存窗口中正确的IAT出现了:
我们拿出ImportREC,然后修复它吧。OEP填
10ad45 IAT 起始值为 14D000 大小为AD8 不要按搜索OEP ,直接GET表吧,呵呵,CUT掉几个无效的,OK。运行了。减小一下体积吧。呵呵。
感谢所有人,呵呵,不过总觉得最后那里有点问题,要花点时间研究一下,呵呵,另外magicjump是有规律的,
在3.6中,形式是这样的:
00F31349 FF15 C480F300 CALL NEAR DWORD PTR DS:[F380C4] ; kernel32.GetModuleHandleA
00F3134F 3985 BCE8FFFF CMP DWORD PTR SS:[EBP-1744],EAX
00F31355 75 0F JNZ SHORT 00F31366
00F31357 C785 B8E8FFFF 3>MOV DWORD PTR SS:[EBP-1748],0F3C530
00F31361 E9 C4000000 JMP 00F3142A
00F31366 83A5 94E6FFFF 0>AND DWORD PTR SS:[EBP-196C],0
00F3136D C785 90E6FFFF 4>MOV DWORD PTR SS:[EBP-1970],0F3CB48
00F31377 EB 1C JMP SHORT 00F31395
00F31379 8B85 90E6FFFF MOV EAX,DWORD PTR SS:[EBP-1970]
00F3137F 83C0 0C ADD EAX,0C
00F31382 8985 90E6FFFF MOV DWORD PTR SS:[EBP-1970],EAX
00F31388 8B85 94E6FFFF MOV EAX,DWORD PTR SS:[EBP-196C]
00F3138E 40 INC EAX
00F3138F 8985 94E6FFFF MOV DWORD PTR SS:[EBP-196C],EAX
00F31395 8B85 90E6FFFF MOV EAX,DWORD PTR SS:[EBP-1970]
00F3139B 8338 00 CMP DWORD PTR DS:[EAX],0
00F3139E 0F84 86000000 JE 00F3142A
看到了吧,而且在3.6中这个跳转地址总是XXXX139E 呵呵呵,未经一一核实,呵呵,OEP往下找找就可以了,CALL EDI ,呵呵,写了8个小时,终于好了,希望大家能有所进步,呵呵,错误之处难免,请提出。
本本都快烫了,呵呵。
感谢我们的组织 NUKE 感谢老大伊万,呵呵,欢迎大家来。