请问下
,
在
VC
中函数调用的时候不是
push
了
edi,esi,ebx,ebp
吗
其他三个还好理解是什么 在数值上也好判断 那个 ebx 到底存放了什么啊 ? 为什么每次运行都不一样 , 有时是 7FFDD000, 都在这个值附近 在线等 |
难道是真正的内存地址
?
而不象其他几个所示的是虚拟内存地址
?
|
高手都上哪去了
? 回答问题啦 |
你可以在在单步调试中反汇编代码,就清楚了。
|
我就是反汇编了才看到
ebx
的值
没看懂 已经单步到最细了 |
谁给我个满意的答案
马上给
50
分
|
根据我的试验,在
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 ,但两者还是被保存了。 |
感觉上这几个寄存器应该是用来恢复现场的
从 edi,esi,ebp 都可以看出来 ,PUSH 在被调函数的栈里 应该是调用完后回去的用的 但是 ebx 是什么用的 ? main 调用一个函数的时候 一般的 edi = 0 esi = 0012ff80 ebp = 0012ff80 但是 EBX 怎么就是一个不确定的数值呢 就想知道 ebx 到底在这个时候存放的是什么 |
#include <stdio.h> void mytrangle() { int p[10] = {1}; //p[10] = p[10] - 1; p[10] = 0x0012FF2C; p[10 - 32] = 0x0012FF2C; } int main() { mytrangle(); //test(); return 0; } 现在帖的这个函数 我本想用这个来直接换掉 ebp edi 的值 也确实换掉了 而且换成本身自己这个函数的基址 , 但是按原来的想法他应该返回到自己这个函数 成为不断 自我调用才对 为什么没有效果呢 只是程序最后结束才出错 |
一般按照函数间调用的约定
,
函数中可以自由使用
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 ,继续做该做的事情。 汇编能用的寄存器就那么几个,不够用的,所以只能这样了。 |
成功了
,
终于按照我说的直接在代码里实现跳转了
不容易 ..... 小时候没好好学汇编 |