滴水三期:day13.1-返回值,参数,局部变量反汇编

一、返回值反汇编

  • char类型的返回值存储到al中,即返回值数据类型宽度为8位时,用al存储结果

  • short类型的返回值存储到ax中

  • int类型的返回值存储到eax中

  • 宽度为64位的返回值数据类型怎么存储?使用两个32位寄存器来存储。见作业中的第一题

  • 反汇编如下:

    char Func(){
    	return 4;  //对逆向来说,不同的数据类型中存的都是二进制数,所以只用在意宽度即可
    }
    short Func2(){
        return 4;
    }
    int Func3(){
        return 6;
    }
    void main(int argc,char* argv[]){
        int m = Func();  //用一个变量去接返回值
        short n = Func2();
        int i = Func3();
    }                     
    

    如果返回值类型为char则在函数中使用al存储返回值,在函数外将al的值再赋给局部变量等操作,如果变量数据类型宽度比返回值类型数据宽度大,则先进行有符号扩展,再传值

    image-20211207094055359 image-20211207094305640

    返回值类型为short则在函数中使用ax存储,在函数外再将ax中存储的返回值存入内存(局部变量)

    image-20211207094834177 image-20211207094803610

    int类型返回值同理

二、参数传递反汇编

1.本机尺寸

  • 本机尺寸的概念:如果本机是32位的,那么对32位的数据支持最好,如果是64位的,那么对64位的支持。即计算机是32位那么如果一次读取或存入的数据也是32位的,计算机的效率是最高的!64位PC读取64位数据亦是如此

  • 编译器是知道本机尺寸的,且编译器遵守了这个规则:所以你即使传参时用的16位或者8位数据类型,如果PC是32位的,那么都会用32位的寄存器或者内存来存储参数。char类型或者short类型的参数不但没有节省空间,反而浪费了多余的操作

    void Func(char x,short y,int z){
    
    }
    void main(int argc,char* argv[]){
    	Func(1,2,3);
    }
    
    image-20211207101119286

    任何数据类型参数都用32位内存(堆栈)来存取,因为全是push操作。至于用32位内存还是32位寄存器存储看函数使用的调用约定

  • 但是注意:虽然传任何类型参数都用32位容器存,但是用的时候不会把32位容器的32位全部使用,而是根据数据类型的宽度来选择指定宽度的数据使用

    void Func(char x,short y,int z){
    	int m;
        m = x + y + z;
    }
    void main(int argc,char* argv[]){
    	Func(1,2,3);
    }
    
    屏幕截图 2021-12-07 101402
  • 结论:整数类型的参数,一律使用int类型(效率最高)

2.参数传递本质

  • 将上层函数的变量,或者表达式的值“复制一份”,传递给下层函数

    void Plus(int x){		
    	x = x + 1;	    //在调用Plus函数前先将main函数中x的值复制一份,而不是直接使用那块地址(不是指针),所以这里做运算的变量不是main中x变量本身,接着做完运算将结果存储在此时的Plus函数堆栈的[ebp+8]
    }		
    		
    int main(int argc, char* argv[]){		
    	int x = 1;		//这里定义的mian函数局部变量x的值存储在此时main堆栈的[ebp-4]
    	Plus(x);	    //综上所以这里的值依然是1
    	printf("%d\n",x);
    	return 0;	
    }		
    
    • 先把局部变量值复制一份,存到eax中再入栈

      image-20211207103529782
    • 在函数中运算的是复制过来的值,而不是main函数中局部变量x本身的值

      image-20211207103608013

三、局部变量反汇编

  • 数据类型小于等于32位的局部变量,空间在分配时,按32位分配;但使用时按实际的宽度使用

    比如一个short a变量,也会分配32位内存表示这个局部局部变量;比如一个char arr[3],虽然3个char类型数组元素只有24位,但是还是会分配32位来存储arr中的三个元素,只不过有8位是空出来的,浪费了;再比如char arr[5],会分配8字节内存

  • 不要定义char/short类型的局部变量(32位计算机用int效率最高,原理还是按照本机尺寸规则)

  • 函数的参数与局部变量没有本质区别,都是局部变量,都在栈中分配,只不过一个在[ebp+],一个在[ebp-]。所以完全可以把参数当初局部变量使用

  • 局部变量会影响编译器在函数调用初始化时的缓冲区空间:VC6中的编译器默认提升堆栈,开辟的缓冲区(内存)大小为0x40字节–64字节,即16块内存单元(一块内存单元32位,即4字节),如果函数中每定义了一个任意类型局部变量,开辟的缓冲区大小就增加4字节(32位)。比如现在函数中定义了两个任意类型局部变量,那么开辟的缓冲区大小为0x48

    void Func(){
    	char x = 1;
    	int y = 1;
    }
    void main(int argc,char* argv[]){
        Func();
    }
    
    屏幕截图 2021-12-07 105629

    注意不同的编译器开辟的大小规则是不一样的!

  • 而且虽然局部变量中不同类型类型占的宽度不一样,但是小于等于32位的变量都是用4字节内存单位来存储的,只是占用4字节内存中的宽度不同,比如char类型就占分配给它的32位中的8位;short类型就占分配给它的32位中的16位;int类型就占分配给它的32位中的32位

    屏幕截图 2021-12-09 103222

四、作业

1.返回值超过32位时,存在哪里?用long long(__int64)类型做实验

  • long long也是一种数据类型,在VC6中对应的表示形式是__int64,因为long long在VC6中没法直接使用,long long类型变量宽度是64bit,即8字节

    __int64 Func(){
        __int64 x = 0x1234567890;
    	return x;	//返回值占8字节	
    }
    void main(int argc,char* argv[]){
       __int64 i = x;  
    }
    
  • 分析反汇编:

    • 可以发现如果局部变量定义的数据类型是64位宽度,即8字节,那么提升堆栈时要开辟的缓冲区大小应该为0x40 + 0x8 = 0x48字节大小

      image-20211207121905744
    • 而且将一个8字节的数存到内存中要占用两个内存块,因为一个内存块的地址表示4字节的内存,那么此时就要将0x1234567890拆成两半,低4字节部分存到[ebp-8]中,高4字节部分存到[ebp-4]中

      image-20211207121847290
    • 此时会使用两个32位寄存器来存储long long类型的返回值

      image-20211207121942817
    • 如果main函数中的long long类型的局部变量i要使用Func函数的64位返回值,那么同样也是将eax和edx两个寄存器中存储的返回值存储到main函数为变量i开辟的两个内存块的空间中,且eax赋值给低位内存,edx赋值给高位内存

      image-20211207122543272
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值