声明:本文整理自《逆向工程权威指南(上册)》
非常乱,不行整理了。
第三章
RET 将控制权交给调用程序(将控制权交给操作系统)
编译器在字符串常量的尾部添加了00H,原因是为这个字符串常量添加结束标志(即数值为0的单个字节)
push offset $SG3803
通过push,把字符串指针推送入栈。
call _printf
printf函数结束之后,程序的控制流返回到main函数,字符串地址仍然在栈中,需要调整栈指针(ESP寄存器的值)来释放这个指针。
add esp,4;等价于POP 寄存器
把esp寄存器的值加4来释放这个指针。加4的原因是x86内存地址使用32位(即4字节),在x64的平台上,就得加8。
IntelC++编译器使用POPECX而不是add来释放数据栈,是因为POP ECX对应的Opcode比add esp的opcode要短。
main函数的最后一项任务是让eax的值为0
AND ESP,0FFFFFFF0H
令ESP的值向16字节边界对齐(成为16的整数倍)。主流的编译规则规定“程序访问的地址必须向16字节对齐(被16整除)”
SUB ESP,10h
在栈中分配0x10bytes,即16字节。
LEAVE指令,等效于“MOVESP,EBP”和“POPEBP”两条指令。此指令调整了数据栈指针ESP,并将EBP的数值恢复到调用这个函数之前的初始状态。程序段在开始部分就对EBP进行了操作,所以函数要在退出之前恢复这些寄存器的值。
x86-64硬件平台上,寄存器和指针都是64位的,存储在R-字头的寄存器里。编译器会优先使用寄存器传递部分参数,再利用内存传递其余的参数。windows64的程序还会使用RCX、RDX、R8、R9这四个寄存器来存放函数函数参数。
ARM模式
STMFD SP!,{R4,LR}
把R4寄存器和LR寄存器的数值放到数据栈中,此指令首先将SP递减,在栈中分配一个新的空间以便存储R4和LR的值。STMFD可以用来在指定的内存空间存储多个寄存器的值。
ADR R0,ahelloworld
先对PC进行取值操作,将字符串的偏移量与PC值相加,将结果存储到R0之中
push ebp;在栈里保存EBP寄存器的内容
BL _2printf
BL实施的操作是1.将下一条指令的地址写入LR寄存器2.将printf函数的地址写入PC寄存器,引导系统执行该函数。
CISC(复杂指令集)和RISC(精简指令集)在工作模式上的区别,就是如果拥有复杂指令集,操作系统可以利用栈存储返回地址。
Mov R0,#0
将R0寄存器清0
LDMFD SP!,R4,PC
把栈里存储的PC的值和R4寄存器的值恢复回来。
mov ebp,esp;将ESP的值复制到EBP寄存器
sub esp,x;修改栈的高度,以便为函数的局部变量申请存储空间。
在函数执行期间,EBP是函数访问局部变量和函数参数的基准值。
mov esp,ebp
pop ebp
RET 0
释放栈中申请的内存,还原EBP寄存器的值,将代码控制权还原给调用者函数
第5章 栈
ARM的栈分为递增栈和递减栈。递减栈的首地址是栈的最高地址,栈向低地址增长,栈指针的值随栈的增长而减少。递增栈则相反。
栈的用途:
1.保存函数结束时的返回地址。
call指令结束后返回地址将被保存在栈里;在call所调用的函数结束之后,程序将执行无条件跳转指令,跳转到这个返回地址。
2.参数传递
cdecl,通过栈指针获取其所需要的参数。
3.存储局部变量
向栈底调整栈指针,函数可在数据栈里分配出一片可用于存储局部变量的内存空间。
4.alloca函数
alloca函数直接使用栈来分配内存,这是他与malloc的区别。程序不需要特地使用free()来释放由alloca函数申请的内存。
ESP-0xC | 第2个局部变量,在IDA中为var_8 |
ESP-8 | 第1个局部变量,在IDA中为var_4 |
ESP-4 | 保存的EBP值 |
ESP | 返回地址 |
ESP+4 | arg1,在IDA中记为arg_0 |
ESP+8 | arg2,在IDA中记为arg_4 |
ESP+0xc | arg3,在IDA中记为arg_8 |