【PE】PE文件结构6-异常表(Exception)

date: 2023/12/19

概述:x86系统采用动态的方式构建SEH结构,相比而言x64系统下采用静态的方式处理SEH结构,它保存在PE文件中,通常在.pdata区段。因此本文的例子采用x64编译过的程序。异常表在资源表的后面。

0x01 异常表解析

数据目录表的第四个元素指向异常表,RVA指向的是一个IMAGE_IA64_RUNTIME_FUNCTION_ENTRY的结构体,本例的RVA是0xA000(也就是.pdata段的起始位置),转换成offset是0x7a00。它的结构体是:

1
2
3
4
5
typedef struct _IMAGE_IA64_RUNTIME_FUNCTION_ENTRY {
DWORD BeginAddress; //与SEH相关代码的起始偏移地址
DWORD EndAddress; //与SEH相关代码的末尾偏移地址
DWORD UnwindInfoAddress; //指向描述上面两个字段之间代码异常信息的UNWIND_INFO
} IMAGE_IA64_RUNTIME_FUNCTION_ENTRY, *PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY;

​ BeginAddress 与 EndAddress之间,是异常处理函数的内容。UnwindInfoAddress 指向的位置是用来描述 BeginAddress 与 EndAddress 之间的代码异常属性信息的UNWIND_INFO。UNWIND_INFO 也叫作异常展开信息,此结构用来描述堆栈指针的记录属性与寄存器中保存的地址属性,它的结构体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct _UNWIND_INFO {
UBYTE Version:3;
UBYTE Flags:5;
UBYTE SizeOfProlog;
UBYTE CountOfCodes;
UBYTE FrameRegister:4;
UBYTE FrameOffset:4;
UNWIND_CODE UnwindCode[1];
union {
// If (Flags & UNW_FLAG_EHANDLER)
OPTIONAL ULONG ExceptionHandler;//异常/终止函数的映像相对地址指针
// Else if (Flags & UNW_FLAG_CHAININFO)
OPTIONAL ULONG FunctionEntry;//展开信息链的映像相对地址指针
};
// If (Flags & UNW_FLAG_EHANDLER)
ULONG ExceptionData[1];//异常处理程序的数据
} UNWIND_INFO, *PUNWIND_INFO;

Version:异常展开信息的版本号,一般为0x001

Flags:共有四种标志:

  1. 当它为0x0的时候表示UNW_FLAG_NHANDLER,没有异常处理函数。
  2. 当它为0x1的时候表示UNW_FLAG_EHANDLER,有异常处理函数。
  3. 当它为0x2的时候表示UNW_FLAG_UHANDLER,有系统默认的处理函数。
  4. 当它为0x4的时候表示UNW_FLAG_CHAININFO,表示FunctionEntry指向的是前一个RUNTIME_FUNCTION的RAV。

SizeOfProlog:函数起始部分字节的长度

CountOfCodes:UNWIND_INFO结构包含的UNWIND_CODE结构数

FrameRegister:寄存器帧指针,为0则指定函数不使用框架

FrameOffset:若上面字段不为0,表示函数偏移

UnwindCode:指定永久性寄存器与RSP的数组项目数

ExceptionHandler:异常句柄

FunctionEntry:展开信息链(函数)的映像相对地址指针(如果设置了UNW_FLAG_CHAININFO标识)

ExceptionData:异常处理程序的数据

查找SEH表

以下代码说明:打开一个进程模块,然后查找 .pdata 段,依次遍历异常表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
const char	gPDataSectionName[] = ".pdata";

typedef struct _ModuleSectionInfo_T {
VOID* pBase;
UINT Size;
}ModuleSectionInfo_T;

// 查找 .pdata 段地址和大小
BOOL getModulePdataSection(
__in HANDLE hProcess,
__in VOID * pModule,
__out ModuleSectionInfo_T * pModuleSectionInfo)
{
IMAGE_DOS_HEADER ImageDosHeader;
IMAGE_NT_HEADERS ImageNtHeaders;
IMAGE_SECTION_HEADER* pImageSectionHeader;
UINT SectionIdx;
IMAGE_SECTION_HEADER ImageSectionHeader;
UCHAR* pBase = (UCHAR*)pModule;
BOOL bResult = FALSE;

if (!ReadProcessMemory(hProcess, pBase, &ImageDosHeader, sizeof(ImageDosHeader), NULL)) goto End;
if (ImageDosHeader.e_magic != IMAGE_DOS_SIGNATURE) goto End;
if (!ReadProcessMemory(hProcess, pBase + ImageDosHeader.e_lfanew, &ImageNtHeaders, sizeof(ImageNtHeaders), NULL)) goto End;
if (ImageNtHeaders.Signature != IMAGE_NT_SIGNATURE) goto End;
//
// For 64bits, add the size of IMAGE_NT_HEADERS64 to its own base to get the base of the IMAGE_SECTION_HEADER table
//
#ifdef _WIN64
#pragma warning(push)
#pragma warning(disable:4127)
if (SQLPLUGIN_IS_WOW64 == TRUE) pImageSectionHeader = (IMAGE_SECTION_HEADER*)((UINT_PTR)pBase + ImageDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS));
#pragma warning(pop)
else
#endif
pImageSectionHeader = (IMAGE_SECTION_HEADER*)((UINT_PTR)pBase + ImageDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS32));

for (SectionIdx = 0; SectionIdx < ImageNtHeaders.FileHeader.NumberOfSections; SectionIdx++)
{
if (!ReadProcessMemory(hProcess, &pImageSectionHeader[SectionIdx], &ImageSectionHeader, sizeof(ImageSectionHeader), NULL)) goto End;
const char* SectionName = (const char*)ImageSectionHeader.Name;
if (strncmp(SectionName, gPDataSectionName, sizeof(gPDataSectionName)) == 0)
{
pModuleSectionInfo->pBase = pBase + ImageSectionHeader.VirtualAddress;
pModuleSectionInfo->Size = ImageSectionHeader.Misc.VirtualSize;
bResult = TRUE;
break;
}
}
End:
return (bResult);
}


BOOL getFunWithSepAddr(MODULEENTRY32 me32, ULONGLONG InputAddr, ULONGLONG& RetAddr)
{
BOOL bRet = FALSE;
ModuleSectionInfo_T ModuleSectionInfo;
ULONGLONG ulModuleBase = (ULONGLONG)me32.modBaseAddr;
ULONGLONG ulModuleSize = (ULONGLONG)me32.modBaseSize;

if (!getModulePdataSection(m_hProcess, me32.hModule, &ModuleSectionInfo)) goto End;

PVOID pCandidate;
IMAGE_IA64_RUNTIME_FUNCTION_ENTRY RuntimeEntry;

#pragma warning(push)
#pragma warning(disable:4305)
pCandidate = ModuleSectionInfo.pBase;
#pragma warning(pop)
unsigned int iCount = 0;

// 这里遍历的就是 SEH 表
for (iCount = 0; iCount < ModuleSectionInfo.Size; iCount += sizeof(IMAGE_IA64_RUNTIME_FUNCTION_ENTRY))
{
if (!ReadProcessMemory(m_hProcess, pCandidate, &RuntimeEntry, sizeof(IMAGE_IA64_RUNTIME_FUNCTION_ENTRY), NULL)) goto NextCandidate;

ULONGLONG begin = (ULONGLONG)ulModuleBase + RuntimeEntry.BeginAddress;
ULONGLONG end = (ULONGLONG)ulModuleBase + RuntimeEntry.EndAddress;

if (IsInThisFun(iCount, InputAddr, begin, end))
{
bRet = TRUE;
RetAddr = begin;
break;
}

NextCandidate:
pCandidate = (PVOID)((ULONGLONG)pCandidate + sizeof(IMAGE_IA64_RUNTIME_FUNCTION_ENTRY));
}
End:
return bRet;
}

【PE】PE文件结构6-异常表(Exception)
https://hodlyounger.github.io/wiki/PE/【PE】PE文件结构6-异常表(Exception)/
作者
mingming
发布于
2024年8月31日
许可协议