网站建设插件代码大全,网站优化图片,网页案例,网站手机微信三合一怎么做目录 1、调用函数流程#xff08;main函数调用print函数#xff09;#xff1a;Step1 保存main函数现场地址等信息Step2 跳转到print函数的位置Step3 执行print函数的指令Step4 返回main函数#xff0c;执行下一条指令流程连续性总结 2、其他知识总结 1、调用函数流程… 目录 1、调用函数流程main函数调用print函数Step1 保存main函数现场地址等信息Step2 跳转到print函数的位置Step3 执行print函数的指令Step4 返回main函数执行下一条指令流程连续性总结 2、其他知识总结 1、调用函数流程main函数调用print函数
Step1. 保存main函数现场地址等信息
Step2. 跳转到print函数的位置
Step3. 执行print函数的指令
Step4. 返回main函数执行下一条指令
Step1 保存main函数现场地址等信息
要存储的内容主要有3个
1传入print函数的形参【父函数中实现】
2原eipmain函数call指令的下一条指令的地址【父函数中实现】
3原ebpmain函数的ebp地址因为是main函数直接调用print函数【子函数中实现】
实现方式
在main函数中依次执行以下指令
push eax; # 传入函数的形参arg
call print; #作用是保存eip。当然也有跳转的作用见Step2在print函数开头依次执行以下指令
push ebp; # 保存父函数main函数的栈底地址方便调用函数结束后pop回去
mov ebp esp; # 指定print函数的栈帧基地址print函数对应的栈底地址Step2 跳转到print函数的位置
核心是修改eip寄存器把eip寄存器的值修改为print函数的入口地址。
实现方式 在main函数中调用call指令
call print.77c70888; # 相当于esp esp - 4; push eip(此时的eip值就是下面main函数中的下一条指令的地址); mov eip print函数的地址77c70888跳转到新函数
main函数中的下一条指令;Step3 执行print函数的指令
子函数最开始一般要执行以下的命令
push ebp; # 保存原ebp
mov ebp esp; # ebp指向当前函数的栈底
...
sub esp, num; # 为子程序分配栈空间
...
pop ebp; # 让ebp指向父函数的栈帧基地址
ret n; # 让eip指向“main函数call指令的下一条指令的地址”n是指平衡堆栈即让最先push进去的形参args清空Step4 返回main函数执行下一条指令
核心是修改eip寄存器的值把eip寄存器的值修改为main函数中下一条指令的地址。
实现方式在print函数中调用ret命令
ret ; #相当于pop eip; esp esp 4注意如果原先栈中还有其他数据esp 没有归位会导致主函数引用栈中数据出错。在这种背景下出现了堆栈平衡的概念。即还需对esp 进行单独操作才能将 esp 指向原函数栈顶。以常见的 c 语言函数有好几种调用规则。比如 cdecl 方式和 stdcall 方式。
cdecl 方式中由主程序执行 add esp, n 指令调整 esp达到堆栈平衡。在 stdcall 方式中由子程序在返回时执行 ret n 平衡堆栈。n 其实就是函数的参数所占的空间大小。
流程连续性总结
重要参考《从汇编角度理解 ebpesp 寄存器、函数调用过程、函数参数传递以及堆栈平衡》
函数调用
在一个函数中调用另外一个函数往往有以下几个步骤
汇编指令指令归属函数SP 变化作用push arg2主函数sp-4push arg1主函数sp-4call function主函数sp-4开始调用子程序同时保存返回地址push ebp子函数sp-4mov ebp, esp子函数sp-4将当前esp 存入 ebp目的是定位函数参数sub sp, #num子函数sp-num为子程序分配栈空间…子函数…函数的具体实现逻辑pop ebp子函数sp4ret子函数sp4
说明
push arg 在调用一个函数之前需要把传递的参数压入栈因此需要有。每次 push 之后栈多了一个字长32 位系统 -- 4 字节因此栈顶需要往上移动 4 字节该指令暗含 sub sp, #4call call 指令用来调用某个函数该指令有3个操作1sp sp - 42将返回地址压入栈 (3)修改eippush ebp mov ebp, esp 这样的操作你会在各个函数的开头见到保存上一个函数栈的基址并更新本函数的基址ret即 return此时 sp 应该指向 call 指令刚刚压入的返回地址执行 ret 其实就是将此时栈中的数据弹出存至 eip 寄存器。eip 存放的是下一条即将执行的指令的地址。 同时 sp sp 4ret 指令相当于 pop eipesp esp 4call 指令相当于 push eipesp esp - 4; mov eip 新函数的地址
下图左边是主函数调用子函数右边是子函数返回主函数 2、其他知识总结
1、esp寄存器永远指向整个栈帧的栈顶esp寄存器保存的值是堆栈的地址值栈顶中的内容可能是一个地址值也可能是一个立即数等等。esp寄存器指向的堆栈地址始终有值所以push是先移esp后压栈pop是先弹栈后移esp。
2、ebp寄存器永远指向当前函数所在栈帧的栈底ebp寄存器保存的值是堆栈的地址值栈底中的内容永远保存的是上一个函数父函数的ebp地址方便函数执行完后pop回去即修改ebp
ebp 的作用之一就是找到函数的形参(通过ebp 偏移量)当然栈中的局部变量也是可以通过 ebp 来定位的ebp - 偏移量)
3、父函数调用子函数子函数的栈帧低地址是紧挨着父函数的栈帧高地址。 所以父函数调用子函数一定是①args先压榨 ② 然后 原eip压栈by call③ 再 ebp压栈 ④ 最后函数的局部变量压榨 父函数栈帧 和 子函数栈帧隔着的东西就是args 和 eip
4、栈的增长方向永远向着低地址的方向增长。
5、call 指令相当于esp esp - 4; push eip; mov eip 函数的地址.77c70888 (注意push中已包含esp移动这里只是表示先后)
6、ret指令相当于pop eip; esp esp 4 (注意pop中已包含esp移动这里只是表示先后)
7、push eax指令只会修改栈和esp① esp esp - 4 ② mov [esp], eax 这个[esp]表示esp指向的栈值(内存值)被赋值。
8、pop eax指令不仅会修改栈和esp而且还会修改eax(寄存器被赋值)① mov eax, [esp] ②esp esp 4
9、寄存器的地址值是不会变的变的只是寄存器中装的堆栈地址值以及这个堆栈地址中的内存内容值。这个类似并区别于C语言的指针变量。C语言指针变量有2个地址值一个是指针变量装的堆栈地址值另一个是指针本身的地址值也位于堆栈。指针变量声明后它存在于内存中堆栈中指针释放了这个指针的地址值也就无了。区别点在于 指针有本身的地址值可以通过取出来而寄存器没有本身的地址值的说法取不出来或者有也是固定的但不常用。指针变量的常操作数据有3个①p ②*p ③pesp寄存器常操作的数据有2个①esp ②[esp]
espesp寄存器的内容值即栈/内存的某个地址[esp]栈中的值 / 内存中的值