0

对不起,很长的帖子:)

我想为.exe文件编写一个 pintool,它会在异常情况下执行以下操作:

  1. 打印异常指令地址。
  2. 打印将处理此异常的处理程序的地址。
  3. 打印程序返回的指令地址。

我已经阅读了有关 Windows SEH 机制的所有内容,并且对 intel pin-tool 本身非常熟悉。为了开始,我编写了以下测试程序:

#include <stdio.h>
void bar(){
        throw 20;
}
void foo(char *s, int a){
    printf(s,a);
}
int main(){
    try{
        bar();
    }
    catch(int e){
        foo("%d\n",e );
    }
}

然后,我使用 pintool 打印了所有例程(在调用 main 之后调用的)以及该程序的返回地址。是列表:

然后我阅读了这些例程的文档。我认为这些例程的参数可能包含我需要的信息。但一切都是徒劳的。RtlRaiseException参数确实让我可以访问该ExceptionRecord结构,但它的ExceptionAddress字段包含起始地址RaiseException而不是bar.

而且我无法找到任何方法来获取将处理throw.

任何帮助表示赞赏;谢谢 :)

4

1 回答 1

1

这不是直接针对 PIN 解决方案的答案,而是关于如何找到处理程序的帮助。

以下是未经优化编译的代码的有趣部分(我printf在函数下方添加了一个,bar因此处理程序不太接近bar;使用 VS 2015 编译的代码):

CPU Disasm
Address    Command                                Comments
011D1071   CALL bar                               ; [bar
011D1076   PUSH OFFSET 011D2110                   ; ASCII "you wont see me..."
011D107B   CALL printf                            ; [printf
011D1080   ADD ESP,4
011D1083   JMP SHORT 011D109C
011D1085   MOV EAX,DWORD PTR SS:[EBP-14]          ; Handler start
011D1088   PUSH EAX
011D1089   PUSH OFFSET 011D2124                   ; ASCII "%d"
011D108E   CALL foo                               ; [foo

这是 bar() 函数的代码:

CPU Disasm
Address    Command                                Comments
011D1000 b PUSH EBP                               ; TestCppException.bar(void)
011D1001   MOV EBP,ESP
011D1003   PUSH ECX
011D1004   MOV DWORD PTR SS:[EBP-4],14
011D100B   PUSH OFFSET _TI1H                      ; /Arg2 = TestCppException._TI1H
011D1010   LEA EAX,[EBP-4]                        ; |
011D1013   PUSH EAX                               ; |Arg1
011D1014   CALL _CxxThrowException                ; \VCRUNTIME140._CxxThrowException
011D1019   MOV ESP,EBP
011D101B   POP EBP
011D101C   RETN

C++ 异常与其他异常(访问冲突、除以 0 等)不同,因为在到达处理程序(catch 部分)之前有很多事情发生在幕后。

如您所见,使用以下命令引发了 C++ 异常:

调用时RtlRaiseException,异常记录如下所示:

CPU Stack
Address   Value                Comments
0018FAF8  |E06D7363    ; ExceptionCode=E06D7363
0018FAFC  |00000001    ; Flags=EXCEPTION_NONCONTINUABLE
0018FB00  |00000000    ; pExceptionRecord=NULL
0018FB04  |7736DAA0    ; ExceptionAddress=KERNELBASE.RaiseException
0018FB08  |00000003    ; Nparm=3
0018FB0C  |19930520    ; exception version identifier
0018FB10  |0018FBA4    ; pObject
0018FB14  |011D2638    ; _ThrowInfo*
  • 异常代码 0xE06D7363标识 C++ 异常(所有 C++ 异常的代码相同)。

  • Flags设置为是EXCEPTION_NONCONTINUABLE因为 C++ 异常不支持继续(代码无法在异常发生的点继续执行)。

  • pExceptionRecord为 NULL,因为 CPP 异常中没有异常链接。

  • ExceptionAddress是因为它不是“直接”引发异常的RaiseException代码,而是系统为你做的(即:它不像你有一个除以 0 可以精确定位到一个非常具体的汇编指令;这里系统为您引发异常,并在 RaiseException 处引发)。

  • 此异常记录有 3 个参数:

    • 第一个是异常标识符(这是未记录的,但可能意味着这是一个 VC6 样式的异常)。

    • pObject 是一个指向抛出对象的指针(在我们的例子中,这是一个指向整数 20 的指针)

    • 最后是系统使用的_ThrowInfo结构(与传递给 的参数相同_CxxThrowException),更准确地说是异常调度程序,以查看哪个处理程序可以捕获此异常。

从这一点开始,我们有了“通常”的 SEH 内容,系统在其中分派异常并搜索正确的 SEH 处理程序。在我们的示例中,“哪个 catch 块可以捕获一个 int”。

碰巧链中的最后一个 SEH 可以处理抛出的 int。

这是 SEH 的装配视图:

CPU Disasm
Address    Command                           Comments
00CA1D91   MOV EAX,OFFSET 011D2580        ; pointer to __ehfuncinfo
00CA1D96   JMP __CxxFrameHandler3            ; Jump to VCRUNTIME140.__CxxFrameHandler

我们只有一个结构(通过eax寄存器)传递给名为__CxxFrameHandler3.

命名的结构__ehfuncinfo如下所示:

struct ehfuncinfo1200 //_s_ESTypeList
{
  /* 0x00 */  uint32_t        magic : 30;
  /* 0x04 */  ehstate_t       unwindtable_size;
  /* 0x08 */  unwindtable *   unwindtable;
  /* 0x0C */  size_t          tryblocktable_size;
  /* 0x10 */  tryblock *      tryblocktable;
  /* 0x14 */  size_t          _size;
  /* 0x18 */  void *          _;
/* … snip … */
};

在我们的例子中,我们有:

CPU 
Address    Value       Comments
011D2580   19930522    ; magic
011D2584   00000002    ; unwind table size 
011D2588   011D25A4    ; unwind table
011D258C   00000001    ; try block table size
011D2590   011D25B4    ; try block table
011D2594   00000000
011D2598   00000000

中的条目try block table如下所示:

struct tryblock
{
  ehstate_t   trylow;
  ehstate_t   tryhigh;
  ehstate_t   catchhigh;
  int         ncatches;
  ehandler *  catchsym;

  /* snip */
};

以下是我们示例中的值:

CPU Stack
Address   Value      Comments
011D25B4   00000000  
011D25B8   00000000
011D25BC   00000001  
011D25C0   00000001 ; ncatches
011D25C4   011D25C8 ; catchsym

catchsym字段是一个ehandler结构:

/// This type represents the catch clause
struct ehandler
{
//  union { uint32_t  adjectives; void * ptr; };
  uint32_t isconst      : 1; /* + 00 */
  uint32_t isvolatile   : 1;
  uint32_t isunaligned  : 1;
  uint32_t isreference  : 1;

  const type_info *   typeinfo; /* + 04 */
  ptrdiff_t           eobject_bpoffset; // 0 = no object (catch by type)
  generic_function_t *  handler; /* + 0x0C */

  /* snip */
};

注意:该handler字段是 catch 块的地址!

和价值:

CPU Stack
Address   Value        Comments
011D25C8   00000000    ; 
011D25CC   011D3030    ; Typeinfo (OFFSET TestCppException.int `RTTI Type Descriptor')
011D25D0   FFFFFFEC    
011D25D4   011D1085    ; Handler entry point

在我们的例子中,处理程序字段指向 catch 块的精确位置:

CPU Disasm
Address    Command                            Comments
011D1085   MOV EAX,DWORD PTR SS:[EBP-14]      ; handler start
011D1088   PUSH EAX
011D1089   PUSH OFFSET 011D2124               ; ASCII "%d"
011D108E   CALL foo                           ;

概括

如果发生异常(请参阅PIN 手册中的异常 API

  • 检查异常 ocde:如果是0xE06D7363则它是 C++ 异常
  • 等待__CxxFrameHandler3被调用(从技术上讲,它是该函数的 JMP,而不是 CALL)
  • 检查入境登记eax处。__CxxFrameHandler3
  • eax寄存器是指向结构的指针__ehfuncinfo
  • 跟随所有结构直到结构的handler领域ehandler
    • handler字段是地址catch block

关于这个主题的一些好的建议:


针工具代码

/* ===================================================================== */
/* This example demonstrates finding a function by name on Windows.      */
/* ===================================================================== */

#include "pin.H"
#include <iostream>
#include <fstream>

/*
* C++ exception structures
*/

// This type represents the catch clause
typedef struct _ehandler
{
    //  union { uint32_t  adjectives; void * ptr; };
    uint32_t            adjectives;         /* + 0x00 */
    const type_info *   typeinfo;           /* + 0x04 */
    ptrdiff_t           eobject_bpoffset;   /* + 0x08 */
    void*               handler;            /* + 0x0C */ 
    /* snip */
} ehandler;

typedef struct _tryblock
{
    uint32_t   trylow;
    uint32_t   tryhigh;
    uint32_t   catchhigh;
    int         ncatches;
    ehandler *  catchsym;
    /* snip */
} tryblock;

typedef struct ehfuncinfo1200 //_s_ESTypeList
{
    /* 0x00 */  uint32_t        magic : 30;
    /* 0x04 */  uint32_t       unwindtable_size;
    /* 0x08 */  void *   unwindtable;
    /* 0x0C */  size_t          tryblocktable_size;
    /* 0x10 */  tryblock *      tryblocktable;
    /* 0x14 */  size_t          _size;
    /* 0x18 */  void *          _;
    /* snip */
} ehfuncinfo;

/* ===================================================================== */
/* Global Variables */
/* ===================================================================== */

std::ofstream TraceFile;

/* ===================================================================== */
/* Commandline Switches */
/* ===================================================================== */

KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
    "o", "check_handler.out", "specify trace file name");

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    cerr << "This tool produces a trace of calls to RtlAllocateHeap.";
    cerr << endl << endl;
    cerr << KNOB_BASE::StringKnobSummary();
    cerr << endl;
    return -1;
}

/* ===================================================================== */
/* Analysis routines                                                     */
/* ===================================================================== */

VOID Before(CHAR * name, ADDRINT RegValue)
{
    TraceFile << "At function entry (" << name << "); EAX: " << hex << RegValue << endl;

    // eax is a pointer to ehfuncinfo struct
    ehfuncinfo* funcinfo = reinterpret_cast<ehfuncinfo*>(RegValue);

    // get the tryblock table from ehfuncinfo
    tryblock* tryb = funcinfo->tryblocktable;

    // get ehandler struct from try block
    ehandler* ehand = tryb->catchsym;

    // from ehandler structure, get handler address
    void* handler = ehand->handler;

    // save it to file
    TraceFile << "Handler Address: " << hex << handler << endl;
}

/* ===================================================================== */
/* Instrumentation routines                                              */
/* ===================================================================== */

VOID Image(IMG img, VOID *v)
{
    // Walk through the symbols in the symbol table.
    //
    for (SYM sym = IMG_RegsymHead(img); SYM_Valid(sym); sym = SYM_Next(sym))
    {
        string undFuncName = PIN_UndecorateSymbolName(SYM_Name(sym), UNDECORATION_NAME_ONLY);

        //  Find function.
        if (undFuncName == "__CxxFrameHandler3")
        {
            std::cout << "OK! found __CxxFrameHandler3" << std::endl;

            RTN allocRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym));

            if (RTN_Valid(allocRtn))
            {
                // Instrument to print the input argument value and the return value.
                RTN_Open(allocRtn);

                RTN_InsertCall(allocRtn, IPOINT_BEFORE, (AFUNPTR)Before,
                    IARG_ADDRINT, "__CxxFrameHandler3",
                    IARG_REG_VALUE, REG::REG_EAX,
                    IARG_END);

                RTN_Close(allocRtn);
            }
        }
    }
}

/* ===================================================================== */

VOID Fini(INT32 code, VOID *v)
{
    TraceFile.close();
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */

int main(int argc, char *argv[])
{
    // Initialize pin & symbol manager
    PIN_InitSymbols();
    if (PIN_Init(argc, argv))
    {
        return Usage();
    }

    // Write to a file since cout and cerr maybe closed by the application
    TraceFile.open(KnobOutputFile.Value().c_str());
    TraceFile << hex;
    TraceFile.setf(ios::showbase);

    // Register Image to be called to instrument functions.
    IMG_AddInstrumentFunction(Image, 0);
    PIN_AddFiniFunction(Fini, 0);

    // Never returns
    PIN_StartProgram();

    return 0;
}

/* ===================================================================== */
/* eof */
/* ===================================================================== */

输出

At function entry (__CxxFrameHandler3); EAX: 0xcc2580
Handler Address: 0x00cc1085

两者都匹配给定的代码(减去 ASLR 的偏移量):

 MOV EAX,OFFSET 011D2580        ; pointer to __ehfuncinfo

 ...

011D1085   MOV EAX,DWORD PTR SS:[EBP-14]  ; Handler start 

于 2016-06-10T10:20:43.183 回答