1. intel X86有哪些寄存器

- 累加寄存器(
AX)。用在算数运算
- 基址寄存器(
BX)。作为一个指向数据的指针(在分段模式下,位于段及寄存器DS)
- 计数寄存器(
CX)。用于位移/循环指令和循环
- 数据寄存器(
DX)。用在算数运算和I/O操作
- 堆栈指针寄存器(
SP)。用于指向堆栈的顶部
- 栈基址指针寄存器(
BP)。用于指向堆栈的底部
- 源变址寄存器(
SI)。在流操作中用作源的一个指针
- 目标索引寄存器(
DI)。用作在流操作中指向目标的指针

CF(bit 0)[Cary fag] 若算术操作产生的结果在最高有效位(mos-significant bit)发生进位或借位则将其置1,反之清零。这个标志指示无符号整型运算的溢出状态,这个标志同样在多倍精度运算(multiple-precision arithmetic)中使用。
PF(bit 2)[Parity flag]如果结果的最低有效字节(least-significant byte)包含偶数个1位则该位置1,否则清零。
AF(bit 4) [Adiust fiag]如果算术提作在结果的第3位发生进位或借位则将该标志置1,否则清零,这个标志在BCD(binary-code decimal)算术运算中被使用。
ZF(bit 6)[Zero flag]若结果为0则将其置1,反之清零。
SF(bit 7)[Sign flag] 该标志被设置为有符号整型的最高有效位。(0指示结果为正,反之则为负)
OF(bit 11)[Overflow flag]如果整型结果是较大的正数或较小的负数,并且无法匹配目的操作数时将该位置1,反之清零,这个标志为带符号整型运耸指示溢出状态。
2. X86架构的内存和寻址模式有哪些
==立即数寻址方式==
mov al, 5
mov eax, 231h
==寄存器寻址方式==
操作数放在寄存器中,指令指定寄存器号
mov ax, bx
==直接寻址方式==
操作数在存储器中,指令给出存储器的偏移地址
mov ax, [2000h]
==寄存器间接寻址==
操作数的地址在基址存储器或者变址存储器,而操作数则在存储器中
mov ax, [bx]
==基址变址寻址==
mov ax, [bx+si]
mov bx, [bp+si]
==寄存器相对寻址==
mov ax, [di+100h]
==相对基址变址寻址==
mov AX, [BX+SI+200H]
==相对基址比例变址寻址==
mov [EAX+EDI*2+15H], DX
3.在逆向中我们会遇到哪些常见的X86指令
数据传送指令
[!MOV]
mov Move(Opcodes: 88,89,8A,8B, 8C,8E, ...)mov指令将第二个操作数(可以是寄存器的内容、内存中的内容或值)复制到第一个操作数(寄存器或内存)。mov不能用于直接从内存复制到内存。
Examples
mov eax, ebx
mov byte ptr [eax], 5
[push]
push Push stack(Opcodes: FF, 89,8A,8B, 8C, 8E, ...)push指令将操作数压入内存的栈中,栈是程序设计中一种非常重要的数据结构,其主要用于函数调用过程中,其中ESP只是栈顶。在压栈前首先将ESP值减4(X86栈增长方向与内存地址编号增长方向相反),然后将操作数内容压入ESP指示的位置
Examples
push eax
push [eax]
[Pop]
pop- Pop stack
pop指令与push指令相反,它执行的是出栈的工作。它首先将ESP指示的地址中的内容出栈,然后将ESP值加4.其语法如下所示
Examples
pop edi
pop [ebx]
[Lea]
lea- Load effective address
lea实际上是一个载入有效地址指令,将第二个操作数表示的地址载入到第一个操作数(寄存器)中。
Examples
lea eax, [var]
lea edi, [ebx+4*esi]
逻辑计算指令&控制流指令
[Jz]
说明:如果零标志(ZF=1),则跳转到目标地址。
用途:通常在结果为零时进行跳转。
Examples
cmp eax, 0 ; 比较 eax 和 0
jz target ; 如果等于 0,跳转到 target
[Ja]
说明:如果无符号比较中,前一个操作数大于后一个操作数(CF=0 且 ZF=0),则跳转到目标地址。
用途:处理无符号数的大小比较。
Examples
cmp eax, ebx ; 比较 eax 和 ebx
ja target ; 如果 eax > ebx,则跳转到 target
[Jae]
说明:如果无符号比较中,前一个操作数大于或等于后一个操作数(CF=0),则跳转到目标地址。
用途:处理无符号数的“大于或等于”比较。
Examples
cmp eax, ebx ; 比较 eax 和 ebx
jae target ; 如果 eax >= ebx,则跳转到 target
[Jb]
说明:如果无符号比较中,前一个操作数小于后一个操作数(CF=1),则跳转到目标地址。
用途:处理无符号数的小于比较。
Examples
cmp eax, ebx ; 比较 eax 和 ebx
jb target ; 如果 eax < ebx,则跳转到 target
[jbe]
说明:如果无符号比较中,前一个操作数小于或等于后一个操作数(CF=1 或 ZF=1),则跳转到目标地址。
用途:处理无符号数的“小于或等于”比较。
Examples
cmp eax, ebx ; 比较 eax 和 ebx
jbe target ; 如果 eax <= ebx,则跳转到 target
[je]
说明:如果两个值相等(ZF=1),则跳转到目标地址。此指令是 jz 的同义词。
用途:在需要检测相等性时使用。
Examples
cmp eax, ebx ; 比较 eax 和 ebx
je target ; 如果 eax == ebx,则跳转到 target
4. 32位程序的调用约定
stdcall
push ebp; 保存ebp寄存器,该寄存器将调用来保存堆栈的栈顶指针,可以在函数退出是恢复
mov ebp, esp; 保存栈顶指针
mov eax, [ebp + 8H]; 堆栈中ebp指向位置之前依次保存右ebp,cs:eip,a,b,ebp +8指向a
add eax, [ebp + 0CH]; 堆栈中ebp + 12 处保存了b
mov esp, ebp; 恢复esp
pop ebp;
ret 8; 自动调整栈平衡,push几个参数就 ret 几个字节 2 * 4
显示调用stdcall
__attribute__((stdcall)) int a3(int a, int b, int c, int d)
{
return 3 * a + 2 * b + 2 * c + d;
}
cdecl
push ebp; 保存ebp寄存器,该寄存器将调用来保存堆栈的栈顶指针,可以在函数退出是恢复
mov ebp, esp; 保存栈顶指针
mov eax, [ebp + 8H]; 堆栈中ebp指向位置之前依次保存右ebp,cs:eip,a,b,ebp +8指向a
add eax, [ebp + 0CH]; 堆栈中ebp + 12 处保存了b
mov esp, ebp; 恢复esp
pop ebp;
ret;
显示调用cdecl
__attribute__((cdecl)) int a1(int a, int b, int c, int d)
{
return a + b + c + d;
}
fastcall
- 函数第一个和第二个DWORD参数通过ecx和edx传递
- 其他参数通过从右向左的顺序压栈
- 被调用函数清理堆栈

显示调用fastcall
__attribute__((fastcall)) int a2(int a, int b, int c, int d)
{
return a + b + 2 * c + d;
}
实验
apt install gcc-multilib g++-multilib libc6-dev-i386
example.c
#include <stdio.h>
__attribute__((cdecl)) int a1(int a, int b, int c, int d)
{
return a + b + c + d;
}
__attribute__((fastcall)) int a2(int a, int b, int c, int d)
{
return a + b + 2 * c + d;
}
__attribute__((stdcall)) int a3(int a, int b, int c, int d)
{
return 3 * a + 2 * b + 2 * c + d;
}
int main()
{
int a, b, c, d;
a = 10;
b = 20;
c = 30;
d = 40;
a1(a, b, c, d);
a2(a, b, c, d);
a3(a, b, c, d);
return 0;
}
Makefile
sample: example.c
gcc -m32 -g -O0 $^ -o $@ # -m32 32位平台 -O0 表示不让编译器采用任何优化
.PHONY: clean
clean:
rm sample

a1不负责清栈


a2 将第一个第二个参数压入edx,eax中,自己负责清栈


a3从右向左压栈

