函数调用及返回汇编浅析-C语言部分之无参无返回

举报
媒体服务小助手ultra 发表于 2023/11/10 15:14:20 2023/11/10
【摘要】 函数调用过程就是内存申请和数据流动的过程,熟悉其中原理,对于写出“人类高质量源码”有很好的辅助作用 后面将分别从C、C++、Objective-C三种语言来解析

函数调用过程就是内存申请和数据流动的过程,熟悉其中原理,对于写出“人类高质量源码”有很好的辅助作用

后面将分别从C、C++、Objective-C三种语言来解析

篇幅所限,此篇文章只介绍C语言部分

C语言

C语言的函数调用有如下种类

1、无参无返回

2、无参有返回

3、有参无返回

4、有参有返回

这里取几个典型:

1、无参无返回

2、有基础类型参数无返回(篇幅所限,下篇介绍)

3、有基础类型参数有返回(篇幅所限,下篇介绍)

无参无返回

取简单的helloword来分析下

第一步、将源码编译链接成可执行程序

第二步、用IDA查看生成的汇编程序,这里生成的是x86-64汇编

主函数的汇编如下

sayHello函数生成的汇编

第三步、分析汇编

在进行汇编分析前,先对其中的一些关键字进行解释

寄存器(关于寄存器的知识,感兴趣的可以自行查询):

rbp:是栈帧指针寄存器,用于标识当前栈帧的起始位置,函数的返回地址,rbp和rsp配合使用,rsp和rbp得差值,就是当前函数申请得栈内存得大小

rsp:是堆栈指针寄存器,通常会指向栈顶位置,堆栈的 pop 和push 操作就是通过改变 rsp 的值即移动堆栈指针的位置来实现的。

edi:32位寄存器,用于传递第一个参数

rdi:64位寄存器,用于传递第一个参数

rsi:64位寄存器,用于传递第二个参数

eax:32位寄存器,累加寄存器,用于存储返回值

al:8位寄存器,累加寄存器,用于存储返回值

汇编执行:

push:入栈,将寄存器的值存入栈顶

mov:移动

call:调用

xor:异或,主要用于清空寄存器的值

sub:减

add:加

retn:返回

lea:load effective address, 加载有效地址,将地址加载到寄存器中

 

main执行过程

push    rbp    //将调用方分配的栈帧地址存入栈内存,函数执行完后,需要恢复此寄存器,目的是可以返回调用方的内存地址,所保存的地址是rsp寄存器指向的地址

mov     rbp, rsp  //将堆栈指针寄存器存入rbp

sub     rsp, 10h    //rsp寄存器的值减0x10,也就是栈顶向下移动0x10大小,用于保存当前函数得栈数据

mov     [rbp+var_4], 0    //前面可以看到:var_4           = dword ptr -4,此句话就是mov     [rbp -4], 0,其中"[]",用于将寄存器的值转成内存地址(栈地址),也就是将                                               rbp-4的地址存入0

mov     [rbp+var_8], edi    //和前面一句一样:将edi里的值存入[rbp - 8]的内存地址,保存main函数的的第一个参数,argc,4个字节

mov     [rbp+var_10], rsi    //和前面一句一样:将rsi里的值存入[rbp - 0x10]的内存地址,保存main函数的第二个参数的地址,argv,8个字节

call    _sayHello    //并未传输任何参数调用sayHello函数

xor     eax, eax    //返回结果置零

add     rsp, 10h    //恢复堆栈指针寄存器,相当于回收局部变量得内存,但是并未将相应得内存置0,有被重新获取到得风险

pop     rbp    //恢复栈帧指针寄存器,恢复调用前的帧位置

retn    //返回调用方

 

通过对上述main函数的分析,我们可以将main函数置行过程分为如下几个阶段

第一、保存调用点的栈帧寄存器到栈内存,并重置堆栈指针环境

第二阶段、开辟空间,保存局部变量值

第三阶段、调用sayHello

第四阶段、将返回值置为0,存入寄存器

第五阶段、恢复rsp、rbp

sayHello执行过程

push    rbp    //同上

mov     rbp, rsp    //同上

lea     rdi, aHelloWorld ; "Hello, World!\n"    //加载有效地址,将字符串地址加载到寄存器中,其中rdi用于传入第一个参数

mov     al, 0    //清空返回值位0

call    _printf    //调用printf函数

pop     rbp    //恢复栈

retn    //返回

 

对sayHello执行的几个阶段进行图形解析

第一阶段、同上

第二阶段、准备参数调用printf

 第三阶段、恢复

总结

通过对无参无返回的函数进行分析,我们可以得出如下几个结论

1、栈是自大向小的方向进行扩展的(堆是反过来的,这里没做介绍)

2、函数调用,迭代使用的寄存器是,rbp和rsp

3、函数调用后并不会清理使用过的内存,数据还在,通过一定的手段是可恢复的,所以华为C/C++规范里就明确表示敏感数据用后需清理,敏感数据不能放到string的局部变量里


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。