解决什么问题:
解决功能性shellcode开发中的某些限制:不能使用静态变量,不能使用全局变量,不能使用字符串,不能使用虚类。
PE2Shellcode解决方案可以缓解shellcode开发中的这些限制,让shellcode开发能够更为快速方便。
不能解决什么问题:
PE文件中导入表未被处理,使用者要么进行二次开发,在shellcode前处理导入函数,要么在开发前消除PE文件的导入表。
CRT(C runtime)函数系列.大量的C语言标准库函数都会在其内部分配和释放内存,这会破坏shellcode的内存环境,所有的CRT函数要么自行重写,要么不再使用。
原理:
shellcode开发中不能使用静态变量,全局变量和字符串等等限制的原因,是这些代码在生成机器码的过程中,没有值写入,只有地址写入。在PE文件加载的时候,Windows的PE加载器再重新对这些数据地址进行重写。
这个小工具的原理就是在提取PE的shellcode基础上,再对shellcode进行重定位处理。
说明:
仅适用于32位程序。
没有测试过图像界面,不确定资源段内容是否能够被释放。
代码中的静态变量、全局变量、字符串地址在下面的文档中都被称为全局变量——从shellcode的视角来看,他们都是同一类代码。
基础:
PE文件结构。
《加密解密》
http://drops.wooyun.org/tips/8361
python基础
pefile库
第0步:PE文件的要求
- 重载全局的new和delete操作符,使用HeapAlloc函数替换
- 不能使用异常,包括c++异常和Windows的SEH.
- 不能使用运行时分配内存的全局变量。所有的全局变量都要在编译时确定值。
- 不能包含自定义区段
- 编译选项:
- /GS- 禁止栈保护
- no c++ exception,关闭C++异常
- /Gy,函数级链接
- /Oi,意义不明
- /O1,最小化代码
- /MT,运行时库
- /arch:IA32 仅使用I686指令集
- 链接选项:
- No Debug Info
- /SAFESEH:NO
- Function Order:order.txt 链接时要求将入口函数放在.text段的开始部分
- /MAP:map.txt map文件用于查看二进制文件的地址和符号表
- /OPT:REF
- /OPT:ICF 以上两项原理不明
第一步:处理PE段(SECTION)表(sections_list函数)
PE重定位表指向的数据可能在PE的任何一个段上,因此第一步就是要处理段表。
将所有的段表放入all_sections中,保存了段表的以下信息:数据,磁盘文件长度,内存长度。
PE段表信息中的内存长度比较特殊,它是一个被我们计算出来的值。指该段在Shellcode内存中的长度。
段表长度会和未初始化的全局变量和静态变量相关,因此需要手动确认长度。
段表的section.SizeOfRawData指明了磁盘文件长度对齐内存后的长度。section.VirtualAddress则指明了该段未对齐内存时的长度。我们取两者较大的值作为该段在Shellcode的长度。
第二步:将段表合成一个段数列(sections_list),并记录其在段数列的偏移(sections_list函数)
将.text段放在最前面,保证shellcode在运行是能够运行到我们的目标代码,然后依次将其他段放在后面。
每个段在sections_list的长度是按照其['mem_size']来确定的。对于长度不够的情况,在后面添加\x00.如前所述,这是为了保证未初始化的全局变量也在内存中有一席之地。
第三步:重写将PE的导入函数地址
在实际的应用中,建议在生成PE文件前,在代码中消除导入表。使用汇编动态加载导入表比较困难。
第四步:获得重定位表信息(reloc_process函数)
这是整个工具核心的一部。首先看看PE的重定位信息是如何工作的:
图中.RELOC段有两个项目,第一个项目062h表示重定位项目在文件偏移。它指向的位置是一个4字节的地址,
0x402002。这个地址表示PE在使用基地址加载的情况下,需要重定位数据的内存地址。所有的重定位信息都只有地址没有长度,重定位的长度都是8字节(DWORD)。
如果PE没有在基地址加载,这个地址就要减去基地址得到rva,然后加上目前的基地址,获得运行时的新地址。
pefile库已经帮我们把.RELOC的项目地址转为了rva地址,可以直接读取而不需要额外的转换。
我们的目标是将reloc信息转为下面类似的形式:
这样就很明确了,这个函数需要把reloc段中的信息转为sections_list的偏移量,并把重定位的数据地址减去IMAGE_BASE.
首先是获得重定位项在Sections_list的偏移量:将reloc指向的va减去IMAGEBASE得到rva.找到该rva属于哪个区段,然后rva-区段start+区段在sections_list的起始偏移。
另一个需要获得的是重定位项所指的内存在sections_list的偏移量。重定位项目一定指示了内存中的某些东西,可能是值,可能是字符串。在PE结构被重新组织到sections_list的时候,这个地址在sections_list的偏移量也需要重写。获得地址的方式同上。
第五部:根据重定位表信息生成shellcode
生成shellcode需要几部分内容:sections_list,重定位项目在sections_list的偏移,重定位项指示的位置在sections_list的新位置。
在sections_list中,将重定位项目指示的位置的数据逐个写到对应的位置。
shellcode的前面是一串loader
shellcode loader
code = [
0xE8, 0x00, 0x00, 0x00, 0x00, # CALL here
# here:
0x5E, # POP ESI
0x8B, 0xFE, # MOV EDI, ESI
0x81, 0xC6, reloc_start_offset[0], reloc_start_offset[1], reloc_start_offset[2], reloc_start_offset[3], # ADD ESI, shellcode_start + len(shellcode) - here
0xB9, reloc_size[0], reloc_size[1], reloc_size[2], reloc_size[3], # MOV ECX, len(relocs)
0xFC, # CLD
# again:
0xAD, # LODSD
0x01, 0x3C, 0x07, # ADD [EDI+EAX], EDI
0xE2, 0xFA # LOOP again
# shellcode_start:
];
shellcode loader是shellcode运行前的一段代码,负责从Shellcode尾部的重定位列表中,逐项将shellcode的重定位数据重写。
shellcode的尾部是重定位项目的偏移量。
整个shellcode看起来是这样的:
|--loader--|----target code---|---reloc item list from begin--|
使用shellcode测试程序可以尝试运行该shellcode.
https://github.com/after1990s/pe2shellcode
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法