Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/143.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 结构运行时函数_C++_Exception Handling_Stack Unwinding - Fatal编程技术网

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;

我在IDA的运行时函数结构的.pdata段中发现了一个大数组。 所以,在那里我可以找到信息:从它编译的,我如何创建这个,以及如何在C++中使用它。
请给我一些书,或是有关于此结构的异常处理和解绕的详细说明和教程的链接。

您可以在上找到有关运行时函数和相关结构的更多信息

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}