汇编的艺术(01)sizeof operator

以前在百度的博客里面学习了逆向一些基本的C语言知识。一方面不能让学习的汇编知识荒废,另外一方面是由于经常碰到一些细节性的问题,需要温故而知新。

学习汇编对于我自己的感觉是:可以从更加底层的角度来窥视C语言以及其他高层语言的细节。这是一件很舒服的事情~

同时一些特殊的工作,比如内核调试,内存错误,函数调用等问题,用汇编的角度来看待会更加方便,更加深刻的理解其机制。

废话不多说,作为开篇《汇编的艺术》,是我今天更巧碰到的一个问题,想记下来,先从最简单的入手,感觉是慢慢来的~

先看一段代码:

int main()  
{  
    char a = 255;  
    printf("%d\n",sizeof(++a));  
    printf("%d\n",a);  
    return 0;  
}  

出乎意料的是,输出的值是:1,-1

难道是sizeof里面的++a没有执行么?带着这样的疑问,看看反汇编代码是啥样的:

int main()  
{  
    char a = 255;  
00000000  push        ebp  
00000001  mov         ebp,esp 
00000003  sub         esp,8 
00000006  cmp         dword ptr ds:[00192E14h],0 
0000000d  je          00000014 
0000000f  call        696E67F9 
00000014  xor         edx,edx 
00000016  mov         dword ptr [ebp-4],edx 
00000019  mov         dword ptr [ebp-8],0 
00000020  xor         edx,edx 
00000022  mov         dword ptr [ebp-4],edx 
00000025  mov         dword ptr [ebp-8],0FFFFFFFFh 
    printf("%d\n",sizeof(++a));  
0000002c  push        1B31B8h 
00000031  push        1    
00000033  push        4F01B8h 
00000038  call        FEEA58CC 
0000003d  add         esp,0Ch 
00000040  nop              
    printf("%d\n",a);  
00000041  push        1B31BCh 
00000046  push        dword ptr [ebp-8] 
00000049  push        4F01C8h 
0000004e  call        FEEA58CC 
00000053  add         esp,0Ch 
00000056  nop              
    return 0;  
00000057  xor         edx,edx 
00000059  mov         dword ptr [ebp-4],edx 
}  
0000005c  mov         eax,dword ptr [ebp-4] 
0000005f  mov         esp,ebp 
00000061  pop         ebp  
00000062  ret              

看到橙黄色标注的部分,果然传参的时候是直接push了1,而++a这个指令在没有在汇编中出现的痕迹。

但是sizeof操作符并不像#define这样的宏一样在预处理阶段就把其替换掉了,sizeof是在编译阶段替换的。

于是理解了,sizeof里面的expression都是不执行的,只关心里面类型的大小。

类似的问题还有sizeof('a'),貌似不同的编译器说法不一,C标准应该是把'a'看成了97,也就是int类型,等于4,

如果里面的数字再大的话,超过了int范围,则是8了,以此类推。

还有是sizeof("a"),这个问题不该有争议,因为传进去的是字符串a的地址,就是一个指针的大小了,32位机器上面是4,64位机器上面应该是8.

btw:

很久没有看过反汇编代码了,很多东西都生疏了。

比如说里面不理解的是,main()函数明明只申请了一个char 的却把栈空间抬高了8个字节,边界对齐为char开辟4字节还能理解。

那另外的4个字节又是什么意思呢?函数最后返回的代码利用到了这4个字节,把里面的值传给eax。

这是汇编的风格,函数的返回值是传给eax的。但是不清楚为什么还要“多此一举”。

我的理解是,因为函数最终要返回的,对于一般的函数如果返回比如 return ans; ans变量肯定要开辟字节空间的,于是return 0; 也按照这个思路照做了?有时间再探究。

第二个问题就是又一次的复习了函数传参时堆栈的情况。感觉都有点生疏了。

printf 第一个参数压入的是格式化字符串的地址,后面压入的几个参数则是变量。对于这类参数可变的函数,平衡堆栈的工作要交给母函数来处理的。

关于这方面详细的介绍请看:http://bbs.pediy.com/showthread.php?t=56518

-------------------------------------------------------

kedebug

Department of Computer Science and Engineering,

Shanghai Jiao Tong University

E-mail: kedebug0@gmail.com

GitHub: http://github.com/kedebug

-------------------------------------------------------

原文地址:https://www.cnblogs.com/kedebug/p/2800225.html