大三暑假莫邪以内联汇编_3

大三暑假莫邪之内联汇编__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;
} 


还是老规矩 我们反汇编分析 呵呵

 

图一

大三暑假莫邪以内联汇编_3

 

老规矩了 前面是 prolog 工作 今天在这里补充下栈的初始化,ecx 存的是重复次数 stos dword 以双字节形式初始化为 0xcccccccc (cc 为软中断的机器码)。

接下来,就是一个指针的操作, 我们知道 指针可以指向任何地方,当然指针本身也是有地址的。怎么看? 就是将指针指向自己,我们的目的,就是为了得到

ebp-4的 地址。 因为第一个local对象ebp-4。

接下来, 我们要 移动到 返回地址处, 就是ebp+4。 对于C++程序 ,我们的做法就是 将address(此时存的是自身地址,相当于二级指针)强制转换为2级的函数指针,然后取值 将 _Robber的地址写入该函数内存的 ebp+4 处。

我们通过标记此处的断点来查看 ebp+4的 内容 如下图所示

 

大三暑假莫邪以内联汇编_3

 

图中ebp的地址正是0012FF24, 对于ebp+4 的内容 便是 " 0F 10 40 00  " ebp+8便是第一个参数 我们取到前面就是了。至于该怎么读它, 我们知道windows是小端模式

先存低位再存高位, 所以 正确的读法 应该是 "10 0F 00 40” 结束了么? 没有 刚才是16位下的做法 我们现在是32位 所以类比下 再读一次 0040100F记住这个地址,我

们下面的时候再核对下

 

返回刚才说的地方 因为我们对__Robber2只是简单的call 这里就不说了。

我们看下面的函数调用处

 

大三暑假莫邪以内联汇编_3

 

正如函数调用那样 __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)呢? 让我们去掉它只会再运行一遍 你会看到 下面的结果

大三暑假莫邪以内联汇编_3

 

这种情况,我相信 我们有时候软件或者程序奔溃之后都会出现。假如你选择 关闭它,那么(...........0.0)好吧, 我们还是来看看什么情况再说

打开我们的memory查看器 输入 00241ff1如下图所示

 

大三暑假莫邪以内联汇编_3

 

我们看到了一个熟悉的影子 system32\kernel32.dll 哈哈

这里 就留给 看雪论坛的那帮疯子去解释吧!:)

 

总结:

        函数的返回地址, ebp+4 是个好东西哈哈,我在这里也不再演示怎么干坏事的程序。 正如我文章开头提及的  徒弟领进门 ,修行看个人。 哈哈哈哈哈