Loading...

文章背景图

游戏逆向与驱动开发学习路径

本文详细规划了从编程基础到逆向工程与驱动开发专家的18-30个月综合学习路径,分为九个阶段: **基础阶段(3-5个月)** 1. 编程基础(C语言/数据结构/x86汇编):重点掌握指针、内存管理、虚函数等核心概念 2. 初级逆向:学习IDA、x64dbg等工具使用和简单程序分析 **进阶阶段(8-10个月)** 3. C++与x64编程:深入理解虚表机制和64位调用约定 4. Windows开发:掌握进程/内存操作和内核API 5. 核心逆向技术:DLL注入、多种Hook技术实现 6. 驱动开发:从基础驱动到VT虚拟化进阶 **专业领域(4-8个月)** 7. 图形渲染:DirectX Hook和ImGui界面开发 8. Lua框架分析:游戏脚本系统逆向 9. 引擎逆向:UE/Unity引擎深度分析 **关键学习点** - 逆向分析:PE结构、反调试对抗、加壳脱壳 - 内核开发:IRQL同步、进程回调、内存管理 - 游戏安全:VT技术、D3D绘制、引擎SDK生成 **推荐资源** - 经典书籍:《加密与解密》《Windows核心编程》《逆向工程核心原理》 - 工具链:IDA Pro/x64dbg/WinDbg/Cheat Engine - 社区资源:看雪学院/UnknownCheats/Guided Hacking 该路径强调理论与实践结合,每个阶段包含明确的技能目标和练习项目(如编写内存修改器、实现Hook框架等),需要约600小时的有效学习时间,建议通过实际项目巩固技能。

a0yark a0yark
|
2025-12-03
|
24
|
-
|
- min
|

逆向工程与驱动开发综合学习路径

📅 创建日期:2025-12-03
🎯 目标:从编程基础到高级逆向工程师 + 驱动开发专家
⏱️ 学习周期:18-30 个月
📝 文档版本:v2.0(详细版)


📑 目录


阶段一:编程基础

⏱️ 总时长:2-3 个月
🎯 目标:打下坚实的编程和汇编基础


1.1 C语言 ⭐⭐⭐⭐⭐

🎯 目标:掌握 C 语言核心概念,为后续所有学习打基础
⏱️ 时长:4-6 周

1.1.1 基础语法

学习小点

  • [ ] 变量与数据类型(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 (正确)

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

翁恺《C 语言程序设计》

B 站 / 中国大学 MOOC

⭐⭐⭐⭐⭐

📹视频

郝斌 C 语言教程

B 站

⭐⭐⭐⭐

📹视频

黑马程序员 C 语言

B 站

⭐⭐⭐⭐

📖书籍

《C Primer Plus》

第 1-7 章

⭐⭐⭐⭐⭐

📖书籍

《C 程序设计语言》K&R

C 语言圣经

⭐⭐⭐⭐⭐

🌐在线

菜鸟教程 C 语言

runoob.com

⭐⭐⭐


1.1.2 指针与内存管理(最重要!)

学习小点

  • [ ] 指针的概念(内存地址、解引用、取地址运算符)

  • [ ] 指针变量的声明与初始化

  • [ ] 指针运算(加减、比较、偏移计算)

  • [ ] 数组与指针的关系

  • [ ] 指针数组 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;  // 避免悬空指针!

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

小甲鱼 C 语言指针专题

B 站

⭐⭐⭐⭐⭐

📹视频

指针详解(一口 Linux)

B 站

⭐⭐⭐⭐

📖书籍

《C 和指针》Kenneth Reek

指针专项深入

⭐⭐⭐⭐⭐

📖书籍

《C 专家编程》

深入理解 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)

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

结构体内存对齐详解

B 站搜索

⭐⭐⭐⭐⭐

📖书籍

《C Primer Plus》第 14 章

结构体专题

⭐⭐⭐⭐

📖书籍

《深入理解计算机系统》

第 3 章数据对齐

⭐⭐⭐⭐⭐

🌐文档

MSDN Structure Alignment

Microsoft 文档

⭐⭐⭐⭐

✏️练习

编写 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

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

位运算从入门到精通

B 站

⭐⭐⭐⭐

📖书籍

《Hacker’s Delight》

位运算圣经

⭐⭐⭐⭐⭐

🌐在线

Bit Twiddling Hacks

graphics.stanford.edu

⭐⭐⭐⭐⭐

✏️练习

LeetCode 位运算专题

⭐⭐⭐⭐


1.1.5 文件操作

学习小点

  • [ ] 文件打开模式(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)

  • [ ] 链表在内存中的特征识别

代码示例

// 单链表节点
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;
}

// 遍历
void PrintList(NODE* head) {
    while (head != NULL) {
        printf("%d -> ", head->data);
        head = head->next;
    }
    printf("NULL\n");
}

// ========== 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;
    }
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

数据结构与算法(青岛大学王卓)

B 站

⭐⭐⭐⭐⭐

📹视频

郝斌数据结构

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;

// 创建节点
TREE_NODE* CreateTreeNode(int data) {
    TREE_NODE* node = (TREE_NODE*)malloc(sizeof(TREE_NODE));
    node->data = data;
    node->left = node->right = NULL;
    return 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;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

数据结构 - 树(王卓)

B 站

⭐⭐⭐⭐

🌐可视化

VisuAlgo

visualgo.net

⭐⭐⭐⭐⭐

✏️练习

LeetCode 树专题

94, 102, 104 等

⭐⭐⭐⭐


1.2.4 哈希表

学习小点

  • [ ] 哈希函数设计原则

  • [ ] 冲突处理方法

    • [ ] 链地址法(拉链法)

    • [ ] 开放寻址法

  • [ ] 哈希表在逆向中的识别特征

  • [ ] 常见哈希算法(DJB2, FNV, CRC32)

代码示例

#define TABLE_SIZE 256

typedef struct _HASH_NODE {
    char* key;
    void* value;
    struct _HASH_NODE* next;
} HASH_NODE;

typedef struct _HASH_TABLE {
    HASH_NODE* buckets[TABLE_SIZE];
    int count;
} HASH_TABLE;

// 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;
}

// 插入
void HashInsert(HASH_TABLE* table, const char* key, void* value) {
    unsigned int index = djb2_hash(key);
    HASH_NODE* node = (HASH_NODE*)malloc(sizeof(HASH_NODE));
    node->key = strdup(key);
    node->value = value;
    node->next = table->buckets[index];
    table->buckets[index] = node;
    table->count++;
}

// 查找
void* HashFind(HASH_TABLE* table, const char* key) {
    unsigned int index = djb2_hash(key);
    HASH_NODE* node = table->buckets[index];
    while (node) {
        if (strcmp(node->key, key) == 0)
            return node->value;
        node = node->next;
    }
    return NULL;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

哈希表原理详解

B 站

⭐⭐⭐⭐

📖书籍

《算法》第 4 版

第 3 章

⭐⭐⭐⭐

✏️练习

实现简单哈希表

⭐⭐⭐⭐⭐


✅ 数据结构阶段检查清单

  • [ ] 能实现单链表的增删改查

  • [ ] 理解 Windows 内核链表 LIST_ENTRY

  • [ ] 理解函数调用栈的结构

  • [ ] 能实现基本的树遍历

  • [ ] 理解哈希表原理


1.3 x86汇编 ⭐⭐⭐⭐

🎯 目标:掌握 32 位汇编基础,能读懂简单反汇编
⏱️ 时长:3-4 周

1.3.1 寄存器

学习小点

  • [ ] 通用寄存器

    • [ ] EAX(累加器,函数返回值)

    • [ ] EBX(基址寄存器)

    • [ ] ECX(计数器,this 指针)

    • [ ] 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

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

小甲鱼汇编教程

B 站,强烈推荐

⭐⭐⭐⭐⭐

📹视频

王爽汇编语言配套

B 站

⭐⭐⭐⭐⭐

📖书籍

《汇编语言》王爽

必读经典

⭐⭐⭐⭐⭐

📖书籍

《x86 汇编语言:从实模式到保护模式》

进阶

⭐⭐⭐⭐

🔧工具

emu8086

汇编模拟器

⭐⭐⭐⭐


1.3.2 基本指令

学习小点

  • [ ] 数据传送指令

    • [ ] MOV(数据传送)

    • [ ] LEA(加载有效地址)

    • [ ] XCHG(交换)

    • [ ] PUSH/POP(栈操作)

    • [ ] MOVZX/MOVSX(零扩展 / 符号扩展)

  • [ ] 算术运算指令

    • [ ] ADD/SUB(加减)

    • [ ] INC/DEC(自增自减)

    • [ ] MUL/IMUL(无符号 / 有符号乘法)

    • [ ] DIV/IDIV(无符号 / 有符号除法)

    • [ ] 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 (只计算地址,不访问内存)
; LEA常用于计算地址或简单算术

XCHG EAX, EBX          ; 交换EAX和EBX

; 栈操作
PUSH EAX               ; ESP -= 4; [ESP] = EAX
POP EBX                ; EBX = [ESP]; ESP += 4
PUSHAD                 ; 压入所有通用寄存器
POPAD                  ; 弹出所有通用寄存器

; 扩展
MOVZX EAX, BYTE PTR [ESI]  ; 零扩展:AL = [ESI], 高位清0
MOVSX EAX, BYTE PTR [ESI]  ; 符号扩展:保持符号

; ========== 算术运算 ==========
ADD EAX, 10            ; EAX += 10
SUB EAX, 5             ; EAX -= 5
INC ECX                ; ECX++
DEC ECX                ; ECX--

; 乘法
MUL EBX                ; EDX:EAX = EAX * EBX (无符号)
IMUL EBX               ; EDX:EAX = EAX * EBX (有符号)
IMUL EAX, EBX, 5       ; EAX = EBX * 5 (三操作数形式)

; 除法
; 被除数在EDX:EAX中,除数在操作数中
XOR EDX, EDX           ; 清零EDX (无符号除法前必须)
DIV ECX                ; EAX = EDX:EAX / ECX, EDX = 余数

; ========== 逻辑运算 ==========
AND EAX, 0xFF          ; 保留低8位,清除高位
OR  EAX, 0x80000000    ; 设置最高位
XOR EAX, EAX           ; 清零(比MOV EAX, 0更快)
NOT EAX                ; 按位取反

TEST EAX, EAX          ; 检查EAX是否为0,设置ZF,不修改EAX
; TEST常用于检查位或判断是否为0

; ========== 移位 ==========
SHL EAX, 2             ; EAX <<= 2 (相当于 *4)
SHR EAX, 1             ; EAX >>= 1 (无符号,相当于 /2)
SAR EAX, 1             ; EAX >>= 1 (有符号,保持符号位)

; ========== 比较与跳转 ==========
CMP EAX, 10            ; 比较EAX和10,设置标志位
JE  equal_label        ; 相等跳转 (ZF=1)
JNE not_equal_label    ; 不等跳转 (ZF=0)
JG  greater_label      ; 大于跳转 (有符号)
JL  less_label         ; 小于跳转 (有符号)
JA  above_label        ; 大于跳转 (无符号)
JB  below_label        ; 小于跳转 (无符号)
JGE greater_equal      ; 大于等于 (有符号)
JLE less_equal         ; 小于等于 (有符号)

; 循环
MOV ECX, 10
loop_start:
    ; 循环体
    LOOP loop_start    ; ECX--; if ECX != 0 goto loop_start

; ========== 函数调用 ==========
CALL my_function       ; 等价于: PUSH EIP+5; JMP my_function
; ...
my_function:
    PUSH EBP           ; 保存旧栈帧
    MOV EBP, ESP       ; 建立新栈帧
    SUB ESP, 0x20      ; 分配局部变量空间
    
    ; 函数体...
    ; 参数访问: [EBP+8], [EBP+C], ...
    ; 局部变量: [EBP-4], [EBP-8], ...
    
    MOV ESP, EBP       ; 恢复ESP
    POP EBP            ; 恢复EBP
    RET                ; 等价于: POP EIP
    
; 或使用LEAVE
    LEAVE              ; 等价于: MOV ESP, EBP; POP EBP
    RET

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖文档

Intel 指令参考手册

权威参考

⭐⭐⭐⭐⭐

🌐在线

x86 Assembly Guide

cs.virginia.edu

⭐⭐⭐⭐

🌐在线

x86 Instruction Reference

felixcloutier.com

⭐⭐⭐⭐⭐

✏️练习

用汇编写简单程序

⭐⭐⭐⭐


1.3.3 调用约定

学习小点

  • [ ] __cdecl(C 语言默认)

    • [ ] 参数从右到左入栈

    • [ ] 调用者清理栈

  • [ ] __stdcall(Windows API 标准)

    • [ ] 参数从右到左入栈

    • [ ] 被调用者清理栈

  • [ ] __fastcall

    • [ ] 前两个参数用 ECX, EDX

    • [ ] 其余参数入栈

  • [ ] __thiscall(C++ 成员函数)

    • [ ] this 指针在 ECX 中

  • [ ] 栈帧结构理解

  • [ ] 函数序言和尾声识别

代码示例

; ========== __cdecl (C语言默认) ==========
; 特点:调用者清理栈
; 调用 int func(int a, int b, int c)
PUSH 3                 ; 参数c
PUSH 2                 ; 参数b
PUSH 1                 ; 参数a
CALL func
ADD ESP, 12            ; 调用者清理3个参数 (3*4=12字节)
; 返回值在EAX

; ========== __stdcall (Windows API) ==========
; 特点:被调用者清理栈
; 调用 int __stdcall func(int a, int b, int c)
PUSH 3
PUSH 2
PUSH 1
CALL func              ; 函数内部 RET 12

; 函数定义示例
func_stdcall:
    PUSH EBP
    MOV EBP, ESP
    ; 函数体
    MOV EAX, [EBP+8]   ; 参数a
    ADD EAX, [EBP+C]   ; + 参数b
    ADD EAX, [EBP+10]  ; + 参数c
    POP EBP
    RET 12             ; 返回并清理12字节参数

; ========== __fastcall ==========
; 特点:前两个参数用寄存器
; 调用 int __fastcall func(int a, int b, int c, int d)
PUSH d                 ; 第4个参数入栈
PUSH c                 ; 第3个参数入栈
MOV EDX, b             ; 第2个参数
MOV ECX, a             ; 第1个参数
CALL func

; ========== __thiscall (C++成员函数) ==========
; 特点:this指针在ECX
; 调用 obj->method(int a, int b)
PUSH b
PUSH a
MOV ECX, obj           ; this指针
CALL method

; ========== 标准栈帧结构 ==========
; 调用 func(1, 2) 后的栈:
;
; +------------------+ 高地址
; |     参数2 (2)    |  [EBP+0x0C]
; +------------------+
; |     参数1 (1)    |  [EBP+0x08]
; +------------------+
; |     返回地址     |  [EBP+0x04]
; +------------------+
; |     旧EBP        |  [EBP]      <- 当前EBP指向
; +------------------+
; |    局部变量1     |  [EBP-0x04]
; +------------------+
; |    局部变量2     |  [EBP-0x08]
; +------------------+ 低地址     <- ESP指向

; 典型函数序言
PUSH EBP               ; 保存调用者的栈帧
MOV EBP, ESP           ; 建立新栈帧
SUB ESP, N             ; 分配N字节局部变量
PUSH EBX               ; 保存被调用者保存寄存器
PUSH ESI
PUSH EDI

; 典型函数尾声
POP EDI                ; 恢复寄存器
POP ESI
POP EBX
MOV ESP, EBP           ; 释放局部变量
POP EBP                ; 恢复调用者栈帧
RET                    ; 返回

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖文档

MSDN 调用约定

docs.microsoft.com

⭐⭐⭐⭐⭐

📹视频

函数调用约定详解

B 站 / 看雪

⭐⭐⭐⭐⭐

✏️练习

用 IDA 分析不同调用约定

⭐⭐⭐⭐⭐


1.3.4 常见汇编模式识别

学习小点

  • [ ] 变量访问模式

    • [ ] 全局变量:直接地址

    • [] 局部变量:[EBP-X]

    • [] 函数参数:[EBP+X]

  • [ ] 数组访问模式

  • [ ] 结构体访问模式

  • [ ] if-else 模式

  • [ ] switch-case 模式(跳转表)

  • [ ] for/while 循环模式

  • [ ] 函数调用模式

代码示例

; ========== 变量访问模式 ==========

; 全局变量 - 直接地址访问
MOV EAX, [0x401000]         ; 读取全局变量
MOV [0x401000], EAX         ; 写入全局变量

; 局部变量 - 栈上负偏移
MOV EAX, [EBP-4]            ; 第1个局部变量
MOV EBX, [EBP-8]            ; 第2个局部变量

; 函数参数 - 栈上正偏移
MOV EAX, [EBP+8]            ; 第1个参数
MOV EBX, [EBP+C]            ; 第2个参数

; ========== 数组访问模式 ==========

; arr[i] 访问
; 基址 + 索引 * 元素大小
MOV EAX, [EBX+ECX*4]        ; int arr[],EBX=基址,ECX=索引
MOV AL, [ESI+EDI]           ; char arr[],ESI=基址,EDI=索引

; 固定索引
MOV EAX, [EBX+20]           ; arr[5],假设int数组

; ========== 结构体访问模式 ==========

; obj->field 或 (*ptr).field
; 基址 + 固定偏移
MOV EAX, [ESI+0]            ; 第1个成员
MOV EBX, [ESI+4]            ; 第2个成员
MOV ECX, [ESI+10h]          ; 偏移0x10的成员

; ========== if-else 模式 ==========

; C代码: if (a > 10) { ... } else { ... }
CMP EAX, 10
JLE else_branch             ; 小于等于则跳到else
    ; if 分支代码
    JMP end_if
else_branch:
    ; else 分支代码
end_if:

; ========== switch-case 模式 ==========

; 小范围连续case - 跳转表
CMP EAX, 5                  ; 检查是否超出范围
JA default_case             ; 超出则跳到default
JMP DWORD PTR [jump_table+EAX*4]  ; 跳转表

jump_table:
    DD case_0
    DD case_1
    DD case_2
    DD case_3
    DD case_4
    DD case_5

; ========== for循环模式 ==========

; C代码: for (int i = 0; i < 10; i++) { ... }
XOR ECX, ECX                ; i = 0
loop_start:
    CMP ECX, 10             ; i < 10?
    JGE loop_end            ; 不满足则退出
    
    ; 循环体
    
    INC ECX                 ; i++
    JMP loop_start
loop_end:

; 或使用LOOP指令
MOV ECX, 10                 ; 循环次数
loop_body:
    ; 循环体
    LOOP loop_body          ; ECX--; if ECX!=0 goto loop_body

; ========== while循环模式 ==========

; C代码: while (condition) { ... }
while_start:
    ; 检查条件
    TEST EAX, EAX
    JZ while_end            ; 条件不满足则退出
    
    ; 循环体
    
    JMP while_start
while_end:

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《逆向工程核心原理》

强烈推荐

⭐⭐⭐⭐⭐

📖书籍

《加密与解密》第 4 版

经典教材

⭐⭐⭐⭐⭐

✏️练习

编写小程序然后反编译

对照学习

⭐⭐⭐⭐⭐

🔧工具

IDA Free

hex-rays.com

⭐⭐⭐⭐⭐

🔧工具

x64dbg

x64dbg.com

⭐⭐⭐⭐⭐


✅ x86汇编阶段检查清单

  • [ ] 认识所有 x86 寄存器及用途

  • [ ] 能读懂基本汇编指令

  • [ ] 理解三种主要调用约定

  • [ ] 能识别函数序言和尾声

  • [ ] 能识别 if-else、循环等控制结构

  • [ ] 能识别数组和结构体访问模式

  • [ ] 能使用 IDA 或 x64dbg 查看反汇编



阶段二:初级逆向

⏱️ 总时长:1-2 个月
🎯 目标:掌握逆向工具使用,能分析简单程序


2.1 逆向工具的使用 ⭐⭐⭐⭐⭐

🎯 目标:熟练使用 IDA、x64dbg、Cheat Engine
⏱️ 时长:2-3 周

2.1.1 IDA Pro

学习小点

  • [ ] IDA 界面熟悉

    • [ ] 反汇编视图(Text View / Graph View)

    • [ ] 十六进制视图

    • [ ] 结构体视图

    • [ ] 字符串窗口

    • [ ] 导入 / 导出表

    • [ ] 交叉引用窗口

  • [ ] 基本操作

    • [ ] 打开文件与分析

    • [ ] 导航与跳转

    • [ ] 重命名函数和变量

    • [ ] 添加注释

    • [ ] 创建结构体

    • [ ] 修改数据类型

  • [ ] 反编译器使用(F5)

    • [ ] 查看伪代码

    • [ ] 修改变量类型

    • [ ] 同步反汇编与伪代码

  • [ ] 搜索功能

    • [ ] 字符串搜索

    • [ ] 二进制搜索

    • [ ] 立即数搜索

  • [ ] 脚本与插件

    • [ ] IDAPython 基础

    • [ ] 常用插件

快捷键速查表

快捷键

功能

说明

Space

切换视图

Text/Graph 视图切换

G

跳转地址

输入地址跳转

N

重命名

重命名当前项目

X

交叉引用

查看谁调用了这里

F5

反编译

生成伪代码

;

添加注释

在当前行添加注释

/

添加注释

伪代码中添加注释

Y

修改类型

修改变量 / 函数类型

D

转换数据

db/dw/dd 切换

C

转换代码

将数据转为代码

U

取消定义

Undefine

P

创建函数

在当前位置创建函数

T

结构体偏移

应用结构体

Alt+T

文本搜索

搜索文本

Alt+B

二进制搜索

搜索字节序列

Alt+I

立即数搜索

搜索数值

Ctrl+S

保存

保存数据库

Shift+F12

字符串窗口

查看所有字符串

IDA实战练习

练习1:分析简单CrackMe
1. 打开CrackMe程序
2. 找到main函数
3. 定位密码校验逻辑
4. 分析校验算法
5. 找出正确密码

练习2:分析API调用
1. 查看导入表
2. 找到MessageBoxA的调用
3. 通过交叉引用定位调用位置
4. 分析调用参数

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

IDA Pro 从入门到精通

B 站搜索

⭐⭐⭐⭐⭐

📹视频

逆向工程入门(看雪)

看雪学院

⭐⭐⭐⭐⭐

📖书籍

《IDA Pro 权威指南》

官方教程

⭐⭐⭐⭐⭐

📖书籍

《逆向工程核心原理》

第一部分

⭐⭐⭐⭐⭐

🌐在线

IDA 官方文档

hex-rays.com/docs

⭐⭐⭐⭐

✏️练习

CrackMe 挑战

crackmes.one

⭐⭐⭐⭐⭐

🔧下载

IDA Free

hex-rays.com/ida-free

免费版


2.1.2 x64dbg

学习小点

  • [ ] 界面熟悉

    • [ ] CPU 窗口(反汇编、寄存器、栈、内存)

    • [ ] 日志窗口

    • [ ] 断点窗口

    • [ ] 内存映射

    • [ ] 调用栈

    • [ ] 线程列表

  • [ ] 基本调试操作

    • [ ] 附加进程 / 打开程序

    • [ ] 运行、暂停、重启

    • [ ] 单步步入(F7)、单步步过(F8)

    • [ ] 运行到光标(F4)

    • [ ] 运行到返回(Ctrl+F9)

  • [ ] 断点使用

    • [ ] 软件断点(INT3)

    • [ ] 硬件断点(执行 / 读 / 写)

    • [ ] 内存断点

    • [ ] 条件断点

  • [ ] 内存操作

    • [ ] 查看内存

    • [ ] 修改内存

    • [ ] 搜索内存

    • [ ] 内存映射查看

  • [ ] 补丁与修改

    • [ ] 汇编修改指令

    • [ ] NOP 填充

    • [ ] 导出补丁

  • [ ] 脚本功能

    • [ ] 命令行使用

    • [ ] 脚本编写基础

快捷键速查表

快捷键

功能

说明

F2

断点

设置 / 取消断点

F7

单步步入

进入函数

F8

单步步过

跳过函数

F9

运行

继续执行

F4

运行到光标

执行到选中位置

Ctrl+F9

运行到返回

执行到 RET

Ctrl+G

跳转

跳转到地址

Ctrl+F

搜索

搜索指令

Space

汇编

修改当前指令

;

注释

添加注释

:

标签

添加标签

Ctrl+B

二进制搜索

搜索字节

Ctrl+E

编辑

编辑内存

Ctrl+P

补丁

导出补丁

x64dbg实战练习

练习1:动态分析程序流程
1. 在main函数入口下断点
2. 单步执行观察流程
3. 观察寄存器和栈变化
4. 找到关键判断点

练习2:修改程序行为
1. 找到跳转指令
2. 使用NOP或修改跳转
3. 观察程序行为变化
4. 保存补丁

练习3:分析加密算法
1. 在输入函数后下断点
2. 观察输入数据如何被处理
3. 使用硬件断点跟踪数据流
4. 还原加密逻辑

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

x64dbg 调试教程

B 站搜索

⭐⭐⭐⭐⭐

📹视频

动态调试入门

吾爱破解

⭐⭐⭐⭐

📖文档

x64dbg 官方文档

help.x64dbg.com

⭐⭐⭐⭐

📖书籍

《加密与解密》

调试章节

⭐⭐⭐⭐⭐

🔧下载

x64dbg

x64dbg.com

免费开源

✏️练习

160 个 CrackMe

吾爱破解

⭐⭐⭐⭐⭐


2.1.3 Cheat Engine

学习小点

  • [ ] 基本功能

    • [ ] 附加进程

    • [ ] 数值搜索(精确值、变动值)

    • [ ] 数据类型选择

    • [ ] 修改数值

  • [ ] 进阶搜索

    • [ ] 未知初始值搜索

    • [ ] 增加 / 减少搜索

    • [ ] 变动 / 未变搜索

    • [ ] 范围搜索

  • [ ] 指针扫描

    • [ ] 找到动态地址

    • [ ] 扫描指针路径

    • [ ] 构建指针链

  • [ ] 代码注入

    • [ ] 找到修改代码的位置

    • [ ] 使用 "找出是什么访问了这个地址"

    • [ ] 使用 "找出是什么写入了这个地址"

    • [ ] 代码注入器基础

  • [ ] 内置教程

    • [ ] 完成 CE Tutorial(必做!)

CE操作流程

基本数值搜索流程:
1. 打开CE,附加目标进程
2. 选择正确的数据类型(4字节/浮点等)
3. 输入当前数值,首次扫描
4. 在游戏中改变数值
5. 输入新数值,再次扫描
6. 重复直到找到唯一地址
7. 修改数值或锁定

指针扫描流程:
1. 找到动态地址
2. 右键 -> 指针扫描
3. 设置扫描参数(通常Max Level 5-7)
4. 重启游戏验证
5. 比较结果,找出稳定指针路径

找代码注入点:
1. 找到目标地址
2. 右键 -> "找出是什么写入了这个地址"
3. 在游戏中触发写入
4. 查看写入的代码位置
5. 分析代码逻辑

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

CE 从入门到精通

B 站搜索

⭐⭐⭐⭐⭐

📹视频

Guided Hacking CE 教程

YouTube

⭐⭐⭐⭐⭐

🔧内置

CE Tutorial

CE 自带教程

⭐⭐⭐⭐⭐

📖文档

CE 官方 Wiki

wiki.cheatengine.org

⭐⭐⭐⭐

🔧下载

Cheat Engine

cheatengine.org

免费

✏️练习

各种小游戏修改

⭐⭐⭐⭐⭐


2.1.4 其他实用工具

学习小点

  • [ ] PE 分析工具

    • [ ] CFF Explorer

    • [ ] PE-bear

    • [] DIE (Detect It Easy)

  • [ ] 十六进制编辑器

    • [ ] HxD

    • [ ] 010 Editor

  • [ ] 资源查看器

    • [ ] Resource Hacker

  • [ ] 进程工具

    • [ ] Process Monitor

    • [ ] Process Explorer

    • [ ] API Monitor

  • [ ] 网络分析

    • [ ] Wireshark

    • [ ] Fiddler

工具用途说明

CFF Explorer / PE-bear:
- 查看PE文件结构
- 分析导入导出表
- 修改PE头信息

DIE (Detect It Easy):
- 检测加壳类型
- 识别编译器
- 检测保护方式

Process Monitor:
- 监控文件操作
- 监控注册表操作
- 监控网络操作

API Monitor:
- 监控API调用
- 查看参数和返回值
- 分析程序行为

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

🔧工具

CFF Explorer

ntcore.com

⭐⭐⭐⭐⭐

🔧工具

DIE

github.com/horsicq/DIE

⭐⭐⭐⭐⭐

🔧工具

Process Monitor

Sysinternals

⭐⭐⭐⭐⭐

🔧工具

API Monitor

apimonitor.com

⭐⭐⭐⭐


✅ 逆向工具阶段检查清单

  • [ ] 能用 IDA 打开程序并分析函数

  • [ ] 会使用 IDA 的交叉引用功能

  • [ ] 能用 x64dbg 进行动态调试

  • [ ] 会设置各种类型的断点

  • [ ] 能用 CE 搜索和修改游戏数值

  • [ ] 会用 CE 进行指针扫描

  • [ ] 能识别程序是否加壳


2.2 C语言逆向 ⭐⭐⭐⭐

🎯 目标:能将反汇编代码还原为 C 代码
⏱️ 时长:2-3 周

2.2.1 变量识别

学习小点

  • [ ] 全局变量识别

    • [ ] 直接地址访问特征

    • [ ] .data/.bss 段分布

  • [ ] 局部变量识别

    • [] [EBP-X] 模式

    • [] [ESP+X] 模式

  • [ ] 函数参数识别

    • [] [EBP+X] 模式

    • [ ] 根据调用约定分析

  • [ ] 数组识别

    • [ ] 基址 + 索引 * 大小 模式

    • [ ] 连续内存访问

  • [ ] 结构体识别

    • [ ] 基址 + 固定偏移 模式

    • [ ] 多个字段访问

汇编特征与C代码对照

; ========== 全局变量 ==========
; 汇编特征:直接地址访问
MOV EAX, DWORD PTR DS:[0x00404000]
MOV DWORD PTR DS:[0x00404000], 100

; 对应C代码:
int g_value;  // 地址 0x00404000
g_value = 100;

; ========== 局部变量 ==========
; 汇编特征:[EBP-X] 或 [ESP+X] 访问
MOV DWORD PTR SS:[EBP-4], 10      ; 第1个局部变量
MOV DWORD PTR SS:[EBP-8], 20      ; 第2个局部变量

; 对应C代码:
int local1 = 10;
int local2 = 20;

; ========== 函数参数 ==========
; 汇编特征:[EBP+X] 访问,X >= 8
MOV EAX, DWORD PTR SS:[EBP+8]     ; 第1个参数
MOV EBX, DWORD PTR SS:[EBP+C]     ; 第2个参数

; 对应C代码:
void func(int param1, int param2) {
    // param1 = [EBP+8]
    // param2 = [EBP+C]
}

; ========== 数组访问 ==========
; 汇编特征:基址 + 索引 * 元素大小
MOV EAX, DWORD PTR DS:[ECX*4+0x00404000]  ; arr[i]
MOV EAX, DWORD PTR DS:[ESI+EDI*4]         ; arr[i],ESI为基址

; 对应C代码:
int arr[100];  // 基址 0x00404000
x = arr[i];    // ECX = i

; ========== 结构体访问 ==========
; 汇编特征:基址 + 固定偏移
MOV EAX, DWORD PTR DS:[ESI]       ; obj->field1 (offset 0)
MOV EBX, DWORD PTR DS:[ESI+4]     ; obj->field2 (offset 4)
MOV ECX, DWORD PTR DS:[ESI+10]    ; obj->field3 (offset 0x10)

; 对应C代码:
struct MyStruct {
    int field1;    // +0x00
    int field2;    // +0x04
    // padding
    int field3;    // +0x10
};
MyStruct* obj;
x = obj->field1;
y = obj->field2;
z = obj->field3;

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《逆向工程核心原理》

第一部分

⭐⭐⭐⭐⭐

📖书籍

《加密与解密》

基础逆向章节

⭐⭐⭐⭐⭐

✏️练习

编写 C 程序并反编译对照

⭐⭐⭐⭐⭐


2.2.2 控制流识别

学习小点

  • [ ] if-else 识别

    • [ ] CMP + 条件跳转模式

    • [ ] 分支结构

  • [ ] switch-case 识别

    • [ ] 跳转表模式

    • [ ] if-else 链模式

  • [ ] for 循环识别

    • [ ] 初始化 - 条件 - 递增结构

  • [ ] while 循环识别

    • [ ] 条件在开头

  • [ ] do-while 循环识别

    • [ ] 条件在结尾

汇编特征与C代码对照

; ========== if-else ==========
; 汇编模式:
CMP DWORD PTR SS:[EBP-4], 10    ; if (x > 10)
JLE short loc_401020            ; 不满足则跳过if块
    ; if 块代码
    MOV EAX, 1
    JMP short loc_401030
loc_401020:                      ; else 块
    MOV EAX, 0
loc_401030:

; 对应C代码:
if (x > 10) {
    return 1;
} else {
    return 0;
}

; ========== if-else if-else ==========
CMP EAX, 1
JNE loc_case2
    ; case 1
    JMP loc_end
loc_case2:
CMP EAX, 2
JNE loc_default
    ; case 2
    JMP loc_end
loc_default:
    ; default
loc_end:

; 对应C代码:
if (x == 1) {
    // case 1
} else if (x == 2) {
    // case 2
} else {
    // default
}

; ========== switch-case (跳转表) ==========
CMP EAX, 4                      ; 检查范围
JA loc_default                  ; > 4 则default
JMP DWORD PTR DS:[EAX*4+jump_table]

jump_table:
    DD loc_case0
    DD loc_case1
    DD loc_case2
    DD loc_case3
    DD loc_case4

; 对应C代码:
switch (x) {
    case 0: /* ... */ break;
    case 1: /* ... */ break;
    case 2: /* ... */ break;
    case 3: /* ... */ break;
    case 4: /* ... */ break;
    default: /* ... */
}

; ========== for 循环 ==========
; 特征:初始化 -> 条件检查(在开头) -> 循环体 -> 递增 -> 跳回条件检查
XOR ECX, ECX                    ; i = 0
JMP short loc_cond_check
loc_loop_body:
    ; 循环体
    ; ...
    INC ECX                     ; i++
loc_cond_check:
    CMP ECX, 10                 ; i < 10
    JL short loc_loop_body

; 对应C代码:
for (int i = 0; i < 10; i++) {
    // 循环体
}

; ========== while 循环 ==========
; 特征:条件检查在开头
loc_while_check:
    CMP DWORD PTR SS:[EBP-4], 0
    JE short loc_while_end      ; 不满足则退出
    ; 循环体
    DEC DWORD PTR SS:[EBP-4]
    JMP short loc_while_check
loc_while_end:

; 对应C代码:
while (x != 0) {
    // 循环体
    x--;
}

; ========== do-while 循环 ==========
; 特征:条件检查在结尾
loc_do_body:
    ; 循环体
    DEC DWORD PTR SS:[EBP-4]
    CMP DWORD PTR SS:[EBP-4], 0
    JNE short loc_do_body       ; 满足则继续

; 对应C代码:
do {
    // 循环体
    x--;
} while (x != 0);

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《C++ 反汇编与逆向分析技术揭秘》

控制流章节

⭐⭐⭐⭐⭐

✏️练习

编写各种控制结构并分析

⭐⭐⭐⭐⭐


2.2.3 函数识别

学习小点

  • [ ] 函数边界识别

    • [ ] 序言特征

    • [ ] 尾声特征

  • [ ] 参数数量判断

    • [ ] 根据调用约定

    • [ ] 栈清理大小

  • [ ] 返回值识别

    • [ ] EAX 保存返回值

    • [ ] 结构体返回特殊处理

  • [ ] 调用约定识别

    • [ ] cdecl/stdcall/fastcall 区分

汇编特征

; ========== 函数序言 ==========
PUSH EBP                        ; 保存旧栈帧
MOV EBP, ESP                    ; 建立新栈帧
SUB ESP, X                      ; 分配局部变量空间
PUSH EBX                        ; 保存被调用者保存寄存器
PUSH ESI
PUSH EDI

; 有时会有:
AND ESP, 0xFFFFFFF0             ; 栈对齐

; ========== 函数尾声 ==========
POP EDI                         ; 恢复寄存器
POP ESI
POP EBX
MOV ESP, EBP                    ; 或 ADD ESP, X
POP EBP
RET                             ; __cdecl
; 或
RET X                           ; __stdcall,X = 参数大小

; ========== 调用约定识别 ==========

; __cdecl 特征:
PUSH arg3
PUSH arg2
PUSH arg1
CALL func
ADD ESP, 0xC                    ; 调用者清理12字节

; __stdcall 特征:
PUSH arg3
PUSH arg2
PUSH arg1
CALL func                       ; 调用者不清理
; func内部:
RET 0xC                         ; 被调用者清理

; __fastcall 特征:
PUSH arg4
PUSH arg3
MOV EDX, arg2                   ; 第2个参数用EDX
MOV ECX, arg1                   ; 第1个参数用ECX
CALL func

; ========== 参数数量判断 ==========
; 方法1:查看栈清理大小
ADD ESP, 0x10                   ; 0x10 / 4 = 4个参数

; 方法2:查看RET指令
RET 0x14                        ; 0x14 / 4 = 5个参数

; 方法3:查看函数内部参数访问
MOV EAX, [EBP+8]                ; 参数1
MOV EBX, [EBP+C]                ; 参数2
MOV ECX, [EBP+10]               ; 参数3
; 最高偏移 (0x10 - 8) / 4 + 1 = 3个参数

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《逆向工程核心原理》

函数分析章节

⭐⭐⭐⭐⭐

✏️练习

分析各种函数并还原

⭐⭐⭐⭐⭐


2.2.4 常用算法识别

学习小点

  • [ ] 字符串操作

    • [ ] strlen

    • [ ] strcpy/memcpy

    • [ ] strcmp

  • [ ] 简单加密

    • [ ] XOR 加密

    • [ ] 凯撒密码

    • [ ] Base64

  • [ ] 哈希算法特征

    • [ ] CRC32

    • [ ] MD5

    • [ ] SHA 系列

汇编特征

; ========== strlen ==========
XOR ECX, ECX                    ; len = 0
loc_loop:
    CMP BYTE PTR [EAX+ECX], 0   ; str[len] == 0?
    JE short loc_end
    INC ECX                     ; len++
    JMP short loc_loop
loc_end:
; ECX = 字符串长度

; ========== strcpy/memcpy ==========
; REP MOVSB/MOVSW/MOVSD 特征
MOV ESI, src                    ; 源
MOV EDI, dst                    ; 目标
MOV ECX, len                    ; 长度
REP MOVSB                       ; 按字节复制

; ========== strcmp ==========
loc_cmp:
    MOV AL, [ESI]
    MOV BL, [EDI]
    CMP AL, BL
    JNE short loc_diff
    TEST AL, AL                 ; 检查结束
    JE short loc_equal
    INC ESI
    INC EDI
    JMP short loc_cmp

; ========== XOR加密 ==========
MOV ECX, len
MOV ESI, data
loc_xor:
    XOR BYTE PTR [ESI], key     ; data[i] ^= key
    INC ESI
    LOOP loc_xor

; ========== Base64特征 ==========
; 查找特征字符串
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

; 查找特征操作
SHR x, 2                        ; >> 2
AND x, 0x3F                     ; & 0x3F (63)

; ========== CRC32特征 ==========
; 查找多项式常数
0xEDB88320                      ; 反射多项式
0x04C11DB7                      ; 正向多项式

; 查找预计算表(256项)

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《加密与解密》

算法识别章节

⭐⭐⭐⭐⭐

🌐在线

findcrypt IDA 插件

自动识别加密算法

⭐⭐⭐⭐⭐

✏️练习

分析 CrackMe 中的算法

⭐⭐⭐⭐⭐


✅ C语言逆向阶段检查清单

  • [ ] 能识别各种变量类型

  • [ ] 能识别 if-else 结构

  • [ ] 能识别各种循环结构

  • [ ] 能识别 switch-case

  • [ ] 能判断函数参数数量

  • [ ] 能识别调用约定

  • [ ] 能识别常见字符串操作

  • [ ] 能识别简单加密算法



阶段三:进阶编程

⏱️ 总时长:2-3 个月
🎯 目标:掌握 C++ 逆向特征和 64 位汇编


3.1 C++ ⭐⭐⭐⭐⭐

🎯 目标:掌握 C++ 面向对象,深入理解虚表机制(逆向核心)
⏱️ 时长:4-6 周

3.1.1 类与对象

学习小点

  • [ ] 类的定义与实例化

  • [ ] 访问修饰符(public, private, protected)

  • [ ] 构造函数与析构函数

  • [ ] 成员变量与成员函数

  • [ ] this 指针

  • [ ] 静态成员

  • [ ] 类的内存布局

代码示例

class Player {
public:
    int m_health;      // +0x00
    int m_mana;        // +0x04
    float m_posX;      // +0x08
    float m_posY;      // +0x0C
    
    Player() : m_health(100), m_mana(50), m_posX(0), m_posY(0) {}
    
    void TakeDamage(int damage) {
        m_health -= damage;
        if (m_health < 0) m_health = 0;
    }
    
    int GetHealth() const { return m_health; }
};

// 内存布局 (无虚函数):
// +0x00: m_health (4 bytes)
// +0x04: m_mana (4 bytes)
// +0x08: m_posX (4 bytes)
// +0x0C: m_posY (4 bytes)
// Total: 16 bytes

// this指针在汇编中 (x86 __thiscall)
// ECX = this pointer

C++成员函数调用的汇编特征

; C++调用: player->TakeDamage(10)

; x86 __thiscall:
PUSH 10                         ; 参数
MOV ECX, player                 ; this指针放ECX
CALL Player::TakeDamage

; 在TakeDamage函数内部:
Player::TakeDamage:
    PUSH EBP
    MOV EBP, ESP
    ; ECX = this
    MOV EAX, [ECX]              ; this->m_health
    SUB EAX, [EBP+8]            ; -= damage
    MOV [ECX], EAX              ; 写回
    ; ...

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

黑马程序员 C++ 教程

B 站

⭐⭐⭐⭐

📹视频

侯捷 C++ 面向对象

B 站

⭐⭐⭐⭐⭐

📖书籍

《C++ Primer》

第 7 章类

⭐⭐⭐⭐⭐

📖书籍

《Effective C++》

进阶必读

⭐⭐⭐⭐⭐


3.1.2 虚函数与虚表(逆向核心!)

学习小点

  • [ ] 虚函数概念

  • [ ] 多态实现原理

  • [ ] 虚表(vtable)结构

  • [ ] 虚表指针(vfptr)位置

  • [ ] 单继承虚表布局

  • [ ] 多继承虚表布局

  • [ ] 虚析构函数

  • [ ] 纯虚函数与抽象类

虚表机制详解

class Animal {
public:
    int m_age;                              // +0x08 (x64)
    virtual void Speak() { printf("...\n"); }  // vtable[0]
    virtual void Move() { printf("move\n"); }  // vtable[1]
};

class Dog : public Animal {
public:
    int m_breed;                            // +0x0C
    virtual void Speak() override { printf("Woof!\n"); }  // 覆盖
    virtual void Fetch() { printf("fetch\n"); }           // 新增
};

// Animal对象内存布局 (x64):
// +0x00: vfptr -> Animal::vtable
// +0x08: m_age
// Size: 16 bytes (对齐)

// Dog对象内存布局 (x64):
// +0x00: vfptr -> Dog::vtable
// +0x08: m_age (继承自Animal)
// +0x0C: m_breed
// Size: 16 bytes

// Animal的虚表:
// vtable[0] = Animal::Speak
// vtable[1] = Animal::Move

// Dog的虚表:
// vtable[0] = Dog::Speak    (覆盖了Animal::Speak)
// vtable[1] = Animal::Move  (继承)
// vtable[2] = Dog::Fetch    (新增)

虚函数调用的汇编特征

; C++代码: animal->Speak()
; 其中 animal 是 Animal* 类型,可能指向 Dog 对象

; x64汇编:
MOV RAX, [RCX]              ; RAX = *this = vfptr (虚表指针)
MOV RAX, [RAX]              ; RAX = vtable[0] = Speak函数地址
CALL RAX                    ; 调用虚函数

; x86汇编:
MOV EAX, [ECX]              ; EAX = vfptr
MOV EAX, [EAX]              ; EAX = vtable[0]
CALL EAX

; 调用第2个虚函数 (Move):
MOV RAX, [RCX]              ; RAX = vfptr
MOV RAX, [RAX+8]            ; RAX = vtable[1] (偏移8字节,x64指针大小)
CALL RAX

; 特征识别:
; 1. 首先读取对象的第一个成员 (vfptr)
; 2. 然后从该地址读取函数指针
; 3. 间接调用 (CALL EAX/RAX)

多继承虚表布局

class A {
public:
    int a;
    virtual void funcA() {}
};

class B {
public:
    int b;
    virtual void funcB() {}
};

class C : public A, public B {
public:
    int c;
    virtual void funcA() override {}
    virtual void funcB() override {}
    virtual void funcC() {}
};

// C对象内存布局 (x64):
// +0x00: vfptr_A -> C的A部分虚表
// +0x08: a
// +0x10: vfptr_B -> C的B部分虚表  
// +0x18: b
// +0x1C: c

// C的A部分虚表:
// [0] = C::funcA
// [1] = C::funcC

// C的B部分虚表:
// [0] = thunk -> C::funcB (需要调整this指针)

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

C++ 虚函数表详解

B 站搜索

⭐⭐⭐⭐⭐

📖书籍

《深度探索 C++ 对象模型》

必读经典

⭐⭐⭐⭐⭐

📖书籍

《C++ 反汇编与逆向分析技术揭秘》

逆向视角

⭐⭐⭐⭐⭐

✏️练习

用 IDA 分析虚表结构

⭐⭐⭐⭐⭐


3.1.3 STL容器

学习小点

  • [ ] vector 内存布局

  • [ ] string 内存布局(SSO 优化)

  • [ ] list 内存布局

  • [ ] map 内存布局

  • [ ] 逆向中识别 STL 容器

STL容器内存布局

// ========== std::vector ==========
// MSVC实现 (x64):
// +0x00: _Myfirst (指向首元素)
// +0x08: _Mylast  (指向最后元素之后)
// +0x10: _Myend   (指向分配内存末尾)
// Size: 24 bytes

std::vector<int> vec = {1, 2, 3, 4, 5};
// vec._Myfirst -> [1, 2, 3, 4, 5, ?, ?...]
//                  ^             ^      ^
//                  _Myfirst  _Mylast  _Myend

// 获取size: (_Mylast - _Myfirst) / sizeof(element)
// 获取capacity: (_Myend - _Myfirst) / sizeof(element)

// ========== std::string ==========
// MSVC实现 (x64) - SSO优化:
// 短字符串 (<=15字符):
// +0x00: 内联buffer[16]  (直接存储字符串)
// +0x10: _Mysize (长度)
// +0x18: _Myres  (容量,SSO时为15)

// 长字符串:
// +0x00: _Bx._Ptr (指向堆上的字符串)
// +0x08: (padding)
// +0x10: _Mysize
// +0x18: _Myres (>=16表示使用堆)

// 判断是否SSO: _Myres < 16

// ========== std::map ==========
// 红黑树实现:
// map对象:
// +0x00: _Myhead (指向头节点)
// +0x08: _Mysize (元素数量)

// 树节点:
// +0x00: _Left   (左子节点)
// +0x08: _Parent (父节点)
// +0x10: _Right  (右子节点)
// +0x18: _Color  (红/黑)
// +0x19: _Isnil  (是否为nil节点)
// +0x20: _Myval  (键值对)

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

侯捷 STL 源码剖析

B 站

⭐⭐⭐⭐⭐

📖书籍

《STL 源码剖析》

深入理解

⭐⭐⭐⭐⭐

📖文档

cppreference.com

STL 参考

⭐⭐⭐⭐


3.1.4 C++逆向特征总结

识别特征

1. 类对象识别:
   - 对象首地址通常有vfptr(如果有虚函数)
   - 成员访问使用 [this+offset] 模式
   - 构造函数初始化vfptr

2. 虚函数调用特征:
   - MOV REG, [this]         ; 获取vfptr
   - MOV REG, [REG+offset]   ; 获取函数指针
   - CALL REG                ; 间接调用

3. 构造函数特征:
   - 初始化vfptr
   - 调用父类构造函数
   - 初始化成员变量

4. 析构函数特征:
   - 反向调用(先子类后父类)
   - 将vfptr设为当前类的虚表
   - 释放资源

5. this指针传递:
   - x86: ECX (__thiscall)
   - x64: RCX (第一个参数)

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《C++ 反汇编与逆向分析技术揭秘》

系统讲解

⭐⭐⭐⭐⭐

✏️练习

逆向分析 C++ 程序

⭐⭐⭐⭐⭐


✅ C++阶段检查清单

  • [ ] 理解类的内存布局

  • [ ] 完全理解虚表机制

  • [ ] 能在反汇编中识别虚函数调用

  • [ ] 了解 STL 容器内存布局

  • [ ] 能识别构造函数和析构函数

  • [ ] 能分析 C++ 程序的类结构


3.2 x64汇编 ⭐⭐⭐⭐

🎯 目标:掌握 64 位汇编和 Windows x64 调用约定
⏱️ 时长:2-3 周

3.2.1 x64寄存器

学习小点

  • [ ] 64 位通用寄存器(RAX-RDX, RSI, RDI, RBP, RSP)

  • [ ] 新增寄存器(R8-R15)

  • [ ] 寄存器分割(RAX, EAX, AX, AL)

  • [ ] 特殊寄存器(RIP, RFLAGS)

寄存器详解

; x64通用寄存器 (16个)

; 原有寄存器的64位扩展:
RAX, RBX, RCX, RDX          ; 数据寄存器
RSI, RDI                     ; 源/目标索引
RBP, RSP                     ; 栈帧/栈顶
RIP                          ; 指令指针

; 新增寄存器:
R8, R9, R10, R11            ; 通用
R12, R13, R14, R15          ; 通用(被调用者保存)

; 寄存器分割(以RAX为例):
; RAX (64位) = 0x1122334455667788
; EAX (低32位) = 0x55667788
; AX  (低16位) = 0x7788
; AL  (低8位)  = 0x88
; AH  (次低8位) = 0x77

; R8-R15的分割:
; R8  (64位)
; R8D (低32位)
; R8W (低16位)
; R8B (低8位)

; 注意:写入32位寄存器会清零高32位!
MOV EAX, 1                  ; RAX = 0x0000000000000001
; 但写入8/16位不会影响高位
MOV AL, 1                   ; 只修改AL,RAX高56位不变

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖文档

Intel SDM Volume 1

官方参考

⭐⭐⭐⭐

🌐在线

x86-64 Instruction Reference

felixcloutier.com

⭐⭐⭐⭐⭐


3.2.2 x64 Windows调用约定(重要!)

学习小点

  • [ ] 参数传递规则(前 4 个用寄存器)

  • [ ] 寄存器分配(RCX, RDX, R8, R9)

  • [ ] 浮点参数(XMM0-XMM3)

  • [ ] Shadow Space(32 字节预留空间)

  • [ ] 栈对齐要求(16 字节)

  • [ ] 返回值规则

  • [ ] 易失性 / 非易失性寄存器

调用约定详解

; ========== x64 Windows调用约定 ==========

; 参数传递:
; 参数1: RCX (或XMM0如果是浮点)
; 参数2: RDX (或XMM1)
; 参数3: R8  (或XMM2)
; 参数4: R9  (或XMM3)
; 参数5+: 栈传递 [RSP+0x28], [RSP+0x30], ...

; Shadow Space (32字节):
; 调用者必须为前4个参数预留栈空间
; 即使参数用寄存器传递,也必须预留
; [RSP+0x00]: 返回地址
; [RSP+0x08]: 参数1的home space
; [RSP+0x10]: 参数2的home space
; [RSP+0x18]: 参数3的home space
; [RSP+0x20]: 参数4的home space
; [RSP+0x28]: 参数5 (如果有)

; 函数调用示例:func(1, 2, 3, 4, 5, 6)
SUB RSP, 0x38              ; 分配栈空间 (shadow 32 + 2参数 16 + 对齐)
MOV DWORD PTR [RSP+0x30], 6   ; 参数6
MOV DWORD PTR [RSP+0x28], 5   ; 参数5
MOV R9D, 4                 ; 参数4
MOV R8D, 3                 ; 参数3
MOV EDX, 2                 ; 参数2
MOV ECX, 1                 ; 参数1
CALL func
ADD RSP, 0x38

; 栈必须16字节对齐:
; CALL前RSP必须是16的倍数+8(因为CALL会压入8字节返回地址)

; 易失性寄存器(调用者保存,函数可能修改):
; RAX, RCX, RDX, R8, R9, R10, R11
; XMM0-XMM5

; 非易失性寄存器(被调用者保存,函数必须保存/恢复):
; RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15
; XMM6-XMM15

; 返回值:
; 整数/指针: RAX
; 浮点: XMM0
; 小结构体: RAX (如果<=8字节)
; 大结构体: 隐藏参数传递地址

函数序言/尾声

; 典型x64函数序言:
MyFunction:
    ; 可能有:
    MOV [RSP+0x08], RCX        ; 保存参数到shadow space
    MOV [RSP+0x10], RDX
    
    PUSH RBP                    ; 保存非易失性寄存器
    PUSH RBX
    PUSH RSI
    PUSH RDI
    PUSH R12
    PUSH R13
    PUSH R14
    PUSH R15
    
    SUB RSP, 0x??              ; 分配局部变量空间
    LEA RBP, [RSP+0x??]        ; 建立栈帧(可选)
    
    ; 函数体...
    
; 典型x64函数尾声:
    LEA RSP, [RBP-0x??]        ; 或 ADD RSP, 0x??
    
    POP R15
    POP R14
    POP R13
    POP R12
    POP RDI
    POP RSI
    POP RBX
    POP RBP
    
    RET

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖文档

MSDN x64 调用约定

docs.microsoft.com

⭐⭐⭐⭐⭐

📹视频

x64 汇编与调用约定

B 站搜索

⭐⭐⭐⭐

✏️练习

用 x64dbg 分析 64 位程序

⭐⭐⭐⭐⭐


3.2.3 x64特有指令

学习小点

  • [ ] RIP 相对寻址

  • [ ] MOVSXD(符号扩展到 64 位)

  • [ ] 新增条件移动指令

  • [ ] 64 位立即数加载

代码示例

; ========== RIP相对寻址 ==========
; x64默认使用RIP相对寻址访问全局变量
; 更紧凑,位置无关

; 访问全局变量:
MOV RAX, [RIP+offset]          ; RIP相对
; IDA中显示为:
MOV RAX, cs:g_variable

; vs x86:
MOV EAX, [0x00401000]          ; 绝对地址

; ========== 64位立即数 ==========
; MOV指令可以加载64位立即数到寄存器
MOV RAX, 0x123456789ABCDEF0    ; 10字节指令

; 但不能直接移动64位立即数到内存
; 需要先加载到寄存器
MOV RAX, 0x123456789ABCDEF0
MOV [RBX], RAX

; ========== MOVSXD (符号扩展) ==========
; 32位有符号数扩展到64位
MOVSXD RAX, EBX                ; RAX = (int64_t)(int32_t)EBX
MOVSXD RAX, DWORD PTR [RCX]    ; 从内存读取

; ========== LEA的64位用法 ==========
; 计算复杂地址/算术
LEA RAX, [RBX+RCX*8+0x100]     ; RAX = RBX + RCX*8 + 0x100

; 获取RIP相对地址
LEA RAX, [RIP+offset]          ; 获取全局变量/函数地址

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《x86-64 汇编语言程序设计》

专门讲 x64

⭐⭐⭐⭐

🌐在线

x64 Cheat Sheet

速查表

⭐⭐⭐⭐


✅ x64汇编阶段检查清单

  • [ ] 认识所有 x64 寄存器

  • [ ] 完全理解 x64 调用约定

  • [ ] 理解 Shadow Space 概念

  • [ ] 理解栈对齐要求

  • [ ] 能识别 x64 函数序言 / 尾声

  • [ ] 理解 RIP 相对寻址


3.3 PR(保护研究)⭐⭐⭐⭐

🎯 目标:了解反调试、加壳脱壳等保护技术
⏱️ 时长:2-3 周

3.3.1 反调试技术

学习小点

  • [ ] IsDebuggerPresent

  • [ ] PEB.BeingDebugged

  • [ ] NtQueryInformationProcess

  • [ ] CheckRemoteDebuggerPresent

  • [ ] 时间检测

  • [ ] 硬件断点检测

  • [ ] SEH 检测

反调试代码示例

// ========== IsDebuggerPresent ==========
if (IsDebuggerPresent()) {
    // 检测到调试器
    ExitProcess(0);
}

// ========== PEB.BeingDebugged ==========
// 直接读取PEB
#ifdef _WIN64
    PPEB pPeb = (PPEB)__readgsqword(0x60);
#else
    PPEB pPeb = (PPEB)__readfsdword(0x30);
#endif

if (pPeb->BeingDebugged) {
    ExitProcess(0);
}

// ========== NtQueryInformationProcess ==========
typedef NTSTATUS (NTAPI *pNtQueryInformationProcess)(
    HANDLE, UINT, PVOID, ULONG, PULONG);

pNtQueryInformationProcess NtQIP = (pNtQueryInformationProcess)
    GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");

// ProcessDebugPort (0x07)
DWORD debugPort = 0;
NtQIP(GetCurrentProcess(), 7, &debugPort, sizeof(debugPort), NULL);
if (debugPort != 0) {
    ExitProcess(0);
}

// ProcessDebugObjectHandle (0x1E)
HANDLE debugObject = 0;
NtQIP(GetCurrentProcess(), 0x1E, &debugObject, sizeof(debugObject), NULL);
if (debugObject != 0) {
    ExitProcess(0);
}

// ========== 时间检测 ==========
DWORD t1 = GetTickCount();
// 代码块
DWORD t2 = GetTickCount();
if (t2 - t1 > 100) {  // 正常执行应该很快
    ExitProcess(0);   // 可能被单步调试
}

// ========== RDTSC时间检测 ==========
unsigned __int64 t1 = __rdtsc();
// 代码块
unsigned __int64 t2 = __rdtsc();
if (t2 - t1 > 1000000) {
    ExitProcess(0);
}

// ========== 硬件断点检测 ==========
CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread(), &ctx);
if (ctx.Dr0 || ctx.Dr1 || ctx.Dr2 || ctx.Dr3) {
    ExitProcess(0);
}

绕过反调试

绕过方法:

1. IsDebuggerPresent绕过:
   - 修改PEB.BeingDebugged为0
   - Hook IsDebuggerPresent返回FALSE
   - NOP掉检测代码

2. NtQueryInformationProcess绕过:
   - Hook函数返回伪造结果
   - 使用x64dbg的ScyllaHide插件

3. 时间检测绕过:
   - Hook GetTickCount等时间函数
   - 修改RDTSC指令返回值

4. 通用方法:
   - 使用ScyllaHide插件(推荐)
   - 使用TitanHide驱动

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

反调试与反反调试

B 站 / 看雪

⭐⭐⭐⭐⭐

📖书籍

《加密与解密》

反调试章节

⭐⭐⭐⭐⭐

🔧项目

al-khaser

GitHub

⭐⭐⭐⭐⭐

🔧插件

ScyllaHide

x64dbg 反反调试

⭐⭐⭐⭐⭐


3.3.2 加壳与脱壳

学习小点

  • [ ] 壳的原理与分类

  • [ ] 常见壳的识别

  • [ ] 手动脱壳流程

  • [ ] 自动脱壳工具

壳的分类

压缩壳:
- UPX (最简单,入门首选)
- ASPack
- PECompact

保护壳:
- Themida/WinLicense
- VMProtect
- Enigma Protector

虚拟机壳:
- VMProtect
- Code Virtualizer

手动脱壳流程

通用脱壳步骤:

1. 识别壳类型
   - 使用DIE (Detect It Easy)
   - 使用ExeInfo PE

2. 找OEP (原始入口点)
   - ESP定律
   - 单步跟踪
   - 内存断点

3. Dump内存
   - 使用x64dbg的Scylla插件
   - 或OllyDumpEx

4. 修复IAT (导入表)
   - 使用Scylla修复
   - 手动修复

ESP定律脱壳步骤:
1. 运行程序,在入口点暂停
2. 观察ESP值 (通常 ESP = 原始ESP - 4)
3. 在ESP处设置硬件访问断点
4. F9运行,断在OEP附近
5. 单步找到真正的OEP
6. Dump并修复

UPX脱壳示例:
1. 直接用命令行: upx -d packed.exe
2. 或手动:
   - pushad 后设置ESP硬件断点
   - 断下来后找 popad + jmp OEP

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

脱壳入门系列

B 站 / 看雪

⭐⭐⭐⭐⭐

📖书籍

《加密与解密》

脱壳章节

⭐⭐⭐⭐⭐

🔧工具

DIE

查壳工具

⭐⭐⭐⭐⭐

🔧工具

Scylla

脱壳修复

⭐⭐⭐⭐⭐

✏️练习

UnpackMe 挑战

⭐⭐⭐⭐⭐


✅ PR阶段检查清单

  • [ ] 了解常见反调试技术

  • [ ] 会使用 ScyllaHide 绕过反调试

  • [ ] 能识别常见壳

  • [ ] 会手动脱 UPX 壳

  • [ ] 理解脱壳的基本流程

  • [ ] 会使用 Scylla 修复 IAT



阶段四:Windows开发

⏱️ 总时长:2-3 个月
🎯 目标:掌握 Windows 核心 API 和应用程序开发


4.1 Win32 API ⭐⭐⭐⭐⭐

🎯 目标:掌握 Windows 核心 API,为后续逆向和驱动开发打基础
⏱️ 时长:4-6 周

4.1.1 进程与线程

学习小点

  • [ ] 进程概念与创建

    • [ ] CreateProcess

    • [ ] 进程句柄与 ID

    • [ ] 进程终止

  • [ ] 线程概念与创建

    • [ ] CreateThread

    • [ ] 线程同步

    • [ ] 线程本地存储(TLS)

  • [ ] 进程 / 线程枚举

    • [ ] CreateToolhelp32Snapshot

    • [ ] Process32First/Next

    • [ ] Thread32First/Next

代码示例

#include <windows.h>
#include <tlhelp32.h>

// ========== 创建进程 ==========
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;

BOOL success = CreateProcessA(
    NULL,                       // 应用程序名
    "notepad.exe",              // 命令行
    NULL,                       // 进程安全属性
    NULL,                       // 线程安全属性
    FALSE,                      // 是否继承句柄
    0,                          // 创建标志
    NULL,                       // 环境变量
    NULL,                       // 当前目录
    &si,                        // 启动信息
    &pi                         // 进程信息
);

if (success) {
    printf("进程ID: %d\n", pi.dwProcessId);
    printf("线程ID: %d\n", pi.dwThreadId);
    
    // 等待进程结束
    WaitForSingleObject(pi.hProcess, INFINITE);
    
    // 关闭句柄
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
}

// ========== 创建线程 ==========
DWORD WINAPI ThreadFunc(LPVOID lpParam) {
    int* pValue = (int*)lpParam;
    printf("线程参数: %d\n", *pValue);
    return 0;
}

int param = 100;
HANDLE hThread = CreateThread(
    NULL,                       // 安全属性
    0,                          // 栈大小(0=默认)
    ThreadFunc,                 // 线程函数
    &param,                     // 参数
    0,                          // 创建标志
    NULL                        // 线程ID
);

WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);

// ========== 枚举进程 ==========
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot != INVALID_HANDLE_VALUE) {
    PROCESSENTRY32 pe = { sizeof(pe) };
    
    if (Process32First(hSnapshot, &pe)) {
        do {
            printf("PID: %5d  Name: %s\n", pe.th32ProcessID, pe.szExeFile);
        } while (Process32Next(hSnapshot, &pe));
    }
    
    CloseHandle(hSnapshot);
}

// ========== 根据名称查找进程ID ==========
DWORD GetProcessIdByName(const char* processName) {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe = { sizeof(pe) };
    DWORD pid = 0;
    
    if (Process32First(hSnapshot, &pe)) {
        do {
            if (_stricmp(pe.szExeFile, processName) == 0) {
                pid = pe.th32ProcessID;
                break;
            }
        } while (Process32Next(hSnapshot, &pe));
    }
    
    CloseHandle(hSnapshot);
    return pid;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《Windows 核心编程》

进程线程章节

⭐⭐⭐⭐⭐

📖文档

MSDN Process Functions

docs.microsoft.com

⭐⭐⭐⭐⭐

📹视频

Win32 API 教程

B 站搜索

⭐⭐⭐⭐


4.1.2 内存操作(逆向核心!)

学习小点

  • [ ] 进程内存操作

    • [ ] OpenProcess

    • [ ] ReadProcessMemory

    • [ ] WriteProcessMemory

    • [ ] VirtualAllocEx

    • [ ] VirtualProtectEx

    • [ ] VirtualFreeEx

  • [ ] 内存属性

    • [ ] PAGE_EXECUTE_READWRITE

    • [ ] MEM_COMMIT / MEM_RESERVE

  • [ ] 模块枚举

    • [ ] EnumProcessModules

    • [ ] GetModuleInformation

代码示例

#include <windows.h>
#include <psapi.h>

// ========== 读写其他进程内存 ==========
DWORD pid = 1234;  // 目标进程ID
HANDLE hProcess = OpenProcess(
    PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION,
    FALSE,
    pid
);

if (hProcess) {
    // 读取内存
    int value;
    SIZE_T bytesRead;
    LPVOID address = (LPVOID)0x12345678;
    
    if (ReadProcessMemory(hProcess, address, &value, sizeof(value), &bytesRead)) {
        printf("读取值: %d\n", value);
    }
    
    // 写入内存
    int newValue = 999;
    SIZE_T bytesWritten;
    
    // 可能需要先修改内存保护
    DWORD oldProtect;
    VirtualProtectEx(hProcess, address, sizeof(newValue), 
                     PAGE_EXECUTE_READWRITE, &oldProtect);
    
    if (WriteProcessMemory(hProcess, address, &newValue, sizeof(newValue), &bytesWritten)) {
        printf("写入成功\n");
    }
    
    // 恢复保护
    VirtualProtectEx(hProcess, address, sizeof(newValue), oldProtect, &oldProtect);
    
    CloseHandle(hProcess);
}

// ========== 在目标进程分配内存 ==========
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

LPVOID remoteMem = VirtualAllocEx(
    hProcess,
    NULL,                       // 让系统选择地址
    4096,                       // 大小
    MEM_COMMIT | MEM_RESERVE,   // 分配类型
    PAGE_EXECUTE_READWRITE      // 保护属性
);

if (remoteMem) {
    printf("分配的远程内存: %p\n", remoteMem);
    
    // 写入数据
    char shellcode[] = { /* ... */ };
    WriteProcessMemory(hProcess, remoteMem, shellcode, sizeof(shellcode), NULL);
    
    // 使用完后释放
    VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE);
}

// ========== 枚举模块 ==========
HMODULE hMods[1024];
DWORD cbNeeded;

if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
    int moduleCount = cbNeeded / sizeof(HMODULE);
    
    for (int i = 0; i < moduleCount; i++) {
        char modName[MAX_PATH];
        MODULEINFO modInfo;
        
        GetModuleFileNameExA(hProcess, hMods[i], modName, sizeof(modName));
        GetModuleInformation(hProcess, hMods[i], &modInfo, sizeof(modInfo));
        
        printf("模块: %s\n", modName);
        printf("  基址: %p\n", modInfo.lpBaseOfDll);
        printf("  大小: 0x%X\n", modInfo.SizeOfImage);
    }
}

// ========== 获取模块基址 ==========
DWORD_PTR GetModuleBaseAddress(DWORD pid, const char* moduleName) {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);
    MODULEENTRY32 me = { sizeof(me) };
    DWORD_PTR baseAddr = 0;
    
    if (Module32First(hSnapshot, &me)) {
        do {
            if (_stricmp(me.szModule, moduleName) == 0) {
                baseAddr = (DWORD_PTR)me.modBaseAddr;
                break;
            }
        } while (Module32Next(hSnapshot, &me));
    }
    
    CloseHandle(hSnapshot);
    return baseAddr;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《Windows 核心编程》

虚拟内存章节

⭐⭐⭐⭐⭐

📖文档

MSDN Memory Management

docs.microsoft.com

⭐⭐⭐⭐⭐

✏️练习

编写简单内存修改器

⭐⭐⭐⭐⭐


4.1.3 DLL操作

学习小点

  • [ ] DLL 加载与卸载

    • [ ] LoadLibrary / LoadLibraryEx

    • [ ] FreeLibrary

    • [ ] GetProcAddress

  • [ ] DLL 编写

    • [ ] DllMain 入口

    • [ ] 导出函数

  • [ ] DLL 注入基础

    • [ ] GetModuleHandle

代码示例

// ========== 加载和使用DLL ==========
HMODULE hDll = LoadLibraryA("user32.dll");

if (hDll) {
    // 获取函数地址
    typedef int (WINAPI *MessageBoxA_t)(HWND, LPCSTR, LPCSTR, UINT);
    MessageBoxA_t pMessageBoxA = (MessageBoxA_t)GetProcAddress(hDll, "MessageBoxA");
    
    if (pMessageBoxA) {
        pMessageBoxA(NULL, "Hello", "Title", MB_OK);
    }
    
    FreeLibrary(hDll);
}

// ========== 编写DLL ==========
// mydll.cpp
#include <windows.h>

// 导出函数
extern "C" __declspec(dllexport) int Add(int a, int b) {
    return a + b;
}

extern "C" __declspec(dllexport) void ShowMessage(const char* msg) {
    MessageBoxA(NULL, msg, "MyDLL", MB_OK);
}

// DLL入口点
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) {
    switch (reason) {
    case DLL_PROCESS_ATTACH:
        // DLL被加载时执行
        // 常用于注入后的初始化
        break;
        
    case DLL_PROCESS_DETACH:
        // DLL被卸载时执行
        break;
        
    case DLL_THREAD_ATTACH:
        // 新线程创建时
        break;
        
    case DLL_THREAD_DETACH:
        // 线程退出时
        break;
    }
    return TRUE;
}

// ========== .def文件导出 ==========
// mydll.def
LIBRARY "mydll"
EXPORTS
    Add @1
    ShowMessage @2

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《Windows 核心编程》

DLL 章节

⭐⭐⭐⭐⭐

📖文档

MSDN DLL

docs.microsoft.com

⭐⭐⭐⭐

✏️练习

编写注入用 DLL

⭐⭐⭐⭐⭐


4.1.4 文件与注册表

学习小点

  • [ ] 文件操作

    • [ ] CreateFile

    • [ ] ReadFile / WriteFile

    • [ ] SetFilePointer

    • [ ] 文件映射(CreateFileMapping, MapViewOfFile)

  • [ ] 注册表操作

    • [ ] RegOpenKeyEx

    • [ ] RegQueryValueEx

    • [ ] RegSetValueEx

    • [ ] RegCreateKeyEx

代码示例

// ========== 文件操作 ==========
HANDLE hFile = CreateFileA(
    "test.bin",
    GENERIC_READ | GENERIC_WRITE,
    0,                          // 不共享
    NULL,
    CREATE_ALWAYS,
    FILE_ATTRIBUTE_NORMAL,
    NULL
);

if (hFile != INVALID_HANDLE_VALUE) {
    // 写入数据
    char data[] = "Hello World";
    DWORD bytesWritten;
    WriteFile(hFile, data, sizeof(data), &bytesWritten, NULL);
    
    // 移动文件指针到开头
    SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
    
    // 读取数据
    char buffer[100];
    DWORD bytesRead;
    ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL);
    
    CloseHandle(hFile);
}

// ========== 文件映射(分析大文件利器)==========
HANDLE hFile = CreateFileA("large.exe", GENERIC_READ, FILE_SHARE_READ, 
                           NULL, OPEN_EXISTING, 0, NULL);
                           
HANDLE hMapping = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);

LPVOID pFileBase = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);

if (pFileBase) {
    // 直接访问文件内容
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFileBase;
    if (pDos->e_magic == IMAGE_DOS_SIGNATURE) {
        printf("有效的PE文件\n");
    }
    
    UnmapViewOfFile(pFileBase);
}

CloseHandle(hMapping);
CloseHandle(hFile);

// ========== 注册表操作 ==========
HKEY hKey;
LONG result = RegOpenKeyExA(
    HKEY_CURRENT_USER,
    "Software\\MyApp",
    0,
    KEY_READ | KEY_WRITE,
    &hKey
);

if (result == ERROR_SUCCESS) {
    // 读取值
    char value[256];
    DWORD valueSize = sizeof(value);
    DWORD type;
    
    RegQueryValueExA(hKey, "Setting1", NULL, &type, (LPBYTE)value, &valueSize);
    
    // 写入值
    const char* newValue = "NewData";
    RegSetValueExA(hKey, "Setting1", 0, REG_SZ, 
                   (const BYTE*)newValue, strlen(newValue) + 1);
    
    RegCloseKey(hKey);
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《Windows 核心编程》

文件系统章节

⭐⭐⭐⭐⭐

📖文档

MSDN File Management

docs.microsoft.com

⭐⭐⭐⭐


4.1.5 窗口与消息

学习小点

  • [ ] 窗口创建流程

    • [ ] RegisterClass

    • [ ] CreateWindow

    • [ ] ShowWindow

    • [ ] 消息循环

  • [ ] 消息处理

    • [ ] WndProc 回调

    • [ ] 常见消息(WM_CREATE, WM_DESTROY 等)

  • [ ] 窗口查找

    • [ ] FindWindow

    • [ ] EnumWindows

代码示例

// ========== 创建窗口 ==========
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_CREATE:
        // 窗口创建时
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
    // 注册窗口类
    WNDCLASSA wc = {0};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = "MyWindowClass";
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    RegisterClassA(&wc);
    
    // 创建窗口
    HWND hWnd = CreateWindowA(
        "MyWindowClass",
        "My Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, NULL, hInstance, NULL
    );
    
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    
    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    return (int)msg.wParam;
}

// ========== 查找窗口 ==========
HWND hWnd = FindWindowA("Notepad", NULL);  // 查找记事本
if (hWnd) {
    // 获取进程ID
    DWORD pid;
    GetWindowThreadProcessId(hWnd, &pid);
    printf("记事本PID: %d\n", pid);
    
    // 发送消息
    SendMessage(hWnd, WM_CLOSE, 0, 0);
}

// ========== 枚举所有窗口 ==========
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) {
    char title[256];
    GetWindowTextA(hWnd, title, sizeof(title));
    if (strlen(title) > 0) {
        printf("HWND: %p  Title: %s\n", hWnd, title);
    }
    return TRUE;  // 继续枚举
}

EnumWindows(EnumWindowsProc, 0);

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

Win32 窗口程序设计

B 站

⭐⭐⭐⭐

📖书籍

《Windows 程序设计》Perta

经典教材

⭐⭐⭐⭐⭐

📖文档

MSDN Window Functions

docs.microsoft.com

⭐⭐⭐⭐


✅ Win32 API阶段检查清单

  • [ ] 能创建进程和线程

  • [ ] 能枚举系统进程

  • [ ] 能读写其他进程内存

  • [ ] 能加载 DLL 并调用函数

  • [ ] 能编写简单的 DLL

  • [ ] 理解文件映射

  • [ ] 能操作注册表

  • [ ] 能创建窗口程序

  • [ ] 能查找和枚举窗口


4.2 MFC ⭐⭐⭐

🎯 目标:了解 MFC 框架,能逆向 MFC 程序
⏱️ 时长:1-2 周

4.2.1 MFC基础

学习小点

  • [ ] MFC 框架结构

    • [ ] CWinApp 应用程序类

    • [ ] CFrameWnd 框架窗口

    • [ ] CView 视图类

    • [ ] CDocument 文档类

  • [ ] 消息映射机制

    • [ ] BEGIN_MESSAGE_MAP / END_MESSAGE_MAP

    • [ ] ON_COMMAND

    • [ ] ON_MESSAGE

  • [ ] MFC 程序识别特征

MFC程序结构

// ========== MFC应用程序基本结构 ==========

// 应用程序类
class CMyApp : public CWinApp {
public:
    virtual BOOL InitInstance();
};

// 主框架窗口
class CMainFrame : public CFrameWnd {
    DECLARE_MESSAGE_MAP()
public:
    CMainFrame();
    afx_msg void OnButtonClick();
};

// 消息映射
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_COMMAND(ID_BUTTON1, OnButtonClick)
    ON_WM_CREATE()
END_MESSAGE_MAP()

// 实现
BOOL CMyApp::InitInstance() {
    CMainFrame* pFrame = new CMainFrame();
    pFrame->Create(NULL, _T("My MFC App"));
    pFrame->ShowWindow(SW_SHOW);
    m_pMainWnd = pFrame;
    return TRUE;
}

CMyApp theApp;  // 全局应用程序对象

MFC逆向特征

MFC程序识别特征:

1. 导入表特征:
   - 导入 MFC42.dll / MFC140u.dll 等
   - 导入 MSVCRT.dll / MSVCP140.dll

2. 函数命名特征:
   - AFX开头的函数 (AfxWinMain, AfxGetApp等)
   - 类名格式:C????? (CWnd, CButton等)

3. 消息映射特征:
   - 消息映射表结构
   - AFX_MSGMAP结构

4. 运行时类型信息:
   - CRuntimeClass结构
   - DECLARE_DYNAMIC / IMPLEMENT_DYNAMIC

消息映射表结构:
struct AFX_MSGMAP_ENTRY {
    UINT nMessage;      // Windows消息
    UINT nCode;         // 控制代码
    UINT nID;           // 控件ID
    UINT nLastID;       // 范围最后ID
    UINT_PTR nSig;      // 签名
    AFX_PMSG pfn;       // 处理函数
};

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

MFC 入门教程

B 站

⭐⭐⭐

📖书籍

《深入浅出 MFC》侯捷

经典教材

⭐⭐⭐⭐⭐

📖书籍

《C++ 反汇编与逆向分析》

MFC 逆向

⭐⭐⭐⭐⭐

✏️练习

逆向分析 MFC 程序

⭐⭐⭐⭐


✅ MFC阶段检查清单

  • [ ] 了解 MFC 基本框架

  • [ ] 能识别 MFC 程序

  • [ ] 理解消息映射机制

  • [ ] 能在 IDA 中定位消息处理函数



阶段五:核心逆向技术

⏱️ 总时长:2-3 个月
🎯 目标:掌握 DLL 注入和各种 Hook 技术


5.1 Inject(注入)⭐⭐⭐⭐⭐

🎯 目标:掌握各种 DLL 注入技术
⏱️ 时长:3-4 周

5.1.1 远程线程注入

学习小点

  • [ ] 注入原理理解

  • [ ] 获取 LoadLibrary 地址

  • [ ] 在目标进程分配内存

  • [ ] 写入 DLL 路径

  • [ ] 创建远程线程

  • [ ] 等待注入完成

  • [ ] 清理资源

完整代码示例

#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

// 根据进程名获取PID
DWORD GetProcessIdByName(const char* processName) {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe = { sizeof(pe) };
    DWORD pid = 0;
    
    if (Process32First(hSnapshot, &pe)) {
        do {
            if (_stricmp(pe.szExeFile, processName) == 0) {
                pid = pe.th32ProcessID;
                break;
            }
        } while (Process32Next(hSnapshot, &pe));
    }
    
    CloseHandle(hSnapshot);
    return pid;
}

// 远程线程注入
BOOL InjectDll(DWORD pid, const char* dllPath) {
    BOOL result = FALSE;
    HANDLE hProcess = NULL;
    LPVOID remotePath = NULL;
    HANDLE hThread = NULL;
    
    // 1. 打开目标进程
    hProcess = OpenProcess(
        PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | 
        PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION,
        FALSE, pid
    );
    
    if (!hProcess) {
        printf("[-] OpenProcess failed: %d\n", GetLastError());
        goto cleanup;
    }
    printf("[+] 打开进程成功\n");
    
    // 2. 在目标进程分配内存
    size_t pathLen = strlen(dllPath) + 1;
    remotePath = VirtualAllocEx(
        hProcess, NULL, pathLen,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE
    );
    
    if (!remotePath) {
        printf("[-] VirtualAllocEx failed: %d\n", GetLastError());
        goto cleanup;
    }
    printf("[+] 分配远程内存: %p\n", remotePath);
    
    // 3. 写入DLL路径
    if (!WriteProcessMemory(hProcess, remotePath, dllPath, pathLen, NULL)) {
        printf("[-] WriteProcessMemory failed: %d\n", GetLastError());
        goto cleanup;
    }
    printf("[+] 写入DLL路径成功\n");
    
    // 4. 获取LoadLibraryA地址
    // kernel32.dll在所有进程中基址相同,所以可以直接获取
    LPVOID loadLibraryAddr = GetProcAddress(
        GetModuleHandleA("kernel32.dll"), "LoadLibraryA"
    );
    printf("[+] LoadLibraryA地址: %p\n", loadLibraryAddr);
    
    // 5. 创建远程线程执行LoadLibraryA
    hThread = CreateRemoteThread(
        hProcess, NULL, 0,
        (LPTHREAD_START_ROUTINE)loadLibraryAddr,
        remotePath, 0, NULL
    );
    
    if (!hThread) {
        printf("[-] CreateRemoteThread failed: %d\n", GetLastError());
        goto cleanup;
    }
    printf("[+] 创建远程线程成功\n");
    
    // 6. 等待线程执行完成
    WaitForSingleObject(hThread, INFINITE);
    
    // 获取返回值(模块句柄)
    DWORD exitCode;
    GetExitCodeThread(hThread, &exitCode);
    if (exitCode != 0) {
        printf("[+] 注入成功! 模块句柄: 0x%X\n", exitCode);
        result = TRUE;
    } else {
        printf("[-] LoadLibrary返回NULL,注入可能失败\n");
    }
    
cleanup:
    if (hThread) CloseHandle(hThread);
    if (remotePath) VirtualFreeEx(hProcess, remotePath, 0, MEM_RELEASE);
    if (hProcess) CloseHandle(hProcess);
    
    return result;
}

int main(int argc, char* argv[]) {
    if (argc < 3) {
        printf("用法: injector.exe <进程名> <DLL路径>\n");
        return 1;
    }
    
    const char* processName = argv[1];
    const char* dllPath = argv[2];
    
    DWORD pid = GetProcessIdByName(processName);
    if (pid == 0) {
        printf("[-] 未找到进程: %s\n", processName);
        return 1;
    }
    printf("[+] 找到进程 %s, PID: %d\n", processName, pid);
    
    // 获取DLL完整路径
    char fullPath[MAX_PATH];
    GetFullPathNameA(dllPath, MAX_PATH, fullPath, NULL);
    printf("[+] DLL路径: %s\n", fullPath);
    
    if (InjectDll(pid, fullPath)) {
        printf("[+] 注入完成!\n");
    } else {
        printf("[-] 注入失败!\n");
    }
    
    return 0;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

DLL 注入技术详解

B 站 / 看雪

⭐⭐⭐⭐⭐

📖书籍

《Windows 核心编程》

DLL 注入章节

⭐⭐⭐⭐⭐

📖书籍

《逆向工程核心原理》

注入技术

⭐⭐⭐⭐⭐

🌐GitHub

各种注入器源码

⭐⭐⭐⭐


5.1.2 APC注入

学习小点

  • [ ] APC 原理(异步过程调用)

  • [ ] QueueUserAPC 函数

  • [ ] 目标线程必须处于 alertable 状态

  • [ ] 枚举目标进程的所有线程

代码示例

#include <windows.h>
#include <tlhelp32.h>

BOOL ApcInject(DWORD pid, const char* dllPath) {
    HANDLE hProcess = NULL;
    LPVOID remotePath = NULL;
    BOOL result = FALSE;
    
    // 打开进程
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (!hProcess) return FALSE;
    
    // 分配内存写入DLL路径
    size_t pathLen = strlen(dllPath) + 1;
    remotePath = VirtualAllocEx(hProcess, NULL, pathLen, 
                                MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    WriteProcessMemory(hProcess, remotePath, dllPath, pathLen, NULL);
    
    // 获取LoadLibraryA地址
    LPVOID loadLibrary = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
    
    // 枚举目标进程的所有线程
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    THREADENTRY32 te = { sizeof(te) };
    
    if (Thread32First(hSnapshot, &te)) {
        do {
            if (te.th32OwnerProcessID == pid) {
                // 打开线程
                HANDLE hThread = OpenThread(THREAD_SET_CONTEXT, FALSE, te.th32ThreadID);
                if (hThread) {
                    // 向线程队列添加APC
                    QueueUserAPC(
                        (PAPCFUNC)loadLibrary,
                        hThread,
                        (ULONG_PTR)remotePath
                    );
                    printf("[+] 向线程 %d 添加APC\n", te.th32ThreadID);
                    CloseHandle(hThread);
                    result = TRUE;
                }
            }
        } while (Thread32Next(hSnapshot, &te));
    }
    
    CloseHandle(hSnapshot);
    CloseHandle(hProcess);
    
    // 注意:APC只有在线程进入alertable状态时才会执行
    // 如SleepEx, WaitForSingleObjectEx, MsgWaitForMultipleObjectsEx等
    
    return result;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖文档

MSDN APC

docs.microsoft.com

⭐⭐⭐⭐

📖书籍

《Windows Internals》

APC 原理

⭐⭐⭐⭐⭐


5.1.3 手动映射(Manual Map)

学习小点

  • [ ] PE 文件结构深入理解

  • [ ] 手动映射原理

    • [ ] 读取 DLL 文件

    • [ ] 分配内存

    • [ ] 复制节区

    • [ ] 处理重定位

    • [ ] 处理导入表

    • [ ] 调用 DllMain

  • [ ] 优点:不在模块列表中显示

实现概要

// 手动映射步骤概要(完整实现较复杂)

struct ManualMapData {
    LPVOID imageBase;
    HMODULE (WINAPI *pLoadLibraryA)(LPCSTR);
    FARPROC (WINAPI *pGetProcAddress)(HMODULE, LPCSTR);
};

// 1. 读取DLL文件到内存
BYTE* dllData = ReadFileToMemory(dllPath);
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)dllData;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(dllData + pDos->e_lfanew);

// 2. 在目标进程分配内存
LPVOID remoteBase = VirtualAllocEx(hProcess, NULL, pNt->OptionalHeader.SizeOfImage,
                                   MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

// 3. 复制PE头
WriteProcessMemory(hProcess, remoteBase, dllData, pNt->OptionalHeader.SizeOfHeaders, NULL);

// 4. 复制各节区
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) {
    LPVOID sectionDst = (LPBYTE)remoteBase + pSection[i].VirtualAddress;
    LPVOID sectionSrc = dllData + pSection[i].PointerToRawData;
    WriteProcessMemory(hProcess, sectionDst, sectionSrc, pSection[i].SizeOfRawData, NULL);
}

// 5. 处理重定位
// 如果实际加载地址与ImageBase不同,需要修正重定位表

// 6. 处理导入表
// 解析导入表,获取每个导入函数的地址并填入IAT

// 7. 执行TLS回调(如果有)

// 8. 调用DllMain
// 在目标进程中执行shellcode来调用DllMain

// 优点:
// - 不在PEB的模块列表中
// - 不创建LDR_DATA_TABLE_ENTRY
// - 更难被检测

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

手动映射注入原理

B 站 / 看雪

⭐⭐⭐⭐⭐

🌐GitHub

Manual Map Injector

搜索学习

⭐⭐⭐⭐⭐

📖书籍

《加密与解密》

PE 结构

⭐⭐⭐⭐⭐


5.1.4 其他注入技术

学习小点

  • [ ] SetWindowsHookEx 注入

  • [ ] 注册表 AppInit_DLLs

  • [ ] 劫持 DLL(DLL Hijacking)

  • [ ] 线程劫持注入

  • [ ] NtCreateThreadEx

简要说明

// ========== SetWindowsHookEx 注入 ==========
// 通过全局钩子将DLL注入到所有窗口进程
HHOOK hHook = SetWindowsHookEx(
    WH_GETMESSAGE,           // 钩子类型
    HookProc,                // 钩子函数(必须在DLL中)
    hDllModule,              // DLL模块句柄
    0                        // 0 = 所有线程
);
// 当目标进程接收消息时,系统会自动将DLL加载到该进程

// ========== AppInit_DLLs 注入 ==========
// 注册表位置:
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
// 设置 AppInit_DLLs = C:\path\to\dll.dll
// 设置 LoadAppInit_DLLs = 1
// 所有加载user32.dll的进程都会加载该DLL

// ========== DLL劫持 ==========
// 将恶意DLL命名为程序依赖的DLL名
// 放在程序目录下(DLL搜索顺序问题)
// 程序启动时会加载你的DLL

// ========== 线程劫持 ==========
// 1. SuspendThread暂停目标线程
// 2. GetThreadContext获取线程上下文
// 3. 修改RIP/EIP指向shellcode
// 4. SetThreadContext设置修改后的上下文
// 5. ResumeThread恢复线程

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《Windows 核心编程》

Hook 章节

⭐⭐⭐⭐⭐

🌐在线

各种注入技术文章

看雪 / 吾爱

⭐⭐⭐⭐


✅ 注入技术阶段检查清单

  • [ ] 完全理解远程线程注入原理

  • [ ] 能编写远程线程注入器

  • [ ] 理解 APC 注入原理

  • [ ] 了解手动映射的原理

  • [ ] 了解其他注入方式

  • [ ] 能编写被注入的 DLL


5.2 Hook ⭐⭐⭐⭐⭐

🎯 目标:掌握各种 Hook 技术
⏱️ 时长:3-4 周

5.2.1 IAT Hook

学习小点

  • [ ] IAT(导入地址表)结构

  • [ ] PE 文件导入表解析

  • [ ] 定位目标函数 IAT 项

  • [ ] 修改 IAT 指针

  • [ ] 恢复原函数

完整代码示例

#include <windows.h>
#include <stdio.h>

// 原始函数指针
typedef int (WINAPI *MessageBoxA_t)(HWND, LPCSTR, LPCSTR, UINT);
MessageBoxA_t OriginalMessageBoxA = NULL;

// Hook函数
int WINAPI HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
    printf("[Hook] MessageBoxA被调用!\n");
    printf("  Text: %s\n", lpText);
    printf("  Caption: %s\n", lpCaption);
    
    // 修改参数
    return OriginalMessageBoxA(hWnd, "Hooked!", "IAT Hook", uType);
}

// IAT Hook实现
BOOL IATHook(HMODULE hModule, const char* dllName, const char* funcName, LPVOID hookFunc, LPVOID* origFunc) {
    // 获取PE头
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((LPBYTE)hModule + pDos->e_lfanew);
    
    // 获取导入表
    DWORD importRVA = pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
    if (importRVA == 0) return FALSE;
    
    PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)hModule + importRVA);
    
    // 遍历导入表
    while (pImport->Name) {
        const char* modName = (const char*)((LPBYTE)hModule + pImport->Name);
        
        // 查找目标DLL
        if (_stricmp(modName, dllName) == 0) {
            // 获取IAT和INT(名称表)
            PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((LPBYTE)hModule + pImport->FirstThunk);
            PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((LPBYTE)hModule + pImport->OriginalFirstThunk);
            
            // 遍历函数
            while (pThunk->u1.Function) {
                // 检查是否按名称导入
                if (!(pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)) {
                    PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)((LPBYTE)hModule + pOrigThunk->u1.AddressOfData);
                    
                    // 查找目标函数
                    if (strcmp(pName->Name, funcName) == 0) {
                        printf("[+] 找到函数 %s @ %p\n", funcName, (LPVOID)pThunk->u1.Function);
                        
                        // 保存原函数地址
                        *origFunc = (LPVOID)pThunk->u1.Function;
                        
                        // 修改内存保护
                        DWORD oldProtect;
                        VirtualProtect(&pThunk->u1.Function, sizeof(LPVOID), PAGE_READWRITE, &oldProtect);
                        
                        // 替换为Hook函数
                        pThunk->u1.Function = (ULONG_PTR)hookFunc;
                        
                        // 恢复内存保护
                        VirtualProtect(&pThunk->u1.Function, sizeof(LPVOID), oldProtect, &oldProtect);
                        
                        printf("[+] Hook成功!\n");
                        return TRUE;
                    }
                }
                
                pThunk++;
                pOrigThunk++;
            }
        }
        
        pImport++;
    }
    
    return FALSE;
}

// 测试
int main() {
    // 安装Hook
    if (IATHook(GetModuleHandle(NULL), "user32.dll", "MessageBoxA", 
                HookedMessageBoxA, (LPVOID*)&OriginalMessageBoxA)) {
        printf("[+] IAT Hook安装成功\n");
    }
    
    // 调用测试
    MessageBoxA(NULL, "原始消息", "测试", MB_OK);
    
    return 0;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

IAT Hook 详解

B 站 / 看雪

⭐⭐⭐⭐⭐

📖书籍

《加密与解密》

PE 结构 /Hook

⭐⭐⭐⭐⭐

✏️练习

编写 IAT Hook 框架

⭐⭐⭐⭐⭐


5.2.2 Inline Hook

学习小点

  • [ ] Inline Hook 原理

  • [ ] x86 跳转指令(5 字节:E9 + 偏移)

  • [ ] x64 跳转指令(14 字节:绝对地址跳转)

  • [ ] 跳板(Trampoline)实现

  • [ ] 保存和恢复原始指令

  • [ ] 线程安全问题

x86 Inline Hook

#include <windows.h>
#include <stdio.h>

// 5字节JMP指令
#pragma pack(push, 1)
struct JmpInstruction {
    BYTE opcode;      // E9
    DWORD offset;     // 相对偏移
};
#pragma pack(pop)

// 原始函数指针
typedef int (WINAPI *MessageBoxA_t)(HWND, LPCSTR, LPCSTR, UINT);
MessageBoxA_t TrampolineMessageBoxA = NULL;

// 保存原始字节
BYTE originalBytes[5];
LPVOID targetFunc = NULL;

// Hook函数
int WINAPI HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
    printf("[Inline Hook] MessageBoxA被调用!\n");
    printf("  Text: %s\n", lpText);
    
    // 调用原函数(通过跳板)
    return TrampolineMessageBoxA(hWnd, "Hooked by Inline Hook!", lpCaption, uType);
}

// 安装Inline Hook (x86)
BOOL InstallInlineHook(LPVOID target, LPVOID hook, LPVOID* trampoline) {
    DWORD oldProtect;
    
    // 分配跳板内存
    *trampoline = VirtualAlloc(NULL, 32, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!*trampoline) return FALSE;
    
    // 复制原始指令到跳板(至少5字节)
    // 注意:实际实现需要反汇编确定指令边界
    memcpy(*trampoline, target, 5);
    
    // 在跳板末尾添加跳回原函数的指令
    BYTE* trampolineEnd = (BYTE*)*trampoline + 5;
    trampolineEnd[0] = 0xE9;  // JMP
    *(DWORD*)(trampolineEnd + 1) = (DWORD)((BYTE*)target + 5 - (trampolineEnd + 5));
    
    // 修改目标函数保护属性
    VirtualProtect(target, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
    
    // 保存原始字节
    memcpy(originalBytes, target, 5);
    
    // 写入跳转指令
    BYTE* pTarget = (BYTE*)target;
    pTarget[0] = 0xE9;  // JMP rel32
    *(DWORD*)(pTarget + 1) = (DWORD)((BYTE*)hook - (pTarget + 5));
    
    // 恢复保护属性
    VirtualProtect(target, 5, oldProtect, &oldProtect);
    
    targetFunc = target;
    return TRUE;
}

// 卸载Hook
BOOL UninstallInlineHook() {
    if (!targetFunc) return FALSE;
    
    DWORD oldProtect;
    VirtualProtect(targetFunc, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
    memcpy(targetFunc, originalBytes, 5);
    VirtualProtect(targetFunc, 5, oldProtect, &oldProtect);
    
    return TRUE;
}

int main() {
    LPVOID target = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
    
    if (InstallInlineHook(target, HookedMessageBoxA, (LPVOID*)&TrampolineMessageBoxA)) {
        printf("[+] Inline Hook安装成功\n");
    }
    
    MessageBoxA(NULL, "原始消息", "测试", MB_OK);
    
    UninstallInlineHook();
    
    MessageBoxA(NULL, "Hook已卸载", "测试", MB_OK);
    
    return 0;
}

x64 Inline Hook

// x64需要使用14字节绝对跳转
// FF 25 00 00 00 00     ; JMP [RIP+0]
// XX XX XX XX XX XX XX XX ; 8字节绝对地址

BOOL InstallInlineHook64(LPVOID target, LPVOID hook, LPVOID* trampoline) {
    DWORD oldProtect;
    
    // 分配跳板(需要在目标函数附近以支持短跳转,或使用绝对跳转)
    *trampoline = VirtualAlloc(NULL, 64, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    
    // 复制原始指令到跳板(至少14字节)
    memcpy(*trampoline, target, 14);
    
    // 跳板末尾:跳回原函数+14
    BYTE* trampolineEnd = (BYTE*)*trampoline + 14;
    trampolineEnd[0] = 0xFF;
    trampolineEnd[1] = 0x25;
    *(DWORD*)(trampolineEnd + 2) = 0;
    *(ULONGLONG*)(trampolineEnd + 6) = (ULONGLONG)((BYTE*)target + 14);
    
    // 修改目标函数
    VirtualProtect(target, 14, PAGE_EXECUTE_READWRITE, &oldProtect);
    
    BYTE* pTarget = (BYTE*)target;
    pTarget[0] = 0xFF;
    pTarget[1] = 0x25;
    *(DWORD*)(pTarget + 2) = 0;
    *(ULONGLONG*)(pTarget + 6) = (ULONGLONG)hook;
    
    VirtualProtect(target, 14, oldProtect, &oldProtect);
    
    return TRUE;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

Inline Hook 原理与实现

B 站 / 看雪

⭐⭐⭐⭐⭐

📖书籍

《加密与解密》

Hook 章节

⭐⭐⭐⭐⭐

🌐GitHub

MinHook / Detours

Hook 库源码

⭐⭐⭐⭐⭐

✏️练习

使用 MinHook 库

⭐⭐⭐⭐


5.2.3 VEH Hook

学习小点

  • [ ] VEH(向量异常处理)原理

  • [ ] AddVectoredExceptionHandler

  • [ ] 硬件断点(DR 寄存器)

  • [ ] INT3 断点

  • [ ] 页面保护异常

代码示例

#include <windows.h>
#include <stdio.h>

// 目标函数地址
LPVOID g_targetFunc = NULL;
LPVOID g_hookFunc = NULL;

// VEH处理函数
LONG CALLBACK VEHHandler(PEXCEPTION_POINTERS pExceptionInfo) {
    // 检查是否是我们设置的断点
    if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {
        // 检查是否是目标地址
        if ((LPVOID)pExceptionInfo->ContextRecord->Rip == g_targetFunc) {
            printf("[VEH] 捕获到目标函数调用!\n");
            
            // 重定向到Hook函数
            pExceptionInfo->ContextRecord->Rip = (DWORD64)g_hookFunc;
            
            return EXCEPTION_CONTINUE_EXECUTION;
        }
    }
    
    return EXCEPTION_CONTINUE_SEARCH;
}

// 设置硬件断点
void SetHardwareBreakpoint(LPVOID address, int index) {
    CONTEXT ctx = {0};
    ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
    
    HANDLE hThread = GetCurrentThread();
    GetThreadContext(hThread, &ctx);
    
    // 设置DR0-DR3中的一个
    switch (index) {
        case 0: ctx.Dr0 = (DWORD64)address; break;
        case 1: ctx.Dr1 = (DWORD64)address; break;
        case 2: ctx.Dr2 = (DWORD64)address; break;
        case 3: ctx.Dr3 = (DWORD64)address; break;
    }
    
    // 启用断点 (DR7)
    // 位0,2,4,6: 本地启用DR0-DR3
    // 位16-17, 20-21, 24-25, 28-29: 条件类型 (00=执行)
    // 位18-19, 22-23, 26-27, 30-31: 长度 (00=1字节)
    ctx.Dr7 |= (1 << (index * 2));  // 本地启用
    
    SetThreadContext(hThread, &ctx);
}

// 安装VEH Hook
BOOL InstallVEHHook(LPVOID target, LPVOID hook) {
    g_targetFunc = target;
    g_hookFunc = hook;
    
    // 注册VEH处理函数
    AddVectoredExceptionHandler(1, VEHHandler);
    
    // 设置硬件断点
    SetHardwareBreakpoint(target, 0);
    
    return TRUE;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖文档

MSDN VEH

docs.microsoft.com

⭐⭐⭐⭐

📖书籍

《Windows Internals》

异常处理

⭐⭐⭐⭐⭐


5.2.4 Hook库推荐

常用Hook库

1. MinHook
   - 轻量级、开源
   - 支持x86和x64
   - GitHub: TsudaKageyu/minhook

2. Microsoft Detours
   - 微软官方库
   - 功能强大
   - GitHub: microsoft/Detours

3. PolyHook
   - 现代C++实现
   - 支持多种Hook方式
   - GitHub: stevemk14ebr/PolyHook_2_0

使用MinHook示例:
#include "MinHook.h"

// 原函数类型
typedef int (WINAPI *MessageBoxA_t)(HWND, LPCSTR, LPCSTR, UINT);
MessageBoxA_t fpMessageBoxA = NULL;

// Hook函数
int WINAPI DetourMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
    return fpMessageBoxA(hWnd, "Hooked by MinHook!", lpCaption, uType);
}

int main() {
    // 初始化MinHook
    MH_Initialize();
    
    // 创建Hook
    MH_CreateHook(
        &MessageBoxA,           // 目标函数
        &DetourMessageBoxA,     // Hook函数
        (LPVOID*)&fpMessageBoxA // 原函数指针
    );
    
    // 启用Hook
    MH_EnableHook(&MessageBoxA);
    
    // 测试
    MessageBoxA(NULL, "Test", "Test", MB_OK);
    
    // 禁用Hook
    MH_DisableHook(&MessageBoxA);
    
    // 清理
    MH_Uninitialize();
    
    return 0;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

🌐GitHub

MinHook

TsudaKageyu/minhook

⭐⭐⭐⭐⭐

🌐GitHub

Detours

microsoft/Detours

⭐⭐⭐⭐⭐

🌐GitHub

PolyHook

stevemk14ebr/PolyHook_2_0

⭐⭐⭐⭐


✅ Hook技术阶段检查清单

  • [ ] 理解 IAT Hook 原理并能实现

  • [ ] 理解 Inline Hook 原理

  • [ ] 能实现 x86 Inline Hook

  • [ ] 了解 x64 Inline Hook 的区别

  • [ ] 了解 VEH Hook 原理

  • [ ] 会使用 MinHook 库

  • [ ] 能编写完整的 Hook 框架



阶段六:驱动开发

⏱️ 总时长:4-6 个月
🎯 目标:掌握 Windows 内核编程和驱动开发


6.1 驱动基础 ⭐⭐⭐⭐

🎯 目标:搭建开发环境,编写第一个驱动
⏱️ 时长:4-6 周

6.1.1 环境搭建

学习小点

  • [ ] 安装 Visual Studio 2022

  • [ ] 安装 WDK(Windows Driver Kit)

  • [ ] 配置 VMware 虚拟机

  • [ ] 配置内核调试(WinDbg)

  • [ ] 测试签名设置

环境配置步骤

1. 安装Visual Studio 2022
   - 选择"使用C++的桌面开发"
   - 选择"Windows 10/11 SDK"

2. 安装WDK
   - 下载对应VS版本的WDK
   - 安装后会在VS中添加驱动项目模板

3. 虚拟机配置
   # 在虚拟机中执行(管理员CMD)
   bcdedit /set testsigning on       # 允许测试签名
   bcdedit /debug on                 # 启用调试
   bcdedit /dbgsettings serial debugport:1 baudrate:115200
   
   # VMware设置
   添加串口设备 -> 使用命名管道 -> \\.\pipe\com_1
   设置为"该端是服务器"、"另一端是应用程序"

4. WinDbg连接
   # 在主机执行
   windbg -k com:port=\\.\pipe\com_1,baud=115200,pipe
   
   # 或使用WinDbg Preview
   File -> Attach to kernel -> COM

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📹视频

驱动开发环境搭建

B 站搜索

⭐⭐⭐⭐⭐

📖文档

WDK 官方文档

docs.microsoft.com/windows-hardware/drivers

⭐⭐⭐⭐⭐

🔧工具

WDK 下载

Microsoft 官网

⭐⭐⭐⭐⭐


6.1.2 Hello World驱动

学习小点

  • [ ] 驱动入口函数 DriverEntry

  • [ ] 驱动卸载函数 DriverUnload

  • [ ] DbgPrint 调试输出

  • [ ] 驱动加载与卸载

  • [ ] 使用 DebugView 查看输出

代码示例

// HelloDriver.c
#include <ntddk.h>

// 驱动卸载函数
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
    UNREFERENCED_PARAMETER(DriverObject);
    DbgPrint("[HelloDriver] Driver Unloading...\n");
}

// 驱动入口函数
NTSTATUS DriverEntry(
    PDRIVER_OBJECT DriverObject,
    PUNICODE_STRING RegistryPath)
{
    UNREFERENCED_PARAMETER(RegistryPath);
    
    DbgPrint("[HelloDriver] Hello from Kernel! Driver loaded.\n");
    DbgPrint("[HelloDriver] DriverObject: %p\n", DriverObject);
    
    // 设置卸载函数
    DriverObject->DriverUnload = DriverUnload;
    
    return STATUS_SUCCESS;
}

加载驱动方法

// 方法1:使用SC命令
// 创建服务
sc create HelloDriver binPath= "C:\path\to\HelloDriver.sys" type= kernel

// 启动服务
sc start HelloDriver

// 停止服务
sc stop HelloDriver

// 删除服务
sc delete HelloDriver

// 方法2:编程方式加载
#include <windows.h>

BOOL LoadDriver(const char* driverPath, const char* serviceName) {
    SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!hSCM) return FALSE;
    
    // 创建服务
    SC_HANDLE hService = CreateServiceA(
        hSCM,
        serviceName,
        serviceName,
        SERVICE_ALL_ACCESS,
        SERVICE_KERNEL_DRIVER,
        SERVICE_DEMAND_START,
        SERVICE_ERROR_NORMAL,
        driverPath,
        NULL, NULL, NULL, NULL, NULL
    );
    
    if (!hService) {
        hService = OpenServiceA(hSCM, serviceName, SERVICE_ALL_ACCESS);
    }
    
    if (!hService) {
        CloseServiceHandle(hSCM);
        return FALSE;
    }
    
    // 启动服务
    BOOL result = StartService(hService, 0, NULL);
    
    CloseServiceHandle(hService);
    CloseServiceHandle(hSCM);
    
    return result;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《Windows 驱动开发技术详解》

入门必读

⭐⭐⭐⭐⭐

📖书籍

《Windows Kernel Programming》Pavel

英文经典

⭐⭐⭐⭐⭐

📹视频

内核驱动开发教程

B 站搜索

⭐⭐⭐⭐


6.1.3 设备与通信

学习小点

  • [ ] 创建设备对象(IoCreateDevice)

  • [ ] 创建符号链接(IoCreateSymbolicLink)

  • [ ] 派遣函数(MajorFunction)

  • [ ] IRP 处理

  • [ ] DeviceIoControl 通信

  • [ ] 读写操作

完整示例

// Driver.c - 带设备通信的驱动

#include <ntddk.h>

// IOCTL定义
#define IOCTL_READ_MEMORY  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_WRITE_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)

// 设备名和符号链接
#define DEVICE_NAME    L"\\Device\\MyDriver"
#define SYMBOLIC_LINK  L"\\??\\MyDriverLink"

PDEVICE_OBJECT g_DeviceObject = NULL;

// 通用派遣函数
NTSTATUS DispatchCreateClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    UNREFERENCED_PARAMETER(DeviceObject);
    
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    
    return STATUS_SUCCESS;
}

// DeviceIoControl处理
NTSTATUS DispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    UNREFERENCED_PARAMETER(DeviceObject);
    
    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS status = STATUS_SUCCESS;
    ULONG bytesReturned = 0;
    
    PVOID inputBuffer = Irp->AssociatedIrp.SystemBuffer;
    PVOID outputBuffer = Irp->AssociatedIrp.SystemBuffer;
    ULONG inputLength = stack->Parameters.DeviceIoControl.InputBufferLength;
    ULONG outputLength = stack->Parameters.DeviceIoControl.OutputBufferLength;
    
    switch (stack->Parameters.DeviceIoControl.IoControlCode) {
    case IOCTL_READ_MEMORY:
        DbgPrint("[Driver] IOCTL_READ_MEMORY\n");
        // 处理读内存请求
        break;
        
    case IOCTL_WRITE_MEMORY:
        DbgPrint("[Driver] IOCTL_WRITE_MEMORY\n");
        // 处理写内存请求
        break;
        
    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }
    
    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = bytesReturned;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    
    return status;
}

// 驱动卸载
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING symbolicLink;
    RtlInitUnicodeString(&symbolicLink, SYMBOLIC_LINK);
    IoDeleteSymbolicLink(&symbolicLink);
    
    if (g_DeviceObject) {
        IoDeleteDevice(g_DeviceObject);
    }
    
    DbgPrint("[Driver] Unloaded\n");
}

// 驱动入口
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    UNREFERENCED_PARAMETER(RegistryPath);
    
    NTSTATUS status;
    UNICODE_STRING deviceName, symbolicLink;
    
    DbgPrint("[Driver] Loading...\n");
    
    // 初始化设备名
    RtlInitUnicodeString(&deviceName, DEVICE_NAME);
    RtlInitUnicodeString(&symbolicLink, SYMBOLIC_LINK);
    
    // 创建设备
    status = IoCreateDevice(
        DriverObject,
        0,
        &deviceName,
        FILE_DEVICE_UNKNOWN,
        FILE_DEVICE_SECURE_OPEN,
        FALSE,
        &g_DeviceObject
    );
    
    if (!NT_SUCCESS(status)) {
        DbgPrint("[Driver] IoCreateDevice failed: 0x%X\n", status);
        return status;
    }
    
    // 创建符号链接
    status = IoCreateSymbolicLink(&symbolicLink, &deviceName);
    if (!NT_SUCCESS(status)) {
        DbgPrint("[Driver] IoCreateSymbolicLink failed: 0x%X\n", status);
        IoDeleteDevice(g_DeviceObject);
        return status;
    }
    
    // 设置派遣函数
    DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchCreateClose;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDeviceControl;
    DriverObject->DriverUnload = DriverUnload;
    
    DbgPrint("[Driver] Loaded successfully\n");
    
    return STATUS_SUCCESS;
}

用户态通信代码

// Client.cpp
#include <windows.h>
#include <stdio.h>

#define IOCTL_READ_MEMORY  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

int main() {
    // 打开设备
    HANDLE hDevice = CreateFileA(
        "\\\\.\\MyDriverLink",
        GENERIC_READ | GENERIC_WRITE,
        0, NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    
    if (hDevice == INVALID_HANDLE_VALUE) {
        printf("[-] Failed to open device: %d\n", GetLastError());
        return 1;
    }
    
    printf("[+] Device opened\n");
    
    // 发送IOCTL
    DWORD bytesReturned;
    char inputBuffer[100] = "Hello from user mode";
    char outputBuffer[100] = {0};
    
    BOOL result = DeviceIoControl(
        hDevice,
        IOCTL_READ_MEMORY,
        inputBuffer, sizeof(inputBuffer),
        outputBuffer, sizeof(outputBuffer),
        &bytesReturned,
        NULL
    );
    
    if (result) {
        printf("[+] IOCTL succeeded, bytes returned: %d\n", bytesReturned);
    } else {
        printf("[-] IOCTL failed: %d\n", GetLastError());
    }
    
    CloseHandle(hDevice);
    return 0;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《Windows 驱动开发技术详解》

设备通信章节

⭐⭐⭐⭐⭐

📖文档

WDK Samples

GitHub

⭐⭐⭐⭐


✅ 驱动基础检查清单

  • [ ] 成功搭建开发环境

  • [ ] 能编译和加载驱动

  • [ ] 会使用 WinDbg 调试

  • [ ] 理解设备对象和符号链接

  • [ ] 能实现用户态和内核态通信


6.2 内核编程进阶 ⭐⭐⭐⭐⭐

🎯 目标:掌握 IRQL、同步、进程操作等核心概念
⏱️ 时长:6-8 周

6.2.1 IRQL(中断请求级别)

学习小点

  • [ ] IRQL 概念理解

  • [] PASSIVE_LEVEL (0)

  • [] APC_LEVEL (1)

  • [] DISPATCH_LEVEL (2)

  • [ ] IRQL 规则和限制

  • [ ] KeRaiseIrql / KeLowerIrql

说明

/*
IRQL(Interrupt Request Level)是Windows内核的核心概念

PASSIVE_LEVEL (0):
- 最低级别,普通代码运行在此级别
- 可以访问分页内存
- 可以等待(Wait)
- 可以调用大多数内核函数

APC_LEVEL (1):
- 异步过程调用级别
- 可以访问分页内存
- 可以等待

DISPATCH_LEVEL (2):
- 线程调度器运行在此级别
- 不能访问分页内存!
- 不能等待!
- 只能使用非分页内存
- 只能使用自旋锁同步

DIRQL (3-26):
- 设备中断级别

HIGH_LEVEL (31):
- 最高级别
*/

// 获取当前IRQL
KIRQL currentIrql = KeGetCurrentIrql();

// 提升IRQL
KIRQL oldIrql;
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
// 执行需要高IRQL的代码...
KeLowerIrql(oldIrql);

// 在DISPATCH_LEVEL的限制
// 1. 不能调用:
//    - ZwXxx函数(需要等待)
//    - ExAllocatePool(分页池)
//    - 访问分页内存
// 2. 只能使用:
//    - NonPagedPool内存
//    - 自旋锁

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《Windows Internals》

IRQL 章节

⭐⭐⭐⭐⭐

📖书籍

《Windows 驱动开发技术详解》

⭐⭐⭐⭐⭐


6.2.2 同步机制

学习小点

  • [ ] 自旋锁(Spin Lock)

  • [ ] 互斥体(Mutex)

  • [ ] 快速互斥体(Fast Mutex)

  • [ ] 事件(Event)

  • [ ] 信号量(Semaphore)

代码示例

// ========== 自旋锁(DISPATCH_LEVEL使用)==========
KSPIN_LOCK spinLock;
KIRQL oldIrql;

// 初始化
KeInitializeSpinLock(&spinLock);

// 获取锁(自动提升到DISPATCH_LEVEL)
KeAcquireSpinLock(&spinLock, &oldIrql);

// 临界区代码...

// 释放锁
KeReleaseSpinLock(&spinLock, oldIrql);

// ========== 互斥体(PASSIVE_LEVEL使用)==========
KMUTEX mutex;

// 初始化
KeInitializeMutex(&mutex, 0);

// 获取锁(可以等待)
KeWaitForSingleObject(&mutex, Executive, KernelMode, FALSE, NULL);

// 临界区代码...

// 释放锁
KeReleaseMutex(&mutex, FALSE);

// ========== 快速互斥体 ==========
FAST_MUTEX fastMutex;

ExInitializeFastMutex(&fastMutex);

ExAcquireFastMutex(&fastMutex);
// 临界区...
ExReleaseFastMutex(&fastMutex);

// ========== 事件 ==========
KEVENT event;

// 初始化(同步事件或通知事件)
KeInitializeEvent(&event, SynchronizationEvent, FALSE);

// 等待事件
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);

// 设置事件
KeSetEvent(&event, IO_NO_INCREMENT, FALSE);

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《Windows 驱动开发技术详解》

同步章节

⭐⭐⭐⭐⭐

📖文档

MSDN 同步函数

docs.microsoft.com

⭐⭐⭐⭐


6.2.3 进程与内存操作

学习小点

  • [ ] PsLookupProcessByProcessId

  • [ ] KeStackAttachProcess

  • [ ] MmCopyVirtualMemory

  • [ ] ZwQuerySystemInformation

  • [ ] 进程回调注册

代码示例

// ========== 查找进程 ==========
PEPROCESS targetProcess = NULL;
NTSTATUS status = PsLookupProcessByProcessId((HANDLE)targetPid, &targetProcess);

if (NT_SUCCESS(status)) {
    // 使用进程对象...
    
    // 必须解引用
    ObDereferenceObject(targetProcess);
}

// ========== 附加到进程并访问内存 ==========
KAPC_STATE apcState;

// 附加到目标进程
KeStackAttachProcess(targetProcess, &apcState);

// 现在可以直接访问目标进程的虚拟地址空间
if (MmIsAddressValid(targetAddress)) {
    // 读取
    ULONG value = *(ULONG*)targetAddress;
    
    // 写入
    *(ULONG*)targetAddress = newValue;
}

// 分离
KeUnstackDetachProcess(&apcState);

// ========== 跨进程内存复制 ==========
SIZE_T bytesRead;
status = MmCopyVirtualMemory(
    sourceProcess,      // 源进程
    sourceAddress,      // 源地址
    targetProcess,      // 目标进程
    targetAddress,      // 目标地址
    size,               // 大小
    KernelMode,         // 模式
    &bytesRead          // 实际复制字节数
);

// ========== 进程创建/退出回调 ==========
VOID ProcessNotifyCallback(
    PEPROCESS Process,
    HANDLE ProcessId,
    PPS_CREATE_NOTIFY_INFO CreateInfo)
{
    if (CreateInfo) {
        // 进程创建
        DbgPrint("[Callback] Process created: %wZ, PID: %d\n", 
                 CreateInfo->ImageFileName, (ULONG)(ULONG_PTR)ProcessId);
    } else {
        // 进程退出
        DbgPrint("[Callback] Process exited: PID: %d\n", 
                 (ULONG)(ULONG_PTR)ProcessId);
    }
}

// 注册回调
PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallback, FALSE);

// 注销回调
PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallback, TRUE);

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《Windows Kernel Programming》

进程章节

⭐⭐⭐⭐⭐

🌐GitHub

各种驱动源码

学习参考

⭐⭐⭐⭐


✅ 内核编程进阶检查清单

  • [ ] 完全理解 IRQL 概念

  • [ ] 会使用各种同步机制

  • [ ] 能进行跨进程内存操作

  • [ ] 能注册进程回调

  • [ ] 理解内核编程的各种限制


6.3 高级保护技术(VT)⭐⭐⭐⭐⭐

🎯 目标:了解 VT 虚拟化和 EPT Hook
⏱️ 时长:8-12 周

6.3.1 VT虚拟化基础

学习小点

  • [ ] Intel VT-x 概念

  • [ ] VMX 操作模式(root/non-root)

  • [ ] VMCS 结构

  • [ ] VM Entry / VM Exit

  • [ ] VT 环境搭建

概念说明

Intel VT-x 虚拟化技术:

1. VMX模式
   - VMX root模式:Hypervisor运行(Ring -1)
   - VMX non-root模式:Guest OS运行
   
2. VMCS(Virtual Machine Control Structure)
   - 控制VM的行为
   - Guest状态区
   - Host状态区
   - VM执行控制区
   - VM退出控制区
   - VM进入控制区
   
3. VM Exit
   - Guest执行特定指令或事件时退出到Hypervisor
   - 如:CPUID, RDMSR, WRMSR, CR访问等
   
4. EPT(Extended Page Table)
   - 物理地址到机器地址的转换
   - 可实现内存级别的隐藏Hook

学习资源

推荐学习顺序:

1. 阅读Intel SDM Volume 3C(VMX章节)
2. 学习简单的Hypervisor实现:
   - SimpleVisor
   - HyperPlatform
   - hvpp
3. 理解EPT原理
4. 学习EPT Hook实现

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖文档

Intel SDM Volume 3C

VMX 章节

⭐⭐⭐⭐⭐

🌐GitHub

HyperPlatform

tandasat/HyperPlatform

⭐⭐⭐⭐⭐

🌐GitHub

SimpleVisor

ionescu007/SimpleVisor

⭐⭐⭐⭐⭐

🌐GitHub

hvpp

wbenny/hvpp

⭐⭐⭐⭐⭐

📹视频

VT 虚拟化技术

B 站 / 看雪

⭐⭐⭐⭐


✅ VT阶段检查清单

  • [ ] 理解 VMX 基本概念

  • [ ] 理解 VMCS 结构

  • [ ] 理解 VM Exit 机制

  • [ ] 能阅读简单 Hypervisor 代码

  • [ ] 了解 EPT 原理


阶段七:图形渲染

⏱️ 总时长:1-2 个月
🎯 目标:使用 ImGui 和 DirectX 制作游戏内置菜单


7.1 ImGui ⭐⭐⭐⭐

🎯 目标:使用 ImGui 创建游戏内置 GUI
⏱️ 时长:2-3 周

7.1.1 ImGui基础

学习小点

  • [ ] ImGui 项目配置

  • [ ] ImGui 初始化流程

  • [ ] 基本控件使用

  • [ ] 窗口与布局

  • [ ] 样式自定义

代码示例

#include "imgui.h"
#include "imgui_impl_win32.h"
#include "imgui_impl_dx11.h"

// 初始化
bool InitImGui(HWND hWnd, ID3D11Device* pDevice, ID3D11DeviceContext* pContext) {
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    
    ImGuiIO& io = ImGui::GetIO();
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
    
    // 设置样式
    ImGui::StyleColorsDark();
    
    // 初始化后端
    ImGui_ImplWin32_Init(hWnd);
    ImGui_ImplDX11_Init(pDevice, pContext);
    
    return true;
}

// 渲染
void RenderImGui() {
    ImGui_ImplDX11_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();
    
    // 绘制菜单
    static bool showMenu = true;
    if (showMenu) {
        ImGui::Begin("Game Menu", &showMenu);
        
        // 基本控件
        static bool godMode = false;
        ImGui::Checkbox("God Mode", &godMode);
        
        static int health = 100;
        ImGui::SliderInt("Health", &health, 0, 100);
        
        static float speed = 1.0f;
        ImGui::SliderFloat("Speed", &speed, 0.1f, 10.0f);
        
        if (ImGui::Button("Teleport")) {
            // 传送逻辑
        }
        
        static char name[64] = "";
        ImGui::InputText("Name", name, sizeof(name));
        
        // 分组
        if (ImGui::CollapsingHeader("Advanced")) {
            static int selectedItem = 0;
            const char* items[] = { "Option 1", "Option 2", "Option 3" };
            ImGui::Combo("Select", &selectedItem, items, IM_ARRAYSIZE(items));
        }
        
        ImGui::End();
    }
    
    ImGui::Render();
    ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}

// 清理
void CleanupImGui() {
    ImGui_ImplDX11_Shutdown();
    ImGui_ImplWin32_Shutdown();
    ImGui::DestroyContext();
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

🌐GitHub

Dear ImGui

ocornut/imgui

⭐⭐⭐⭐⭐

📖文档

ImGui Wiki

GitHub Wiki

⭐⭐⭐⭐

📹视频

ImGui 教程

B 站 /YouTube

⭐⭐⭐⭐


7.2 DirectX绘制 ⭐⭐⭐⭐

🎯 目标:Hook DirectX 进行绘制
⏱️ 时长:2-3 周

7.2.1 D3D11 Hook

学习小点

  • [ ] D3D11 SwapChain 结构

  • [ ] Hook Present 函数

  • [ ] 获取设备和上下文

  • [ ] 集成 ImGui

代码示例

#include <d3d11.h>
#include <dxgi.h>

// 函数类型
typedef HRESULT(__stdcall* Present_t)(IDXGISwapChain*, UINT, UINT);
Present_t oPresent = nullptr;

// 全局变量
ID3D11Device* g_pDevice = nullptr;
ID3D11DeviceContext* g_pContext = nullptr;
ID3D11RenderTargetView* g_pRenderTargetView = nullptr;
bool g_initialized = false;

// Hooked Present
HRESULT __stdcall hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags) {
    if (!g_initialized) {
        // 获取Device和Context
        pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&g_pDevice);
        g_pDevice->GetImmediateContext(&g_pContext);
        
        // 获取后缓冲
        ID3D11Texture2D* pBackBuffer = nullptr;
        pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer);
        g_pDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_pRenderTargetView);
        pBackBuffer->Release();
        
        // 获取窗口句柄
        DXGI_SWAP_CHAIN_DESC desc;
        pSwapChain->GetDesc(&desc);
        HWND hWnd = desc.OutputWindow;
        
        // 初始化ImGui
        InitImGui(hWnd, g_pDevice, g_pContext);
        
        g_initialized = true;
    }
    
    // 设置渲染目标
    g_pContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL);
    
    // 渲染ImGui
    RenderImGui();
    
    return oPresent(pSwapChain, SyncInterval, Flags);
}

// 获取Present地址(通过虚表)
void* GetPresentAddress() {
    // 创建临时SwapChain获取虚表
    DXGI_SWAP_CHAIN_DESC sd = {};
    sd.BufferCount = 1;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = GetDesktopWindow();
    sd.SampleDesc.Count = 1;
    sd.Windowed = TRUE;
    
    ID3D11Device* pDevice;
    IDXGISwapChain* pSwapChain;
    ID3D11DeviceContext* pContext;
    
    D3D11CreateDeviceAndSwapChain(
        NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0,
        NULL, 0, D3D11_SDK_VERSION,
        &sd, &pSwapChain, &pDevice, NULL, &pContext
    );
    
    // 获取虚表
    void** vtable = *(void***)pSwapChain;
    void* presentAddr = vtable[8];  // Present是第9个函数
    
    pSwapChain->Release();
    pDevice->Release();
    pContext->Release();
    
    return presentAddr;
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖文档

DirectX 11 文档

docs.microsoft.com

⭐⭐⭐⭐

🌐在线

Guided Hacking

D3D Hook 教程

⭐⭐⭐⭐⭐

🌐GitHub

各种 D3D Hook 示例

⭐⭐⭐⭐


✅ 图形渲染检查清单

  • [ ] 能配置 ImGui 项目

  • [ ] 会使用 ImGui 各种控件

  • [ ] 理解 D3D11 基本结构

  • [ ] 能 Hook Present 函数

  • [ ] 能制作完整的游戏内置菜单


阶段八:脚本框架

⏱️ 总时长:1-2 个月
🎯 目标:分析游戏中的 Lua 脚本系统


8.1 Lua框架分析 ⭐⭐⭐⭐

🎯 目标:逆向分析游戏 Lua 脚本系统
⏱️ 时长:3-4 周

8.1.1 Lua C API

学习小点

  • [ ] Lua 栈操作

  • [ ] Lua 类型系统

  • [ ] C 调用 Lua

  • [ ] Lua 调用 C

  • [ ] 注册自定义函数

代码示例

#include "lua.hpp"

// 创建Lua状态
lua_State* L = luaL_newstate();
luaL_openlibs(L);

// 执行Lua代码
luaL_dostring(L, "print('Hello from Lua')");

// 调用Lua函数
lua_getglobal(L, "myFunction");
lua_pushnumber(L, 10);
lua_pushstring(L, "test");
lua_pcall(L, 2, 1, 0);
int result = lua_tointeger(L, -1);
lua_pop(L, 1);

// 注册C函数给Lua调用
int l_myFunc(lua_State* L) {
    int arg1 = luaL_checkinteger(L, 1);
    const char* arg2 = luaL_checkstring(L, 2);
    
    // 处理...
    
    lua_pushinteger(L, result);
    return 1;  // 返回值个数
}

lua_register(L, "myFunc", l_myFunc);

// 获取/设置全局变量
lua_getglobal(L, "playerHealth");
int health = lua_tointeger(L, -1);

lua_pushinteger(L, 999);
lua_setglobal(L, "playerHealth");

8.1.2 Hook Lua函数

学习小点

  • [ ] 定位 Lua 函数地址

  • [ ] Hook lua_pcall

  • [ ] Hook luaL_loadbuffer

  • [ ] 执行自定义 Lua 代码

代码示例

// Hook luaL_loadbuffer以拦截脚本加载
typedef int (*luaL_loadbuffer_t)(lua_State*, const char*, size_t, const char*);
luaL_loadbuffer_t orig_luaL_loadbuffer = nullptr;

int hooked_luaL_loadbuffer(lua_State* L, const char* buff, size_t sz, const char* name) {
    printf("[Lua] Loading script: %s, size: %zu\n", name, sz);
    
    // 可以在这里保存或修改脚本内容
    
    return orig_luaL_loadbuffer(L, buff, sz, name);
}

// Hook lua_pcall以监控函数调用
typedef int (*lua_pcall_t)(lua_State*, int, int, int);
lua_pcall_t orig_lua_pcall = nullptr;

int hooked_lua_pcall(lua_State* L, int nargs, int nresults, int errfunc) {
    // 可以在这里打印调用栈等
    return orig_lua_pcall(L, nargs, nresults, errfunc);
}

// 执行自定义Lua代码
void ExecuteCustomLua(lua_State* L) {
    const char* code = R"(
        print("Injected Lua code!")
        if player then
            player.health = 999
        end
    )";
    
    luaL_dostring(L, code);
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

📖书籍

《Programming in Lua》

Lua 官方教程

⭐⭐⭐⭐⭐

📖文档

Lua 5.3 Reference Manual

lua.org

⭐⭐⭐⭐⭐

🌐在线

Lua C API 教程

⭐⭐⭐⭐


✅ Lua框架检查清单

  • [ ] 理解 Lua C API

  • [ ] 能 Hook Lua 函数

  • [ ] 能执行自定义 Lua 代码

  • [ ] 能分析游戏 Lua 脚本


阶段九:游戏引擎逆向

⏱️ 总时长:3-4 个月
🎯 目标:逆向分析 UE 和 Unity 引擎游戏


9.1 UE引擎逆向 ⭐⭐⭐⭐⭐

🎯 目标:逆向分析虚幻引擎游戏
⏱️ 时长:6-8 周

9.1.1 UE核心概念

学习小点

  • [ ] UObject 系统

  • [ ] GUObjectArray

  • [ ] GNames

  • [ ] Actor 和 Component

  • [ ] ProcessEvent

关键结构

// UObject基本结构 (UE4/UE5)
class UObject {
    void** VTable;           // +0x00
    int32 ObjectFlags;       // +0x08
    int32 InternalIndex;     // +0x0C
    UClass* ClassPrivate;    // +0x10
    FName NamePrivate;       // +0x18
    UObject* OuterPrivate;   // +0x20
};

// FName结构
struct FName {
    int32 ComparisonIndex;
    int32 Number;
};

// 从GNames获取名称
const char* GetNameFromFName(FName name) {
    // GNames是全局名称表
    return GNames->GetEntry(name.ComparisonIndex)->AnsiName;
}

// 遍历所有对象
void DumpObjects() {
    for (int i = 0; i < GUObjectArray->NumElements; i++) {
        UObject* obj = GUObjectArray->GetObjectPtr(i);
        if (obj) {
            const char* name = GetNameFromFName(obj->NamePrivate);
            const char* className = GetNameFromFName(obj->ClassPrivate->NamePrivate);
            printf("%s : %s\n", name, className);
        }
    }
}

9.1.2 SDK生成

学习小点

  • [ ] 查找 GNames 特征

  • [ ] 查找 GUObjectArray 特征

  • [ ] 遍历对象生成 SDK

  • [ ] ProcessEvent Hook

代码示例

// 特征搜索示例
// GNames特征 (x64):
// 48 8B 05 ? ? ? ? 48 85 C0 75 50

// GUObjectArray特征:
// 48 8B 05 ? ? ? ? 48 8B 0C C8

uintptr_t FindGNames() {
    // 搜索特征码
    uintptr_t addr = PatternScan(module, "48 8B 05 ? ? ? ? 48 85 C0 75 50");
    if (addr) {
        int32_t offset = *(int32_t*)(addr + 3);
        return addr + 7 + offset;
    }
    return 0;
}

// ProcessEvent Hook
typedef void (*ProcessEvent_t)(UObject*, UFunction*, void*);
ProcessEvent_t orig_ProcessEvent = nullptr;

void hooked_ProcessEvent(UObject* obj, UFunction* func, void* params) {
    const char* funcName = GetNameFromFName(func->NamePrivate);
    printf("[ProcessEvent] %s\n", funcName);
    
    orig_ProcessEvent(obj, func, params);
}

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

🌐GitHub

UnrealDumper

各种 SDK Dumper

⭐⭐⭐⭐⭐

🌐在线

UnknownCheats UE 论坛

⭐⭐⭐⭐⭐

🌐在线

Guided Hacking UE 教程

⭐⭐⭐⭐⭐


9.2 Unity逆向 ⭐⭐⭐⭐⭐

🎯 目标:逆向分析 Unity 引擎游戏
⏱️ 时长:6-8 周

9.2.1 Mono游戏

学习小点

  • [ ] Mono 运行时

  • [ ] Assembly-CSharp.dll 分析

  • [ ] dnSpy 使用

  • [ ] Mono API Hook

代码示例

// Mono API函数类型
typedef void* (*mono_get_root_domain_t)();
typedef void* (*mono_thread_attach_t)(void*);
typedef void* (*mono_domain_assembly_open_t)(void*, const char*);
typedef void* (*mono_assembly_get_image_t)(void*);
typedef void* (*mono_class_from_name_t)(void*, const char*, const char*);
typedef void* (*mono_class_get_method_from_name_t)(void*, const char*, int);
typedef void* (*mono_runtime_invoke_t)(void*, void*, void**, void**);

// 调用C#方法
void CallCSharpMethod() {
    // 获取Mono函数
    HMODULE mono = GetModuleHandleA("mono-2.0-bdwgc.dll");
    auto mono_get_root_domain = (mono_get_root_domain_t)GetProcAddress(mono, "mono_get_root_domain");
    auto mono_thread_attach = (mono_thread_attach_t)GetProcAddress(mono, "mono_thread_attach");
    // ... 获取其他函数
    
    // 附加线程
    void* domain = mono_get_root_domain();
    mono_thread_attach(domain);
    
    // 打开程序集
    void* assembly = mono_domain_assembly_open(domain, "Assembly-CSharp");
    void* image = mono_assembly_get_image(assembly);
    
    // 获取类和方法
    void* klass = mono_class_from_name(image, "", "Player");
    void* method = mono_class_get_method_from_name(klass, "SetHealth", 1);
    
    // 调用方法
    int health = 999;
    void* args[1] = { &health };
    mono_runtime_invoke(method, playerInstance, args, NULL);
}

9.2.2 IL2CPP游戏

学习小点

  • [ ] IL2CPP 原理

  • [ ] global-metadata.dat

  • [ ] il2cppdumper 使用

  • [ ] GameAssembly.dll 分析

流程

IL2CPP逆向流程:

1. 识别IL2CPP游戏
   - 存在 GameAssembly.dll
   - 存在 global-metadata.dat

2. 使用Il2CppDumper
   - 输入: GameAssembly.dll + global-metadata.dat
   - 输出: dump.cs (类定义)
   - 输出: script.json (IDA脚本)

3. 分析GameAssembly.dll
   - 导入Il2CppDumper生成的IDA脚本
   - 恢复函数名和类型信息

4. 定位目标函数
   - 在dump.cs中搜索目标类/方法
   - 在IDA中找到对应地址

5. Hook或修改
   - 使用标准Hook技术

📚 学习资料

类型

资源名称

链接 / 说明

推荐度

🔧工具

dnSpy

Mono 游戏分析

⭐⭐⭐⭐⭐

🔧工具

Il2CppDumper

Perfare/Il2CppDumper

⭐⭐⭐⭐⭐

🌐在线

Unity 逆向教程

看雪 / 吾爱

⭐⭐⭐⭐


✅ 游戏引擎逆向检查清单

  • [ ] 理解 UE UObject 系统

  • [ ] 能生成 UE SDK

  • [ ] 能 Hook ProcessEvent

  • [ ] 能分析 Mono Unity 游戏

  • [ ] 会使用 dnSpy

  • [ ] 能分析 IL2CPP Unity 游戏

  • [ ] 会使用 Il2CppDumper


学习资源汇总

必读书籍

阶段

书籍

说明

基础

《C Primer Plus》

C 语言入门

基础

《汇编语言》王爽

x86 汇编入门

进阶

《C++ Primer》

C++ 入门

进阶

《深度探索 C++ 对象模型》

虚表机制

逆向

《逆向工程核心原理》

逆向入门

逆向

《加密与解密》第 4 版

逆向综合

Windows

《Windows 核心编程》

Win32 API

驱动

《Windows Kernel Programming》

驱动开发

驱动

《Windows Internals》

内核原理

在线资源

资源

网址

说明

看雪论坛

bbs.pediy.com

中文逆向社区

吾爱破解

52pojie.cn

中文破解社区

UnknownCheats

unknowncheats.me

游戏逆向

Guided Hacking

guidedhacking.com

游戏逆向教程

常用工具

工具

用途

IDA Pro

静态分析

x64dbg

动态调试

Cheat Engine

内存搜索

WinDbg

内核调试

dnSpy

.NET 逆向

Il2CppDumper

Unity IL2CPP


📌 相关文档:

  • [[逆向工程完整学习路径]]

  • [[Windows 驱动开发完整学习路径]]

  • [[逆向与驱动开发综合学习路径]]

分享文章

未配置分享平台

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

评论