逆核系列No.3--节区删除

本节内容

EXE格式的PE文件中,基址重定位表对运行没有影响,实际上将其删除后文件仍可正常运行,本节在于删除示例程序reloc.exe的.reloc节区并完成PE格式相关字段的修正使得删除后文件仍可正常运行。

(PS:基址重定位表对于DLL/SYS形式的可执行文件来说几乎是必须的,不可作为本节的实例文件)


正文

删除节区的步骤

1⃣️:NOP填充节区头IMAGE_SECTION_HEADER(使之无效,因此不会出现PE装载器装载时出现PE不合法的情况)

2⃣️:删除整个节区内容

3⃣️:修正节区数(节区头数量决定,由于NOP掉了一个因此-1),修正Image size(删除了节区的内容,注意节区大小对齐问题)

PS:目前不清楚步骤的看完后文估计能有很好的了解。

_IMAGE_SECTION_HEADER

节区头的结构体是_IMAGE_SECTION_HEADER

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData; //后续会删除整个节区因此指示节区起始的位置需要做变动
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

文件在PEView中:

可见.reloc节区头信息起始地址(RVA)= 0x270到节区头结构体最后一个成员characteristic起始地址(RVA)为0x294,由于characteristic成员值占4byte,故节区头的大小是28(294-270+4)。

另外删除节区是在文件中删除以及修改文件的,因此需要计算出相应的信息在文件中的偏移,这个过程需要用到的是目标节区头中的Pointer to Raw data字段,记录的是节区起始文件偏移。这里是0xC000

NULL覆盖.reloc节区头信息
删除节区.reloc的内容

上文提到IMAGE_SECTION_HEADER中字段Pointer To Raw Data可知节区起始文件偏移是0xC000,这里由于节区.reloc是PE文件的最后一个节区,因此删除从此处开始到文件末的内容

工具也提示了会改变文件大小,因此后续需要做的工作是修改相应的字段保持PE格式的完整


修改NumberOfSections
1
2
3
4
5
6
7
8
9
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections; //做相应的节数-1
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

需要修改的是==节区数 -1==, 即对应位置要修正5个节区为4个节区,下图在IMAGE_FILE_HEADER中的NumberOfSection字段在文件偏移为0xDE

完成节区数修改


修改SizeOfImage

删除节区内容后的PE文件会变小,需要修正PE文件中的相应字段,使得PE装载器在装载的过程中没有出现差错。

从上文提到的.reloc节区信息中得到该节区的vitual size为0xE40,但由于存在节区对齐的机制,相应的节区大小会做对齐处理,具体对齐格式在IMAGE_OPTIONAL_HEADER中有字段进行记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment; //节区对齐方式
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

这也是为什么我们在看到节区信息时看到的vitual size是0xE40而Raw Size是0x1000的原因

故需要将==IMAGE_OPTIONAL_HEADER的size of image成员==的值减去 0x1000 进行修改,

该字段值在文件偏移是0x128修改前:

修改后:

将文件进行保存,看是否可以正常启动,可以则说明删除没有伤害到其他运行必要的数据(p s: .bak是修改前的,可以看到文件大小缩小了4KB,且程序可以正常运行,修改成功)

Author: Victory+
Link: https://cvjark.github.io/2022/04/28/逆核系列No.3--节区删除/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.