-
-
[原创]UPack壳的PE头部分析
-
发表于: 2025-9-15 21:52 776
-

根据PE文件结构标准,DOS头在执行过程中需要检查MZ签名和根据e_lfanew字段定位NT头,因此无论中间的值如何奇怪,可以定位到NT头在地址00 00 00 10处。在upack打包程序中,upack通过修改中间字段使DOS头和NT头混合,e_lfanew字段同时也作为IMAGE_OPTIONAL_HEADER中的BaseofCode使用。
接下来说一下常见的关注点,但在本例中可能不涉及。
NT文件头的SizeOfOptionalHeader(可选头大小)定义了标准头结束和节区表开始的位置。当SizeOfOptionalHeader被修改时,程序能够正常执行(其实是有时候可以,可以参考我写的另外一篇Windows加载文件全流程文档),但对于很多分析器像调试器、文件解析工具、反汇编器等将造成影响。修改目的大部分是为了反调试,但也有一些版本的upack壳中在此处扩展字段来插入解码代码。
可选头的NumberOfRvaAndSizes(数据目录项数),通常值为16(0x10),修改后可能干扰工具解析文件,运行时不受影响,但对于工具来说就是分析数据目录项数的解析标杆,可能出现无意义值或者显示不全。我在未打包程序上测试的可修改后正常运行的最小值为0x06。当修改值小于16时,后面的条目将无法被读取,变成程序加载时读取不到的地方。
严格根据e_lfanew + 4(签名) + 20(COFF头) + SizeofOptionalHeader计算节区头的起始位置,本例中计算得到10H + 4H + 14H + 148H = 170H,跳转至170地址处就是节区头。

upack打包程序有三个节区头,根据信息可以知道一些关键信息:
节区 | SizeOfRawData | PointerToRawData | VirtualAdress | VirtualSize |
1 | 0x000001F0 | 0x00000010 | 0x00001000 | 0x0000F000 |
2 | 0x00005358 | 0x00000200 | 0x00010000 | 0x0000A000 |
3 | 0x000001F0 | 0x00000010 | 0x0001A000 | 0x00001000 |
在磁盘中upack打包程序的第1、3个节区是重合的,但是在内存中的VirtualAress和VirtualSize却是不同的,说明磁盘中的某个区域被映射了两次,在映射之初肯定是完全相同的,但是在内存中随着程序运行这两个节区一定会发生变化。
接下来查看程序的入口,几乎所有壳都会将原始代码跳转处作为壳代码,将真正的OEP藏在壳代码内部。AddressOfEntryPoint(RVA) = 0x00001018,计算出RAW = 0x18(要考虑FileAlinment影响,PointerToRawData = 10时,取整得0)
0x18地址处的代码片段为:
BE B0 11 40 00 mov esi, 0x004011B0 ; 将地址 0x4011B0 加载到 ESI 寄存器
AD lodsd ; 从 [ESI] 加载双字到 EAX,然后 ESI+4
50 push eax ; 将 EAX 的值压入栈
FF 76 34 push dword ptr [esi+34h] ; 将 [ESI+0x34] 处的双字压入栈
...
这里就是解壳代码。
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!