fz12 发表于 2022-5-5 11:17

快速查询PE文件知识点和PE文件解析(下)

本帖最后由 fz12 于 2022-5-5 11:21 编辑

延迟加载导入表
延迟加载导入表(Delay Load Import Table)是PE中引入的专门用来描述与动态链接库延迟加载相关的数据,因为这些数据所起的作用和结构与导入表数据基本一致,所以称为延迟加载导入表。延迟加载导入的概念:系统开始运行程序时被指定的延迟加载的 DLL是不被载入的,只有等到程序调用了该动态链接库的函数时,系统才将该链接库载入内存空间,并执行相关函数代码
typedef struct _IMAGE_DELAYLOAD_DESCRIPTOR {
    union {
      DWORD AllAttributes; // 属性,必须为0
      struct {
            DWORD RvaBased : 1;             // Delay load version 2
            DWORD ReservedAttributes : 31;
      } DUMMYSTRUCTNAME;
    } Attributes;

    DWORD DllNameRVA;                     // 指向DLL名称的RVA
    DWORD ModuleHandleRVA;                  // DLL模块句柄的RVA
    DWORD ImportAddressTableRVA;            // 延迟加载导入IAT的RVA
    DWORD ImportNameTableRVA;               // 延迟加载导入INT的RVA
    DWORD BoundImportAddressTableRVA;       // 绑定延迟加载导入表的RVA
    DWORD UnloadInformationTableRVA;      // 卸载延迟加载导入地址表的RVA
    DWORD TimeDateStamp;                  // 此映像绑定到DLL的时间戳

} IMAGE_DELAYLOAD_DESCRIPTOR, *PIMAGE_DELAYLOAD_DESCRIPTOR;

typedef const IMAGE_DELAYLOAD_DESCRIPTOR *PCIMAGE_DELAYLOAD_DESCRIPTOR;
延迟导入表解析
#include<Windows.h>
#include<iostream>
using namespace std;

DWORD RvaToFoa(_In_ DWORD rva, _In_ PIMAGE_SECTION_HEADER p, _In_ PIMAGE_FILE_HEADER f)
{
      for (int i = 0; i < f->NumberOfSections; i++)
      {
                // FOA = 数据的RVA + 区段的RVA - 区段的FOA
                if (rva >= p->VirtualAddress && rva < (p->VirtualAddress + p->Misc.VirtualSize))
                {
                        return rva - p->VirtualAddress + p->PointerToRawData;
                }
                f++;
                p++;
      }
      return 0;
}

void ImageNtHeader(_In_z_ const char* path)
{
      // 获取文件对象
      HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
      // 获取文件大小
      DWORD fSize = GetFileSize(hfile, NULL);
      char* pBuff = new char;
      DWORD dwReadSize = 0;
      // 读文件
      BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
      if (bSuccess)
      {
                PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
                PIMAGE_NT_HEADERS32 pNtHeader{ 0 };
                pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + pBuff);
                PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
                PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&pNtHeader->OptionalHeader;
                PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
                PIMAGE_DATA_DIRECTORY dataDirectory = (PIMAGE_DATA_DIRECTORY)&pOptionalHeader->DataDirectory;
                //填充延迟导入表数据结构
                PIMAGE_DELAYLOAD_DESCRIPTOR pDelayLoad = (PIMAGE_DELAYLOAD_DESCRIPTOR)(RvaToFoa(dataDirectory->VirtualAddress, pSectionHeader, pFileHeader) + pBuff);
                while (pDelayLoad->DllNameRVA)
                {
                        char* szDllName = (char*)(RvaToFoa(pDelayLoad->DllNameRVA, pSectionHeader, pFileHeader) + pBuff);
                        cout << "DllName:" << szDllName << endl;
                        cout << "AllAttributes:" << pDelayLoad->Attributes.AllAttributes << endl;
                        cout << "RvaBased:" << pDelayLoad->Attributes.RvaBased << endl;
                        cout << "ModuleHandleRVA:" << pDelayLoad->ModuleHandleRVA << endl;
                        cout << "ImportAddressTableRVA:" << pDelayLoad->ImportAddressTableRVA << endl;
                        cout << "ImportNameTableRVA:" << pDelayLoad->ImportNameTableRVA << endl;
                        cout << "BoundImportAddressTableRVA:" << pDelayLoad->BoundImportAddressTableRVA << endl;
                        cout << "UnloadInformationTableRVA:" << pDelayLoad->UnloadInformationTableRVA << endl;
                        cout << "TimeDateStamp:" << pDelayLoad->TimeDateStamp << endl;
                        pDelayLoad++;
                }
      }
      else (cout.write("打开文件失败", 20));
      CloseHandle(hfile);
      delete[] pBuff;
}

void main()
{
      ImageNtHeader(R"(C:\Users\11981\Desktop\Project1\2.exe)");
}
TLS表
使用线程本地存储器(TLS)可以将数据与执行的特定线程联系起来。当使用_declspec(thread)声明的TLS 变量时,编译器将它们放入一个.tls 区块。当应用程序加载到内存中时,系统要寻找可执行文件中的.tls 区块,并且动态地分配一个足够大的内存块,以便存放所有的 TLS变量。系统也将一个指向已分配的内存的指针放到TLS数组里,这个数组由 FS:指向(在x86架构上)IMAGE_TLS_DIRECTORY结构中的地址是虚拟地址,而不是 RVA。这样,如果PE 文件不是从基地址载入的,那么这些地址就会通过基址重定位来修正。而且,IMAGE_TLS_DIRECTORY 本身不在.tls 区块中,而在 .rdata 区块中。在一个可执行文件中,线程局部存储(TLS)数据是由数据目录表中的 IMAGE_DIRECTORY_ENTRY_TLS条目指出的。如果数据是非零的,这个字段指向一个IMAGE_TLS_DIRECTORY结构,其定义如下
typedef struct _IMAGE_TLS_DIRECTORY32 {
    DWORD   StartAddressOfRawData; // TLS模板的起始地址
    DWORD   EndAddressOfRawData; // TLS模板的结束地址
    DWORD   AddressOfIndex; // TLS索引的位置,运行库使用这个索引来定位线程局部数据
    DWORD   AddressOfCallBacks; // PIMAGE_TLS_CALLBACK 函数指针数组的地址
    DWORD   SizeOfZeroFill; // 后面跟0的个数
    union {
      DWORD Characteristics; // 保留,目前设为0
      struct {
            DWORD Reserved0 : 20;
            DWORD Alignment : 4;
            DWORD Reserved1 : 8;
      } DUMMYSTRUCTNAME;
    } DUMMYUNIONNAME;

} IMAGE_TLS_DIRECTORY32;
typedef IMAGE_TLS_DIRECTORY32 * PIMAGE_TLS_DIRECTORY32;

typedef struct _IMAGE_TLS_DIRECTORY64 {
    ULONGLONG StartAddressOfRawData;
    ULONGLONG EndAddressOfRawData;
    ULONGLONG AddressOfIndex;         // PDWORD
    ULONGLONG AddressOfCallBacks;   // PIMAGE_TLS_CALLBACK *;
    DWORD SizeOfZeroFill;
    union {
      DWORD Characteristics;
      struct {
            DWORD Reserved0 : 20;
            DWORD Alignment : 4;
            DWORD Reserved1 : 8;
      } DUMMYSTRUCTNAME;
    } DUMMYUNIONNAME;

} IMAGE_TLS_DIRECTORY64;

typedef IMAGE_TLS_DIRECTORY64 * PIMAGE_TLS_DIRECTORY64;
TLS回调函数
(NTAPI *PIMAGE_TLS_CALLBACK) (
    PVOID DllHandle,
    DWORD Reason,
    PVOID Reserved
    );
AddressOfCallBacks是线程建立和退出时的回调函数,包括主线程和其他线程。当一个线程被创建或销毁时,列表中的回调函数会被调用。由于程序大都没有回调函数,这个列表是空的。有一点需要特别注意,程序运行时,TLS数据初始化和 TLS回调函数都在入口点(AddressOfEntryPoint)之前执行,也就是说,TLS 是程序开始运行的地方,许多病毒或外壳程序会利用这一点执行一些特殊操作。程序退出时,TLS回调函数会再执行一次。
TLS表解析
#include<Windows.h>
#include<iostream>
using namespace std;

DWORD RvaToFoa(_In_ DWORD rva, _In_ PIMAGE_SECTION_HEADER p, _In_ PIMAGE_FILE_HEADER f)
{
      for (int i = 0; i < f->NumberOfSections; i++)
      {
                // FOA = 数据的RVA + 区段的RVA - 区段的FOA
                if (rva >= p->VirtualAddress && rva < (p->VirtualAddress + p->Misc.VirtualSize))
                {
                        return rva - p->VirtualAddress + p->PointerToRawData;
                }
                f++;
                p++;
      }
      return 0;
}

void ImageNtHeader(_In_z_ const char* path)
{
      // 获取文件对象
      HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
      // 获取文件大小
      DWORD fSize = GetFileSize(hfile, NULL);
      char* pBuff = new char;
      DWORD dwReadSize = 0;
      // 读文件
      BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
      if (bSuccess)
      {
                PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
                PIMAGE_NT_HEADERS32 pNtHeader{ 0 };
                pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + pBuff);
                PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
                PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&pNtHeader->OptionalHeader;
                PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
                PIMAGE_DATA_DIRECTORY dataDirectory = (PIMAGE_DATA_DIRECTORY)&pOptionalHeader->DataDirectory;
                //填充结构
                PIMAGE_TLS_DIRECTORY pTLS = (PIMAGE_TLS_DIRECTORY)(RvaToFoa(dataDirectory->VirtualAddress, pSectionHeader, pFileHeader) + pBuff);
                cout << "数据块开始VA:" << pTLS->StartAddressOfRawData << endl;
                cout << "数据块结束VA:" << pTLS->EndAddressOfRawData << endl;
                cout << "索引变量VA:" << pTLS->AddressOfIndex << endl;
                cout << "回调表VA:" << pTLS->AddressOfCallBacks << endl;
                cout << "填充大小:" << pTLS->SizeOfZeroFill << endl;
                cout << "特征值:" << pTLS->Characteristics << endl;
      }
      else (cout.write("打开文件失败", 20));
      CloseHandle(hfile);
      delete[] pBuff;
}

void main()
{
      ImageNtHeader(R"(C:\Users\11981\Desktop\Project1\2.exe)");
}
资源目录
数据目录表中的IMAGE_DIRECTORY_ENTRY_RESOURCE条目包含资源的BVA和大小。资源目录结构中的每一个节点都是由 IMAGE_RESOURCE_DIRECTORY 结构和紧随其后的数个IMAGE_RESOURCE_DIRECTORY_ENTRY结构组成的,这两种结构组成了一个目录块。IMAGE_RESOURCE_DIRECTORY结构长度为16字节,共有6个字段,其定义如下。
typedef struct _IMAGE_RESOURCE_DIRECTORY {
    DWORD   Characteristics; // 理论上是资源的属性标志,但是通常为0
    DWORD   TimeDateStamp; // 资源建立的时间
    WORD    MajorVersion; // 理论上是放置资源的版本,但是通常为0
    WORD    MinorVersion;
    WORD    NumberOfNamedEntries; // 使用名字的资源条目的个数
    WORD    NumberOfIdEntries; // 使用ID数字资源条目的个数
//IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
资源目录入口
紧跟资源目录结构的就是资源目录入口(Resource Dir Entries)结构,此结构长度为8字节,包含2个字段。IMAGE_RESOURCE_DIRECTORY_ENTRY结构定义如下
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
    union {
      struct {
            DWORD NameOffset:31;
            DWORD NameIsString:1;
      } DUMMYSTRUCTNAME;
      DWORD   Name;
      WORD    Id;
    } DUMMYUNIONNAME; // 目录项的名称字符串指针或ID
    union {
      DWORD   OffsetToData;
      struct {
            DWORD   OffsetToDirectory:31;
            DWORD   DataIsDirectory:1;
      } DUMMYSTRUCTNAME2;
    } DUMMYUNIONNAME2; // 资源数据偏移地址或子目录偏移地址
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
根据不同的情况,这 2个字段的含义有所不同。
[*]Name 字段:定义目录项的名称或 ID。当结构用于第1层目录时,定义的是资源类型;当结构用于第 2 层目录时,定义的是资源的名称;当结构用干第 3 层目录时,定义的是代码页编号。当最高位为 0时,表示字段的值作为ID使用;当最高位为1时。表示字段的低位作为指针使用,资源名称字符串使用Unicode 编码,这个指针不直接指向字符串,而指向一个IMAGE_RESOURCE_DIR_STRING_U结构。Name字段定义如下

typedef struct _IMAGE_RESOURCE_DIR_STRING_U {
    WORD    Length; // 字符串的长度
    WCHAR   NameString[ 1 ]; // Unicode字符串,按字对齐,长度可变
                                                 // 由 Length 指明 Unicode 字符串的长度
} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;

[*]OffsetToData字段∶是一个指针。当最高位(位31)为1时,低位数据指向下一层目录块的起始地址;当最高位为0时,指针指向IMAGE_RESOURCE_DATA_ENTRY结构。在将Name 和 OffsetToData 作为指针时需要注意,该指针从资源区块开始处计算偏移量,并非从 RVA (根目录的起始位置)开始处计算偏移量。
有一点要说明的是,当 IMAGE_RESOURCE_DIRECTORY_ENTRY 用在第 1层目录中时,它的Name 字段作为资源类型使用。当资源类型以ID定义且数值在1到16 之间时,表示是系统预定义的类型
#define RT_CURSOR         MAKEINTRESOURCE(1) // 光标(Cursor)
#define RT_BITMAP         MAKEINTRESOURCE(2) // 位图(Bitmap)
#define RT_ICON             MAKEINTRESOURCE(3) // 图标(Icon)
#define RT_MENU             MAKEINTRESOURCE(4) // 菜单(Menu)
#define RT_DIALOG         MAKEINTRESOURCE(5) // 对话框(Dialog)
#define RT_STRING         MAKEINTRESOURCE(6) // 字符串(String)
#define RT_FONTDIR          MAKEINTRESOURCE(7) // 字体目录(Font Directory)
#define RT_FONT             MAKEINTRESOURCE(8) // 字体(Font)
#define RT_ACCELERATOR      MAKEINTRESOURCE(9) // 加速键(Acelerators)
#define RT_RCDATA         MAKEINTRESOURCE(10) // 未格式化资源(Unformatted)
#define RT_MESSAGETABLE   MAKEINTRESOURCE(11) // 消息表(MessageTable)

#define DIFFERENCE   11
#define RT_GROUP_CURSOR MAKEINTRESOURCE((ULONG_PTR)(RT_CURSOR) + DIFFERENCE) // 光标组(Group Cursor)
#define RT_GROUP_ICON   MAKEINTRESOURCE((ULONG_PTR)(RT_ICON) + DIFFERENCE) // 图标组(Group Ieon)
#define RT_VERSION      MAKEINTRESOURCE(16) // 版本信息(Version Information)资源头资源表数据从第一级资源目录开始。资源的每一级目录都会有一个资源目录头,它标识了该类资源的属性、创建日期和版本等信息,其中也包含了随后的目录项的数量描述信息。typedef struct _IMAGE_RESOURCE_DIRECTORY {
    DWORD   Characteristics; // 资源属性
    DWORD   TimeDateStamp; // 时间戳
    WORD    MajorVersion; // 资源大版本号
    WORD    MinorVersion; // 资源下版本号
    WORD    NumberOfNamedEntries; // 以名称命名的入口数量
    WORD    NumberOfIdEntries; // 以ID命名的入口数量
//IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
资源数据入口
经过3层IMAGE_RESOURCE_DIRECTORY_ENTRY(一般是3层,也有可能更少,第1层是资源类型,第2层是资源名,第3层是资源的Language),第 3层目录结构中的 OffsetToData 将指向IMAGE_RESOURCE_DATA_ENTRY结构。该结构描述了资源数据的位置和大小,其定义如下。typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
    DWORD   OffsetToData; // 资源数据的RVA
    DWORD   Size; // 资源数据的长度
    DWORD   CodePage; // 代码页,一般为0
    DWORD   Reserved; // 保留字段
} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;经过多层结构,此处的 IMAGE_RESOURCE_DATA_ENTRY 结构就是真正的资源数据了。结构中的OffsetToData指向资源数据的指针(其为 RVA值)。
资源目录解析
#include<Windows.h>
#include<iostream>
using namespace std;

DWORD RvaToFoa(_In_ DWORD rva, _In_ PIMAGE_SECTION_HEADER p, _In_ PIMAGE_FILE_HEADER f)
{
      for (int i = 0; i < f->NumberOfSections; i++)
      {
                // FOA = 数据的RVA + 区段的RVA - 区段的FOA
                if (rva >= p->VirtualAddress && rva < (p->VirtualAddress + p->Misc.VirtualSize))
                {
                        return rva - p->VirtualAddress + p->PointerToRawData;
                }
                f++;
                p++;
      }
      return 0;
}

void ImageNtHeader(_In_z_ const char* path)
{
      // 获取文件对象
      HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
      // 获取文件大小
      DWORD fSize = GetFileSize(hfile, NULL);
      char* pBuff = new char;
      DWORD dwReadSize = 0;
      // 读文件
      BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
      if (bSuccess)
      {
                PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
                PIMAGE_NT_HEADERS32 pNtHeader{ 0 };
                pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + pBuff);
                PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
                PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&pNtHeader->OptionalHeader;
                PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
                PIMAGE_DATA_DIRECTORY dataDirectory = (PIMAGE_DATA_DIRECTORY)&pOptionalHeader->DataDirectory;
                //填充结构
                PIMAGE_RESOURCE_DIRECTORY pResource = (PIMAGE_RESOURCE_DIRECTORY)(RvaToFoa(dataDirectory->VirtualAddress, pSectionHeader, pFileHeader) + pBuff);
                cout << "数据块开始VA:" << pResource->NumberOfIdEntries << endl;
                cout << "数据块结束VA:" << pResource->TimeDateStamp << endl;
      }
      else (cout.write("打开文件失败", 20));
      CloseHandle(hfile);
      delete[] pBuff;
}

void main()
{
      ImageNtHeader(R"(C:\Users\11981\Desktop\Project1\2.exe)");
}
调试目录
当使用调试信息构建个可执行文件时。按照惯例,应该句括这种信息的格式及位置细节。操作系统不需要通过它来运行可执行文件,但它对开发工具是有用的。数据目录表的第 7 个条目(IMAGE_DIRECTORY_ENTRY_DEBUG)指向调试目录,它由一个IMAGE_DEBUG_DIRECTORY 结构数组组成。这些结构用于保存存储在文件中变量的类型、尺寸和位置的调试信息。debug目录中的元素数量可以通过 DataDirectory 中的 Size 字段来计算,其结构定义如下。typedef struct _IMAGE_DEBUG_DIRECTORY {
    DWORD   Characteristics; // 未使用,设为0
    DWORD   TimeDateStamp; // debug信息的时间/日期戳
    WORD    MajorVersion; // debug信息的主版本,未使用
    WORD    MinorVersion; // debug信息的次版本,未使用
    DWORD   Type; // debug信息的类型
    DWORD   SizeOfData; // debug数据的大小
    DWORD   AddressOfRawData; // 当被映射到内存时 debug 数据的 RVA,为0表示不被映射
    DWORD   PointerToRawData; // debug数据的文件偏移(不是 RVA)PointerToRawData
} IMAGE_DEBUG_DIRECTORY, *PIMAGE_DEBUG_DIRECTORY;到目前为止,存储 debug 信息的最普遍形式是PDB 文件。PDB 文件基本上是 CodeView样式的debug 信息的演变。PDB 信息是由一个 IMAGE_DEBUG_TYPE_CODEVIEW 类型的调试目录字段指出的。如果检查这个条目所指向的数据,将发现一个简短的 CodeView样式的头部。这个 debug数据多半指向外部 PDB 文件的路径。在 Visual Studio 6.0 中,debug 头部以 NB10标识开始。在 Visual Studio.NET中,debug头部以RSDS开始。在Visual Studio 6.0中,COFF格式的 debug信息能用/DEBUGTYPE:COFF链接器开关生成。在Visual Studio.NET中没有这个选项。
Type:调试类型
#define IMAGE_DEBUG_TYPE_UNKNOWN                0
#define IMAGE_DEBUG_TYPE_COFF                   1
#define IMAGE_DEBUG_TYPE_CODEVIEW               2
#define IMAGE_DEBUG_TYPE_FPO                  3
#define IMAGE_DEBUG_TYPE_MISC                   4
#define IMAGE_DEBUG_TYPE_EXCEPTION            5
#define IMAGE_DEBUG_TYPE_FIXUP                  6
#define IMAGE_DEBUG_TYPE_OMAP_TO_SRC            7
#define IMAGE_DEBUG_TYPE_OMAP_FROM_SRC          8
#define IMAGE_DEBUG_TYPE_BORLAND                9
#define IMAGE_DEBUG_TYPE_RESERVED10             10
#define IMAGE_DEBUG_TYPE_CLSID                  11
#define IMAGE_DEBUG_TYPE_VC_FEATURE             12
#define IMAGE_DEBUG_TYPE_POGO                   13
#define IMAGE_DEBUG_TYPE_ILTCG                  14
#define IMAGE_DEBUG_TYPE_MPX                  15
#define IMAGE_DEBUG_TYPE_REPRO                  16
#define IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS20
调试目录解析
#include<Windows.h>
#include<iostream>
using namespace std;

DWORD RvaToFoa(_In_ DWORD rva, _In_ PIMAGE_SECTION_HEADER p, _In_ PIMAGE_FILE_HEADER f)
{
      for (int i = 0; i < f->NumberOfSections; i++)
      {
                // FOA = 数据的RVA + 区段的RVA - 区段的FOA
                if (rva >= p->VirtualAddress && rva < (p->VirtualAddress + p->Misc.VirtualSize))
                {
                        return rva - p->VirtualAddress + p->PointerToRawData;
                }
                f++;
                p++;
      }
      return 0;
}

void ImageNtHeader(_In_z_ const char* path)
{
      // 获取文件对象
      HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
      // 获取文件大小
      DWORD fSize = GetFileSize(hfile, NULL);
      char* pBuff = new char;
      DWORD dwReadSize = 0;
      // 读文件
      BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
      if (bSuccess)
      {
                PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
                PIMAGE_NT_HEADERS32 pNtHeader{ 0 };
                pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + pBuff);
                PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
                PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&pNtHeader->OptionalHeader;
                PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
                PIMAGE_DATA_DIRECTORY dataDirectory = (PIMAGE_DATA_DIRECTORY)&pOptionalHeader->DataDirectory;
                //填充结构
                PIMAGE_DEBUG_DIRECTORY pDebug = (PIMAGE_DEBUG_DIRECTORY)(RvaToFoa(dataDirectory->VirtualAddress, pSectionHeader, pFileHeader) + pBuff);
                cout << "debug数据的大小:" << pDebug->SizeOfData << endl;
                cout << "debug信息的类型:" << pDebug->Type << endl;

      }
      else (cout.write("打开文件失败", 20));
      CloseHandle(hfile);
      delete[] pBuff;
}

void main()
{
      ImageNtHeader(R"(C:\Users\11981\Desktop\Project1\2.exe)");
}
加载配置目录
typedef struct _IMAGE_LOAD_CONFIG_DIRECTORY32 {
    DWORD   Size; // 加载配置属性
    DWORD   TimeDateStamp; // 时间戳
    WORD    MajorVersion; // 主版本号
    WORD    MinorVersion; // 次版本号
    DWORD   GlobalFlagsClear; // 标志1
    DWORD   GlobalFlagsSet; // 标志2
    DWORD   CriticalSectionDefaultTimeout; // 超时
    DWORD   DeCommitFreeBlockThreshold; // 释放内存数量
    DWORD   DeCommitTotalFreeThreshold; // 空闲内存总量
    DWORD   LockPrefixTable;// 使用LOCK前缀的指令地址
    DWORD   MaximumAllocationSize; // 最大的分配粒度
    DWORD   VirtualMemoryThreshold; // 最大的虚拟内存大小
    DWORD   ProcessHeapFlags; // 进程堆标识
    DWORD   ProcessAffinityMask; // SetProcessAffinityMask 函数参数
    WORD    CSDVersion; // Service Pack 版本标识
    WORD    DependentLoadFlags;
    DWORD   EditList;                     // VA
    DWORD   SecurityCookie;               // VA
    DWORD   SEHandlerTable;               // VA
    DWORD   SEHandlerCount;
    DWORD   GuardCFCheckFunctionPointer;    // VA
    DWORD   GuardCFDispatchFunctionPointer; // VA
    DWORD   GuardCFFunctionTable;         // VA
    DWORD   GuardCFFunctionCount;
    DWORD   GuardFlags;
    IMAGE_LOAD_CONFIG_CODE_INTEGRITY CodeIntegrity;
    DWORD   GuardAddressTakenIatEntryTable; // VA
    DWORD   GuardAddressTakenIatEntryCount;
    DWORD   GuardLongJumpTargetTable;       // VA
    DWORD   GuardLongJumpTargetCount;
    DWORD   DynamicValueRelocTable;         // VA
    DWORD   CHPEMetadataPointer;
    DWORD   GuardRFFailureRoutine;          // VA
    DWORD   GuardRFFailureRoutineFunctionPointer; // VA
    DWORD   DynamicValueRelocTableOffset;
    WORD    DynamicValueRelocTableSection;
    WORD    Reserved2;
    DWORD   GuardRFVerifyStackPointerFunctionPointer; // VA
    DWORD   HotPatchTableOffset;
    DWORD   Reserved3;
    DWORD   EnclaveConfigurationPointer;    // VA
    DWORD   VolatileMetadataPointer;      // VA
    DWORD   GuardEHContinuationTable;       // VA
    DWORD   GuardEHContinuationCount;
} IMAGE_LOAD_CONFIG_DIRECTORY32, *PIMAGE_LOAD_CONFIG_DIRECTORY32;

typedef struct _IMAGE_LOAD_CONFIG_DIRECTORY64 {
    DWORD      Size;
    DWORD      TimeDateStamp;
    WORD       MajorVersion;
    WORD       MinorVersion;
    DWORD      GlobalFlagsClear;
    DWORD      GlobalFlagsSet;
    DWORD      CriticalSectionDefaultTimeout;
    ULONGLONGDeCommitFreeBlockThreshold;
    ULONGLONGDeCommitTotalFreeThreshold;
    ULONGLONGLockPrefixTable;                // VA
    ULONGLONGMaximumAllocationSize;
    ULONGLONGVirtualMemoryThreshold;
    ULONGLONGProcessAffinityMask;
    DWORD      ProcessHeapFlags;
    WORD       CSDVersion;
    WORD       DependentLoadFlags;
    ULONGLONGEditList;                     // VA
    ULONGLONGSecurityCookie;               // VA
    ULONGLONGSEHandlerTable;               // VA
    ULONGLONGSEHandlerCount;
    ULONGLONGGuardCFCheckFunctionPointer;    // VA
    ULONGLONGGuardCFDispatchFunctionPointer; // VA
    ULONGLONGGuardCFFunctionTable;         // VA
    ULONGLONGGuardCFFunctionCount;
    DWORD      GuardFlags;
    IMAGE_LOAD_CONFIG_CODE_INTEGRITY CodeIntegrity;
    ULONGLONGGuardAddressTakenIatEntryTable; // VA
    ULONGLONGGuardAddressTakenIatEntryCount;
    ULONGLONGGuardLongJumpTargetTable;       // VA
    ULONGLONGGuardLongJumpTargetCount;
    ULONGLONGDynamicValueRelocTable;         // VA
    ULONGLONGCHPEMetadataPointer;            // VA
    ULONGLONGGuardRFFailureRoutine;          // VA
    ULONGLONGGuardRFFailureRoutineFunctionPointer; // VA
    DWORD      DynamicValueRelocTableOffset;
    WORD       DynamicValueRelocTableSection;
    WORD       Reserved2;
    ULONGLONGGuardRFVerifyStackPointerFunctionPointer; // VA
    DWORD      HotPatchTableOffset;
    DWORD      Reserved3;
    ULONGLONGEnclaveConfigurationPointer;   // VA
    ULONGLONGVolatileMetadataPointer;         // VA
    ULONGLONGGuardEHContinuationTable;      // VA
    ULONGLONGGuardEHContinuationCount;
} IMAGE_LOAD_CONFIG_DIRECTORY64, *PIMAGE_LOAD_CONFIG_DIRECTORY64;
配置目录解析
#include<Windows.h>
#include<iostream>
using namespace std;

DWORD RvaToFoa(_In_ DWORD rva, _In_ PIMAGE_SECTION_HEADER p, _In_ PIMAGE_FILE_HEADER f)
{
      for (int i = 0; i < f->NumberOfSections; i++)
      {
                // FOA = 数据的RVA + 区段的RVA - 区段的FOA
                if (rva >= p->VirtualAddress && rva < (p->VirtualAddress + p->Misc.VirtualSize))
                {
                        return rva - p->VirtualAddress + p->PointerToRawData;
                }
                f++;
                p++;
      }
      return 0;
}

void ImageNtHeader(_In_z_ const char* path)
{
      // 获取文件对象
      HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
      // 获取文件大小
      DWORD fSize = GetFileSize(hfile, NULL);
      char* pBuff = new char;
      DWORD dwReadSize = 0;
      // 读文件
      BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
      if (bSuccess)
      {
                PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
                PIMAGE_NT_HEADERS32 pNtHeader{ 0 };
                pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + pBuff);
                PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
                PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&pNtHeader->OptionalHeader;
                PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
                PIMAGE_DATA_DIRECTORY dataDirectory = (PIMAGE_DATA_DIRECTORY)&pOptionalHeader->DataDirectory;
                //填充结构
                PIMAGE_LOAD_CONFIG_DIRECTORY32 pDebug = (PIMAGE_LOAD_CONFIG_DIRECTORY32)(RvaToFoa(dataDirectory->VirtualAddress, pSectionHeader, pFileHeader) + pBuff);
                cout << "加载配置属性:" << pDebug->Size << endl;
                cout << "最大的分配粒度:" << pDebug->MaximumAllocationSize << endl;
                cout << "最大的虚拟内存大小:" << pDebug->VirtualMemoryThreshold << endl;

      }
      else (cout.write("打开文件失败", 20));
      CloseHandle(hfile);
      delete[] pBuff;
}

void main()
{
      ImageNtHeader(R"(C:\Users\11981\Desktop\Project1\2.exe)");
}
参考文章
https://blog.csdn.net/Gamma_lab/article/details/123869956
https://blog.csdn.net/adam001521/article/details/84658708
查考书
加密与解密
Windows PE权威指南
PE文件其他资料




fz12 发表于 2022-5-8 02:32

metoo2 发表于 2022-5-7 22:16
最新360解压也有问题,里面pe文件.pdf打开出错,另外一个文件打开正常。楼主你检查下呢?

你把后面的zip去掉,用360解压

metoo2 发表于 2022-5-7 22:16

fz12 发表于 2022-5-7 17:59
我用的是360压缩

最新360解压也有问题,里面pe文件.pdf打开出错,另外一个文件打开正常。楼主你检查下呢?

wangxiaohu104 发表于 2022-5-5 20:40

谢谢楼主无私分享。

lyliucn 发表于 2022-5-6 07:20

pe资料,学习。谢谢楼主无私分享。

iloveasdl 发表于 2022-5-6 08:31


pe资料,学习。谢谢楼主无私分享

CXC303 发表于 2022-5-6 22:29

pe资料,学习

sun152121 发表于 2022-5-7 10:03

好东西啊~~~~~~~

metoo2 发表于 2022-5-7 13:33

点赞支持,感谢楼主

metoo2 发表于 2022-5-7 13:39

附件用什么软件压缩的?7z打不开,提示文件错误

fz12 发表于 2022-5-7 17:59

metoo2 发表于 2022-5-7 13:39
附件用什么软件压缩的?7z打不开,提示文件错误

我用的是360压缩
页: [1] 2
查看完整版本: 快速查询PE文件知识点和PE文件解析(下)