前言
通过分析和解析Windows PE格式,并使用qt进行图形化显示
一、什么是Windows PE格式中的导入表?
在Windows中,PE文件格式中的导入表(Import Table)是一个关键的数据结构,它记录了程序在运行时所需调用的外部动态链接库(DLL)中的函数和变量。导入表的主要作用是在程序加载时,帮助操作系统定位并加载这些外部库,以便程序能够正确地调用其中的函数和访问变量。
导入表由一系列_IMAGE_IMPORT_DESCRIPTOR结构组成,每个结构都包含一个指向外部库的导入地址表(Import Address Table,IAT)的指针。IAT是一个包含函数名和对应函数地址的列表,它提供了程序在运行时查找和调用外部库函数所需的信息。
当程序被加载到内存中时,操作系统会使用导入表中的信息来解析IAT,从而将外部库的函数地址填充到正确的位置。这样,当程序调用一个外部库函数时,它可以通过IAT直接找到正确的函数地址,并执行相应的操作。
总之,PE文件格式中的导入表是连接程序与外部动态链接库的关键桥梁,它确保了程序在运行时能够正确地调用外部库中的函数和访问变量。
二、解析导入表并显示
1.导入表的结构
导入表的结构如下:
IMAGE_IMPORT_DESCRIPTOR结构数组:这是一个可变长度的数组,每个元素描述一个导入的模块。数组的最后一个元素的所有字段都为零,表示数组的结束。
struct IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
};
每个IMAGE_IMPORT_DESCRIPTOR结构包含以下字段:
OriginalFirstThunk:指向一个IMAGE_THUNK_DATA结构数组,该数组包含了导入函数的原始名称或序号。
TimeDateStamp:时间戳,表示导入表的创建时间。如果此值为零,表示导入表未被绑定。
ForwarderChain:链表的前一个结构的索引,用于处理转发器导入。
Name:以null结尾的字符串,表示导入的模块名称。
FirstThunk:指向一个IMAGE_THUNK_DATA结构数组,该数组包含了导入函数的序号或地址。
IMAGE_THUNK_DATA结构:这个结构表示一个导入函数的信息。它的大小取决于操作系统(32位或64位)。 对于32位系统,IMAGE_THUNK_DATA结构定义如下:
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString;
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData;
} u1;
} IMAGE_THUNK_DATA32, *PIMAGE_THUNK_DATA32;
对于64位系统,IMAGE_THUNK_DATA结构定义如下:
typedef struct _IMAGE_THUNK_DATA64 {
union {
ULONGLONG ForwarderString;
ULONGLONG Function;
ULONGLONG Ordinal;
ULONGLONG AddressOfData;
} u1;
} IMAGE_THUNK_DATA64, *PIMAGE_THUNK_DATA64;
2.解析导入表
bool PEParser::parserFileData(const QByteArray &fileData)
{
//判断是否是MZ开头的文件
if (fileData.left(2) != "MZ")
{
return false;
}
//解析DOS头
parserDOSHeader(fileData.left(sizeof(IMAGE_DOS_HEADER)));
//DOSStub数据
m_dosStubData = fileData.mid(sizeof(IMAGE_DOS_HEADER), m_dosHeader.e_lfanew - sizeof(IMAGE_DOS_HEADER));
long peAddress = m_dosHeader.e_lfanew;
if (fileData.mid(peAddress, 2) != "PE")
{
return false;
}
m_fileData = fileData;
//去除前4个字节的PE头标识
long fileHeaderIndex = peAddress + 4;
//记录文件头索引
m_fileHeaderIndex = fileHeaderIndex;
//解析标准PE文件头
paserFileHeader(fileData.mid(fileHeaderIndex, sizeof(IMAGE_FILE_HEADER)));
//解析扩展PE文件头
long optionHeaderIndex = fileHeaderIndex + sizeof(IMAGE_FILE_HEADER);
//记录扩展PE文件头索引
m_optionHeaderIndex = optionHeaderIndex