PE学习(3) 代码节空白区任意添加代码

上一篇中我以手动的方式将硬编码的SHELLCODE添加到.text节空白区,并手动修改OEP地址,成功弹框。这一节将以代码的方式计算偏移以及跳转地址,自动完成之前的任务

 

填充SHELLCODE

定义需要的SHELLCODE(地址为空,后续添加)

FileBuffer拉伸转换到ImageBuffer

计算SHELLCODE长度后续使用(18 0x12)

NT头以DWORD Signature开始,通过e_lfanew得到NT头偏移,加4得到PE头,而PE头大小是固定的IMAGE_SIZEOF_FILE_HEADER,相加得到可选PE头首地址

节表偏移需要可选PE头的大小信息,可以通过两种方式获得:

计算剩余空间:使用SIzeOfRawData-Misc拿到可用的空白区

代码如下,确保空白区可以写得下硬编码的SHELLCODE

通过VirtualAddressMisc得到拉伸后ImageBuffer中可用空白区偏移

使用memcpySHELLCODE复制到对应的位置

到这里我们已经成功将写入SHELLCODEImageBuffer第一个节的空白区中

 

E8 CALL

上文提到,每台机器的MessageBox函数地址不一样,通过某些方式我找到自己机器是0x75789350

修正方式是:X = 要跳转的地址 - 下一条指令地址

注意实际跳转偏移必须用ImageBuffer计算,这是加载到内存中的数据,而FileBuffer仅是文件

目前得到的codeBegin是相对于当前pImageBuffer地址6A的偏移,使用codeBegin - pImageBuffer得到从0开始计算的6A偏移,而E9相对于6A偏移是0x0D,相加得到下一条指令从0开始的偏移

最后E8这里CALL的参数计算方式如下:

修改E8之后CALL的参数:注意这里是修改了ImageBuffer中之前占位的00(偏移9)

下一步应该修改E9JMP的参数

 

E9 JMP

目标是实现JMP AddressOfEntryPoint,参考公式X = 要跳转的地址 - 下一条指令地址

所以这里的jmpOffset计算方式如下,用相对于ImageBuffer的空白区偏移codeBegin减去ImageBuffer的地址,得到从0开始的空白区偏移地址,加上SHELLCODE长度得到下一条指令的地址(虽然为空)

使用公式得到EPJMP真正的参数

修改E9参数(E9SHELLCODE的偏移是0x0E

 

修正 OEP

现在已经实现了正确可用的SHELLCODE但还需要修改程序入口,使其跳转到空白区新增的代码,即从0开始的空白区偏移地址,也就是SHELLCODE第一个字节6A的地址,比较简单

 

写回 EXE

以上我们的操作都是在ImageBuffer中完成的,下一步需要利用之前的FileBufferImageBuffer互转代码,得到一个可用的EXE数据指针pNewBuffer,再将它保存到文件即可

 

拓展

如果代码节内不够写怎么办?虽然这种情况不太常见,但可以做一个思考

这个问题的解决办法如下,节表指针加一得到下一个节

 

如果发现修改后无法执行,可以尝试复制代码节的属性(我没有修改属性就触发了)

 

代码

FileBufferImageBuffer互转代码在上文中已经提到,所以省略

添加任意代码到节空白区的函数如下

 

读写文件代码