call $5
pop eax
and eax,0xffff0000
and eax,base
jmp eax
delphi可以这样
mov ecx,$12345678 //这里写入原来OEP入口
//动态基址获取
//call $+5
CALL @@1
@@1:
pop eax
and eax,$ffff0000
and ecx,$0000ffff
or eax,ecx
//jmp eax
push eax
上面再实际情况中并不起作用,而应该用以下方式
mov ecx,$12345678 //这里写入原来OEP入口
mov edx,$12345678 //这里写入原ImageNtHeaders.OptionalHeader.ImageBase基址
mov edi,$12345678 //这里写入新ep入口
//动态基址获取
//call $+5
CALL @@1
@@1:
pop eax
sub eax,35 //得到现在的ep+基址
sub eax,edi //减去ep 获得新的基址
add ecx,eax //得到动态的原oep
//or eax,ecx
//jmp eax
//push eax
push ecx
在进行病毒分析时,或者调试漏洞的shellcode时,经常看到标题中的指令流(E800000000 58,第二条肯定是pop指令,但是目的寄存器不一定是eax),这是干什么的呢?
看起来貌似很神秘,其实结合前后代码的意图可以知道(进行实时调试),最终eax中存放的数据是0x00413935(就上面的代码),而这个0x00413935,恰好就是pop eax指令的地址,因此实际上这条指令流的作用就是获取执行call $+5时,eip的值,也可以简单认为是获取当前指令地址,获取当前指令地址有什么用呢?我所见过最多是使用场景是通过得到当前指令地址来获取硬编码到shellcode中的字符串的地址,通过简单计算当前指令地址+偏移(这个已知,因为shellcode就是自己写的),就可以获取到字符串的地址。
这条指令流的原理也很简单,E8是call指令中的相对跳转指令(可以参考:http://www.mouseos.com/assembly/example/calling.html),它可以带一个dword参数,指明相对跳转的距离(E8 dword offset),计算方式很简单:下一条指令地址+ offset 即是call指令的目的地。下一条指令地址就是pop eax的地址啦。因此这个地方场景是这样滴:
1、CPU执行到E800000000时,eip中此时存放的是下一条指令的地址,也就是0x00413935(计算机组成原理);
2、CPU执行call指令,将call指令的下一条指令地址push到栈中(也就是0x00413935被push到栈中,这是用于函数调用后返回的);
3、对E8指令进行解析,offset == 0,计算call指令目的跳转地址:下一条指令地址+ offset ,eip被存入0x00413935;
4、跳转到0x00413935执行(实际就是call的下一条指令),执行pop eax,从上面的分析可知,此时栈顶的值是0x00413935,因此eax就被赋值0x00413935。
有人可能想,获取当前指令地址,直接mov eax, eip不就行了么?还真不行,因为木有直接操作eip的指令的。