汇编部分
1、call 的本质相当于push+jmp,ret的本质相当于pop+jmp。
2、Windows中,不管哪种调用方式都是返回值放在eax中,然后返回。外部从eax中得到值。
3、Ebp总是被我们用来保存这个函数执行之前的esp的值。
4、把局部变量区域初始化成全0cccccccch,0cch实际是int 3 指令的机器码,这是一个断点中断指令。
5、任何一段中间不加任何跳转,连续的mov和加减乘除指令一般都可以还原为一个C表达式。
如果有下面的代码段,说明可能是含有数组或结构体。
Mov eax,<数组下标>
Inul eax,eax,<结构大小>
Mov ecx, <结构数组开始的地址>
Mov eax, dword ptr [ecx + eax]
6、分析汇编指令时:
与堆栈操作相关的,call,ret等相关指令,我们叫做函数调用([函数])指令:F
流程控制代码,涉及判断和跳转指令:C
数据处理指令,其它一般为数据处理指令:D。常用寄存器
寄存器 | 名称 | 常见用途(未完) |
eax | 累加器(Accumulator) | 函数返回值 |
ebx | 基址寄存器(Base) | 可作为存储指针来使用 |
ecx | 计数器(Counter) | 在循环和字符串操作时,用来控制循环次数 __thiscall中传递this指针 |
edx | 数据寄存器(Data) | |
esp | 堆栈指针寄存器(Stack) | |
ebp | 基地址指针寄存器(Base) | |
esi | 源地址寄存器(Source Index) | |
edi | 目的地址寄存器(Destination) |
常用汇编指令
push | 把一个32位的操作数压入堆栈,这个操作会导致esp减4. |
pop | 与push相反,esp加4,一个数据出栈 |
call | 调用函数。将下一条指令的地址压栈,然后跳转到所调用函数的开始处,本质相当于push+jump |
ret | 与call相对应,跳转到栈顶数据所指的地址,本质相当于pop+jump。对于_cdecl 调用的函数,通常会在ret之后进行exp-[n],用于清理调用参数堆栈 |
xor | 异或,常用于清零操作,例如: xor eax eax |
lea | 取得地址(第二个参数)后放入前面的寄存器中。 |
stosw | 将eax中的数据传送给edi,之后edi+4。常与rep一起使用,用于初始化内存段 |
rep | 当eax>0时,重复后面的指令 |
jp,jl,jge | 根据eax中值与0的关系跳转 |
cmp | 比较指令,将结果放入eax中,往往是jp,jl,jge之类跳转指令的执行条件 |
函数调用规则
调用方式 | 简要说明 | 堆栈清理 | 参数传递规则 |
_cdecl | C 编译器的默认调用规则 | Caller | 从右到左 |
_stdcall | 又称为WINAPI | Callee | 从右到左 |
__thiscall | C++成员函数调用方式 | Callee | this放入ecx,其他从右到左 |
__fastcall | Callee | 前两个等于或者小于DWORD大小的参数放入ecx和edx,其他参数从右到左 |
_cdecl调用通常的asm代码:
被调用方:
1.保存ebp。ebp总是用来保存这个函数执行之前的esp值。执行完毕之后,我们用ebp回复esp;同时,调用此函数的上层函数也用ebp做同样的事情。
2.保存esp到ebp中。



3.在堆栈中预留一个区域用于保存局部变量。方法是将esp减少一个数值,这样就等于压入了一堆变量。要恢复的时候直接把esp回复成ebp保存的数据就可以了。
4.保存ebx、esi、edi到堆栈中,函数调用完成后恢复。





5.(debug版)把局部变量全部初始化为0xcccccccch.

