代码空白区添加 MessageBox
-
测试代码,寻找目标地址
#include "stdafx.h" #include <windows.h> void fun() { printf("Hello Cntf\r\n"); MessageBox(0,0,0,0); } int main(int argc, char* argv[]) { fun(); return 0; }
上面代码编译执行,弹框,实现MessageBox的功能;
我们需要找到MessageBox在程序执行的起始位置;
在OD工具里面设置断点,命令行输入bp MessageBoxA,然后在菜单栏找到"B" 点击进去就发现了设置的断点;
然后双击即可查看MessageBox的断点位置,如下:
77D5050B > 8BFF MOV EDI,EDI
最终确认MessageBox在程序中执行的地址:0x77D5050B
-
E8,E9的计算
E8是call
的硬编码,E9是jmp
的硬编码,6A是push
的硬编码公式一:
真正要跳转的地址 = E8这条指令的下一行地址 + X
即:X = 真正要跳转的地址 - E8这条指令的下一行地址
该指令的下一行地址为待执行的地址,因为当执行call指令后,机器的硬编码将不知道将执行哪一部分,所以如此设计了,加入了返回后该执行的地址编码(本人理解)-
验证E8 call后面4个字节
如下为上c代码,进行编译,调试,进入反汇编得到的结果15: fun(); 00401098 E8 68 FF FF FF call @ILT+0(fun) (00401005) 16: return 0; 0040109D 33 C0 xor eax,eax
解释:
E8 —> call的硬编码
E8后面的4个字节是通过计算的出来的,我们上面的公式就是要验证确认他们的计算方式根据公式,已知:真正要跳转的地址和E8这条指令的下一行地址;
开始计算
X = 真正要跳转的地址 - E8这条指令的下一行地址
X = 0x00401005 - 0x0040109D = 0xFFFFFF68小端存储,通过上面对比得知他们是一样的;
-
验证E9 jmp后面4个字节
生成的反汇编代码
@ILT+0(?fun@@YAXXZ): 00401005 E9 16 00 00 00 jmp fun (00401020) @ILT+5(_main): 0040100A E9 71 00 00 00 jmp main (00401080)
开始计算
X = 真正要跳转的地址 - E8这条指令的下一行地址
X = 0x00401020 - 0x0040100A = 0x00000016小端存储,通过上面对比得知他们是一样的;
公式二:
要跳转的地方 = (E8当前的地址 + 5)1 + X
即:
X = 要跳转的地址 - (E8的地址 + 5)
MessageBox的反汇编信息如下
10: MessageBox(0,0,0,0); 00401045 8B F4 mov esi,esp // 反汇编后,发现MessageBox有4个push的参数 00401047 6A 00 push 0 00401049 6A 00 push 0 0040104B 6A 00 push 0 0040104D 6A 00 push 0 0040104F FF 15 8C 52 42 00 call dword ptr [__imp__MessageBoxA@16 (0042528c)] 00401055 3B F4 cmp esi,esp 00401057 E8 F4 00 00 00 call __chkesp (00401150) 11: }
所以总结下来,需要向程序的代码区域插入如下18个字节的代码即可,而E8,E9后面的四个字节是需要算的
6A 00 6A 00 6A 00 6A 00 E8 00 00 00 00 E9 00 00 00 00 —> 18个字节大小
-
-
在代码区手动添加 shellcode 代码
VirtualSize: 0x000001f8 000440A2 [V(VS),内存中大小(对齐前的长度).] VirtualAddress: 0x000001fc 00001000 [V(VO),内存中偏移(该块的RVA).] SizeOfRawData: 0x00000200 00045000 [R(RS),文件中大小(对齐后的长度).] PointerToRawData: 0x00000204 00001000 [R(RO),文件中偏移.]
00045000 - 000440A2 = F5E
- - -> 此处计算结果必须大于我们上面要插入的18个字节大小,不然会出问题使用16进制编辑器打开exe二进制文件
在第一个节后面的代码空白区域添加我们的代码;就是在SizeOfRawData -> 0x00045000的后面理解:
第一个节从PointerToRawData
文件偏移开始–>0x00001000
开始向后数,数到0x00045000
结束;
然后根据对齐宽度是1000H
,所以向后再加上1000H
就是0x00046000
是第二个节的开始位置;
这里需要注意,我使用编辑器打开ipmsg.exe文件之后,找到0x00045000
的位置发现还有数据;
向后扩展1000H
到了0x00046000
位置是对的,刚好是下一个节的开始位置,而这期间;
0x00045000 - 0x00046000
这里面开头一点还是有数据的,到了一定的位置发现都是00,那么确认此处;
到0x00046000
开始的上一个字节位置都是空白区域,都是可以填充数据的;我这里找到代码空白区域的位置是
0x000450a2
开始,从这里开始的,我们从此处的下一行开始填充代码;也就是从
0x000450b0
位置开始,具体如下:000450b0h: 6A 00 6A 00 6A 00 6A 00 E8 00 00 00 00 E9 00 00 000450c0h: 00 00
添加执行函数call汇编指令和jmp汇编指令后面的硬编码数据;
-
添加E8 call 指令
需要的MessageBox数据 ---->
0x77D5050B
,根据公式二:X = 要跳转的地址 - (E8当前的地址 + 5)
,来计算E8,E9后面部分的硬编码填充数据:
要跳转的地址 =0x77D5050B
E8当前的地址 + 5 = 下一个jmp的汇编指令开头 —> E9的位置 —>0x000450bd
得到上面的地址,一定要再考虑(
ImageBuffer
里面的偏移)内存偏移和 因为我们加入的代码;
最终是要在内存中运行,所以一定是要考虑在内存中偏移和对齐;
要考虑这两个 —>VirtualAddress
和PointerToRawData
VirtualAddress
: 0x000001fc 00001000 [V(VO),内存中偏移(该块的RVA).]
PointerToRawData
: 0x00000204 00001000 [R(RO),文件中偏移.]
ImageBase
: [地址(RAW):0x0000012c] [长度:04h] [偏移量:e_lfanew+0x34]
[数据:0x00400000] [基址,程序默认装入的基地址.]本次测试使用的程序很巧,刚好文件和内存中的偏移是一样的,都是
1000H
ImageBase
和内存偏移地址1000H
得出地址:0x000450bd
+0x00400000
=0x004450bd
X = 要跳转的地址 - (E8当前的地址 + 5)
要跳转的地址 -->
0x77D5050B
(这里要跳转的地址就是上面我们找到的MessageBox的地址)
(E8当前的地址 + 5) -->0x004450bd
(上述所计算的地址)X =
0x77D5050B
-0x004450bd
=0x7790B44E
0x7790B44E --> 77 90 B4 4E --> 小端模式 --> 4E B4 90 77 --> 放入E8的后面
000450b8h: E8 4E B4 90 77
- - -> 这就是最终的E8后面的数据 -
添加E9 jmp指令
加入代码之后,需要再回到程序的入口处,所以E9要跳转的地址就是OEP的地址
AddressEntryPoint --> 0x000441EC ImageBase --> 0x00400000 OEP地址 --> 0x000441EC + 0x00400000 = 0x004441EC
要跳转的地址 -->
0x004441EC
(E9当前的地址 + 5) -->0x004450C2
X = 要跳转的地址 - (E9当前的地址 + 5)
X =0x004441EC
-0x004450C2
=0xFFFFF12A
0xFFFFF12A --> FF FF F1 2A --> 小端模式 --> 2A F1 FF FF --> 放入E9的后面000450bdh: E9 2A F1 FF FF
- - -> 这就是最终的E9后面的数据
-
-
修改OEP的值,指向我们设置的 shellcode
修改的 shellcode 地址:
0x00450b0
原来的OEP地址位置:0x000441EC
对应位置是可选PE文件头 -->00000120h: EC 41 04 00
将其更改为这个地址内容 -->0x00000120 --> B0 50 04 00
-
修改图示
参考文献https://www.cnblogs.com/autopwn/tag/逆向/default.html?page=4
5为 call 或者 jmp 后面的5个字节跳过,则 E8 + 5 恰好为下一行的地址 ↩︎