阶段一:编程基础
📅 创建日期:2025-12-03
⏱️ 总时长:2-3 个月
🎯 目标:打下坚实的编程和汇编基础
📌 前置要求:无
🔗 返回主路径:[[逆向与驱动开发学习路径(详细版)]]
📑 本阶段目录
- [[#1.1 C语言]]
- [[#1.1.1 基础语法]]
- [[#1.1.2 指针与内存管理]]
- [[#1.1.3 结构体与联合体]]
- [[#1.1.4 位运算]]
- [[#1.1.5 文件操作]]
- [[#1.2 数据结构]]
- [[#1.2.1 链表]]
- [[#1.2.2 栈和队列]]
- [[#1.2.3 树]]
- [[#1.2.4 哈希表]]
- [[#1.3 x86汇编]]
- [[#1.3.1 寄存器]]
- [[#1.3.2 基本指令]]
- [[#1.3.3 调用约定]]
- [[#1.3.4 常见汇编模式识别]]
1.1 C语言 ⭐⭐⭐⭐⭐
🎯 目标:掌握 C 语言核心概念,为后续所有学习打基础
⏱️ 时长:4-6 周
1.1.1 基础语法
📝 学习笔记
核心概念
C 语言是逆向工程的基础,几乎所有底层程序都是用 C/C++ 编写的。
学习小点
- [ ] 变量与数据类型(int, char, float, double, long long)
- [ ] 有符号与无符号类型的区别及范围
- [ ] 类型转换(隐式转换、显式转换)
- [ ] sizeof运算符与数据类型大小
- [ ] 运算符及优先级
- [ ] printf/scanf格式化输入输出
- [ ] 条件语句(if-else, switch-case)
- [ ] 循环语句(for, while, do-while)
- [ ] break、continue与goto
💻 代码示例
// 数据类型大小
printf("int: %zu bytes\n", sizeof(int)); // 4
printf("char: %zu bytes\n", sizeof(char)); // 1
printf("long long: %zu bytes\n", sizeof(long long)); // 8
printf("pointer: %zu bytes\n", sizeof(void*)); // 4(x86) / 8(x64)
// 有符号与无符号
unsigned int u = 0xFFFFFFFF; // 4294967295
int s = 0xFFFFFFFF; // -1 (补码表示)
// 类型转换陷阱
int a = 5, b = 2;
float result1 = a / b; // 2.0 (整数除法后转float)
float result2 = (float)a / b; // 2.5 (正确)
🔑 关键要点
[!important] 逆向中的重要性
- 理解数据类型大小对于分析内存布局至关重要
- 有符号/无符号的区别影响汇编中的比较指令(JG vs JA)
- sizeof在x86和x64下指针大小不同(4 vs 8字节)
📚 学习资料
| 类型 | 资源名称 | 链接/说明 | 推荐度 |
|---|---|---|---|
| 📹视频 | 翁恺《C语言程序设计》 | B站/中国大学MOOC | ⭐⭐⭐⭐⭐ |
| 📹视频 | 郝斌C语言教程 | B站 | ⭐⭐⭐⭐ |
| 📖书籍 | 《C Primer Plus》 | 第1-7章 | ⭐⭐⭐⭐⭐ |
| 📖书籍 | 《C程序设计语言》K&R | C语言圣经 | ⭐⭐⭐⭐⭐ |
1.1.2 指针与内存管理(最重要!)
📝 学习笔记
核心概念
指针是 C 语言的灵魂,也是逆向工程的核心。在汇编代码中,几乎所有的内存访问都涉及指针操作。
学习小点
- [ ] 指针的概念(内存地址、解引用、取地址运算符)
- [ ] 指针变量的声明与初始化
- [ ] 指针运算(加减、比较、偏移计算)
- [ ] 数组与指针的关系
- [ ] 指针数组 vs 数组指针
- [ ]
int *arr[10]- 指针数组 - [ ]
int (*arr)[10]- 数组指针
- [ ]
- [ ] 多级指针(二级指针、三级指针)
- [ ] 函数指针
- [ ] 定义与声明
- [ ] 作为参数传递(回调函数)
- [ ] 函数指针数组
- [ ] void指针与类型转换
- [ ] const与指针的四种组合
- [ ]
const int *p- 指向常量的指针 - [ ]
int * const p- 常量指针 - [ ]
const int * const p- 指向常量的常量指针
- [ ]
- [ ] 动态内存分配
- [ ] malloc/calloc/realloc/free
- [ ] 内存泄漏检测
- [ ] 野指针与悬空指针避免
💻 代码示例
// 基本指针操作
int value = 100;
int *p = &value; // p存储value的地址
printf("地址: %p\n", p);
printf("值: %d\n", *p); // 解引用获取值
*p = 200; // 通过指针修改值
// 指针运算(逆向分析中非常常见)
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr; // 数组名就是首元素地址
printf("%d\n", *ptr); // 10
printf("%d\n", *(ptr + 2)); // 30,偏移2个int(8字节)
printf("%d\n", ptr[2]); // 30,等价写法
// 多级指针(用于修改指针本身)
int x = 10;
int *p1 = &x;
int **pp = &p1; // 指针的指针
printf("%d\n", **pp); // 10
// 函数指针(逆向分析核心!虚表就是函数指针数组)
typedef int (*Operation)(int, int); // 定义类型别名
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
Operation op = add; // 函数指针赋值
printf("%d\n", op(3, 5)); // 8
// 函数指针数组(模拟虚表)
Operation operations[4] = {add, sub, mul, div_func};
for (int i = 0; i < 4; i++) {
printf("%d\n", operations[i](10, 5));
}
// 回调函数
void process(int arr[], int size, int (*callback)(int)) {
for (int i = 0; i < size; i++)
arr[i] = callback(arr[i]);
}
// 动态内存管理
int size = 100;
int *buffer = (int*)malloc(size * sizeof(int));
if (buffer == NULL) {
printf("内存分配失败\n");
return;
}
memset(buffer, 0, size * sizeof(int)); // 初始化为0
// 使用buffer...
free(buffer);
buffer = NULL; // 避免悬空指针!
🔑 关键要点
[!important] 逆向中的指针模式
[EBX+ECX*4]- 数组访问:基址 + 索引 * 元素大小[ESI+0x10]- 结构体成员访问:基址 + 固定偏移CALL [EAX]- 间接调用,通常是虚函数调用
[!warning] 常见陷阱
- 野指针:指向已释放内存的指针
- 悬空指针:未初始化的指针
- 内存泄漏:分配后未释放
- 越界访问:访问数组范围外的内存
📚 学习资料
| 类型 | 资源名称 | 链接/说明 | 推荐度 |
|---|---|---|---|
| 📹视频 | 小甲鱼C语言指针专题 | B站 | ⭐⭐⭐⭐⭐ |
| 📹视频 | 指针详解(一口Linux) | B站 | ⭐⭐⭐⭐ |
| 📖书籍 | 《C和指针》Kenneth Reek | 指针专项深入 | ⭐⭐⭐⭐⭐ |
| 📖书籍 | 《C专家编程》 | 深入理解C | ⭐⭐⭐⭐⭐ |
| ✏️练习 | LeetCode链表题目 | 实践指针操作 | ⭐⭐⭐⭐⭐ |
1.1.3 结构体与联合体
📝 学习笔记
核心概念
结构体是逆向分析的核心,几乎所有的游戏对象、系统数据结构都是结构体。理解内存对齐对于正确分析结构体至关重要。
学习小点
- [ ] 结构体定义与初始化
- [ ] 结构体成员访问(. 和 -> 运算符)
- [ ] 结构体内存对齐原理(非常重要!)
- [ ] 对齐规则
- [ ] 计算结构体大小
- [ ]
#pragma pack的使用
- [ ]
offsetof宏计算成员偏移 - [ ] 嵌套结构体
- [ ] 自引用结构体(链表节点)
- [ ] 位域(bit field)定义与使用
- [ ] 联合体(union)
- [ ] 与结构体的区别
- [ ] 内存共享特性
- [ ] 类型双关(type punning)应用
- [ ] typedef的使用(Windows风格)
💻 代码示例
// Windows风格结构体定义
typedef struct _MY_STRUCT {
int field1; // +0x00, 4字节
char field2; // +0x04, 1字节
// 3字节padding // +0x05-0x07
void *field3; // +0x08, 8字节(x64)
} MY_STRUCT, *PMY_STRUCT;
// 计算偏移(逆向分析必备)
#include <stddef.h>
printf("field1 offset: %zu\n", offsetof(MY_STRUCT, field1)); // 0
printf("field2 offset: %zu\n", offsetof(MY_STRUCT, field2)); // 4
printf("field3 offset: %zu\n", offsetof(MY_STRUCT, field3)); // 8
printf("struct size: %zu\n", sizeof(MY_STRUCT)); // 16
// 修改对齐方式
#pragma pack(push, 1) // 1字节对齐
typedef struct _PACKED_STRUCT {
int field1; // +0x00
char field2; // +0x04
void *field3; // +0x05
} PACKED_STRUCT;
#pragma pack(pop)
printf("packed size: %zu\n", sizeof(PACKED_STRUCT)); // 13
// 联合体(类型转换技巧)
typedef union _VALUE {
int i;
float f;
char bytes[4];
struct {
unsigned short low;
unsigned short high;
};
} VALUE;
VALUE v;
v.i = 0x12345678;
printf("整数: 0x%X\n", v.i);
printf("低16位: 0x%X\n", v.low); // 0x5678
printf("高16位: 0x%X\n", v.high); // 0x1234
printf("字节: %02X %02X %02X %02X\n",
v.bytes[0], v.bytes[1], v.bytes[2], v.bytes[3]);
// 位域(用于标志位)
typedef struct _FLAGS {
unsigned int readable : 1; // 1位
unsigned int writable : 1; // 1位
unsigned int executable : 1; // 1位
unsigned int reserved : 29; // 29位
} FLAGS;
FLAGS f = {0};
f.readable = 1;
f.executable = 1;
printf("flags value: %u\n", *(unsigned int*)&f); // 5 (101b)
🔑 关键要点
[!important] 内存对齐规则
- 结构体成员按其自身大小对齐
- 结构体总大小是最大成员大小的倍数
- 可使用
#pragma pack修改对齐
[!tip] 逆向分析技巧
- 在IDA中使用
T键应用结构体- 观察
[base+固定偏移]模式识别结构体访问- 使用ReClass工具辅助分析结构体
📚 学习资料
| 类型 | 资源名称 | 链接/说明 | 推荐度 |
|---|---|---|---|
| 📹视频 | 结构体内存对齐详解 | B站搜索 | ⭐⭐⭐⭐⭐ |
| 📖书籍 | 《C Primer Plus》第14章 | 结构体专题 | ⭐⭐⭐⭐ |
| 📖书籍 | 《深入理解计算机系统》 | 第3章数据对齐 | ⭐⭐⭐⭐⭐ |
| ✏️练习 | 编写PE文件解析器 | 实践结构体 | ⭐⭐⭐⭐⭐ |
1.1.4 位运算
📝 学习笔记
核心概念
位运算在逆向工程中无处不在,特别是在加密算法、标志位处理、哈希计算等场景。
学习小点
- [ ] 位运算符
- [ ] AND(&)、OR(|)、XOR(^)、NOT(~)
- [ ] 左移(<<)、右移(>>)
- [ ] 设置位、清除位、翻转位、检查位
- [ ] 位掩码的创建与使用
- [ ] 常见位运算技巧
- [ ] 大小端字节序理解与检测
💻 代码示例
unsigned int flags = 0;
// 设置第n位
#define SET_BIT(x, n) ((x) |= (1 << (n)))
// 清除第n位
#define CLEAR_BIT(x, n) ((x) &= ~(1 << (n)))
// 翻转第n位
#define TOGGLE_BIT(x, n) ((x) ^= (1 << (n)))
// 检查第n位
#define CHECK_BIT(x, n) (((x) >> (n)) & 1)
SET_BIT(flags, 3); // 设置第3位
printf("after set: 0x%X\n", flags); // 0x8
CLEAR_BIT(flags, 3); // 清除第3位
printf("after clear: 0x%X\n", flags); // 0x0
// 常用技巧
int x = 12; // 1100b
x & (x - 1); // 清除最低位的1 -> 8 (1000b)
x & (-x); // 获取最低位的1 -> 4 (0100b)
x ^ x; // 清零
~0; // 全1
// 字节序检测
int test = 1;
if (*(char*)&test == 1)
printf("Little Endian (x86/x64)\n");
else
printf("Big Endian\n");
// 字节交换
unsigned int value = 0x12345678;
unsigned int swapped = ((value >> 24) & 0xFF) |
((value >> 8) & 0xFF00) |
((value << 8) & 0xFF0000) |
((value << 24) & 0xFF000000);
printf("swapped: 0x%X\n", swapped); // 0x78563412
🔑 关键要点
[!important] 逆向中常见的位运算
XOR EAX, EAX- 清零(比MOV更快)AND EAX, 0xFF- 保留低8位SHL EAX, 2- 乘以4(快速乘法)SHR EAX, 1- 除以2(快速除法)
📚 学习资料
| 类型 | 资源名称 | 链接/说明 | 推荐度 |
|---|---|---|---|
| 📹视频 | 位运算从入门到精通 | B站 | ⭐⭐⭐⭐ |
| 📖书籍 | 《Hacker’s Delight》 | 位运算圣经 | ⭐⭐⭐⭐⭐ |
| 🌐在线 | Bit Twiddling Hacks | graphics.stanford.edu | ⭐⭐⭐⭐⭐ |
1.1.5 文件操作
📝 学习笔记
核心概念
文件操作是分析二进制文件(如 PE、ELF)的基础。
学习小点
- [ ] 文件打开模式(r, w, a, rb, wb, ab, r+, w+)
- [ ] fopen/fclose 文件打开关闭
- [ ] fread/fwrite 二进制读写
- [ ] fgets/fputs/fprintf 文本读写
- [ ] fseek/ftell/rewind 文件定位
- [ ] feof/ferror 错误检测
- [ ] 二进制文件分析基础
💻 代码示例
// 读取二进制文件(PE分析基础)
FILE* fp = fopen("test.exe", "rb");
if (!fp) {
perror("打开文件失败");
return;
}
// 获取文件大小
fseek(fp, 0, SEEK_END);
long fileSize = ftell(fp);
fseek(fp, 0, SEEK_SET); // 或 rewind(fp);
// 分配内存并读取
unsigned char* buffer = (unsigned char*)malloc(fileSize);
size_t bytesRead = fread(buffer, 1, fileSize, fp);
fclose(fp);
// 检查PE签名
if (buffer[0] == 'M' && buffer[1] == 'Z') {
printf("有效的PE文件\n");
// 获取PE头偏移
int peOffset = *(int*)(buffer + 0x3C);
printf("PE头偏移: 0x%X\n", peOffset);
// 检查PE签名
if (*(unsigned int*)(buffer + peOffset) == 0x00004550) {
printf("PE签名有效\n");
}
}
free(buffer);
📚 学习资料
| 类型 | 资源名称 | 链接/说明 | 推荐度 |
|---|---|---|---|
| 📖书籍 | 《C Primer Plus》第13章 | 文件I/O | ⭐⭐⭐⭐ |
| ✏️练习 | 编写十六进制查看器 | 实践文件操作 | ⭐⭐⭐⭐⭐ |
| ✏️练习 | PE文件解析器 | 综合练习 | ⭐⭐⭐⭐⭐ |
✅ C语言阶段检查清单
- [ ] 能熟练使用基本语法编写程序
- [ ] 完全理解指针的概念和操作
- [ ] 能正确计算结构体大小和成员偏移
- [ ] 熟练使用位运算
- [ ] 能正确管理动态内存
- [ ] 能使用函数指针实现回调
- [ ] 能读写二进制文件
1.2 数据结构 ⭐⭐⭐⭐
🎯 目标:掌握常用数据结构,能在内存中识别它们
⏱️ 时长:2-3 周
1.2.1 链表
📝 学习笔记
核心概念
链表是最基本的动态数据结构。Windows 内核中大量使用 LIST_ENTRY 双向链表。
学习小点
- [ ] 单链表(创建、插入、删除、遍历、反转)
- [ ] 双链表
- [ ] 循环链表
- [ ] Windows内核风格链表(LIST_ENTRY)
- [ ] 链表在内存中的特征识别
💻 代码示例
// 单链表节点
typedef struct _NODE {
int data;
struct _NODE* next;
} NODE, *PNODE;
// 创建节点
NODE* CreateNode(int data) {
NODE* node = (NODE*)malloc(sizeof(NODE));
node->data = data;
node->next = NULL;
return node;
}
// 尾插法
void AppendNode(NODE** head, int data) {
NODE* newNode = CreateNode(data);
if (*head == NULL) {
*head = newNode;
return;
}
NODE* current = *head;
while (current->next != NULL)
current = current->next;
current->next = newNode;
}
// ========== Windows内核风格链表(驱动开发必备)==========
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink; // 前向指针
struct _LIST_ENTRY *Blink; // 后向指针
} LIST_ENTRY, *PLIST_ENTRY;
// 包含LIST_ENTRY的数据结构
typedef struct _MY_DATA {
int id;
char name[32];
LIST_ENTRY ListEntry; // 嵌入链表节点
} MY_DATA, *PMY_DATA;
// 通过LIST_ENTRY获取包含它的结构(重要!)
#define CONTAINING_RECORD(address, type, field) \
((type *)((char*)(address) - (unsigned long long)(&((type *)0)->field)))
// 使用示例
void ProcessList(PLIST_ENTRY listHead) {
PLIST_ENTRY entry = listHead->Flink;
while (entry != listHead) {
PMY_DATA data = CONTAINING_RECORD(entry, MY_DATA, ListEntry);
printf("ID: %d, Name: %s\n", data->id, data->name);
entry = entry->Flink;
}
}
🔑 关键要点
[!important] CONTAINING_RECORD 宏
这个宏在 Windows 内核编程中非常重要,用于从嵌入的 LIST_ENTRY 获取外层结构体指针。
📚 学习资料
| 类型 | 资源名称 | 链接/说明 | 推荐度 |
|---|---|---|---|
| 📹视频 | 数据结构与算法(青岛大学王卓) | B站 | ⭐⭐⭐⭐⭐ |
| 📖书籍 | 《大话数据结构》 | 通俗易懂 | ⭐⭐⭐⭐ |
| ✏️练习 | LeetCode链表专题 | 206, 21, 141等 | ⭐⭐⭐⭐⭐ |
1.2.2 栈和队列
📝 学习笔记
核心概念
理解栈对于逆向分析至关重要,因为函数调用完全依赖栈。
学习小点
- [ ] 栈的概念(LIFO)
- [ ] 栈的实现(数组、链表)
- [ ] 栈的应用
- [ ] 函数调用栈(重要!)
- [ ] 表达式求值
- [ ] 括号匹配
- [ ] 队列的概念(FIFO)
- [ ] 队列的实现
- [ ] 环形队列
💻 代码示例
// 栈实现
#define STACK_SIZE 100
typedef struct {
int data[STACK_SIZE];
int top;
} Stack;
void StackInit(Stack* s) { s->top = -1; }
int StackEmpty(Stack* s) { return s->top == -1; }
int StackFull(Stack* s) { return s->top == STACK_SIZE - 1; }
void Push(Stack* s, int value) {
if (!StackFull(s))
s->data[++s->top] = value;
}
int Pop(Stack* s) {
if (!StackEmpty(s))
return s->data[s->top--];
return -1;
}
🔑 函数调用栈(逆向核心概念)
调用 func(1, 2) 时的栈布局:
高地址
+-------------------+
| 参数2 (2) | [EBP+0x0C]
+-------------------+
| 参数1 (1) | [EBP+0x08]
+-------------------+
| 返回地址 | [EBP+0x04] <- CALL指令压入
+-------------------+
| 旧EBP | [EBP+0x00] <- 函数序言保存
+-------------------+ <- EBP 指向这里
| 局部变量1 | [EBP-0x04]
+-------------------+
| 局部变量2 | [EBP-0x08]
+-------------------+
低地址 <- ESP 指向这里
📚 学习资料
| 类型 | 资源名称 | 链接/说明 | 推荐度 |
|---|---|---|---|
| 📹视频 | 函数调用栈详解 | B站/看雪 | ⭐⭐⭐⭐⭐ |
| 📖文档 | 调用约定与栈帧 | 看雪文章 | ⭐⭐⭐⭐⭐ |
| ✏️练习 | 用调试器观察栈 | x64dbg实践 | ⭐⭐⭐⭐⭐ |
1.2.3 树
📝 学习笔记
学习小点
- [ ] 二叉树基础概念
- [ ] 二叉树的遍历(前序、中序、后序、层序)
- [ ] 二叉搜索树(BST)
- [ ] 平衡二叉树(AVL)了解
- [ ] 红黑树(了解原理,Windows内核使用)
💻 代码示例
typedef struct _TREE_NODE {
int data;
struct _TREE_NODE* left;
struct _TREE_NODE* right;
} TREE_NODE, *PTREE_NODE;
// 中序遍历(BST排序输出)
void InorderTraversal(TREE_NODE* root) {
if (root == NULL) return;
InorderTraversal(root->left);
printf("%d ", root->data);
InorderTraversal(root->right);
}
// BST插入
TREE_NODE* BSTInsert(TREE_NODE* root, int data) {
if (root == NULL) return CreateTreeNode(data);
if (data < root->data)
root->left = BSTInsert(root->left, data);
else
root->right = BSTInsert(root->right, data);
return root;
}
1.2.4 哈希表
📝 学习笔记
学习小点
- [ ] 哈希函数设计原则
- [ ] 冲突处理方法(链地址法、开放寻址法)
- [ ] 哈希表在逆向中的识别特征
- [ ] 常见哈希算法(DJB2, FNV, CRC32)
💻 代码示例
// DJB2哈希算法
unsigned int djb2_hash(const char* str) {
unsigned int hash = 5381;
int c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; // hash * 33 + c
return hash % TABLE_SIZE;
}
✅ 数据结构阶段检查清单
- [ ] 能实现单链表的增删改查
- [ ] 理解Windows内核链表LIST_ENTRY
- [ ] 理解函数调用栈的结构
- [ ] 能实现基本的树遍历
- [ ] 理解哈希表原理
1.3 x86汇编 ⭐⭐⭐⭐
🎯 目标:掌握 32 位汇编基础,能读懂简单反汇编
⏱️ 时长:3-4 周
1.3.1 寄存器
📝 学习笔记
学习小点
- [ ] 通用寄存器(EAX, EBX, ECX, EDX)
- [ ] 索引寄存器(ESI, EDI)
- [ ] 栈寄存器(ESP, EBP)
- [ ] 指令指针(EIP)
- [ ] 标志寄存器(EFLAGS)- ZF, SF, CF, OF
- [ ] 段寄存器(CS, DS, SS, ES, FS, GS)
- [ ] 寄存器分割(AL, AH, AX, EAX)
💻 代码示例
; 32位通用寄存器及用途
; EAX - 累加器,函数返回值存这里
; EBX - 基址寄存器,通用
; ECX - 计数器,循环计数,C++中存this指针
; EDX - 数据寄存器,除法存高位,I/O端口操作
; 寄存器分割
; EAX (32位) = AX (低16位)
; AX = AH (高8位) + AL (低8位)
MOV EAX, 0x12345678
; 此时:
; EAX = 0x12345678
; AX = 0x5678
; AH = 0x56
; AL = 0x78
; 标志寄存器常用位
; ZF (Zero Flag) - 运算结果为0时置1
; SF (Sign Flag) - 运算结果为负时置1
; CF (Carry Flag) - 无符号运算进位/借位时置1
; OF (Overflow Flag) - 有符号运算溢出时置1
XOR EAX, EAX ; EAX=0, ZF=1
SUB EAX, 1 ; EAX=0xFFFFFFFF, SF=1, CF=1
1.3.2 基本指令
📝 学习笔记
学习小点
- [ ] 数据传送指令(MOV, LEA, XCHG, PUSH, POP, MOVZX, MOVSX)
- [ ] 算术运算指令(ADD, SUB, MUL, DIV, INC, DEC, NEG)
- [ ] 逻辑运算指令(AND, OR, XOR, NOT, TEST)
- [ ] 移位指令(SHL, SHR, SAL, SAR, ROL, ROR)
- [ ] 比较与跳转(CMP, TEST, JMP, JE/JNE, JG/JL, JA/JB)
- [ ] 函数调用(CALL, RET, LEAVE)
💻 代码示例
; ========== 数据传送 ==========
MOV EAX, 100 ; EAX = 100
MOV EBX, EAX ; EBX = EAX
MOV ECX, [0x401000] ; ECX = 内存[0x401000]的值
MOV [EBX], EAX ; 内存[EBX] = EAX
LEA EAX, [EBX+ECX*4+8] ; EAX = EBX + ECX*4 + 8 (只计算地址,不访问内存)
; ========== 算术运算 ==========
ADD EAX, 10 ; EAX += 10
SUB EAX, 5 ; EAX -= 5
INC ECX ; ECX++
DEC ECX ; ECX--
; 乘法
MUL EBX ; EDX:EAX = EAX * EBX (无符号)
IMUL EAX, EBX, 5 ; EAX = EBX * 5 (三操作数形式)
; 除法
XOR EDX, EDX ; 清零EDX (无符号除法前必须)
DIV ECX ; EAX = EDX:EAX / ECX, EDX = 余数
; ========== 逻辑运算 ==========
AND EAX, 0xFF ; 保留低8位
OR EAX, 0x80000000 ; 设置最高位
XOR EAX, EAX ; 清零(比MOV EAX, 0更快)
TEST EAX, EAX ; 检查EAX是否为0,设置ZF
; ========== 比较与跳转 ==========
CMP EAX, 10 ; 比较EAX和10,设置标志位
JE equal_label ; 相等跳转 (ZF=1)
JNE not_equal_label ; 不等跳转 (ZF=0)
JG greater_label ; 大于跳转 (有符号)
JA above_label ; 大于跳转 (无符号)
; ========== 函数调用 ==========
CALL my_function ; 等价于: PUSH EIP+5; JMP my_function
my_function:
PUSH EBP ; 保存旧栈帧
MOV EBP, ESP ; 建立新栈帧
SUB ESP, 0x20 ; 分配局部变量空间
; 函数体...
MOV ESP, EBP ; 恢复ESP
POP EBP ; 恢复EBP
RET ; 等价于: POP EIP
1.3.3 调用约定
📝 学习笔记
学习小点
- [ ] __cdecl(C语言默认)
- [ ] __stdcall(Windows API标准)
- [ ] __fastcall
- [ ] __thiscall(C++成员函数)
💻 代码示例
; ========== __cdecl (C语言默认) ==========
; 特点:调用者清理栈
PUSH 3 ; 参数c
PUSH 2 ; 参数b
PUSH 1 ; 参数a
CALL func
ADD ESP, 12 ; 调用者清理3个参数
; ========== __stdcall (Windows API) ==========
; 特点:被调用者清理栈
PUSH 3
PUSH 2
PUSH 1
CALL func ; 函数内部 RET 12
; ========== __fastcall ==========
; 特点:前两个参数用寄存器
MOV EDX, arg2 ; 第2个参数
MOV ECX, arg1 ; 第1个参数
PUSH arg3 ; 第3个参数入栈
CALL func
; ========== __thiscall (C++成员函数) ==========
; 特点:this指针在ECX
PUSH arg2
PUSH arg1
MOV ECX, obj ; this指针
CALL method
1.3.4 常见汇编模式识别
📝 学习笔记
学习小点
- [ ] 变量访问模式(全局、局部、参数)
- [ ] 数组访问模式
- [ ] 结构体访问模式
- [ ] if-else模式
- [ ] switch-case模式(跳转表)
- [ ] for/while循环模式
💻 代码示例
; ========== 变量访问模式 ==========
; 全局变量 - 直接地址访问
MOV EAX, [0x00401000]
; 局部变量 - [EBP-X]
MOV EAX, [EBP-4] ; 第1个局部变量
; 函数参数 - [EBP+X], X >= 8
MOV EAX, [EBP+8] ; 第1个参数
; ========== 数组访问模式 ==========
; arr[i] = 基址 + 索引 * 元素大小
MOV EAX, [EBX+ECX*4] ; int arr[], EBX=基址, ECX=索引
; ========== 结构体访问模式 ==========
; obj->field = 基址 + 固定偏移
MOV EAX, [ESI+0x10] ; 偏移0x10的成员
; ========== if-else 模式 ==========
CMP EAX, 10
JLE else_branch ; 小于等于则跳到else
; if 分支代码
JMP end_if
else_branch:
; else 分支代码
end_if:
; ========== for循环 模式 ==========
XOR ECX, ECX ; i = 0
loop_start:
CMP ECX, 10 ; i < 10?
JGE loop_end ; 不满足则退出
; 循环体
INC ECX ; i++
JMP loop_start
loop_end:
✅ x86汇编阶段检查清单
- [ ] 认识所有x86寄存器及用途
- [ ] 能读懂基本汇编指令
- [ ] 理解三种主要调用约定
- [ ] 能识别函数序言和尾声
- [ ] 能识别if-else、循环等控制结构
- [ ] 能识别数组和结构体访问模式
- [ ] 能使用IDA或x64dbg查看反汇编
📚 阶段一学习资源汇总
必读书籍
| 书籍 | 说明 |
|---|---|
| 《C Primer Plus》 | C语言入门 |
| 《C和指针》 | 指针深入 |
| 《汇编语言》王爽 | x86汇编入门 |
推荐视频
| 视频 | 平台 |
|---|---|
| 翁恺《C语言程序设计》 | B站/MOOC |
| 小甲鱼汇编教程 | B站 |
| 郝斌数据结构 | B站 |
实践工具
| 工具 | 用途 |
|---|---|
| Visual Studio | C语言开发 |
| IDA Free | 反汇编学习 |
| x64dbg | 动态调试 |
⏭️ 下一阶段
完成本阶段后,请继续学习:
- [[阶段二:初级逆向]]
📌 学习建议
- C语言基础必须扎实,特别是指针
- 多写代码,多练习
- 用调试器观察程序执行
- 编写小程序然后反编译对照学习