JackLSQ 发表于 2024-1-17 22:38

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);


```
最后这是遍历结果

turmasi1234 发表于 2024-1-18 07:39

膜拜呆佬,学习一下

mxn007 发表于 2024-1-18 07:50

,学习一下

ztqddj007 发表于 2024-1-18 08:31

感觉有点复杂

kingmaxking11 发表于 2024-1-18 09:12

学习学习,谢谢分享。

zhhayu 发表于 2024-1-18 09:25

不是一般的难

w759003376 发表于 2024-1-18 09:47

这个还是得耐心点,多次数啊,不然第一次有点难学
页: [1]
查看完整版本: pe后续结构解析