这块儿的知识长时间不用老是忘掉,本文依据《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. 跳转到返回地址
时间: 2024-10-22 18:59:37