【什么是汇编】:汇编就是寄存器跟寄存器,寄存器跟内存,内存跟寄存器之间来回移动数据。没有内存跟内存 如果要操作有特殊的指令操作。(所以指令都是操作内存跟寄存器)
寄存器详解
通用寄存器
x86架构提供了8个32位通用寄存器,每个寄存器都有特定的用途:
| 编号 | 32位 | 16位 | 8位 | 用途 |
| 0 | EAX | AX | AL | 累加 |
| 1 | ECX | CX | CL | 计数 |
| 2 | EDX | DX | DL | 数据 |
| 3 | EBX | BX | BL | 基址 |
| 4 | ESP | SP | AH | 堆栈(栈顶) |
| 5 | EBP | BP | CH | 堆栈(栈底) |
| 6 | ESI | SI | DH | 字符串 |
| 7 | EDI | DI | BH | 字符串 |
ESP和EBP用于管理函数调用堆栈,不可随意修改,否则会导致堆栈失衡,引发错误。
寄存器之间关系图
![图片[1]-x86 汇编语言核心知识整理-萝莉猫博客](https://www.luolimao.com/wp-content/uploads/2026/02/20260226191619250-jcqgx-1024x428.png)
特殊寄存器
EIP:指令指针寄存器,存储下一条要执行的指令地址EFLAGS:标志寄存器,存储CPU运行状态标志
段寄存器
段寄存器是96位的,包含选择子、属性、基址和界限:
struct Sengment//96
{
WORD Selector//选择子
WORD Attributes//属性
DWORD Base//基址
DWORD Limit//界限
}
| 编号 | 段寄存器 | 扩展用途 |
| 0 | ES | ESI、EDI 字符串操作 |
| 1 | CS | EIP 指令指针 |
| 2 | SS | ESP、EBP 堆栈操作 |
| 3 | DS | 通用数据段 |
| 4 | FS | 特殊用语 |
| 5 | GS | 特殊用语 |
| 6 | LDTR | |
| 7 | TR |
标志寄存器
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| OF | DF | IF | TF | SF | ZF | AF | PF | CF |
标志寄存器共两组标志位:运算结果标志、状态控制标志。
运算结果标志
CF进位标志:运算结果最高位产生进位或借位,CF=1 否则 CF=0
0010 0000 001 0<-CF
mov al,0xff
add al,0x1
PF奇偶标志:运算结果中1的个数,如果1的个数为偶数(二进制位限制八位) PF=1 否则 PF=0
0010 0000 0 0<PF 1 0
mov al,0x2
add al,0x8
AF辅助进位标志:低半字节向高半字节产生借位或进位,AF=1 否则 AF=0
0010 000 0<–AF 0 0 1 0
mov al,0x8
add al,0x8
ZF零标志:反映运算结果是否为零,如果运算结果为零,ZF=1 否则 ZF=0
0010 0 0<–ZF 0 0 0 0 1 0
mov eax,0x8
sub eax,0x8
SF符号标志:运算结果最高位为1,SF=1 否则 SF=0
0010 0<–SF 0 0 0 0 0 1 0
mov al,0xff
sub al,0x0
OF溢出标志:用于有符号加减运算中,如果运算结果字节(大于0x7f-小于0x80)、字(大于0x7fff-小于0x8000)、双字(大于0x7fffffff-小于0x8000000)大于或者小于,OF=1 否则 OF=0
0<–OF 0 1 0 0 0 0 0 0 0 1 0
mov al,0x7f
add al,0x1
mov al,0x80
add al,0xff
状态控制标志
TF追踪标志:控制单步执行
IF中断允许标志:控制中断响应
DF方向标志:控制字符串操作方向,0为递增,1为递减
数据类型
无符号类型
| 类型 | 大小 | 说明 |
| QWORD | 8字节 | 四字 |
| FWORD | 6字节 | 六个字节 |
| DWORD | 4字节 | 双字、32位 |
| WORD | 2字节 | 字、16位 |
| BYTE | 1字节 | 字节、8位 |
有符号类型(C语言)
- int:4字节
- long:4字节
- short:2字节
- char:1字节
浮点类型(C语言)
- float:4字节
- double:8字节
内存操作
内存格式与书写方式
[ ] 内存是由两个框号代表的 可以是寄存器,立即数。
dis32
mov eax,dword ptr ds:[ ebx+ecx * 1 + 0x12345678]
2
4
8
//ebx-->基址 ecx-->INDEX s索引 0x12345678-->偏移
//内存里面寄存器都是32位因为内存数据都是4个字节 4个字节对齐
ds-->段寄存器 ptr-->指针 dword-->4个字节
为什么是1 2 4 8 因为2的二次方
//读内存
mov eax,0x0012ff34
mov ecx,dword ptr ds:[eax]
// 写内存
mov eax,0x0012ff34
mov dword ptr ds:[eax],ecx
内存是4个字节4个字节对齐、4G空间 2的32次方刚好4G
0012FF34 A6 F5 AF 0F 3F 9B—>0x9b3f0fafa6
观察内存首先前面是内存地址后面是内存里的内容,内存的数据是反着存储。右高左低
汇编书写格式(C/C++语言)
_asm//汇编格式
{
//这里只能写汇编指令
}
汇编指令集
如何分辨参数
看到MOV r/m8,r8 r代表register m代表memory r8代表8位寄存器
imm代表立即数 r代表寄存器 m代表内存
ib—byte imm(imm8)
iw—word imm(imm16)
id—dword imm(imm32)
0xC0000005 内存访问错误
数据只有反复利用才会产生意义,只要执行指令EIP都会发生变化因为EIP就是指令指针
MOV 移动指令
mov 目标操作数,源操作数—>源操作数移动到目标操作数
目标操作数:r、m
源操作数:r、m、imm
mov ecx,0x11111111
mov eax,0x22222222
mov eax,ecx
ADD 加法指令
add 目标操作数,源操作数—>目标操作数 + 源操作数 把结果放到目标操作数
目标操作数:r、m
源操作数:r、m、imm
mov eax,0x12345678
mov ecx,0x11111111
add eax,ecx
SUB 减法指令
sub 目标操作数,源操作数—>目标操作数 – 源操作数 把结果放到目标操作数
目标操作数:r、m
源操作数:r、m、imm
mov eax,0x12345678
mov ecx,0x11111111
sub eax,ecx
INC 自加一指令
inc 目标操作数—>目标操作数加一
目标操作数:r、m
mov al,0x1
inc al
DEC 自减一指令
dec 目标操作数—>目标操作数减一
mov al,0x1
dec al
LEA 获取内存地址
lea 目标操作数,源操作数—>获取内存地址
目标操作数:必须R16、R32寄存器
源操作数:必须m内存
mov ecx,dword ptr ds:[eax]
lea edx, dword ptr ds:[eax]--->mov edx,eax 值是一样的
MOV 和 LEA指令的区别
- mov 是获取内存地址里面的内容
- lea 是获取内存地址
PUSH 把值压入堆栈
push 目标操作数—>功能:压栈 。分解成两条指令,即使同时改变寄存器也改变内存
目标操作数:r、m、imm、Seg
push eax //先减4在给值
lea esp,dword ptr ds:[esp-0x4]
mov dword ptr ds:[esp],eax
push esp //通用方法 反
mov dword ptr ds:[esp-0x4],esp
lea esp,dword ptr ds:[esp-0x4]
POP 将值弹出堆栈
pop 目标操作数—>功能:将值弹出堆栈
目标操作数:r、m、Seg
pop ecx //先弹赋值在加4
mov ecx,dword ptr ds:[esp]
lea esp,dword ptr ds:[esp+0x4]
pop esp //通用方法 先加4在弹值
lea esp,dword ptr ds:[esp+0x4]
mov esp,dword ptr ds:[esp-0x4]
XCHG 交换指令
xchg 目标操作数,源操作数—>功能:目标操作数和源操作数的值进行交换内容
目标操作数:r、m
源操作数:r、m
mov eax,0x11111111
xchg eax,dword ptr ds:[esp]
NOP 无操作
MUL 无符号乘法
mul 源操作数—>目标操作数 × 源操作数 执行无符号乘法。结果存储到 AX、DX:AX 或 EDX:EAX 寄存器。(取决于操作数的大小)
目标操作数:AL、AX、EAX(取决于操作数的大小)
源操作数:r、m
| 操作数大小 | 目标操作数 | 源操作数 | 目标操作数结果 |
| 字节 | AL | r/m8 | AX |
| 字 | AX | r/m16 | DX:AX |
| 双字 | EAX | r/m32 | EDX:EAX |
mov al,0x8
mov cl,0x2
mul cl
IMUL 有符号乘法
- 单操作数形式
imul 源操作数—>目标操作数 × 源操作数 执行有符号乘法。结果存储到 AX、DX:AX 或 EDX:EAX 寄存器。(取决于操作数的大小)
目标操作数:AL、AX、EAX(取决于操作数的大小)
源操作数:r、m
mov al,0x8
mov cl,0x2
imul cl
- 双操作数形式
imul 目标操作数,源操作数—>目标操作数 × 源操作数 结果存储到目标操作数
目标操作数:r、m
源操作数:r、m、imm
mov cx,0xf
mov dx,0x2
imul cx,dx
- 三操作数形式
imul 目标操作数,源操作数1,源操作数2—>源操作数1 × 源操作数2 结果存储到目标操作数
目标操作数:r
源操作数1:r、m
源操作数2:imm
mov ax,0x0000
mov cx,0x2222
imul ax,cx,0x2
DIV 无符号除法
div 源操作数—>目标操作数 ÷ 源操作数 结果存储到 AX (AH:AL)、DX:AX 或 EDX:EAX 寄存器。(取决于操作数大小)
目标操作数:AX、DX:EAX、EDX:EAX(取决于操作数大小)
源操作数:r、m
| 操作数大小 | 被除数 | 除数 | 商 | 余数 | 商的范围 |
|---|---|---|---|---|---|
| 字/字节 | AX | r/m8 | AL | AH | -128 到 +127 |
| 双字/字 | DX:AX | r/m16 | AX | DX | -32,768 到 +32,767 |
| 四字/双字 | EDX:EAX | r/m32 | EAX | EDX | -231 到 232 – 1 |
mov al,0x10
mov cl,0x2
cbw
div cl
mov ax,0x7000
mov cx,0x4
cwd
div cx
mov eax,0x40000000
mov ecx,0x20
cdq
div ecx
IDIV 有符号除法
idiv 源操作数—>目标操作数 ÷ 源操作数 结果存储到 AX (AH:AL)、DX:AX 或 EDX:EAX 寄存器。(取决于操作数大小)
目标操作数:AX、DX:EAX、EDX:EAX(取决于操作数大小)
源操作数:r、m
mov ax,0xff
mov cx,0x2
cwd
idiv cx
CDQ CWD CBW 符号扩展
按高位扩展最高位是为几那就扩展成几
CDQ: EAX—>EDX:EAX(4字节转化8字节)
CWD:AX—>DX:AX(2字节转化4字节)
CBW:AL—>AX(1字节转化2字节)
ADC 带进位加法
adc 目标操作数,源操作数—>目标操作数+源操作数,如果CF进位标志是1,加法结果递增1,然后存储到目标操作数
目标操作数:r、m
源操作数:r、m、imm
mov ax,0xffff
add ax,0x1
mov cx,0x1
adc cx,0x1
SBB 带借位整数减法
sbb 目标操作数,源操作数—>源操作数+CF进位标志-目标操作数,结果存储目标操作数
目标操作数:r、m
源操作数:r、m、imm
mov ax,0xffff
add ax,0x1
mov cl,0x8
mov al,0x3
SBB cl,al
DAA 加之后AL十进制调整
执行ADD后对AL进行十进制调整(0-F),用于在BCD(二进制编码的十进制)加法运算后,将AL寄存器中的结果调整为标准BCD格式。
调整规则(处理器内部自动判断)
- 如果AL的低4位 > 9 或 AF = 1,则AL = AL + 6,并置AF = 1
- 如果AL的高4位 > 9 或 CF = 1,则AL = AL + 60H,并置CF = 1
MOV AL, 0x35 ; AL = 35H(十进制35的BCD)
ADD AL, 0x47 ; AL = 7CH(35+47=7C,但7C不是有效BCD)
DAA ; 调整后:AL = 82H(35+47=82,正确BCD),CF=0
DAS 减之后AL十进制调整
执行SUB后对AL进行十进制调整(0-F),用于在BCD(二进制编码的十进制)减法运算后,将AL寄存器中的结果调整为标准BCD格式。
调整规则(处理器内部自动判断)
- 如果 AL 的低4位 > 9 或 AF = 1,则 AL = AL – 6,并置 AF = 1
- 如果 AL 的高4位 > 9 或 CF = 1,则 AL = AL – 60H,并置 CF = 1
MOV AL, 0x85 ; AL = 85H(十进制85的BCD)
SUB AL, 0x47 ; AL = 3EH(85-47=3E,但3E不是有效BCD)
DAS ; 调整后:AL = 38H(85-47=38,正确BCD),CF=0
AAA 加之后 ASCII 调整
对 AL 寄存器中的值(由加法指令如 ADD、ADC得到的结果)进行调整,使其成为正确的非压缩BCD(即高4位为0,低4位为十进制数字0–9)。
调整规则
1. 如果 AL 的低4位 > 9 或 AF = 1,则:
- AL = AL + 6
- AH = AH + 1
- AF = 1
- CF = 1
2. 否则 AF = 0,CF = 0。
3. 无论何种情况,AL 的高4位被清零(AL = AL & 0x0F)。
MOV AX, 0x0105 ; AH=01, AL=05(非压缩BCD数字5)
ADD AL, 0x39 ; AL = 05 + 39 = 3E(对应ASCII '9'的值为39H)
AAA ; 调整后:AL = 04, AH = 02, CF=1, AF=1
; 结果表示十进制 5 + 9 = 14,调整后 AH=1(进位)变为2,AL=4
AAS 减之后 ASCII 调整
对 AL 寄存器中的值(由减法指令如 SUB、SBB得到的结果)进行调整,使其成为正确的非压缩BCD(高4位为0,低4位为十进制数字0–9)。
调整规则
- 如果 AL 的低4位 > 9 或 AF = 1,则:
- AL = AL – 6
- AH = AH – 1
- AF = 1
- CF = 1
- 否则 AF = 0,CF = 0。
- 无论何种情况,AL 的高4位被清零(AL = AL & 0x0F)。
MOV AX, 0x0208 ; AH=02, AL=08(非压缩BCD数字8)
SUB AL, 0x35 ; AL = 08 - 35 = D3(借位发生)
AAS ; 调整后:AL = 03, AH = 01, CF=1, AF=1
; 结果表示十进制 28 - 5 = 23,调整后 AH 从2借位变为1,AL=3
JMP 跳转指令
jmp 目标操作数—>改变EIP寄存器,相当mov eip(eip 不能随意修改,除法英特尔提供特殊指令)
目标操作数:r、m、imm
lea ecx,lab
jmp ecx
lab:
mov eax,0x12345678
LAB 标签
lea ecx,lab
jmp ecx
lab:
mov eax,0x12345678
CALL 调用过程
call 目标操作数—>调用过程 先push 后jmp
call edx
push IL //esp+len(il)下一条指令的地址-->当前EIP+本条指令的长度
jmp edx
call edx
lea eax,leb
push eax
jmp edx
leb:
mov eax,0x0
RET 从过程返回
ret 从CALL返回
ret iw
ret
lea esp,dword ptr ds:[esp+0x4]
jmp dword ptr ds:[esp-0x4]--->返回 CALL的下一条指令
ret iw
lea esp,dword ptr ds:[esp+0x4+iw]
jmp dword ptr ds:[esp-0x4-iw]--->返回 CALL的下一条指令
JCC 条件跳转指令
jcc 目标操作数—>立即数(地址)
根据标志位状态决定是否跳转:
| 指令 | 条件 | 说明 |
| JZ、JE | ZF=1 | 等于零/相等跳转 |
| JNZ、JNE | ZF=0 | 不等于零/不相等跳转 |
| JS | SF=1 | 符号为负跳转 |
| JNS | SF=0 | 符号为正跳转 |
| JO | OF=1 | 溢出跳转 |
| JNO | OF=0 | 无溢出跳转 |
| JP、JPE | PF=1 | 1的数为偶数跳转 |
| JNP、JPO | PF=0 | 1的个数不为偶尔跳转 |
| JC | CF=1 | 有进位跳转 |
| JNC | CF=0 | 无进位跳转 |
根据比较决定跳转:
| 指令 | 说明 |
| JG、JNLE | >(有符号) |
| JL、JNGE | <(有符号) |
| JGE、JNL | >=(有符号) |
| JLE、JNG | <=(有符号) |
| JE | =(有符号、无符号) |
| JA、JNBE | >(无符号) |
| JB、JABE | <(无符号) |
| JAE、JNB | >=(无符号) |
| JBE、JNA | <=(无符号) |
CMP 比较两个操作数
cmp 目标操作数,源操作数—>功能:比较两个操作数,相当于SUB指令,结果丢弃,设置标志位
目标操作数:r、m
源操作数:r、m、imm
leb:
mov eax,0x5
mov edx,0x4
cmp eax,edx
jae leb
TEST 逻辑比较指令
test 目标操作数,源操作数—>功能:相当于AND指令(OF CF标志清零),根据结果设置PF ZF SF
目标操作数:r、m
源操作数:r、m、imm
leb:
//案例1
mov eax,0x1
mov ecx,0x1
test eax,ecx
//案例2
mov eax,0x800000000
xor eax,0x4
jnc leb
CMOVCC 条件移动指令(类似JCC指令系列)
cmovcc 目标操作数,源操作数—>根据条件是否满足从源操作数移动到目标操作数
目标操作数:r
源操作数:r、m
mov ax,0xffff
mov cx,0x2222
cmovg ax,cx
SETCC 按条件设置字节(类似JCC指令系列)
setcc 目标操作数—>功能:标志位满足条件,将目标操作数设置为 0 或 1。
目标操作数:r、m8
mov al,0xff
add al,0x2
setc cl
MOVS系列 数据从字符串移到字符串
movs 目标操作数,源操作数—>将 [源操作数] 移动到 [目标操作数] (ESI和EDI 地址不能相同)
目标操作数:[EDI]
源操作数:[ESI]
movsb m8,m8—>MOVSB 字节移动(ESI和EDI同时加4还是减4(看DF位)再把dword ptr ds:[ESI]—>dword ptr ds:[EDI])
movsw m16,m16—>MOVSW 字移动(ESI和EDI同时加2还是减2(看DF位)再把word ptr ds:[ESI]—>word ptr ds:[EDI])
movsd m32,m32—>MOVSD 双字移动(ESI和EDI同时加1还是减1(看DF位)再把byte ptr ds:[ESI]—>byte ptr ds:[EDI])
mov esi,0x0012FD84
mov edi,0x0012FDB8
mov dword ptr ds:[esi],0x12345678
mov dword ptr ds:[edi],0x11111111
movsd
movs dword ptr ds:[edi],dowrd ptr ds:[esi] //MOVSD分解
//看DF是减还是加
add esi,0x4
add edi,0x4
CLD 清除方向标志
cld—>功能:DF标志清零
mov esi,0x0012FD84
mov edi,0x0012FDB8
mov dword ptr ds:[esi],0x12345678
mov dword ptr ds:[edi],0x11111111
cld
movsd
STD 设置方向标志
std—>功能:DF标志置1
mov esi,0x0012FD84
mov edi,0x0012FDB8
mov dword ptr ds:[esi],0x12345678
mov dword ptr ds:[edi],0x11111111
std
movsd
STOS系列 存储字符串
stos—>将AL、AX、EAX 移动到[EDI]内存里
目标操作数:[EDI]
stosb al—>byte ptr ds:[edi] (看DF位) EDI+1还是EDI-1
stosw ax—>word ptr ds:[edi](看DF位) EDI+2还是EDI-2
stosd eax—>dword ptr ds:[edi](看DF位) EDI+4还是EDI-4
mov eax,0x12345678
mov edi,0x0012FF34
stosd
mov dword ptr ds:[edi],eax //stosd 分解
add edi,0x4
REP 重复指令
rep—>功能:重复操作,看ECX值。 依赖ECX ECX=0 结束
| 重复前缀 | 终止条件 1 | 终止条件 2 |
|---|---|---|
| REP | ECX=0 | 无 |
| REPE/REPZ | ECX=0 | ZF=0 |
| REPNE/REPNZ | ECX=0 | ZF=1 |
//rep stosd 案例
mov edi,0x0012FE70
mov eax,0x11111111
mov ecx,0x10
rep stosd
//rep movsd 案例
mov esi,0x0012FE30
mov edi,0x0012FE34
mov dword ptr ds:[esi],0x11111111
mov dword ptr ds:[edi],0x22222222
mov ecx,0x10
rep movsd
逻辑指令
- 位与位之间没有任何联系
- 不进位,取最大值
OR 或运算
or 目标操作数,源操作数—>结果存储目标操作数 。算加法两个操作数对应的位有一个1就为1,否则为0。C语言符号(|)
目标操作数:r、m
源操作数:r、m、imm
mov al,0x8
mov cl,0x9
or al,cl
目标操作数 1000
源操作数 1001
运算结果 1001
AND 与运算
and 目标操作数,源操作数—>结果存储目标操作数。算乘法两个操作数对应的位为1 才为1,否则为0。C语言符号(&)
目标操作数:r、m
源操作数:r、m、imm
mov al,0x5
mov cl,0x6
and al,cl
目标操作数:0101
源操作数: 0110
运算结果: 0100
XOR 异或
xor 目标操作数,源操作数—>结果存储目标操作数,两个操作数对应的位相同为0,不同为1。C语言符号(^)
目标操作数:r、m
源操作数:r、m、imm
mov al,0x8
mov cl,0x9
xor al,cl
目标操作数:1000
源操作数: 1001
运算结果: 0001
NOT 取反
not 目标操作数—>结果存储目标操作数,操作数对应的位是0就为1,是1就为0。(快捷方式用F去减注意宽度) C语言符号(~ !)
目标操作数:r、m
mov cl,0x9 -->根据位宽用比如CL 用FF去减
not cl
直接用F去减
0x5739752bac482432
0xa8c68ad453b7dbcd
SAR 、SHR 右移指令
sar、shr 目标操作数,源操作数—》对目标操作数 ÷ 2的幂(源操作数) 结果存储目标操作数
目标操作数:r、m
源操作数:imm8、CL
mov al,0x8
mov cl,0x2
sar al,cl
SAL、SHL 左移指令
sal、shl 目标操作数,源操作数—》对目标操作数 × 2的幂(源操作数) 结果存储目标操作数
目标操作数:r、m
源操作数:imm8、CL
mov al,0x4
shl al,0x1
函数框架
函数构成
int main(int argc, char* argv[])
{
//;--->变量,语法,数据类型
int c;//类型 类型名字;反汇编:dword ptr ds:[ebp-0x4]
return 0;
}
//int-->代表函数的类型(函数返回类型) //main-->函数的名字 //()-->阔号代表参数 //return 0;--->代表返回值
//dword word byte int char short long void 空类型--->没有返回值
- 函数类型:决定返回值类型
- 函数名字:标识符
- 函数参数:可以有多个,从右往左push
- 函数返回值:通过EAX寄存器返回
函数头(标准框架)
00401020 push ebp --->保存EBP
00401021 mov ebp,esp --->保存ESP
00401023 sub esp,0x40
00401026 push ebx --->保存EBX
00401027 push esi --->保存ESI
00401028 push edi --->保存EDI
00401029 lea edi,dword ptr ds:[ebp-0x40]
0040102C mov ecx,0x10
00401031 mov eax,0xCCCCCCCC
00401036 rep stosd 以上三条指令都在为本条指令做准备
函数尾(标准框架)
//函数尾
0040103A pop edi ---》恢复EDI
0040103B pop esi ---》恢复ESI
0040103C pop ebx ---》恢复EBX
0040103D mov esp,ebp ---》恢复ESP
0040103F pop ebp ---》恢复EBP
00401040 ret
函数堆栈图:
0012FEE0 0x0012FF80 edi ---》恢复EDI
0012FEE4 0x7C939B3F esi ---》恢复ESI
0012FEE8 0x0012FEE8 ebx ---》恢复EBX
0012FEEC 0xCCCCCCCC ---》sub esp-0x40
0012FEF0 0xCCCCCCCC
0012FEF4 0xCCCCCCCC
0012FEF8 0xCCCCCCCC
0012FEFC 0xCCCCCCCC
0012FF00 0xCCCCCCCC
0012FF04 0xCCCCCCCC
0012FF08 0xCCCCCCCC
0012FF0C 0xCCCCCCCC
0012FF10 0xCCCCCCCC
0012FF14 0xCCCCCCCC
0012FF18 0xCCCCCCCC
0012FF1C 0xCCCCCCCC
0012FF20 0xCCCCCCCC
0012FF24 0xCCCCCCCC
0012FF28 0xCCCCCCCC
0012FF2C 0x0012FF80 ebp ---》恢复EBP
0012FF30 0x0040106D 下一条指令地址
0012FF34 esp
为什么减0x40?sub esp,0x40
- 函数跟函数之间方便隔开
- 参数(因为他觉得16个参数够了)
- 因为是16进制每一项4个字节刚好是40
- 为什么赋值是CCCCCCCC 为了安全 防止出错 因为CCC是INT3 断点
怎么画堆栈图:谁改变ESP这段内存就把谁记录下来
只要调用函数都会产生 _chkesp() 检查堆栈平衡
只要是函数肯定用到call指令
_declspec(naked) 裸汇编 自己生成函数头 函数尾
word特殊之处:
因为word比较特殊,因为内存是4个字节4个字节对齐,如果我用内存的话4个对齐,首先从2个字节扩展成4个字节,扩展完了还要进行对齐 所以说这样比较麻烦,编译器干脆把word这种类型把他放到全局变量就是short这种 2个字节的数据 放到全局变量里面 我直接从这个地址,我把全局变量的地址记录下来,然后从这个地址中去取(word是放到堆里面,运行速度快)
参数存放位置
参数是从右往左push 最左边是第一个参数 最右边的最后一个
参数是放到[EBP+0x8]位置 第1个参数放到[EBP+0x8] 第2个参数放到[EBP+0xC] 第3个参数放到[EBP+0x10] 依此类推
int fun(int a, int b, int c)
{
return 0;
}
fun(1,2,3);
//反汇编 fun(1,2,3);
00401068 6A 03 push 3
0040106A 6A 02 push 2
0040106C 6A 01 push 1
0040106E E8 9C FF FF FF call @ILT+10(fun) (0040100f)
00401073 83 C4 0C add esp,0Ch //外平栈
变量存放位置
放到EBP-0x4这段内存里面第1个变量放到[EBP-0x4] 第2个变量放到[EBP-0x8] 第3个变量放到[EBP-0xC] 依此类推
返回值
函数返回值是放到EAX这个寄存器里面的
逆向三要素
- 参数
- 变量
- 返回值
VC6调试快捷键
| 快捷键 | 功能 |
| F7 | 编译 |
| F9 | 设置/取消断点 |
| F5 | 运行到断点处 |
| F10 | 步过(Step Over) |
| F11 | 步入(Step Into) |
| Shift+F5 | 退出调试 |
| Alt+8 | 查看反汇编 |
| F12 | 转到定义处 |
双斜杠代表注释 斜杠+星范围之间全部注释
int main()
{
int a;
int b;
//a = 1;
/*b = a++;
b++;
*/
return 0;
}







暂无评论内容