这个命令是访问子程序的一个汇编基本指令。也许你说,这个我早就知道了!别急请继续看完。
call真正的意义是什么呢?我们可以这样来理解:1.向堆栈中压入下一行程序的地址;2.JMP到call的子程序地址处。例如:
00401029.E8DA240A00call004A3508
0040102E.5Apopedx
在执行了00401029以后,程序会将0040102E压入堆栈,然后JMP到004A3508地址处!
与call对应的就是RET了。对于RET我们可以这样来理解:1.将当前的ESP中指向的地址出栈;2.JMP到这个地址。
如果要返回父程序,则当我们在堆栈中进行堆栈的操作的时候,一定要保证在RET这条指令之前,ESP指向的是我们压入栈中的地址。
ESP定律的原理就是“堆栈平衡”原理。
1.这个是加了UPX壳的入口时各个寄存器的值!
EAX00000000
ECX0012FFB0
EDX7FFE0304
EBX7FFDF000
ESP0012FFC4
EBP0012FFF0
ESI77F51778ntdll.77F51778
EDI77F517E6ntdll.77F517E6
EIP 0040EC90 note-upx.<ModuleEntryPoint>
C0ES002332bit0(FFFFFFFF)
P1CS001B32bit0(FFFFFFFF)
A0SS002332bit0(FFFFFFFF)
Z0DS002332bit0(FFFFFFFF)
S1FS003832bit7FFDE000(FFF)
T 0 GS 0000 NULL
D0
O0LastErrERROR_MOD_NOT_FOUND(0000007E)
2.这个是UPX壳JMP到OEP后的寄存器的值!
EAX00000000
ECX0012FFB0
EDX7FFE0304
EBX7FFDF000
ESP0012FFC4
EBP0012FFF0
ESI77F51778ntdll.77F51778
EDI77F517E6ntdll.77F517E6
EIP004010CCnote-upx.004010CC
C0ES002332bit0(FFFFFFFF)
P1CS001B32bit0(FFFFFFFF)
A0SS002332bit0(FFFFFFFF)
Z1DS002332bit0(FFFFFFFF)
S0FS003832bit7FFDE000(FFF)
T0GS0000NULL
D0
O0LastErrERROR_MOD_NOT_FOUND(0000007E)
以上数据可看出除了EIP不同以外,其他都一模一样,现在我们来看看UPX的壳的第一行:
0040EC90n>60pushad//****注意这里*****
0040EC91BE15B04000movesi,note-upx.0040B015
PUSHAD就是把所有寄存器压栈!我们在到壳的最后看看:
0040EE0F61popad//****注意这里*****
0040EE10-E9B722FFFFjmpnote-upx.004010CC//JMP到OEP
POP就是将所有寄存器出栈!
而当我们PUSHAD的时候,ESP将寄存器压入了0012FFC0--0012FFA4的堆栈中!如下:
0012FFA477F517E6返回到ntdll.77F517E6来自ntdll.77F78C4E//EDI
0012FFA877F51778返回到ntdll.77F51778来自ntdll.77F517B5//ESI
0012FFAC0012FFF0//EBP
0012FFB00012FFC4//ESP
0012FFB47FFDF000//EBX
0012FFB87FFE0304//EDX
0012FFBC0012FFB0//ECX
0012FFC000000000//EAX
对ESP的0012FFA4下硬件访问断点。也就是说当程序要访问这些堆栈,从而恢复原来寄存器的值,准备跳向苦苦寻觅的OEP的时候,OD帮助我们中断下来。
于是我们停在0040EE10这一行!
总结:我们可以把壳假设为一个子程序,当壳把代码解压前和解压后,他必须要做的是遵循堆栈平衡的原理,让ESP执行到OEP的时候,使ESP=0012FFC4。
对只要在0012FFC0下,硬件写入断点,我们就能停在OEP的第二句处!!
下面我们来举个例子,就脱壳进阶第一篇吧!
载入OD后,来到这里:
0040D042 N> B8 00D04000 mov eax,Notepad.0040D000 //停在这里
0040D047684C584000pushNotepad.0040584C
0040D04C64:FF3500000000pushdwordptrfs:[0]//第一次硬件中断,F9
0040D05364:892500000000movdwordptrfs:[0],esp
0040D05A66:9Cpushfw
0040D05C60pushad
0040D05D50pusheax
直接对0012FFC0下硬件写入断点,F9运行。(注意硬件中断)
在0040D04C第一次硬件中断,F9继续!
0040D135 A4 movs byte ptr es:[edi],byte ptr ds:[esi] //访问异常,不管他 shift+F9继续
0040D136 33C9 xor ecx,ecx
0040D138 83FB 00 cmp ebx,0
0040D13B^7EA4jleshortNotepad.0040D0E1
第二次硬件中断。
004058B564db64//断在这里
004058B689db89
004058B71Ddb1D
004058B800db00
004058B900db00
这里也不是,F9继续!
004010CC/.55pushebp
004010CD |. 8BEC mov ebp,esp //断在这里,到了!(如果发现有花指令,用ctrl+A分析一下就能显示出来)
004010CF|.83EC44subesp,44
004010D2|.56pushesi
几乎全部的压缩壳,部分加密壳。只要是在JMP到OEP后,ESP=0012FFC4的壳,理论上我们都可以使用。但是在何时下断点避开校验,何时下断OD才能断下来,这还需要多多总结和多多积累。