1.概述
x86系统采用动态的方式构建SEH结构,相比而言x64系统下采用静态的方式处理SEH结构,它保存在PE文件中,通常在.pdata区段。因此本文的例子采用x64编译过的程序。
异常表在资源表的后面。
2.异常表解析
数据目录表的第四个元素指向异常表,RVA指向的是一个IMAGE_IA64_RUNTIME_FUNCTION_ENTRY的结构体,本例的RVA是0xA000(也就是.pdata段的起始位置),转换成offset是0x7a00。它的结构体是:
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;
找到0x7a00处,根据这个结构体可以分别找到这三个字段的值。
BeginAddress RVA:0x00001020
EndAddress RVA:0x00001063
UnwindInfoAddress RVA:0x00007fd0
转换为offset:
BeginAddress:0x420
EndAddress:0x463
UnwindInfoAddress:0x71d0
0x420-0x463没什么好说的,是异常处理函数的内容,UnwindInfoAddress指向的位置是用来描述BeginAddress与EndAddress之间的代码异常属性信息的UNWIND_INFO。UNWIND_INFO也叫作异常展开信息,此结构用来描述堆栈指针的记录属性与寄存器中保存的地址属性,它的结构体如下:
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:异常处理程序的数据