续接上回,上篇文章中 主要展示了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[i].VirtualAddress,
RVA_TO_FOA(imagebase,pNtHead->OptionalHeader.DataDirectory[i].VirtualAddress),
pNtHead->OptionalHeader.DataDirectory[i].Size,
pNtHead->OptionalHeader.DataDirectory[i].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[1].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 [None] \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[0].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[j]) {
haveName = TRUE;
char* name = (char*)(RVA_TO_FOA(imageBase, name_table[j]) + (DWORD)imageBase);
printf("%5d \t %10p \t 0x%08x \t 0x%08x \t %-35s \n",
i+exportTable->Base,
addr_table[i],
imagebase+addr_table[i],
RVA_TO_FOA(imageBase,addr_table[i]),
name
);
break;
}
}
//如果全部找完还没有名字
if (haveName == false) {
printf("%5d \t %10p \t 0x%08X \t 0x%08X \t None \n",
i + exportTable->Base,
addr_table[i],
imagebase +addr_table[i],
RVA_TO_FOA(imageBase, addr_table[i])
);
}
}
}
//重定位表
void displayRelocTable(HANDLE imageBase)
{
PIMAGE_NT_HEADERS pNtHead = GetNtHead(imageBase);
DWORD base = pNtHead->OptionalHeader.ImageBase;
DWORD relocRVA = pNtHead->OptionalHeader.DataDirectory[5].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[5].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[i].type;
//获取重定位的偏移
DWORD _offset = offset[i].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);
最后这是遍历结果