Loading...

文章背景图

阶段一:编程基础

**摘要** 本文档详细规划了逆向与驱动开发学习路径的第一阶段——编程基础,内容涵盖C语言、数据结构和x86汇编三大板块,旨在为后续学习奠定坚实基础。 **1. C语言(4-6周)** - **核心内容**:数据类型与指针(重点)、内存管理、结构体/联合体、位运算、文件操作 - **关键要点**:指针操作是逆向分析核心,需掌握多级指针、函数指针和内存对齐原理 - **实践建议**:通过PE文件解析器练习二进制文件操作,使用调试器观察内存布局 **2. 数据结构(2-3周)** - **重点内容**:链表(含Windows内核LIST_ENTRY)、栈/队列(函数调用栈)、树、哈希表 - **逆向关联**:理解数据结构的内存特征,识别汇编中的访问模式 - **核心技能**:掌握CONTAINING_RECORD宏和栈帧结构分析 **3. x86汇编(3-4周)** - **学习要点**:寄存器功能、基本指令、调用约定(cdecl/stdcall/thiscall)、控制流模式识别 - **实践方法**:结合IDA/x64dbg反编译C程序,对比源码与汇编代码 **阶段目标**:能熟练使用C语言编写带指针操作的程序,理解常见数据结构的内存布局,读懂基础反汇编代码并识别控制结构。推荐资源包括《C Primer Plus》《汇编语言》(王爽)和翁恺C语言课程,配合Visual Studio和调试工具进行实践。 完成本阶段后,学习者将具备进入下一阶段"初级逆向"所需的核心编程能力。特别强调指针和内存操作的实战训练,这是逆向工程的重要基础。

a0yark a0yark
|
2025-12-03
|
6
|
-
|
- min
|

阶段一:编程基础

📅 创建日期: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] 内存对齐规则

  1. 结构体成员按其自身大小对齐
  2. 结构体总大小是最大成员大小的倍数
  3. 可使用 #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 动态调试

⏭️ 下一阶段

完成本阶段后,请继续学习:

  • [[阶段二:初级逆向]]

📌 学习建议

  1. C语言基础必须扎实,特别是指针
  2. 多写代码,多练习
  3. 用调试器观察程序执行
  4. 编写小程序然后反编译对照学习

分享文章

未配置分享平台

请在主题设置中启用分享平台

评论