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; DWORD EndAddress; DWORD UnwindInfoAddress; } 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 { OPTIONAL ULONG ExceptionHandler; OPTIONAL ULONG FunctionEntry; }; ULONG ExceptionData[1 ]; } UNWIND_INFO, *PUNWIND_INFO;
Version:异常展开信息的版本号,一般为0x001
Flags:共有四种标志:
当它为0x0的时候表示UNW_FLAG_NHANDLER,没有异常处理函数。
当它为0x1的时候表示UNW_FLAG_EHANDLER,有异常处理函数。
当它为0x2的时候表示UNW_FLAG_UHANDLER,有系统默认的处理函数。
当它为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;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; #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 ; 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; }