VC和汇编高手请进
请问下,在VC中函数调用的时候不是push了edi,esi,ebx,ebp吗
其他三个还好理解是什么 在数值上也好判断
那个ebx到底存放了什么啊?
为什么每次运行都不一样,有时是7FFDD000,都在这个值附近
在线等
[解决办法]
根据我的试验,在win2000以上操作系统对于ebx,esi,edi好像是拿来就用,没有进行保护和恢复,如果你的程序中使用了这几个寄存器,请一定先压栈,用完后恢复。否则会使程序在win98下正常,在win2000下出现莫名其妙的非法操作,一般是提示某个地址不能进行读写操作,而这个地址并不在你的程序的控制下。
基于以上的原因,由编译器生成的函数代码一般都先保存了这几个寄存器的内容
反汇编以后的函数大概都有如下的结构,进入函数和退出函数之处都有这几行保存寄存器的代码:
void shellcode_main(void)
{
0041C750 push ebp
0041C751 mov ebp,esp
0041C753 sub esp,40h
0041C756 push ebx
0041C757 push esi
0041C758 push edi
make_shellcode( 4444, shellcode_buf, shellcode_len );
0041C759 push offset shellcode_len (456D00h)
0041C75E push offset shellcode_buf (456900h)
0041C763 push 115Ch
0041C768 call make_shellcode (419BEFh)
0041C76D add esp,0Ch
print_shellcode( shellcode_buf, shellcode_len );
0041C770 mov eax,dword ptr [shellcode_len (456D00h)]
0041C775 push eax
0041C776 push offset shellcode_buf (456900h)
0041C77B call print_shellcode (419E15h)
0041C780 add esp,8
__asm
{
lea eax, shellcode_buf
0041C783 lea eax,[shellcode_buf (456900h)]
jmp eax
0041C789 jmp eax
}
}
0041C78B pop edi
0041C78C pop esi
0041C78D pop ebx
0041C78E mov esp,ebp
0041C790 pop ebp
0041C791 ret
[解决办法]
解析C++汇编代码
首先,进入函数体,就要执行三条初试化指令:
@01: push ebp
@02: mov ebp,esp
@03: sub esp,0C0h
ebp寄存器在Visual C++中是被默认用来做基址指针的。因此,在刚进入函数执行阶段,都要对ebp进行相应的操作。第一步,如@1语句ebp中的值,然后将它用在本函数中。第二步,获取当前堆栈指针。获得的堆栈指针将作为函数局部变量的基址指针使用。第三条语句@3,因为C/C++中,程序局部变量是在堆栈中分配的。可是,我们并没有在每个函数中发现诸如AllocMem等申请内存的函数或指令。实际上,函数中的局部变量空间的分配就是由这条指令完成的。在本例中,程序分配了0C0H(192)字节的空间供该子函数使用。
其次,是辅助寄存器ebx,edi,esi的状态保存。作为通用寄存器,它们经常被用在一些常见的操作中。特别是在字符串、数组等的操作中,edi、esi通常作为存储目的、源数据的地址指针来使用。因此这里先保存这三个寄存器的值。虽然在本例中,并没有用到ebx和esi,但两者还是被保存了。
[解决办法]
一般按照函数间调用的约定,函数中可以自由使用eax,ecx,edx;
其它寄存器如果需要使用则需要保存,用完时恢复;
进入函数后EBX就入栈,这样后面就可以自由使用EBX了。虽然一般的C函数反汇编后看,都没有使用EBX,但是在很多需要内嵌汇编指令的地方,由于80x86系列的CPU通用寄存器非常少,所以非常有可能要用到EBX,而且编译程序不可能先扫描一遍源程序,看看有没有在内嵌汇编指令中用到EBX然后再入栈,通用的做法就是直接将EBX入栈。
如果函数中没有用到EBX寄存器,通过优化指令,就可以去掉EBX入栈出栈的语句。你可以通过一些专门的反汇编工具看看那些优化过的程序反汇编码,你就会发现,里面没有EBX入栈和出栈的指令。
[解决办法]
void overflow_func(const char *str)
{
// + 9 \
// + 8 |
// + 7 |
// + 6 str : char * <-- 函数参数
// + 5 \
// + 4 |
// + 3 |
// + 2 ret : int <-- 返回地址
// + 1 \
// + 0 |
// + 9 |
// + 8 ebp : int <-- esp push ebp
// + 7 buf[7] : char mov ebp,esp
// + 6 sub esp,48h ;72 Byte?
// + 5 push ebx
// + 4 push esi
// + 3 push edi
// + 2
// + 1
// + 0 buf[0]
char buf[8];
wangzhangyong411(B41) :
void mytrangle()
{
int p[10] = {1};
//p[10] = p[10] - 1;
p[10] = 0x0012FF2C;
p[10 - 32] = 0x0012FF2C;
}
在p缓冲区之前还有一个push ebp,所以p[10+4]的位置才是函数的ret地址,
p[10+4] = 0x0012FF2C才能覆盖掉返回地址,让他不断自我调用。
[解决办法]
不用关心ebx什么意思。
程序运行到需要调用函数的时候,保存一下ebx,函数中就可以随便使用ebx,
函数退出就回复ebx,继续做该做的事情。
汇编能用的寄存器就那么几个,不够用的,所以只能这样了。