上边文章:手工向可执行程序添加MessageBox弹窗(一),是在代码区中添加代码,弹出一个默认标题和默认内容的MessagBox,这次是手动给notepad新增一个节,并在节中调用MessageBox,弹出自定义标题和内容的MessageBox。
另外上次是在WinXp3
下做的测试,这次是在win10中,但是用的notepad是从xp3中拷贝出来的。
在上篇中已经知道以下基本信息:
标准PE头IMAGE_FILE_HEADER部分成员信息:
WORD NumberOfSections; // PE节的数量(0x00000003)
IMAGE_OPTIONAL_HEADER(可选头)中部分成员信息:
DWORD AddressOfEntryPoint; // 程序执行入口RVA (0x0000739D)
DWORD ImageBase; // 程序的优先装载地址(基址) (0x01000000)
// 程序运行时,PE装载器先创建进程,再将文件载入内存,然后把EIP寄存器的值设置为:ImageBase + AddressOfEntryPoint;
DWORD SectionAlignment; // 内存中节的对齐粒度 (0x00001000)
DWORD FileAlignment; // 文件中节的对齐粒度 (0x00000200
DWORD SizeOfImage; // 内存中整个PE文件的大小 (0x00013000)
DWORD SizeOfHeaders; // DOS头+PE头+节表的大小 (0x00000400)
1. 新增节区
图1
从图1中可以得到以下信息:
IMAGE_SECTION_HEADER(节表最后一项部分成员信息):
DWORD VirtualSize; // 节区在内存中没有对齐前的实际大小 (0x00007F20)
DWORD VirtualAddress; // 节区在内存中起始位置(RVA) (0x0000B000)
DWORD SizeOfRawData; // 节区在文件中对齐后的大小 (0x00008000)
DWORD PointerToRawData; // 节区在在文件中的偏移 (0x00008400)
1.1 判断是否有空间新增一个节区
要添加节区,首先需要判断最后一个节表项到第一个节区开始之间的空闲区是否够80 Bytes(40Bytes 节区属性 + 40Bytes 空白内容)
SizeOfHeaders(0x400) - 当前节表最后一个节表项结束的偏移(0x250)=0x150 > 0x50
1.2 编辑新增节区的属性
#define IMAGE_SIZEOF_SHORT_NAME 8
// 40 Bytes
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union {
DWORD PhysicalAddress;
DWORD VirtualSize; // 节区在内存中没有对齐前的实际大小
} Misc;
DWORD VirtualAddress; // 节区在内存中起始位置(RVA)
DWORD SizeOfRawData; // 节区在文件中对齐后的大小
DWORD PointerToRawData; // 节区在在文件中的偏移
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics; // 节的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
依次编辑新增节表项的每个成员:
Name:.popmsg.
VirtualSize: 0x64 (100Bytes足够使用)
VirtualAddress:
前一个节表项的(VirtualAddress(0xB000)+VirtualSize对齐后的(0x8000))= 0x00013000
SizeOfRawData: 0x200
PointerToRawData:
前一个节表项的(PointerToRawData(0x8400)+SizeOfRawData(0x8000))= 0x00010400
最后设置节的属性值:
首先节要有代码区的属性:可读、可执行和“节中包含代码”;
另外自定义MessageBox的标题和内容,需要将MessageBox的标题和内容也放到这个节区,也就是节要具有数据区的属性:可读、可写和“包含以初始化数据”。
所以将代码区Characteristics值(0x60000020)和数据区的Characteristics值(0xC0000040)按位或得到的值就是新增节的Characteristics(0xE0000060)。
1.4 修改NumberOfSections
将NumberOfSections中的值改为0x00000004
1.5 修改SizeOfImage
SizeOfImage是内存中整个PE文件的大小,新增节后,必须也增大它的值,新增节只有64Bytes大小,但是要考虑内存对齐(SectionAlignment(0x1000)),所以SizeOfImage的值在原来基础上增加0x1000:
SizeOfImage(0x00013000) + 0x1000 = 0x0x00014000
2. 编辑节内容
在文件末尾插入512(0x200)Bytes的空间,并将其全部置0;
2.1 添加MessageBox标题和内容并计算地址
标题:Congratulation 内容:richarde
文件中标题起始地址(0x00010410)映射到内存中:
0x00010410 - PointerToRawData(0x00010400) + VirtualAddress(0x00013000) + ImageBase(0x01000000) = 0x01013010
文件中内容起始地址(0x00010420)映射到内存中:
0x00010420 - PointerToRawData(0x00010400) + VirtualAddress(0x00013000) + ImageBase(0x01000000) = 0x01013020
2.2 构造shellcode
shellcode [] = {
0x6A, 0x00,
0x68, 0x10, 0x30, 0x01, 0x01, // 标题映射到内存中的起始地址
0x68, 0x20, 0x30, 0x01, 0x01, // 内容映射到内存中的起始地址
0x6A, 0x00,
0xE8, 0x00, 0x00, 0x00, 0x00, // 调用MessageBox
0xE9, 0x00, 0x00, 0x00, 0x00 // 跳到原程序的入口(AddressOfEntryPoint)
};
将构造好的shellcode写入节中,如图2:
图2
剩下的步骤和上一篇文章一样,就是计算文件中的偏移地址映射到内存后的地址,注意的是内存对齐和文件对齐,无论哪步计算错了,都会使可执行程序不可运行。
2.3 计算E8后边的值
真正要跳转的地址:MessageBox地址:0x75148860
文件中E8下一行地址(0x00010443)相对PointerToRawData(0x00010400)偏移量:
0x00010443 - 0x00010400 = 0x43
将文件中E8下一行地址映射到内存中:
ImageBase(0x01000000) + VirtualAddress(0x00013000) + 0x43 = 0x01013043
E8后边的值:MessageBox - 0x01013043 = 0x7413581D
2.4 计算E9后边的值
MessageBox关闭后,程序能够正常运行,需要jmp到原来的OEP(AddressOfEntryPoint:0x739D)
原来OEP(真正要跳转的地址):ImageBase + AddressOfEntryPoint = 0x0100739D
将文件中E9下一行地址(0x00010448)映射到内存中:
ImageBase(0x01000000) + VirtualAddress(0x00013000) + 0x48 = 0x01013048
E9后边的值:0x0100739D - 0x01013048 = 0xFFFF4355
2.5 修改OEP(AddressOfEntryPoint)
文件中shellcode起始地址(0x00010430)相对PointerToRawData(0x00010400)偏移量:
0x00010430 - 0x00010400 = 0x30
将文件中shellcode起始地址映射到内存中:
相对ImageBase偏移:VirtualAddress(0x00013000) + 0x30 = 0x00013030
将原来的OEP(0x0000739D)修改为shellcode映射到内存后的起始地址(0x00013030)。
保存文件,双击运行:
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!