首页 > 代码库 > 【汇编总结3】函数调用

【汇编总结3】函数调用

这块儿的知识长时间不用老是忘掉,本文依据《0Day安全:软件漏洞分析技术》第二章部分内容做个总结。

1. 函数调用约定

TODO

2. 函数调用过程分析 

2.1 函数调用大致包括以下几个步骤

>1. 参数入栈: 根据函数调用约定将参数按照一定顺序依次压入系统栈中,最常用的就是从 右 往 左 压入栈中。这里可以思考下为什么要从右往左? 因为从右往左压入栈的话,被调函数里面取参数使用的时候,就是从左往右的顺序,一般而言 [EBP + 8] 就是第一个参数,[EBP+0xC]是第二个参数,比较方便

>2. 返回地址入栈:将调用指令(CALL指令)的下一条指令的地址压入栈中。

>3. 代码区跳转:处理器从当前代码区跳转到被调用函数的入口处

>4. 栈帧调整: 第一步,保存当前栈帧状态值,以备后面恢复栈帧时使用(PUSH EBP, 这里的EBP实际上指的是其调用函数的栈帧的栈底位置)

                     第二步,将当前栈帧切换到新栈帧,(MOV EBP, ESP,  更新本函数栈帧的栈底位置)

                     第三步,给新栈帧分配空间(SUB ESP,8H, 把ESP减去所需空间(局部变量所占空间)的大小,更新栈顶)

上述步骤可用下述指令描述:

; 调用函数中指令
; 调用前的一堆指令
PUSH 参数3    ; (假设将要调用的函数有3个参数,从右往左入栈)
PUSH 参数2 
PUSH 参数1 
CALL  函数地址        ; CALL指令依次完成了两项工作
                        ; 第一项,将当前指令的下一条指令地址(返回地址)入栈
                        ; 第二项,跳转到所调用函数的入口处
                        ; 假设下一条指令地址是 0x4000 1234,被调用函数地址
                        ;                       为 0x40002345
                        ; 则相当于有,PUSH 0x40001234  ; JMP 0x40002345

; 被调函数中指令
PUSH EBP         ; 保存旧栈帧的底部
MOV  EBP, ESP  ; 设置新栈帧的底部  (上述两条指令顺利完成了栈帧切换)
SUB  ESP, XXX  ; 设置新栈帧的顶部  (为新栈帧开辟了空间)                                                                                   

 2.2 函数返回包含以下几个步骤

>1. 保存返回值: 将函数的返回值保存在EAX寄存器中

>2. 弹出当前栈帧,恢复上一个栈帧:  第一步, 回收当前栈帧的空间(MOV ESP, EBP, 即将栈顶指针恢复到本栈帧的帧底位置)

                                                  第二步,将当前栈帧底部保存的前栈帧的EBP值弹入EBP寄存器,恢复出上一个栈帧

                                                  第三步,将函数返回地址弹给EIP寄存器

>3. 跳转: 根据上面获取到的返回地址,跳转到母函数中继续执行

ADD ESP, XXX    ; 回收当前栈帧空间,或者直接( MOV ESP, EBP )
POP  EBP           ; 将上一个栈帧底部位置恢复到EBP
RETN               ;  1. 弹出返回地址
                   ;  2. 跳转到返回地址

 

【汇编总结3】函数调用