🎨 颜色标记说明

  • 粉色:特别重要、主线必掌握、优先复盘的内容。
  • 蓝色:核心概念、术语、分类名。
  • 橙色:需要关注的边界、易错点、注意事项。
  • 绿色:解释类内容、帮助理解知识点。

从 C 程序看执行框架

一个最小 C 控制台程序看起来只有 main

1
2
3
4
5
6
7
#include <stdio.h>

int main(void)
{
puts("Hello, reverse!");
return 0;
}

但生成 EXE 后,操作系统通常不会直接跳到源码中的 main。在使用 C 运行库时,大致流程是:

1
2
3
4
5
6
Windows Loader
-> PE 入口点
-> CRT 启动代码
-> 初始化运行库与全局对象
-> 调用 main
-> 处理返回值并结束进程

源码入口 main 与 PE 入口点 OEP 不是同一个概念。逆向时经常需要从启动代码继续跟踪,才能定位真正的业务函数。

MASM x86 基本框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.586
.model flat, stdcall
option casemap:none

ExitProcess PROTO :DWORD
includelib kernel32.lib

.data
value dd 12345678h

.code
main proc
mov eax, value
push 0
call ExitProcess
main endp

end main
声明 作用
.586 允许使用相应代际的 x86 指令
.model flat, stdcall 平坦内存模型与默认调用约定
option casemap:none 保留标识符大小写
.data 定义已初始化数据
.code 定义代码
main proc/endp 定义过程范围
end main 告诉汇编器源文件结束及入口符号

MASM x64 基本框架

x64 不再使用 .model flat, stdcall 区分常见调用约定,Windows x64 采用统一的寄存器传参规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
option casemap:none

ExitProcess PROTO
includelib kernel32.lib

.data
value dq 123456789ABCDEF0h

.code
main proc
sub rsp, 28h
mov rcx, 0
call ExitProcess
main endp

end

这里的 sub rsp, 28h 同时照顾了栈对齐和被调用函数需要的影子空间。后续学习 x64 调用约定时再详细展开。

Visual Studio 的 x64 C/C++ 通常不支持传统内联汇编。需要把汇编写入独立 .asm 文件,交给 MASM 构建,再与 C/C++ 目标文件链接。

构建过程

1
2
3
C/C++ 源文件 --编译器--> .obj
汇编源文件 --汇编器--> .obj
多个 .obj + .lib --链接器--> .exe / .dll

编译负责把高级语言转换为目标代码,汇编负责把助记符转换为机器指令,链接负责合并目标文件并解析符号引用。

指令集与工作模式

项目 x86 x64
通用寄存器宽度 32 Bit 64 Bit
指针常见宽度 4 Byte 8 Byte
指令指针 EIP RIP
栈指针 ESP RSP
Windows 常见用户态 保护模式 长模式

CPU 支持某条指令,不代表当前工具链和目标模式一定允许直接使用。分析时要同时确认:

  • PE 是 32 位还是 64 位;
  • 当前反汇编器按哪种模式解码;
  • 编译器启用了哪些扩展指令集;
  • 操作数宽度与地址宽度是否一致。

外部函数与动态调用

汇编代码调用 Windows API 时,需要解决两个问题:

  1. 告诉汇编器函数原型或外部符号名称;
  2. 告诉链接器符号来自哪个导入库。
1
2
ExitProcess PROTO :DWORD
includelib kernel32.lib

链接完成后,PE 导入表会记录依赖。程序加载时,Windows Loader 再把实际函数地址写入对应的导入地址位置。

因此在反汇编里看到的 API 调用,可能是经过 IAT 的间接调用,而不是直接写死系统 DLL 中的绝对地址。

混合编程的接口边界

C/C++ 与汇编互相调用时,双方必须约定一致:

  • 符号名称;
  • 参数顺序和参数位置;
  • 返回值位置;
  • 哪些寄存器需要保存;
  • 栈由谁清理;
  • 结构体和数据的对齐方式。

任何一项不一致,都可能表现为参数错误、返回地址损坏或函数返回后崩溃。

学习检查清单

检查项 状态
能区分源码 main 与 PE 入口点 待复盘
能解释 .data.codeproc/endpend 待复盘
能说出编译、汇编和链接的区别 待复盘
知道 x86 与 x64 的寄存器和指针宽度差异 待复盘
能说明调用外部函数为何需要原型和导入库 待复盘