Last updated on October 7, 2023 pm
PE文件练习
移动导出表、重定位表、手动修复重定位表
回顾
导出表是告诉其他PE文件本身可以提供什么函数,并且提供名字索引和序号索引两种方式找到一个函数地址。导出表下又指向了三张新表,分别是函数地址表,序号表,名称表。
重定位表是在PE文件无法按照预计ImageBase装载时,提供所有需要修改地址的一张表。因为编译器在编译文件时会按照预计ImageBase计算所有地址,以硬编码方式生成,那么在ImageBase无法正确装载时,这些硬编码地址就需要更改,重定位表就负责提供这些硬编码地址。
现在就尝试去移动这两张表,并根据重定位表原理手动修复一下。
移动导出表
先来看看导出表的结构
1 2 3 4 5 6 7 8 9 10 11 12 13
| typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; DWORD AddressOfNames; DWORD AddressOfNameOrdinals; } IMAGE_EXPORT_DIRECTORY, * PIMAGE_EXPORT_DIRECTORY;
|
其中 AddressOfFunctions、AddressOfNames、AddressOfNameOrdinals对应三张表的地址。
我们目标就是把这三张表移动一个新的节区,再把导出表结构放到这三张表后面。
可以分为以下几个步骤来移动
1.创建一个新的节区
2.将函数地址表,即AddressOfFunctions对应的表移到新节区开头,并将原AddressOfFunctions进行修正
3.将函数序号表,即AddressOfNameOrdinals对应的表接续移到新节区,并将原AddressOfNameOrdinals进行修正
4.将函数名称表,即AddressOfNames对应的表接续移到新节区,并将原AddressOfNames进行修正
5.将函数名称表对应的所有名字接续移到新节区,并把函数名称表里地址进行修正
6.将整个导出表接续移到新节区,并把原来的Directory目录项进行修正
NOTE:
1.需要主要各种地址之间的转化,是RVA还是FOA
2.需要主要各种指针类型之间的转化,这在指针运算中十分重要
3.可以尝试使用新移动后的dll,验证是否成功移动
代码
代码中会有详细注释,仅给出移动导出表函数实现,其余函数已在其余文章中有实现,不再重复给出。
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| char* Move_ExportTable(char* Buffer){ PIMAGE_DOS_HEADER DosHeader = NULL; PIMAGE_NT_HEADERS NTHeader = NULL; PIMAGE_FILE_HEADER FILEHeader = NULL; PIMAGE_OPTIONAL_HEADER64 OptionalHeader = NULL; PIMAGE_SECTION_HEADER SectionHeader = NULL; PIMAGE_SECTION_HEADER New_SectionHeader = NULL;
DosHeader = (PIMAGE_DOS_HEADER)Buffer; FILEHeader = (PIMAGE_FILE_HEADER)(Buffer + DosHeader->e_lfanew + 4); OptionalHeader = (PIMAGE_OPTIONAL_HEADER64)(Buffer + DosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER); SectionHeader = (PIMAGE_SECTION_HEADER)(Buffer + DosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER + FILEHeader->SizeOfOptionalHeader); New_SectionHeader=(PIMAGE_SECTION_HEADER)(Buffer + DosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER +IMAGE_SIZEOF_SECTION_HEADER*(FILEHeader->NumberOfSections-1) + FILEHeader->SizeOfOptionalHeader); DWORD Export_DATA_RVA = OptionalHeader->DataDirectory[0].VirtualAddress; DWORD Export_DATA_FOA = RVA_TO_FOA(Export_DATA_RVA, Buffer); PIMAGE_EXPORT_DIRECTORY Export_Table = (PIMAGE_EXPORT_DIRECTORY)(Export_DATA_FOA + Buffer);
DWORD index = 0; char* New_Sec = New_SectionHeader->PointerToRawData + Buffer;
char* AddressOfFunctions = Buffer + RVA_TO_FOA(Export_Table->AddressOfFunctions,Buffer); memcpy(New_Sec, AddressOfFunctions, Export_Table->NumberOfFunctions * 4); Export_Table->AddressOfFunctions = New_SectionHeader->VirtualAddress + index; index += Export_Table->NumberOfFunctions * 4; char* AddressOfOrdinals = Buffer + RVA_TO_FOA(Export_Table->AddressOfNameOrdinals,Buffer);
memcpy(New_Sec + index, AddressOfOrdinals, Export_Table->NumberOfNames * 2); Export_Table->AddressOfNameOrdinals = New_SectionHeader->VirtualAddress + index; index += Export_Table->NumberOfNames * 2;
char* AddressOfNames = Buffer + RVA_TO_FOA(Export_Table->AddressOfNames,Buffer); char* AddressOfNames_FIX = New_Sec + index; Export_Table->AddressOfNames = New_SectionHeader->VirtualAddress + index; index += Export_Table->NumberOfNames * 4; for(int i = 0;i < Export_Table->NumberOfNames;i++){ char* name = Buffer + RVA_TO_FOA(*((DWORD*)AddressOfNames + i),Buffer); int len = strlen(name); memcpy(New_Sec + index,name,len+1); *((DWORD*)AddressOfNames_FIX + i) = FOA_TO_RVA((New_Sec + index) - Buffer,Buffer); index += len + 1; } int Size_Of_Exportable = sizeof(IMAGE_EXPORT_DIRECTORY); memcpy(New_Sec + index,Export_Table,Size_Of_Exportable); OptionalHeader->DataDirectory[0].VirtualAddress = New_SectionHeader->VirtualAddress + index; Export_Table_INFO(Buffer); return Buffer; }
|
移动重定位表
重定位表结构比导出表结构简单许多,只需要把目录项的VirtualAddress指向新节区,并把重定位表数据copy到新节区即可
代码
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 34 35 36 37 38
| char* Move_RelocTable(char* Buffer){ PIMAGE_DOS_HEADER DosHeader = NULL; PIMAGE_NT_HEADERS NTHeader = NULL; PIMAGE_FILE_HEADER FILEHeader = NULL; PIMAGE_OPTIONAL_HEADER64 OptionalHeader = NULL; PIMAGE_SECTION_HEADER SectionHeader = NULL; PIMAGE_SECTION_HEADER New_SectionHeader = NULL;
DosHeader = (PIMAGE_DOS_HEADER)Buffer; FILEHeader = (PIMAGE_FILE_HEADER)(Buffer + DosHeader->e_lfanew + 4); OptionalHeader = (PIMAGE_OPTIONAL_HEADER64)(Buffer + DosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER); SectionHeader = (PIMAGE_SECTION_HEADER)(Buffer + DosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER + FILEHeader->SizeOfOptionalHeader); New_SectionHeader=(PIMAGE_SECTION_HEADER)(Buffer + DosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER +IMAGE_SIZEOF_SECTION_HEADER*(FILEHeader->NumberOfSections-1) + FILEHeader->SizeOfOptionalHeader);
DWORD Reloc_RVA = OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; DWORD Reloc_FOA = RVA_TO_FOA(Reloc_RVA,Buffer); PIMAGE_BASE_RELOCATION Reloc_Table = (PIMAGE_BASE_RELOCATION)(Buffer + Reloc_FOA); DWORD index = 0; while(Reloc_Table->VirtualAddress && Reloc_Table->SizeOfBlock){ memcpy(New_SectionHeader->PointerToRawData + Buffer + index,Reloc_Table,Reloc_Table->SizeOfBlock); index += Reloc_Table->SizeOfBlock; Reloc_Table = (PIMAGE_BASE_RELOCATION) (((byte*)(Reloc_Table) + Reloc_Table->SizeOfBlock)); } OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = New_SectionHeader->VirtualAddress;
return Buffer; }
|
修复重定位表
前面提及,重定位表是在PE文件无法按照语句ImageBase装载时发挥作用,那么我们就可以手动来改一改PE文件的ImageBase然后自己来修复那些地址。
比如说一个重定位表的VisualAddress是0x1000,它其中一个数据项有效值是0x12,那么就说明RVA0x1012处有一个需要修复的地址,需要根据实际ImageBase来重新计算。
那么我们这里修改了ImageBase,就相当于模拟了无法按照ImageBase来装载,我们就得按照重定位表去找那些需要修改的地址,来重新计算。比如我们给ImageBase增加0x100000,那么所有的那些需要修改的地址都一起增加0x100000
NOTE:重定位表是记录需要修改地址的表,其本身是不需要修改的。
代码
INCREATEMENT是我定义的一个宏,作为ImageBase修改的值
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 34 35 36 37 38 39 40 41 42 43 44
| void Reloc_Fix(char* Buffer){ PIMAGE_DOS_HEADER DosHeader = NULL; PIMAGE_NT_HEADERS NTHeader = NULL; PIMAGE_FILE_HEADER FILEHeader = NULL; PIMAGE_OPTIONAL_HEADER64 OptionalHeader = NULL; PIMAGE_SECTION_HEADER SectionHeader = NULL;
DosHeader = (PIMAGE_DOS_HEADER)Buffer; FILEHeader = (PIMAGE_FILE_HEADER)(Buffer + DosHeader->e_lfanew + 4); OptionalHeader = (PIMAGE_OPTIONAL_HEADER64)(Buffer + DosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER); SectionHeader = (PIMAGE_SECTION_HEADER)(Buffer + DosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER + FILEHeader->SizeOfOptionalHeader);
DWORD Reloc_RVA = OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; DWORD Reloc_FOA = RVA_TO_FOA(Reloc_RVA,Buffer); PIMAGE_BASE_RELOCATION Reloc_Table = (PIMAGE_BASE_RELOCATION)(Buffer + Reloc_FOA); OptionalHeader->ImageBase =OptionalHeader->ImageBase + INCREATIMENT;
while(Reloc_Table->VirtualAddress && Reloc_Table->SizeOfBlock){ WORD* Reloc_Add = (WORD*)((char*)Reloc_Table + 8); DWORD RealSize =( Reloc_Table->SizeOfBlock - 8) / 2;
for(int i = 0;i < RealSize ; i++){ DWORD Offset = (*(Reloc_Add + i) )& 0x0fff; DWORD Base = Reloc_Table->VirtualAddress; DWORD* Addr = (DWORD*)(RVA_TO_FOA(Base + Offset,Buffer) + Buffer); printf("Beferoe: %x ",*Addr); *FuncAddr =*Addr + INCREATIMENT; printf("After: %x\n",*Addr); } Reloc_Table =(PIMAGE_BASE_RELOCATION) ((char*)Reloc_Table + Reloc_Table->SizeOfBlock); } }
|