汇编练习

我常常在想一些莫名其妙的问题,走路也想,吃饭也想,睡觉也想,先看段代码:

00BE1380  push        ebp 
00BE1381  mov         ebp,esp 
00BE1383  sub         esp,0D8h        
00BE1389  push        ebx  
00BE138A  push        esi 
00BE138B  push        edi 
00BE138C  lea         edi,[ebp-0D8h] 
00BE1392  mov         ecx,36h        
00BE1397  mov         eax,0CCCCCCCCh     
00BE139C  rep stos    dword ptr es:[edi]     
 
00BE139E  mov         esi,esp 
00BE13A0  push        0Ch 
00BE13A2  call        dword ptr [__imp__malloc (0BE82B0h)] 
00BE13A8  add         esp,4 
00BE13AB  cmp         esi,esp 
00BE13AD  call        @ILT+300(__RTC_CheckEsp) (0BE1131h) 
00BE13B2  mov         dword ptr [heap_array],eax 
 
00BE13B5  mov         dword ptr [idx],2 
 
00BE13BC  mov         eax,dword ptr [heap_array] 
00BE13BF  mov         dword ptr [eax],0Ah 
 
00BE13C5  mov         eax,dword ptr [heap_array] 
00BE13C8  mov         dword ptr [eax+4],14h 
 
00BE13CF  mov         eax,dword ptr [heap_array] 
00BE13D2  mov         dword ptr [eax+8],1Eh 
 
00BE13D9  xor         eax,eax 

代码第一行将ebp值入栈,接着将当前esp的值赋给ebp,好吧,从此以后这个值,也就是ebp成了函数栈帧的基址,接着栈指针esp突然渐少0D8h,也就是13 * 16 + 8 = 216个字节,也就是说,在栈里面分配216个字节单元,接着依次将ebx、esi、edi入栈,栈顶指针再次减少3 * 4 = 12个字节,而代码:
 00BE138C  lea         edi,[ebp-0D8h]
本质上是edi =  ebp-0D8h,因为不可以使用mov edi,ebp-0D8h这样的指令,所以使用lea(load effective address)。
接着将ecx设置为36h,也就是十进制3 * 16 + 6 = 54,接着:
00BE1397  mov         eax,0CCCCCCCCh      
00BE139C  rep stos    dword ptr es:[edi]

第一行将eax设置为 0CCCCCCCCh。接着用eax的值去初始化从ebp-0D8h开始往高地址的216个字节空间,这216个字节空间是上面代码分配的栈空间。如果这个时候用printf函数输出这段内存的内容,会显示:
烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫烫
也就是108个中文汉字“烫”,因为两个0xCC合起来是一个汉字“烫”,而216字节可显示108个“烫”字。

以上代码是在debug模式下,编译器自动生成的一些汇编代码,与程序实际内容毫无关系。

接着正文代码:
00BE139E  mov         esi,esp 
00BE13A0  push        0Ch 
00BE13A2  call        dword ptr [__imp__malloc (0BE82B0h)] 
00BE13A8  add         esp,4 
00BE13AB  cmp         esi,esp 
00BE13AD  call        @ILT+300(__RTC_CheckEsp) (0BE1131h) 
00BE13B2  mov         dword ptr [heap_array],eax

接着将esp赋值给esi,也就是让esi等于当前栈顶指针, 00BE13A0  push        0Ch  中0Ch是一个立即数(中国人总喜欢把简单的东西搞得复杂化,我在看杨季文老师的汇编教材时,看了几遍都没明白他说的“立即数”是啥意思,其实就是一个程序中的常数嘛),0Ch 等于10进制的12,也就是将数字12入栈,接着调用C函数库函数malloc,malloc函数申明于stdlib.h文件,其原型是:
void* malloc(size_t size);
而这个12也就是作为实参传给size, 函数返回值放在eax中,接着代码是:
00BE13A8  add         esp,4
这个地方我看了好久觉得晕,为什么要让栈指针向高地址方向移动4个字节?后来恍然大悟:因为malloc是C库函数,它的调用方式__cdecl,所以调用这个函数之后的栈由调用方清理,也就是主程序清理,那么 add esp,4用来将栈里面四个字节的内存清理掉,但是为什么是4个字节?而不是更多或者更少?因为malloc有一个参数size,这个参数占四个字节,当调用malloc函数结束以后,栈中仍然才在4个字节的空间是为size参数开辟的,我们必须释放掉。

接着代码:
00BE13AB  cmp         esi,esp 
00BE13AD  call        @ILT+300(__RTC_CheckEsp) (0BE1131h)
这是一种保护措施,这个时候(调用malloc函数之后)理论上说,esi应该与esp相等,第二行我猜测:如果esi与esp不相等,那么ZF标志位应该为0,这说明有错误出现(这种错误概率是非常小的), 而函数CheckEsp会根据ZF是否为0来决定一些情况,比如ZF等于0,说明程序出错了,而这个错误是内存错误,这是一个很严重的错误。
接着代码:
 00BE13B2  mov         dword ptr [heap_array],eax
 00BE13B5  mov         dword ptr [idx],2
 也就是:
上文中我们调用了malloc函数,这个函数的返回值放在eax中,现在执行 mov  dword ptr [heap_array],eax ,也就是将heap_array = eax,通俗地说就是将malloc返回值赋给heap_array。
代码mov  dword ptr [idx],2含义是:[idx] = 2。
后续代码:
00BE13BC  mov         eax,dword ptr [heap_array] 
00BE13BF  mov         dword ptr [eax],0Ah
 使用了eax寄存器作为中介容器,因为不可以用mov指令将立即数0Ah赋值给dword ptr [heap_array],所以最终结果是:[heap_array] = 0Ah = 10。同理:
00BE13C5  mov         eax,dword ptr [heap_array] 
00BE13C8  mov         dword ptr [eax+4],14h
这段代码的含义:  [heap_array + 4] = 14h = 1 * 16 + 4 = 20.
而代码:
00BE13CF  mov         eax,dword ptr [heap_array] 
00BE13D2  mov         dword ptr [eax+8],1Eh
含义是:[heap_array + 8] = 1Eh = 1 * 16 + 14 = 30

我们发现 [heap_array]、[heap_array + 4]、[heap_array + 8],这是什么?数组嘛!而heap_array又是函数malloc返回的,所以总结起来,这段汇编代码实际上对应:
 
int *heap_array = (int*)malloc(3 * sizeof(int));
int idx = 2;
heap_array[0] = 10;
heap_array[1] = 20;
heap_array[2] = 30;

别急,还有最后一行:
 00BE13D9  xor         eax,eax
 这行使用xor指令将eax清零,如果这个时候外层函数调用完毕以后,那么eax的值就将作为返回值返回,那么
就是:
return 0;

好了,整段汇编代码分析完毕,但是这段程序存在一个问题,即没有使用delete去释放malloc函数分配的内存。

Okay,that's all.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值