大三暑假莫邪以内联汇编_3
昨天简单的介绍了堆栈(不瞒你说,汇编学校教的是实模式16位的,保护模式下32汇编没怎么接触),发现中间有些地方,我自己也没明白。于是就跳过了说明(为什么我此时想到了一句话“徒弟领进门,修行靠个人呢?”)。其中, 对于 esp+-4 的现象 比如 local对象初始化时 ebp-4, 这里的4指字节我知道,但为什么是4呢? 于是我网上搜也没有搜到 结果,缺 搜到了 我的下一个问题的 大虾写的一篇帖子,我等等介绍
此时,我想ebp既然是一个指针寄存器,那它应该是按照指针的大小偏移的。 那么,照这样的话。64位下 应该是ebp-8.。 由于,我没有64位的环境,我也不能验证。暂时就这么理解了。
还有一个就是 函数返回地址 是 ebp+4 为什么呢?于是我就看到下面的帖子
给一个大侠的 链接帖子:http://blog.****.net/iiprogram/article/details/2050668
最后我在网上零零碎碎的找到 函数call的时候 会把地址push 。那不就是在 参数入栈 之后了么? 其实不然, 第一个参数入栈是从 ebp+8开始的,之后偏移4继续入第二个参数。至于参数入栈的顺序,也取决于调用约定。
再得知这个有趣的结果以及 看了上面那个大侠的帖子 之后, 我写了下面这个程序。
程序内容可以概括为: CodeMonkey 遇到了 _Robber2的XiaoMing和_Robber Tom。(表面上看 好像这两个强盗同时出现) 实际上CodeMonkey 只看到了 XiaoMing(小明躺着也中枪),于是他打电话报警,把XiaoMing的地址给了警察。 然后打完电话后,Tom从背后跳出来,成功抢劫。XiaoMing缺成了替罪羊。:)
#include<iostream> #include<stdlib.h> typedef void (__stdcall *HiJack)(void); void __stdcall Tom(void) { std::cout<<"Show me the money!"<<std::endl; exit(-1); return ; } void __stdcall XiaoMing(void) { std::cout<<"Whoseyourdaddy!"<<std::endl; return ; } int __stdcall CodeMonkey(HiJack _Robber, HiJack _Robber2) { unsigned char* address = reinterpret_cast<unsigned char*>(&address);//ebp-4 address += 8;//ebp+4 (*_Robber2)(); *reinterpret_cast<HiJack*>(address) = _Robber; printf("To 110: 我要报警,_Robber2 的地址是: %p\n", _Robber2); return 0; } int main() { CodeMonkey(Tom, XiaoMing); return 0; }
还是老规矩 我们反汇编分析 呵呵
图一
老规矩了 前面是 prolog 工作 今天在这里补充下栈的初始化,ecx 存的是重复次数 stos dword 以双字节形式初始化为 0xcccccccc (cc 为软中断的机器码)。
接下来,就是一个指针的操作, 我们知道 指针可以指向任何地方,当然指针本身也是有地址的。怎么看? 就是将指针指向自己,我们的目的,就是为了得到
ebp-4的 地址。 因为第一个local对象ebp-4。
接下来, 我们要 移动到 返回地址处, 就是ebp+4。 对于C++程序 ,我们的做法就是 将address(此时存的是自身地址,相当于二级指针)强制转换为2级的函数指针,然后取值 将 _Robber的地址写入该函数内存的 ebp+4 处。
我们通过标记此处的断点来查看 ebp+4的 内容 如下图所示
图中ebp的地址正是0012FF24, 对于ebp+4 的内容 便是 " 0F 10 40 00 " ebp+8便是第一个参数 我们取到前面就是了。至于该怎么读它, 我们知道windows是小端模式
先存低位再存高位, 所以 正确的读法 应该是 "10 0F 00 40” 结束了么? 没有 刚才是16位下的做法 我们现在是32位 所以类比下 再读一次 0040100F记住这个地址,我
们下面的时候再核对下
返回刚才说的地方 因为我们对__Robber2只是简单的call 这里就不说了。
我们看下面的函数调用处
正如函数调用那样 __stdcall的调用约定从右边开始 先push XiaoMing 然后 Tom
我们发现Tom的 地址 是0040100f 和我们刚才 再ebp+4 中看到的地址一样。 看来函数的返回地址就是在ebp+4处 没错。
等等,是不是忘了一个地方了
为什么
void __stdcall Tom(void)
{
std::cout<<"Show me the money!"<<std::endl;
exit(-1);
return ;
}
会有exit(-1)呢? 让我们去掉它只会再运行一遍 你会看到 下面的结果
这种情况,我相信 我们有时候软件或者程序奔溃之后都会出现。假如你选择 关闭它,那么(...........0.0)好吧, 我们还是来看看什么情况再说
打开我们的memory查看器 输入 00241ff1如下图所示
我们看到了一个熟悉的影子 system32\kernel32.dll 哈哈
这里 就留给 看雪论坛的那帮疯子去解释吧!:)
总结:
函数的返回地址, ebp+4 是个好东西哈哈,我在这里也不再演示怎么干坏事的程序。 正如我文章开头提及的 徒弟领进门 ,修行看个人。 哈哈哈哈哈