1、课程目标
- 掌握逆向分析的基本流程和方法
- 学会使用调试器定位关键代码
- 理解静态分析与动态调试的结合
- 掌握常用的断点技术
2、名词解释
| 术语 |
英文 |
说明 |
| 逆向工程 |
Reverse Engineering |
分析程序内部工作原理的过程 |
| 静态分析 |
Static Analysis |
不运行程序直接分析代码 |
| 动态调试 |
Dynamic Debugging |
运行程序并观察其行为 |
| 断点 |
Breakpoint |
使程序在特定位置暂停执行 |
| 交叉引用 |
Cross Reference (XRef) |
代码之间的调用关系 |
3、使用工具
| 工具 |
用途 |
| IDA Pro |
专业反汇编和分析工具 |
| x64dbg/x32dbg |
Windows动态调试器 |
| Ghidra |
免费的逆向分析工具 |
| OllyDbg |
经典的x86调试器 |
| Process Monitor |
监控文件/注册表操作 |
4、技术原理
4.1、逆向分析流程
- 信息收集 - 了解程序功能和行为
- 入口点定位 - 找到程序入口
- 功能定位 - 定位感兴趣的代码
- 代码分析 - 理解实现逻辑
- 验证结论 - 确认分析结果
4.2、定位方法
| 方法 |
适用场景 |
| 字符串搜索 |
有明显提示信息 |
| API断点 |
知道会调用的API |
| 消息断点 |
GUI程序事件处理 |
| 内存断点 |
跟踪数据访问 |
| 条件断点 |
特定条件下触发 |
5、代码实现
5.1、示例1:目标程序(用于练习)
#include <stdio.h>
#include <string.h>
#include <windows.h>
// 模拟的密码验证程序
BOOL CheckPassword(const char* input) {
// 硬编码的密码(逆向目标)
const char* secret = "SecretPass123";
if (strlen(input) != strlen(secret)) {
return FALSE;
}
// 简单的字符比较
for (int i = 0; i < strlen(secret); i++) {
if (input[i] != secret[i]) {
return FALSE;
}
}
return TRUE;
}
int main() {
char password[64] = {0};
printf("=== Password Checker ===\n");
printf("Enter password: ");
scanf("%63s", password);
if (CheckPassword(password)) {
printf("Access Granted!\n");
MessageBoxA(NULL, "Welcome!", "Success", MB_OK);
} else {
printf("Access Denied!\n");
MessageBoxA(NULL, "Wrong password!", "Error", MB_ICONERROR);
}
return 0;
}
5.2、示例2:字符串搜索定位
在IDA中进行字符串搜索:
1. 打开目标程序
2. View -> Open subviews -> Strings (Shift+F12)
3. 搜索关键字:"Access Granted" 或 "password"
4. 双击跳转到字符串位置
5. 查看交叉引用 (X键) 找到使用该字符串的代码
6. 分析周围代码逻辑
5.3、示例3:API断点定位
// 常用断点API及其用途
/*
文件操作:
- CreateFileA/W - 打开文件
- ReadFile/WriteFile - 读写文件
- DeleteFileA/W - 删除文件
注册表:
- RegOpenKeyExA/W - 打开注册表键
- RegSetValueExA/W - 设置键值
- RegQueryValueExA/W - 查询键值
网络:
- socket/connect - 网络连接
- send/recv - 数据传输
内存:
- VirtualAlloc - 分配内存
- VirtualProtect - 修改保护
弹窗:
- MessageBoxA/W - 消息框
*/
// 在x64dbg中设置API断点
// bp MessageBoxA
// bp CreateFileA
5.4、示例4:用程序分析流程
#include <windows.h>
#include <stdio.h>
// 程序化方式查找模块地址
void FindModuleAddress() {
HMODULE hMod = GetModuleHandleA(NULL);
printf("Module Base: 0x%p\n", hMod);
// 获取模块信息
MODULEINFO mi;
GetModuleInformation(GetCurrentProcess(), hMod, &mi, sizeof(mi));
printf("Entry Point: 0x%p\n", mi.EntryPoint);
printf("Image Size: 0x%X\n", mi.SizeOfImage);
}
// 搜索内存中的字符串
void* SearchString(void* start, size_t size, const char* target) {
size_t targetLen = strlen(target);
unsigned char* p = (unsigned char*)start;
for (size_t i = 0; i < size - targetLen; i++) {
if (memcmp(p + i, target, targetLen) == 0) {
return p + i;
}
}
return NULL;
}
int main() {
FindModuleAddress();
// 在自身模块中搜索字符串
HMODULE hMod = GetModuleHandleA(NULL);
MODULEINFO mi;
GetModuleInformation(GetCurrentProcess(), hMod, &mi, sizeof(mi));
void* found = SearchString(hMod, mi.SizeOfImage, "password");
if (found) {
printf("Found 'password' at: 0x%p\n", found);
}
return 0;
}
5.5、示例5:x64dbg调试命令
常用x64dbg命令:
断点:
bp <address> - 设置断点
bp MessageBoxA - 在API上设置断点
bph <addr> r 4 - 硬件读断点
bph <addr> w 4 - 硬件写断点
bc <addr> - 清除断点
搜索:
find <addr>,"text" - 搜索字符串
findall <module>,"pattern" - 搜索特征码
执行:
run - 运行
stepover/sto - 单步越过
stepinto/sti - 单步进入
rtr - 执行到返回
分析:
follow - 跳转到地址
graph - 显示流程图
xref - 查看交叉引用
6、课后作业
- 基础练习:编译示例程序,用IDA找到密码存储位置
- API断点:用x64dbg在MessageBoxA上设置断点,跟踪调用栈
- 字符串定位:在一个实际程序中用字符串搜索定位关键功能
- 综合练习:分析一个简单的CrackMe程序