0211、Windows 逆向入门:数组、指针与结构体逆向
这一部分把高级语言数据结构翻译成内存布局。数组、指针、结构体和对象成员,在反汇编里最后都会变成地址计算、偏移访问和间接调用。
1、内容范围
| 小节 | 内容 | 复盘点 |
|---|---|---|
| 14.0 - 14.4 | 数组逆向 | 数组布局、寻址公式、多维数组、越界覆盖 |
| 14.5 - 14.8 | 移位与数据转换 | SHL/SHR/SAL/SAR、二进制转换、数据完整性 |
| 14.9 - 15.2 | 数组地址与多维数组 | 数组地址传递、返回、指针数组、二维数组 |
| 15.3 - 16.1 | 指针逆向 | 指针赋值、解引用、运算、多级指针、安全性 |
| 16.2 - 16.4 | 函数指针与段机制 | CALL 编码、函数指针、段选择子 |
| 16.5 - 16.9 | 结构体与 this | 结构体布局、对象模型、成员偏移、thiscall |
2、数组内存布局
一维数组是连续内存。
1 | int nums[4] = {1, 2, 3, 4}; |
寻址公式:
1 | 元素地址 = 数组基址 + 下标 * 元素大小 |
反汇编中常看到:
1 | mov eax, [base + index * 4] |
这往往对应 int 数组访问。
3、多维数组
二维数组本质上仍然是连续内存。以 int a[3][4] 为例:
1 | a[row][col] 地址 = base + (row * 4 + col) * sizeof(int) |
复盘时先确认每行列数,再还原索引计算。
4、越界覆盖
越界问题只按防护复盘:数组访问超过边界后,可能覆盖邻近变量、保存寄存器、返回地址或堆元数据。
| 复盘项 | 说明 |
|---|---|
| 缓冲区大小 | 实际分配多少字节 |
| 写入长度 | 外部输入或循环次数是否超限 |
| 覆盖目标 | 邻近内存保存了什么 |
| 修复 | 长度检查、边界判断、安全函数 |
5、移位指令
| 指令 | 含义 |
|---|---|
shl/sal |
左移,常见于乘 2 的幂 |
shr |
逻辑右移,适合无符号 |
sar |
算术右移,保留符号位 |
数组索引乘元素大小时,编译器可能用移位优化。
6、指针赋值与解引用
指针在汇编里就是地址。区别在于是否加了中括号:
1 | mov eax, ecx ; 复制地址/值 |
7、多级指针
多级指针表现为多次间接访问:
1 | int **pp; |
可能对应:
1 | mov eax, [pp] |
复盘时把每一级画出来,不要跳步。
8、函数指针与 CALL
直接调用和间接调用的区别:
1 | call 401000h ; 直接调用 |
函数指针、虚函数、多态、回调都会出现间接调用。
9、结构体布局
结构体成员访问通常是“结构体基址 + 字段偏移”。
1 | typedef struct User { |
可能看到:
1 | mov eax, [ecx+4] ; 读取 age |
要结合字段大小、对齐和访问顺序还原结构体。
10、this 指针
C++ 成员函数会隐式接收 this 指针。x86 thiscall 常用 ECX 传递 this,x64 下则按统一调用约定处理。
| 观察点 | 说明 |
|---|---|
this |
当前对象地址 |
| 成员偏移 | [this + offset] |
| 成员函数 | 普通函数加隐式对象参数 |
| 虚函数 | 通过虚表间接调用 |
11、学习检查清单
| 检查项 | 状态 |
|---|---|
| 能根据寻址公式还原数组访问 | 待复盘 |
| 能画出二维数组线性内存布局 | 待复盘 |
| 能解释越界写会覆盖邻近内存 | 待复盘 |
| 能区分直接调用和函数指针间接调用 | 待复盘 |
| 能根据成员偏移推测结构体字段 | 待复盘 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ruiqy~!









