这不是直接针对 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
。
关于这个主题的一些好的建议:
针工具代码
/* ===================================================================== */
/* 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