文章目录
- 一、先给出结论(逆向视角)
- 二、栈帧 & 数组整体布局(关键)
- 1️⃣ 栈帧大小
- 2️⃣ 数组起始地址
- 3️⃣ 为什么是 `int Arr[10]`?
- 三、数组初始化的逆向特征
- 🔍 逆向判断技巧
- 四、数组下标访问的核心特征(重点)
- 1️⃣ Arr[0] = 11;
- 2️⃣ Arr[1] = 12;
- 3️⃣ Arr[2] / Arr[4](shl)
- 4️⃣ Arr[3] / Arr[5~7](imul)
- 🔍 逆向总结:数组访问的“黄金公式”
- 五、为什么写法不统一?(shl / imul 混用)
- 六、数组在逆向中的“识别清单”(实战)
- 七、最终逆向还原(完整)
- 🔚 一句话总结(逆向口诀)
voidfunc(){intArr[10]={1,2,3,4,5,6,7,8};Arr[0]=11;Arr[1]=12;Arr[2]=13;Arr[3]=14;Arr[4]=15;Arr[5]=16;Arr[6]=17;Arr[7]=18;return;}5:intArr[10]={1,2,3,4,5,6,7,8};000E1C10 mov dword ptr[ebp-30h],1000E1C17 mov dword ptr[ebp-2Ch],2000E1C1E mov dword ptr[ebp-28h],3000E1C25 mov dword ptr[ebp-24h],4000E1C2C mov dword ptr[ebp-20h],5000E1C33 mov dword ptr[ebp-1Ch],6000E1C3A mov dword ptr[ebp-18h],7000E1C41 mov dword ptr[ebp-14h],8000E1C48xoreax,eax000E1C4A mov dword ptr[ebp-10h],eax000E1C4D mov dword ptr[ebp-0Ch],eax6:Arr[0]=11;000E1C50 mov eax,4000E1C55 imul ecx,eax,0000E1C58 mov dword ptr[ebp+ecx-30h],0Bh7:Arr[1]=12;000E1C60 mov eax,4000E1C65 shl eax,0000E1C68 mov dword ptr[ebp+eax-30h],0Ch8:Arr[2]=13;000E1C70 mov eax,4000E1C75 shl eax,1000E1C77 mov dword ptr[ebp+eax-30h],0Dh9:Arr[3]=14;000E1C7F mov eax,4000E1C84 imul ecx,eax,3000E1C87 mov dword ptr[ebp+ecx-30h],0Eh10:Arr[4]=15;000E1C8F mov eax,4000E1C94 shl eax,2000E1C97 mov dword ptr[ebp+eax-30h],0Fh11:Arr[5]=16;000E1C9F mov eax,4000E1CA4 imul ecx,eax,5000E1CA7 mov dword ptr[ebp+ecx-30h],10h12:Arr[6]=17;000E1CAF mov eax,4000E1CB4 imul ecx,eax,6000E1CB7 mov dword ptr[ebp+ecx-30h],11h13:Arr[7]=18;000E1CBF mov eax,4000E1CC4 imul ecx,eax,7000E1CC7 mov dword ptr[ebp+ecx-30h],12h一、先给出结论(逆向视角)
这是一个栈上局部
int Arr[10]数组
在反汇编中它的特征是:
一整块连续的栈空间
基址固定(
ebp-30h)每个元素间隔固定 4 字节
访问形式统一为:
[ebp + index*4 - 30h]下标 × 元素大小(4) = 偏移
只要你在逆向里看到这种模式,99% 可以确定是数组。
二、栈帧 & 数组整体布局(关键)
1️⃣ 栈帧大小
000E1BE3 sub esp,0F4h说明:
函数一共在栈上开了0xF4 = 244 字节
包含:数组、临时变量、调试填充、cookie 等
2️⃣ 数组起始地址
mov dword ptr [ebp-30h],1 mov dword ptr [ebp-2Ch],2 mov dword ptr [ebp-28h],3 ... mov dword ptr [ebp-14h],8这是最直接的数组证据:
| 元素 | 地址 |
|---|---|
| Arr[0] | ebp-30h |
| Arr[1] | ebp-2Ch |
| Arr[2] | ebp-28h |
| Arr[3] | ebp-24h |
| Arr[4] | ebp-20h |
| Arr[5] | ebp-1Ch |
| Arr[6] | ebp-18h |
| Arr[7] | ebp-14h |
👉每次 +4 字节→int数组
3️⃣ 为什么是int Arr[10]?
mov dword ptr [ebp-10h],eax mov dword ptr [ebp-0Ch],eax-30h到-0Ch覆盖40 字节40 ÷ 4 =10 个 int
虽然只初始化了 8 个,数组大小仍然是 10
逆向时:不要被初始化个数骗了,看空间大小
三、数组初始化的逆向特征
源码:
intArr[10]={1,2,3,4,5,6,7,8};对应汇编:
mov dword ptr [ebp-30h],1 mov dword ptr [ebp-2Ch],2 mov dword ptr [ebp-28h],3 mov dword ptr [ebp-24h],4 ... mov dword ptr [ebp-14h],8🔍 逆向判断技巧
连续写入
固定步长
无条件执行
➡️ 这是编译期展开的数组初始化
四、数组下标访问的核心特征(重点)
1️⃣ Arr[0] = 11;
mov eax,4 imul ecx,eax,0 mov dword ptr [ebp+ecx-30h],0Bh还原:
ecx = 4 * 0 [ebp - 30h + ecx] = 11➡️标准公式:
Arr[i] = *(base + i * sizeof(int))2️⃣ Arr[1] = 12;
mov eax,4 shl eax,0 mov dword ptr [ebp+eax-30h],0Chshl 0⇒1 * 4偏移 =
4
➡️ Arr[1]
3️⃣ Arr[2] / Arr[4](shl)
shl eax,1 ; 2 * 4 shl eax,2 ; 4 * 4编译器用移位替代乘法(更快)
4️⃣ Arr[3] / Arr[5~7](imul)
imul ecx,eax,3 imul ecx,eax,5 imul ecx,eax,7下标不是 2 的幂 → 用 imul
🔍 逆向总结:数组访问的“黄金公式”
[ebp + index * element_size + base_offset]在你这段里是:
[ebp + index*4 - 30h]五、为什么写法不统一?(shl / imul 混用)
这是编译器优化策略,不是源码差异:
| 下标 | 生成方式 |
|---|---|
| 1、2、4 | shl |
| 3、5、6、7 | imul |
逆向时:
只要看到 index × 常量 + 固定基址 = 数组
不要纠结具体指令。
六、数组在逆向中的“识别清单”(实战)
你以后看到下面特征,可以直接标注为数组:
✔ 连续栈地址
✔ 固定起始偏移(ebp-30h)
✔ 元素间隔 = 类型大小
✔ 下标参与乘法/移位
✔ 多次访问同一基址
七、最终逆向还原(完整)
voidfunc(){intArr[10];Arr[0]=11;Arr[1]=12;Arr[2]=13;Arr[3]=14;Arr[4]=15;Arr[5]=16;Arr[6]=17;Arr[7]=18;}🔚 一句话总结(逆向口诀)
“连续 + 固定基址 + index × size = 数组”