pe后续结构解析
续接上回,上篇文章中 主要展示了PE结构中的 DOS_HEAD,NT_FILE_HEAD,NT_OPTIONAL_HEAD,Section_head 中部分重要成员数据。这篇文章将介绍上篇文件中说到但是未详细介绍的部分。比如数据目录项中的导入表,导出表,重定位表。# 数据目录项
在该项表中,这是一个**_IMAGE_DATA_DIRECTORY** 结构体,该结构体的组成如以下所示。
```
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
```
VirtualAddress 代表着对应序号在内存中的 虚拟地址,如果想正确的解析到对应项表的数据,需要将虚拟地址转化为文件地址
即 VA-->FOA: 这个是有个公式,公式如下所示:VA--->RVA-->FOA
VA--->RVA: RVA=VA-节首地址
RVA--->FOA:FOA=RVA+该VA地址节对应到文件中的偏移
在该数据项中一共有16个表
| 表名|表序号 | Column 3 |
| -------- | -------- | -------- |
|IMAGE_DIRECTORY_ENTRY_EXPORT | 0|导出表|
|IMAGE_DIRECTORY_ENTRY_IMPORT | 1|导入表|
|IMAGE_DIRECTORY_ENTRY_RESOURCE | 2|资源表|
|IMAGE_DIRECTORY_ENTRY_EXCEPTION | 3|异常|
|IMAGE_DIRECTORY_ENTRY_SECURITY | 4|安全|
|IMAGE_DIRECTORY_ENTRY_BASERELOC | 5|重定位|
|IMAGE_DIRECTORY_ENTRY_DEBUG | 6|调试信息|
|IMAGE_DIRECTORY_ENTRY_COPYRIGHT | 7|(X86 usage) 所有者 x86还在用|
|IMAGE_DIRECTORY_ENTRY_ARCHITECTURE | 7|版权信息|
|IMAGE_DIRECTORY_ENTRY_GLOBALPTR | 8|RVA of GP|
|IMAGE_DIRECTORY_ENTRY_TLS | 9|TLS Directory|
|IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG |10|Load Configuration Directory|
|IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT |11|Bound Import Directory in headers|
|IMAGE_DIRECTORY_ENTRY_IAT |12|导入函数地址|
|IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT |13|Delay Load Import Descriptors|
|IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR |14|COM Runtime descriptor|
下面就是遍历上述中 ,重要表项的代码
```
void displayDataDirectory(HANDLE imagebase)
{
//遍历 DataDirectory
PIMAGE_NT_HEADERS pNtHead = NULL;
pNtHead = GetNtHead(imagebase);
int data_size=pNtHead->OptionalHeader.NumberOfRvaAndSizes;
printf("编号 \t 目录RVA \t 目录FOA \t Size长度(十进制) \t Size长度(16进制) \t 功能描述\n");
for (int i = 0; i < data_size; i++) {
printf("%03d \t 0x%08X \t 0x%08X \t %08d \t\t 0x%08X\t\t",
i + 1,
pNtHead->OptionalHeader.DataDirectory.VirtualAddress,
RVA_TO_FOA(imagebase,pNtHead->OptionalHeader.DataDirectory.VirtualAddress),
pNtHead->OptionalHeader.DataDirectory.Size,
pNtHead->OptionalHeader.DataDirectory.Size
);
switch (i)
{
case 0:
printf("Export sysmbols\n");
break;
case 1:
printf("Import sysmbols\n");
break;
case 2:
printf("Resoucres\n");
break;
case 3:
printf("Exception\n");
break;
case 4:
printf("Security\n");
break;
case 5:
printf("base relocation\n");
break;
case 6:
printf("Debug\n");
break;
case 7:
printf("Copyright string \n");
break;
case 8:
printf("Globalptr \n");
break;
case 9:
printf("Thread loacl storage(TLS)\n");
break;
case 10:
printf("Local Configuration\n");
break;
case 11:
printf("Bound Import \n");
break;
case 12:
printf("import address Table\n");
break;
case 13:
printf("Delay Import\n");
break;
case 14:
printf("COM descriptor\n");
break;
case 15:
printf("NoUse\n");
break;
default:printf("none\n");
break;
}
}
}
//遍历导入表
void displayImportTable(HANDLE imageBase)
{
PIMAGE_NT_HEADERS pNtHead = GetNtHead(imageBase);
//获得导入表的RVA地址
DWORD importTableRVA=pNtHead->OptionalHeader.DataDirectory.VirtualAddress;
auto importTable = (PIMAGE_IMPORT_DESCRIPTOR)(RVA_TO_FOA(imageBase, importTableRVA) + (DWORD)imageBase);
while (importTable->Name) {
char* dllName = (char*)(RVA_TO_FOA(imageBase, importTable->Name) + (DWORD)imageBase);
printf("Hint 值\t\t API序号 \t 文件RVA \t VA地址 \t 函数名称 \t 模块:[ %s ] \n", dllName);
auto Iat = (PIMAGE_THUNK_DATA)(RVA_TO_FOA(imageBase, importTable->FirstThunk) + (DWORD)imageBase);
auto Int=(PIMAGE_THUNK_DATA)(RVA_TO_FOA(imageBase, importTable->OriginalFirstThunk) + (DWORD)imageBase);
//遍历IAT表
while (Iat->u1.Ordinal != 0) {
//判断是否有名字
if (Iat->u1.AddressOfData & 0x80000000) {
// 序号导入,直接输出
printf("[%5d] \t \n", LOWORD(Iat->u1.AddressOfData));
}
else {
auto name = (PIMAGE_IMPORT_BY_NAME)(RVA_TO_FOA(imageBase, Iat->u1.AddressOfData) + (DWORD)imageBase);
DWORD imagebase = pNtHead->OptionalHeader.ImageBase;
DWORD VA = Iat->u1.AddressOfData + imagebase;
printf("[%5d] \t %09d \t %08x \t %08x \t %s \n",
name->Hint,
Iat->u1.Ordinal,
RVA_TO_FOA(imageBase,Iat->u1.AddressOfData),
VA,
name->Name
);
}
Iat++;
}
importTable++;
printf("\n");
}
}
//导出表项只有在dll中存在
void displayExportTable(HANDLE imageBase)
{
PIMAGE_NT_HEADERS pNtHead = GetNtHead(imageBase);
DWORD imagebase = pNtHead->OptionalHeader.ImageBase;
//获得导出表的RVA地址
DWORD RVA = pNtHead->OptionalHeader.DataDirectory.VirtualAddress;
auto exportTable = (PIMAGE_EXPORT_DIRECTORY)(RVA_TO_FOA(imageBase, RVA) + (DWORD)imageBase);
//获取有名字的函数个数和函数总个数
DWORD nameCount = exportTable->NumberOfNames;
DWORD funcCount = exportTable->NumberOfFunctions;
//获取三张表 地址表 名称表 序号表
DWORD* addr_table = (DWORD*)(RVA_TO_FOA(imageBase, exportTable->AddressOfFunctions) + (DWORD)imageBase);
DWORD* name_table = (DWORD*)(RVA_TO_FOA(imageBase, exportTable->AddressOfNames) + (DWORD)imageBase);
WORD* id_table = (WORD*)(RVA_TO_FOA(imageBase, exportTable->AddressOfNameOrdinals) + (DWORD)imageBase);
printf("序号 \t 导出RVA地址 \t 导出VA地址 \t 导出FOA地址 \t 导出函数 \t \n");
//遍历地址表
for (DWORD i = 0; i < funcCount; i++) {
bool haveName = false;
for (DWORD j = 0; j < nameCount; j++) {
if (i == id_table) {
haveName = TRUE;
char* name = (char*)(RVA_TO_FOA(imageBase, name_table) + (DWORD)imageBase);
printf("%5d \t %10p \t 0x%08x \t 0x%08x \t %-35s \n",
i+exportTable->Base,
addr_table,
imagebase+addr_table,
RVA_TO_FOA(imageBase,addr_table),
name
);
break;
}
}
//如果全部找完还没有名字
if (haveName == false) {
printf("%5d \t %10p \t 0x%08X \t 0x%08X \t None \n",
i + exportTable->Base,
addr_table,
imagebase +addr_table,
RVA_TO_FOA(imageBase, addr_table)
);
}
}
}
//重定位表
void displayRelocTable(HANDLE imageBase)
{
PIMAGE_NT_HEADERS pNtHead = GetNtHead(imageBase);
DWORD base = pNtHead->OptionalHeader.ImageBase;
DWORD relocRVA = pNtHead->OptionalHeader.DataDirectory.VirtualAddress;
PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)((DWORD)imageBase + RVA_TO_FOA(imageBase, relocRVA));
printf("映像基址: %08X 虚拟偏移: %08X 重定位表基址: %p \n",base,relocRVA,reloc);
while (reloc->SizeOfBlock != 0) {
DWORD Size = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
printf("起始RVA: %08X \t 块长度: %04d \t 重定位个数:%04d \n",
reloc->VirtualAddress,
reloc->SizeOfBlock,
Size
);
reloc = (PIMAGE_BASE_RELOCATION)(reloc->SizeOfBlock + (DWORD)reloc);
}
}
//重定位表详细信息
void displayRelocTableDetail(HANDLE imageBase)
{
PIMAGE_NT_HEADERS pNtHead = GetNtHead(imageBase);
DWORD base = pNtHead->OptionalHeader.ImageBase;
DWORD relocRVA = pNtHead->OptionalHeader.DataDirectory.VirtualAddress;
PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)((DWORD)imageBase + RVA_TO_FOA(imageBase, relocRVA));
//printf("映像基址: %08X 虚拟偏移: %08X 重定位表基址: %p \n", base, relocRVA, reloc);
printf("起始RVA \t 类型 \t 重定位RVA \t 重定位地址 \t 修正RVA \n");
//遍历重定位表中的重定位块 ,以0结尾
while (reloc->SizeOfBlock != 0) {
//找到重定位项
auto offset = (TypeOffset*)(reloc + 1);
//计算重定位项数目
DWORD Size = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
//遍历所有的重定位项
for (DWORD i = 0; i < Size; i++) {
//获取重定位类型
DWORD type = offset.type;
//获取重定位的偏移
DWORD _offset = offset.offset;
// 获取要重定位的地址所在的RVA: offset+virtualaddress
DWORD rva = _offset + reloc->VirtualAddress;
// 获取要重定位的地址所在的FOA
DWORD foa = RVA_TO_FOA(imageBase, rva);
// 获取要重定位的地址所在的fa
DWORD fa = foa + (DWORD)imageBase;
// 获取要重定位的地址
DWORD addr = *(DWORD*)fa;
// 计算重定位后的数据: addr - oldbase + newbase
DWORD new_addr = addr - base;
printf("%08X \t %d \t %08X \t %08X \t%08X \n", reloc->VirtualAddress, type, rva, addr, new_addr);
}
// 找到下一个重定位块
reloc = (PIMAGE_BASE_RELOCATION)(reloc->SizeOfBlock + (DWORD)reloc);
}
}
//使用方式
HANDLE lpMapAddress = NULL;
wchar_t exePath[] = L"./x32dbg.exe";
lpMapAddress = OpenPeByName(exePath);
printf("================================================\n");
printf("Nt Optional head Data Directory info:\n");
displayDataDirectory(lpMapAddress);
printf("================================================\n");
printf("displayImportTable info:\n");
displayImportTable(lpMapAddress);
printf("================================================\n");
printf("displayRelocTable info:\n");
displayRelocTable(lpMapAddress);
printf("================================================\n");
printf("displayRelocTableDetail info:\n");
displayRelocTableDetail(lpMapAddress);
```
最后这是遍历结果
膜拜呆佬,学习一下 ,学习一下 感觉有点复杂 学习学习,谢谢分享。 不是一般的难 这个还是得耐心点,多次数啊,不然第一次有点难学
页:
[1]