本帖最后由 appsion 于 2020-10-31 12:33 编辑
PE 文件解析2 - 获取PE头、区段、数据目录新手学习PE文件解析记录, 持续更新. 部分参考来源于网络, 无法逐一标注, 如果侵权,请及时联系. 如有错误请指正,误喷.
上文链接: https://www.52pojie.cn/thread-1294150-1-1.html
概念:
TLS:
Thread Local Storage, 线程本地存储,用来保存变量或回调函数.
IAT:
Import Address Table, 导入地址表, 由于导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL 中.当PE 文件被装入内存的时候,Windows 装载器才将DLL 装入,并将调用导入函数的指令和函数实际所处的地址联系起来(动态连接),这操作就需要导入表完成.其中导入地址表就指示函数实际地址.
结构体: IMAGE_DATA_DIRECTORY
说明: 数据目录
头文件: winnt.h
参考文档: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_data_directory
[Asm] 纯文本查看 复制代码
typedef struct _IMAGE_DATA_DIRECTORY
{
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
参数说明: 仅供参考, 详细说明请参考原文.
VirtualAddress:
数据目录相对虚拟地址(RVA)
Size:
表的大小
VirtualAddress成员如下:
Offset (PE/PE32+) | Description | 96/112 | Export table address and size | 104/120 | Import table address and size | 112/128 | Resource table address and size | 120/136 | Exception table address and size | 128/144 | Certificate table address and size | 136/152 | Base relocation table address and size | 144/160 | Debugging information starting address and size | 152/168 | Architecture-specific data address and size | 160/176 | Global pointer register relative virtual address | 168/184 | Thread local storage (TLS) table address and size | 176/192 | Load configuration table address and size | 184/200 | Bound import table address and size | 192/208 | Import address table address and size | 200/216 | Delay import descriptor address and size | 208/224 | The CLR header address and size | 216/232 | Reserved |
2.1 获取PE头信息
IMAGE_FILE_HEADER.Machine // 处理器类型
IMAGE_OPTIONAL_HEADER.MajorLinkerVersion // 链接器版本号
IMAGE_OPTIONAL_HEADER.MinorLinkerVersion
IMAGE_OPTIONAL_HEADER.MajorOperatingSystemVersion // 系统版本号
IMAGE_OPTIONAL_HEADER.MinorOperatingSystemVersion
IMAGE_OPTIONAL_HEADER.Subsystem // 子系统名称
IMAGE_OPTIONAL_HEADER.MajorSubsystemVersion // 子系统版本号
IMAGE_OPTIONAL_HEADER.MinorSubsystemVersion
IMAGE_FILE_HEADER.TimeDateStamp // 映像时间
IMAGE_FILE_HEADER.Characteristics // 映像特征
IMAGE_OPTIONAL_HEADER.Magic // 映像类型
IMAGE_OPTIONAL_HEADER.MajorImageVersion // 映像版本号
IMAGE_OPTIONAL_HEADERMinorImageVersion
IMAGE_OPTIONAL_HEADER.ImageBase // 映像基址
IMAGE_OPTIONAL_HEADER.BaseOfCode // 代码基址
IMAGE_OPTIONAL_HEADER.BaseOfData // 数据基址
IMAGE_OPTIONAL_HEADER.AddressOfEntryPoint // 入口点相对地址
ImageBase + AddressOfEntryPoint // 入口点实际地址
IMAGE_OPTIONAL_HEADER.SizeOfImage // 映像内存大小
IMAGE_OPTIONAL_HEADER.CheckSum // 映像校验合
IMAGE_OPTIONAL_HEADER.DllCharacteristics // 映像的DLL特性
IMAGE_OPTIONAL_HEADER.SizeOfHeaders // 文件头大小
IMAGE_FILE_HEADER.NumberOfSections // 节表数量
IMAGE_OPTIONAL_HEADER.SectionAlignment // 节表内存对齐
IMAGE_OPTIONAL_HEADER.FileAlignment // 文件对齐
IMAGE_FILE_HEADER.PointerToSymbolTable // 符号表偏移地址
IMAGE_FILE_HEADER.NumberOfSymbols // 符号表数量
IMAGE_OPTIONAL_HEADER.SectionAlignment // 节表(区段)对齐
IMAGE_OPTIONAL_HEADER.FileAlignment // 文件对齐
IMAGE_OPTIONAL_HEADER.DataDirectory // 数据目录
IMAGE_NT_HEADERS.OptionalHeader.NumberOfRvaAndSizes // 数据目录数量
IMAGE_OPTIONAL_HEADER.DataDirectory // 数据目录表
2.2 获取区段信息
[Asm] 纯文本查看 复制代码
// 节表数量
IMAGE_FILE_HEADER.NumberOfSections
// 节表头
IMAGE_SECTION_HEADER *SectionHead = (IMAGE_SECTION_HEADER *)(DataBuff + DosHead->e_lfanew + sizeof(IMAGE_NT_HEADERS));
for (size_t i = 0; i < IMAGE_FILE_HEADER.NumberOfSections; i++)
{
IMAGE_SECTION_HEADER Section = SectionHead[i];
// 节表名称
// Section.Name
// 节表的RVA
// Section.VirtualAddress
// 节表的虚拟大小
// Section.Misc.VirtualSize
// 节表的Raw
// Section.PointerToRawData
// 节表的Raw的数据大小
// Section.SizeOfRawData
// 特征
// Section.Characteristics
}
2.3 获取数据目录
// NT头
IMAGE_NT_HEADERS * NTHead = GetNTHead(DataBuff);
// 数据目录数量
DataDirectoryCount = NTHead->OptionalHeader.NumberOfRvaAndSizes;
// 数据目录头
IMAGE_DATA_DIRECTORY *DirectoryHead = NTHead->OptionalHeader.DataDirectory;
// 数据目录说明
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_ARCHITECTURE | 7 | 版权信息 | IMAGE_DIRECTORY_ENTRY_GLOBALPTR | 8 | 全局指针 | IMAGE_DIRECTORY_ENTRY_TLS | 9 | TLS表 | IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG | 10 | 加载配置表 | IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT | 11 | 绑定导入表 | IMAGE_DIRECTORY_ENTRY_IAT | 12 | IAT表 | IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT | 13 | 延迟导入表 | IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR | 14 | COM描述符表 | | 15 | 保留 |
获取数据目录
[Asm] 纯文本查看 复制代码 for (size_t i = 0; i < DataDirectoryCount; i++)
{
// 获取数据目录标题
switch (i)
{
case 0: Caption = "导出表"; break;
case 1: Caption = "导入表"; break;
case 2: Caption = "资源表"; break;
case 3: Caption = "异常表"; break;
case 4: Caption = "安全证书表"; break;
case 5: Caption = "基址重定位表"; break;
case 6: Caption = "调试表"; break;
case 7: Caption = "版权信息"; break;
case 8: Caption = "全局指针"; break;
case 9: Caption = "TLS表"; break;
case 10: Caption = "加载配置表"; break;
case 11: Caption = "绑定导入表"; break;
case 12: Caption = "IAT表"; break;
case 13: Caption = "延迟导入表"; break;
case 14: Caption = "COM描述符表"; break;
case 15: Caption = "保留"; break;
default: break;
}
// 表的RVA
// DataDirectoryHead[i].VirtualAddress
// 表的大小
// DataDirectoryHead[i].Size
// 节表数量
int SECTIONCount = IMAGE_FILE_HEADER.NumberOfSections;
// 节表头
IMAGE_SECTION_HEADER *SectionHead = (IMAGE_SECTION_HEADER *)(DataBuff + DosHead->e_lfanew + sizeof(IMAGE_NT_HEADERS));
// 表的FOA
// 注: 获取表的FOA需要以下几步
//1. 判断表的RVA所在节表中的哪一个.
//2. 表的FOA = 表的RVA - 节表的RVA + 节表FOA
DWORD DataDirectoryFOA = RVAToRaw(SectionHead, SectionCount, DataDirectoryHead[i].VirtualAddress);
}
RVA 转 FOA
[Asm] 纯文本查看 复制代码
DWORD RVAToRaw(IMAGE_SECTION_HEADER *SectionHead, int SECTIONCount, DWORD m_RVA)
{
// 遍历节表
for (size_t i = 0; i < SECTIONCount; i++)
{
IMAGE_SECTION_HEADER section = ((IMAGE_SECTION_HEADER*)SectionHead)[i];
// 判断 RVA 所在的节表
if (m_RVA >= section.VirtualAddress && m_RVA <= (section.VirtualAddress + section.SizeOfRawData))
{
// 获取偏移差
m_RVA = m_RVA - section.VirtualAddress;
m_RVA = m_RVA + section.PointerToRawData;
return m_RVA;
}
}
return 0;
} |