滴水逆向-代码节空白区添加代码(手动)

相关笔记代码测试验证记录

手动在代码空白区添加代码

1.获取MessageBox地址,构造ShellCode代码.

下面是测试代码

#include "stdafx.h"
#include <windows.h>

void fun()
{
    printf("Hello Cntf
");
    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

2.E8 E9计算公式

公式一:

真正要跳转的地址 = E8这条指令的下一行地址 + X

下面是变形公式:

X = 真正要跳转的地址 - E8这条指令的下一行地址

下面是上面写的简单代码进行编译,调试,进入反汇编得到的结果

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

开始比对代码中反汇编的地址和计算出来的地址

00401098 E8 68 FF FF FF       call        @ILT+0(fun) (00401005)

0xFFFFFF68   ---> FF FF FF FF 68
E8 68 FF FF FF

通过上面对比得知他们是一样的;所以最终结果如下:

E8 68 FF FF FF

@ILT+0(?fun@@YAXXZ):
00401005 E9 16 00 00 00       jmp         fun (00401020)


开始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 = 0x00401020 - 0x0040100A = 0x00000016

通过对比生成的反汇编代码,同样可以确认结果是一样的

00401005 E9 16 00 00 00
0x16000000

最终的结果:

E9 16 00 00 00


公式二:

要跳转的地方 = (E8当前的地址 + 5) + X

X = 要跳转的地址 - (E8的地址 + 5)

下面是反汇编代码生成的关于MessageBox的信息

10:       MessageBox(0,0,0,0);
00401045 8B F4                mov         esi,esp
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:   }

通过上面的反汇编代码得知,在程序执行MessageBox的时候有4个push 0

00401047 6A 00                push        0
00401049 6A 00                push        0
0040104B 6A 00                push        0
0040104D 6A 00                push        0

所以总结下来,需要向程序的代码区域插入如下18个字节的代码即可

6A 00 6A 00 6A 00 6A 00 E8 00 00 00 00 E9 00 00 00 00     --->  18个字节大小

3.在代码区手动添加代码


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个字节大小,不然会出问题

6A 00 6A 00 6A 00 6A 00 E8 00 00 00 00 E9 00 00 00 00

打开winhex或者ultraedit编辑器,使用她们打开二进制文件,我们的目的是针对二进制文件;
ipmsg.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汇编指令后面的硬编码数据;

需要的MessageBox数据 ---->  0x77D5050B

要使用到的公式

公式二:

要跳转的地址 = (E8当前的地址 + 5) + X

变形:
X = 要跳转的地址 - (E8当前的地址 + 5)

根据上面的公式,我们最要确定的是算出要跳转的地址,所以需要使用公式:
X = 要跳转的地址 - (E8当前的地址 + 5)

现在我们对比公式填充数据:
要跳转的地址 = 0x77D5050B
E8当前的地址 + 5  = 下一个jmp的汇编指令开头 ---> E9的位置 ---> 0x000450bd

得到上面的地址,一定要再考虑(ImageBuffer里面的偏移)内存偏移和 因为我们加入的代码;
最终是要在内存中运行,所以一定是要考虑在内存中偏移和对齐;
要考虑这两个 ---> VirtualAddress和PointerToRawData

VirtualAddress:         0x000001fc     00001000     [V(VO),内存中偏移(该块的RVA).]
PointerToRawData:       0x00000204     00001000     [R(RO),文件中偏移.]

下面是可选PE头的ImageBase内容
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后面的数据,要清楚,我们加入代码之后,需要再回到程序的入口处;
所以E9要跳转的地址就是OEP的地址,但OEP的地址更改完成之后需要再将程序执行的OEP地址;
更改为上面为我们添加代码的开始地址;

需要用到的OEP地址信息:

AddressEntryPoint  --> 0x000441EC
ImageBase             --> 0x00400000

得到最终的OEP地址   --> 0x000441EC + 0x00400000 = 0x004441EC

X = 要跳转的地址 - (E9当前的地址 + 5)

要跳转的地址       --> 0x004441EC
(E9当前的地址 + 5) --> 0x004450C2

X = 0x004441EC - 0x004450C2 = 0xFFFFF12A

0xFFFFF12A  --> FF FF F1 2A -->  小端模式 --> 2A F1 FF FF --> 放入E9的后面

000450bdh: E9 2A F1 FF FF  ----> 这就是最终的E9后面的数据;

上面完成了之后到了最后一步了,修改OEP的值,在修改的时候我们不需要再加上ImageBase的值;
因为程序在运行的时候会帮我们加上,所以我们只要填充我们代码里面的初始值即可,这里我们;
添加了18个字节的代码,开头的还是6A 00 打头的,就是MessageBox的起始地址,添加这个地址;

需要跟OEP对应的地址为:0x000450b0

我们找到原来的OEP地址位置:0x000441EC (这个地址是没加ImageBase的地址,我们直接更改保存);

对应位置是可选PE文件头里面 --> 00000120h: EC 41 04 00

将其更改为这个地址内容     --> 0x000450b0  --> B0 50 04 00

更改后的最终OEP地址:

00000120h: B0 50 04 00

保存执行测试;

测试执行成功;

4.修改OEP,指向ShellCode.

上面第3步已经操作完成;

相关更改后的二进制文件内容信息:

更改的OEP的地址位置

保存运行后的结果

迷茫的人生,需要不断努力,才能看清远方模糊的志向!
原文地址:https://www.cnblogs.com/autopwn/p/15241984.html