以下这些实验的方案,都是建立在理解windows loader加载机制上,能正常运行来设计。选择的实验对象,虽然是任意的程序,但要保证程序内部不会有校验文件自身完整性的代码段,避免程序自身出错信息和系统loader出错信息弄混。
【实验一】Windows 8.1,使用WinHex打开某程序,查看任意的前后紧挨着的二个节头,找到它们对应的节、将前一个节调换到后一个节后面,然后修改节头中的IMAGE_SECTION_HEADER.PointerToRawData为正确的新值。程序仍然可以正常打开运行。
实验步骤一:使用WinHex打开某程序,记录下要修改的节头
.text EA7C 1000 EC00 600
.data 50 10000 200 F200
.rdata 94A8 11000 9600 F400
实验步骤二:将文件偏移600处的EC00个字节剪切后,插入到文件偏移800处。
实验步骤三:修改二处数据,如下
.text EA7C 1000 EC00 800
.data 50 10000 200 600
.rdata 94A8 11000 9600 F400结论:经以前黑箱实验,windows loader遍历节头加载节,会进行有效性检查:Next VirtualAddress == Pre VirtualAddress + VirtualSize;经此实验,说明不会进行这样的有效性检查:后一个PointerToRawData == 前一个PointerToRawData + SizeOfRawData.
,【实验二】Windows 8.1,使用WinHex打开某程序,找到任意一个节头,将对应的节数据复制粘贴到文件最后(起址是FileAlignment的整数倍),将原位置的节数据全部填上FF,修改PointerToRawData为正确的值,修改IMAGE_OPTIONAL_HEADER.SizeOfImage为正确的值。程序仍然可以正常打开运行。
实验步骤一:使用WinHex打开某程序,记录下某节头的数据
.text EA7C 1000 EC00 600
实验步骤二:文件原尺寸是0x1EDC9F,FileAlignment是0x200,新的起址必须是FileAlignment的整数倍,取0x1EE000。
将文件偏移600处的EC00个字节复制粘贴到文件偏移1EE000处。
实验步骤三:将文件偏移600处的共EC00个字节全部填成FF。
,取
实验步骤四:修改节头中的一个数据,如下
.text EA7C 1000 1EE000 800
实验步骤五:修改IMAGE_OPTIONAL_HEADER为正确的值
结论:再次验证windows loader针对节头的有效性检查;windows loader加载PE文件,不是简单地镜像操作,硬盘文件中未用到部分会被丢弃掉(比如上面填FF的那一大段)。
【实验三】
在WinHex中打开任意一个32位程序,复制数据块,起址为IMAGE_NT_HEADERS头、终址为Min(IMAGE_SECTION_HEADER.PointerToRawData)-1;新的起址为0x10;修改文件偏移0x3C处四个字节为0x00000010。程序仍然可以正常打开运行。
程序开头原来的样子。
实验步骤一:打开程序,查找到IMAGE_NT_HEADERS在偏移0x108处,Min(IMAGE_SECTION_HEADER_HEADER.PointerToRawData)为0x400。复制数据块,从0x108到0x3FF。
实验步骤二:复制数据块0x108~0x3FF到偏移0x0C处,新的终址为0x303。
实验步骤三:修改偏移0x3C处为0x0000000C。新的程序看起来如下图这个样子。双击程序,程序正常执行。
。
结论:PE程序开头的DOS Stub大部分数据域无关紧要,除了e_magic和e_lfanew;IMAGE_OPTIONAL_HEADER中的BaseOfCode,BaseOfData也是无关紧要。
32位程序,从IMAGE_NT_HEADERS开始的头部最多可以前移到文件偏移0x0C处,64位程序,从IMAGE_NT_HEADERS64开始的头部最多可以前移到文件偏移0x10处。