栈回溯----X64

来源:互联网 发布:北京linux培训机构 编辑:程序博客网 时间:2024/06/16 02:20

通过X64-SEH 的学习(http://blog.csdn.net/qq_18218335/article/details/72722320)我们知道了一个函数RtlVirtualUnwind,虚拟展开函数,该函数根据epilog 和 prolog 进行虚拟的寄存器操作(在一个CONTEXT结构体中),函数执行完,CONTEXT结构体中即为函数虚拟执行完后寄存器应该有的状态,其中rip 和 rsp、rbp等与运行堆栈有关的寄存器可用于得到X64 函数调用堆栈,循环调用RtlVirtualUnwind即可得到我们想要的调用堆栈(http://blog.csdn.net/qq_18218335/article/details/71598945)。

#include <stdio.h>
#include <Windows.h>

//    FILTER    HANDLER        FINALLY_HANDLER
#define UNW_FLAG_NHANDLER 0x0        //    NO      NO           
#define UNW_FLAG_EHANDLER 0x1        //    YES      YES
#define UNW_FLAG_UHANDLER 0x2        //                            YES
#define UNW_FLAG_CHAININFO 0x4        //    多个UNWIND_INFO
void Show(PVOID dwAddress)
{
    printf("Call指令地址:%p\r\n",dwAddress);
}

VOID
    StackTrace64(
    VOID
    )
{
    CONTEXT                       Context;
    KNONVOLATILE_CONTEXT_POINTERS NvContext;
    UNWIND_HISTORY_TABLE          UnwindHistoryTable;
    PRUNTIME_FUNCTION             RuntimeFunction;
    PVOID                         HandlerData;
    ULONG64                       EstablisherFrame;
    ULONG64                       ImageBase;


    // 首先得到当前Context

    RtlCaptureContext(&Context);

    //
     // 初始化 UnwindHistoryTable,这个结构体主要用于多次查找RUNTIME_FUNCTION时加快查找效率
    //

    RtlZeroMemory(
        &UnwindHistoryTable,
        sizeof(UNWIND_HISTORY_TABLE));


    // 下面的循环就是得到调用堆栈

    for (ULONG Frame = 0;
        ;
        Frame++)
    {
        //
         // 在PE+ 的.pdata 段中找到函数对应的RuntimeFunction 结构
        // 只有叶函数没有此结构
        // 既不调用函数、又没有修改栈指针,也没有使用 SEH 的函数就叫做“叶函数”。
        RuntimeFunction = RtlLookupFunctionEntry(
            Context.Rip,
            &ImageBase,
            &UnwindHistoryTable
             );

        RtlZeroMemory(
            &NvContext,
             sizeof(KNONVOLATILE_CONTEXT_POINTERS));

        if (!RuntimeFunction)
        {
            //
            // 我们没有得到结构体,我们当前得到了一个叶函数
            // 此时Rsp 直接指向Rip,调用叶函数只包含Call 操作,即只包含push eip 操作,Rsp += 8即可

            Context.Rip  = (ULONG64)(*(PULONG64)Context.Rsp);
            Context.Rsp += 8;
        }
        else
        {
            //
            // 虚拟展开,得到调用堆栈
            // 第一个参数表示Rip 所在函数没有过滤和处理函数
            // 仅仅进行虚拟展开得到上层调用堆栈
            RtlVirtualUnwind(
                UNW_FLAG_NHANDLER,
                ImageBase,
                Context.Rip,
                 RuntimeFunction,
                &Context,
                 &HandlerData,
                &EstablisherFrame,
                 &NvContext);
        }

        //
        // 没有得到Rip 即是调用失败
        //
        if (!Context.Rip)
            break;

        //
        // 展示相关信息
        //
        printf(
            "FRAME %02x: CallAddress=%p\r\n",
            Frame,
            Context.Rip);
    }
    getchar();
    getchar();

    return;
}

void Test2()
{
    StackTrace64();
}
void Test1()
{
    Test2();
}
int main()
{

    Test1();
    return 0;
}

FRAME 00: CallAddress=00007FF7E5691159

FRAME 01: CallAddress=00007FF7E5691169

FRAME 02: CallAddress=00007FF7E5691179

FRAME 03: CallAddress=00007FF7E569130A

FRAME 04: CallAddress=00007FF851C22774

FRAME 05: CallAddress=00007FF854410D61

0:001> u 0x00007FF7E5691159 - 5

Show!Test2+0x4 [f:\program\show\show\show.cpp @ 109]:

00007ff7`e5691154 e8a7feffff call Show!StackTrace64 (00007ff7`e5691000)

00007ff7`e5691159 4883c428 add rsp,28h

00007ff7`e569115d c3 ret

00007ff7`e569115e cc int 3

0:001> u 0x00007FF7E5691169 - 5

Show!Test1+0x4 [f:\program\show\show\show.cpp @ 113]:

00007ff7`e5691164 e8e7ffffff call Show!Test2 (00007ff7`e5691150)

00007ff7`e5691169 4883c428 add rsp,28h

00007ff7`e569116d c3 ret

0:001> u 0x00007FF854410D61 - 5

ntdll!RtlUserThreadStart+0x1c:

00007ff8`54410d5c 159fe20f00 adc eax,0FE29Fh

00007ff8`54410d61 eb20 jmp ntdll!RtlUserThreadStart+0x43 (00007ff8`54410d83)

00007ff8`54410d63 488bca mov rcx,rdx

00007ff8`54410d66 498bc1 mov rax,r9

00007ff8`54410d69 ff1591e20f00 call qword ptr [ntdll!_guard_dispatch_icall_fptr (00007ff8`5450f000)]

00007ff8`54410d6f 8bc8 mov ecx,eax

00007ff8`54410d71 e87accfcff call ntdll!RtlExitUserThread (00007ff8`543dd9f0)

00007ff8`54410d76 90 nop

同样的,我们已经掌握了X64 栈回溯的方法。

原创粉丝点击