0x00 函数
函数是一系列指令的集合,为了某个重复使用的功能
0x01 函数调用
使EIP指向集合入口
JMP
CALL
JMP存在的问题是函数执行完成后不方便返回函数调用的位置,因此常用CALL来调用函数
CALL跳转到函数,RETN返回到调用指令的下一条指令
0x02 参数传递/返回值
函数可能处理外部的数据,函数使用空间(寄存器、内存)临时存放这些数据.
在调用函数之前,将数据写入到函数指定的空间内,这一过程为参数传递.
数据写入到那个空间,根据函数来决定.
汇编中常用EAX存储返回值
0x03 堆栈传参
寄存器的个数是有限的且可能存有稍后会使用到的数据,为了避免这种限制,通过堆栈传递参数是一种不错的选择
call后栈顶保存返回的地址,因此可以使用sp+偏移的形式来获取参数
0x04 堆栈平衡
1) 返回父程序时,ESP要指向保存下一条指令地址的内存(执行CALL指令时压入堆栈的地址),保证程序正确运行
2) 通过堆栈传递参数时,在函数执行完毕后,要平衡参数导致堆栈的变化.
例
1) CALL 0x468F0
MOV EAX,1
PUSH EAX <- 0x468F0
PUSH EBX
ADD ESP,8 //平衡堆栈,使ESP正确指向返回地址
RETN
2) 外平栈
PUSH 0x11223344
PUSH 0X44223311
CALL 0x248EA
ADD ESP,8 //函数外部平衡堆栈,使ESP指向参数前的地址,参数此时没有任何意思,霸占着内存空间
MOV EAX,DWORD PTR DS:[SP+8] <- 0x248EA
ADD EAX,DWORD PTR DS:[SP+4]
RETN
内平栈
PUSH 0x11223344
PUSH 0X44223311
CALL 0x188CFA
MOV EAX,DWORD PTR DS:[SP+8] <- 0x188CFA
ADD EAX,DWORD PTR DS:[SP+4]
RETN 8 //函数内部平衡堆栈,使ESP指向参数前的地址
0x05 ESP寻址
使用堆栈传递参数时,在函数内通过ESP+偏移地址的形式快速定位到参数.
如果函数内对堆栈进行了操作,ESP会改变,那么偏移地址需要考虑到堆栈的改变,偏移地址需要额外的计算,使其ESP+偏移地址变得复杂起来
ESP寻址: 通过ESP+偏移地址确定数据存放位置
0x06 EBP寻址
ESP用于指向栈顶 -> 堆栈内存使用到了那个位置
EBP用于指向栈底 -> 这里的栈底不是真实含义的栈底
在调用函数前,将EBP压栈,然后将ESP写入EBP,此时ESP EBP都指向栈顶,
之后的堆栈传参、函数调用、堆栈操作等修改的是ESP,EBP指向的还是函数调用前的栈顶,
当函数调用完,将EBP写入ESP,此时ESP保存的是EBP的内容,在POP EBP,这样堆栈就恢复到函数调用前的状态了
EBP根据具体实现来判断,这里是我的一种思路
基于函数内堆栈改变,ESP+偏移地址寻址变得复杂,因此通过EBP+偏移地址来定位参数
EBP寻址: 通过EBP+偏移地址确定数据存放位置