C++ 结构运行时函数
我在IDA的运行时函数结构的.pdata段中发现了一个大数组。 所以,在那里我可以找到信息:从它编译的,我如何创建这个,以及如何在C++中使用它。C++ 结构运行时函数,c++,exception-handling,stack-unwinding,C++,Exception Handling,Stack Unwinding,我在IDA的运行时函数结构的.pdata段中发现了一个大数组。 所以,在那里我可以找到信息:从它编译的,我如何创建这个,以及如何在C++中使用它。 请给我一些书,或是有关于此结构的异常处理和解绕的详细说明和教程的链接。您可以在上找到有关运行时函数和相关结构的更多信息 typedef struct _UNWIND_INFO { UBYTE Version : 3; UBYTE Flags : 5; UBYTE SizeOfProlog;
请给我一些书,或是有关于此结构的异常处理和解绕的详细说明和教程的链接。您可以在上找到有关运行时函数和相关结构的更多信息
typedef struct _UNWIND_INFO {
UBYTE Version : 3;
UBYTE Flags : 5;
UBYTE SizeOfProlog;
UBYTE CountOfCodes; //so the beginning of ExceptionData is known as they're both FAMs
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)
//
OPTIONAL ULONG ExceptionData[];
} UNWIND_INFO, *PUNWIND_INFO;
这些结构由编译器生成并用于实现。在代码执行期间,可能会发生异常,运行时系统需要能够遍历调用堆栈以找到该异常的处理程序。为此,运行时系统需要知道函数序言的布局(它们保存的寄存器),以便正确地展开各个函数堆栈帧。更多详情请参阅
RUNTIME_函数是描述单个函数的结构,它包含展开函数所需的数据
如果您在运行时生成代码,并且需要使该代码可供运行时系统使用(因为您的代码调用可能引发异常的已编译代码),那么您可以为生成的每个函数创建实例,填写for each,然后通过调用Windows x64 SEH告诉运行时系统。
编译器在.exe
图像的.pdata
部分中放置一个,但它也可以放置在任何部分,如.rdata
,它由PE头nHeaders64->OptionalHeader.DataDirectory[image\u DIRECTORY\u ENTRY\u EXCEPTION].VirtualAddress指向。编译器使用RUNTIME\u函数
s填充异常目录
typedef struct _RUNTIME_FUNCTION {
ULONG BeginAddress;
ULONG EndAddress;
ULONG UnwindData;
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
每个RUNTIME\u函数
在图像中描述一个函数。程序中的每个函数(除叶函数外)都有一个,无论其中是否有SEH exception子句,因为异常可能发生在被调用函数中,因此您需要使用展开代码来访问可能具有SEH处理程序的调用函数,因此大多数函数将具有展开代码,而不是范围表BeginAddress
指向函数的开头,EndAddress
指向函数的结尾
展开数据指向一个\u UNWIND\u INFO
表结构
typedef struct _UNWIND_INFO {
UBYTE Version : 3;
UBYTE Flags : 5;
UBYTE SizeOfProlog;
UBYTE CountOfCodes; //so the beginning of ExceptionData is known as they're both FAMs
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)
//
OPTIONAL ULONG ExceptionData[];
} UNWIND_INFO, *PUNWIND_INFO;
标志可以是以下之一:
#define UNW_FLAG_NHANDLER 0
#define UNW_FLAG_EHANDLER 1
#define UNW_FLAG_UHANDLER 2
#define UNW_FLAG_FHANDLER 3
#define UNW_FLAG_CHAININFO 4
如果设置了UNW\u FLAG\u EHANDLER
,则ExceptionHandler
指向名为\uu C\u specific\u handler
的通用处理程序(从libcmt.lib导入),其目的是解析ExceptionData
,它是SCOPE\u TABLE
类型的灵活数组成员。如果设置了UNW\u标志\u UHANDLER
,则表明\u C\u特定的\u处理程序也将用于调用finally块,即函数中有finally块。如果设置了UNW\u FLAG\u CHAININFO
标志,则展开信息结构是辅助结构,并在共享异常处理程序/链接信息地址字段中包含一个图像相对指针,该指针指向指向主展开信息的运行时\u函数
条目。这用于非连续函数。UNW标志表示它是一个“帧处理程序”,我不知道那是什么
typedef struct _SCOPE_TABLE {
ULONG Count;
struct
{
ULONG BeginAddress;
ULONG EndAddress;
ULONG HandlerAddress;
ULONG JumpTarget;
} ScopeRecord[1];
} SCOPE_TABLE, *PSCOPE_TABLE;
SCOPE_表
结构是一个可变长度的结构,函数中的每个try块都有一个ScopeRecord
,它包含try块的开始和结束地址(可能是RVA)HandlerAddress
是对代码的一个偏移量,用于计算\uuuuuException
括号中的异常筛选器表达式(exception\uExecute\uHandler
表示始终运行exception,因此它类似于exception异常)而JumpTarget
是\u中除与\u try
块关联的块外的第一条指令的偏移量CountOfCodes
是必需的,因为UnwindCode
也是一个灵活的数组成员,没有其他方法可以知道该灵活数组成员之后的数据从何处开始。如果是try/finally块,则由于finally中没有过滤器,因此使用handleAddress
而不是JumpTarget
来指向finally块的副本,该副本由序言和尾声修饰(当在异常上下文中调用它时,而不是在到达try块末尾后调用它时,需要复制——这在异常情况下是不可能发生的,因为它在成功完成后永远不会运行,所以异常块总是单独的,并且没有原始副本)
处理器引发异常后,IDT中的异常处理程序会将异常信息传递给Windows中的主异常处理函数,该函数会为有问题的指令指针找到运行时函数
,并调用异常处理程序
。如果异常属于该函数而不是t结束语或开场白然后它将调用\u C\u specific\u处理程序
\u C\u specific\u处理程序
然后将开始遍历所有范围表
条目,搜索出错指令上的匹配项,并有望找到一个\u,但包含出错代码的语句除外。()
此外,对于嵌套异常,我认为\uu C\u specific\u处理程序
将始终找到覆盖当前故障指令的最小范围,并将在未处理异常的较大范围内展开。上面源代码上的\uu C\u specific\u处理程序
的实现显示了一个简单的迭代这些记录在实践中不会发生
还不清楚操作系统异常处理程序如何知道要查找哪个dll的异常目录。我想它可以使用RIP并咨询进程VAD,然后获取特定分配的第一个地址,并在其上调用RtlLookupFunctionEntry
__except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {//do something}