zoukankan      html  css  js  c++  java
  • x64下动态代码构建RUNTIME_FUNCTION

    x64调用约定下,不再使用stack frame pointer(如ebp) ,并且unwind info,seh table都是编译期生成。

    对于动态生成的代码,如执行shellcode等,异常表中没有RUMTIME_FUNCTION,编译器无法正常展开调用栈。

    因此需要手动构造动态代码的RUNTIME_FUNCTION信息,并调用系统函数RtlAddFunctionTable,向系统注册动态代码的栈展开或异常处理机制。

    网上讲解x64调用约定的文章很多,但是如何构造异常表的资料很少,自己找了一些别人的代码,做了改进。

    参考资料:

    RUNTIME_FUNCTION结构体详解 https://blog.csdn.net/tutucoo/article/details/83828700

    UNWIND_CODE结构体详解 https://docs.microsoft.com/en-us/previous-versions/ck9asaa9(v=vs.140)

    目前只有了最基础的unwind 和 单条 seh exceptionhandler。源代码如下:

      1 #include <iostream>
      2 #include <vector>
      3 
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <stdint.h>
      7 
      8 #include <windows.h>
      9 
     10 typedef uint8_t UBYTE;
     11 typedef uint16_t USHORT;
     12 
     13 typedef void(__cdecl* CallTestFunc)(void);
     14 
     15 typedef enum _UNWIND_OP_CODES {
     16     UWOP_PUSH_NONVOL = 0,
     17     UWOP_ALLOC_LARGE,       // 1
     18     UWOP_ALLOC_SMALL,       // 2
     19     UWOP_SET_FPREG,         // 3
     20     UWOP_SAVE_NONVOL,       // 4
     21     UWOP_SAVE_NONVOL_FAR,   // 5
     22     UWOP_SPARE_CODE1,       // 6
     23     UWOP_SPARE_CODE2,       // 7
     24     UWOP_SAVE_XMM128,       // 8
     25     UWOP_SAVE_XMM128_FAR,   // 9
     26     UWOP_PUSH_MACHFRAME     // 10
     27 } UNWIND_OP_CODES, *PUNWIND_OP_CODES;
     28 
     29 typedef union _UNWIND_CODE {
     30     struct {
     31         UBYTE CodeOffset;
     32         UBYTE UnwindOp : 4;
     33         UBYTE OpInfo : 4;
     34     };
     35     USHORT FrameOffset;
     36 } UNWIND_CODE, *PUNWIND_CODE;
     37 
     38 typedef struct _UNWIND_INFO {
     39     UBYTE Version : 3;
     40     UBYTE Flags : 5;
     41     UBYTE SizeOfProlog;
     42     UBYTE CountOfCodes;
     43     UBYTE FrameRegister : 4;
     44     UBYTE FrameOffset : 4;
     45     UNWIND_CODE UnwindCode[1];
     46     /*    UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
     47      *    OPTIONAL ULONG ExceptionHandler;
     48      *    OPTIONAL ULONG ExceptionData[]; */
     49 } UNWIND_INFO, *PUNWIND_INFO;
     50 
     51 typedef struct {
     52     uint8_t code[0x1000];
     53     RUNTIME_FUNCTION function_table[1];
     54     UNWIND_INFO unwind_info[1];
     55 } DYNSECTION;
     56 
     57 
     58 //    FILTER    HANDLER        FINALLY_HANDLER
     59 #define UNW_FLAG_NHANDLER 0x0        //    NO      NO           
     60 #define UNW_FLAG_EHANDLER 0x1        //    YES      YES
     61 #define UNW_FLAG_UHANDLER 0x2        //                            YES
     62 #define UNW_FLAG_CHAININFO 0x4        //    multi UNWIND_INFO
     63 
     64 struct STACK_FRAME_INFO {
     65     PVOID returnAddress;
     66     PVOID functionAddress;
     67 };
     68 
     69 struct CALL_STACK_INFO {
     70     PVOID stackBottom;
     71     PVOID stackTop;
     72     DWORD dwFrameCount;
     73     STACK_FRAME_INFO* pFrameList;
     74 };
     75 
     76 typedef ULONG(WINAPI *RTLWALKFRAMECHAIN)(OUT PVOID *Callers, IN ULONG Count, IN ULONG Flags);
     77 
     78 void GetCallStackInfo(CALL_STACK_INFO& callStack, DWORD dwMaxFrame)
     79 {
     80     std::vector<STACK_FRAME_INFO> vecFrames;
     81 #ifdef _M_IX86
     82     STACK_FRAME_INFO StackFrame;
     83     std::vector<PVOID> vecRetAddr;
     84     ULONG StackCount = 0;
     85     RTLWALKFRAMECHAIN pRtlWalkFrameChain =
     86         (RTLWALKFRAMECHAIN)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlWalkFrameChain");
     87 
     88     vecRetAddr.resize(50);
     89     StackCount = pRtlWalkFrameChain(&vecRetAddr[0], vecRetAddr.size(), 0);
     90 
     91     for (ULONG i = 0;i < StackCount;i++)
     92     {
     93         ZeroMemory(&StackFrame, sizeof(StackFrame));
     94 
     95         if (vecRetAddr[i] == nullptr)
     96         {
     97             break;
     98         }
     99 
    100         StackFrame.returnAddress = vecRetAddr[i];
    101 
    102         vecFrames.push_back(StackFrame);
    103     }
    104 
    105 #elif (_M_X64)
    106     CONTEXT                       Context;
    107     KNONVOLATILE_CONTEXT_POINTERS NvContext;
    108     UNWIND_HISTORY_TABLE          UnwindHistoryTable;
    109     PRUNTIME_FUNCTION             RuntimeFunction;
    110     PVOID                         HandlerData;
    111     ULONG64                       EstablisherFrame;
    112     ULONG64                       ImageBase;
    113     STACK_FRAME_INFO              StackFrame;
    114 
    115     RtlCaptureContext(&Context);
    116 
    117     ZeroMemory(&UnwindHistoryTable, sizeof(UNWIND_HISTORY_TABLE));
    118 
    119     for (ULONG Frame = 0; Frame <= dwMaxFrame; Frame++)
    120     {
    121         RuntimeFunction = RtlLookupFunctionEntry(
    122             Context.Rip,
    123             &ImageBase,
    124             &UnwindHistoryTable
    125         );
    126 
    127         ZeroMemory(&NvContext, sizeof(KNONVOLATILE_CONTEXT_POINTERS));
    128 
    129         if (!RuntimeFunction)
    130         {
    131             Context.Rip = (ULONG64)(*(PULONG64)Context.Rsp);
    132             Context.Rsp += 8;
    133         }
    134         else
    135         {
    136             RtlVirtualUnwind(
    137                 UNW_FLAG_NHANDLER,
    138                 ImageBase,
    139                 Context.Rip,
    140                 RuntimeFunction,
    141                 &Context,
    142                 &HandlerData,
    143                 &EstablisherFrame,
    144                 &NvContext);
    145         }
    146 
    147         if (!Context.Rip)
    148         {
    149             break;
    150         }
    151         
    152         ZeroMemory(&StackFrame, sizeof(StackFrame));
    153 
    154         StackFrame.returnAddress = (PVOID)Context.Rip;
    155         StackFrame.functionAddress = (PVOID)(ImageBase + RuntimeFunction->BeginAddress);
    156 
    157         vecFrames.push_back(StackFrame);
    158     }
    159 #endif
    160     
    161     callStack.pFrameList = (STACK_FRAME_INFO*)malloc(vecFrames.size() * sizeof(STACK_FRAME_INFO));
    162     if (!callStack.pFrameList)
    163     {
    164         callStack.dwFrameCount = 0;
    165         return;
    166     }
    167 
    168     callStack.dwFrameCount = vecFrames.size();
    169     for (DWORD dwFrame = 0; dwFrame < vecFrames.size(); dwFrame++)
    170     {
    171         CopyMemory(&callStack.pFrameList[dwFrame], &vecFrames[dwFrame], sizeof(STACK_FRAME_INFO));
    172     }
    173 }
    174 
    175 void Func1()
    176 {
    177     CALL_STACK_INFO callStack;
    178     ZeroMemory(&callStack, sizeof(callStack));
    179     GetCallStackInfo(callStack, 100);
    180 
    181     for (DWORD dwFrame = 0; dwFrame < callStack.dwFrameCount; dwFrame++)
    182     {
    183         printf(
    184             "FRAME %02x: FuncAddrss=%p ReturnAddress=%p 
    ",
    185             dwFrame,
    186             callStack.pFrameList[dwFrame].functionAddress,
    187             callStack.pFrameList[dwFrame].returnAddress);
    188     }
    189 
    190 }
    191 
    192 void DumpCallStack()
    193 {
    194     Func1();
    195 }
    196 
    197 DWORD TestForSehAddFunctionTable();
    198 DWORD TestForUnwindFunctionTable();
    199 
    200 int main()
    201 {
    202     TestForUnwindFunctionTable();
    203 
    204     ::system("pause");
    205 }
    206 
    207 #ifdef _M_X64
    208 
    209 DWORD TestForUnwindFunctionTable()
    210 {
    211     DYNSECTION *pDynData = (DYNSECTION*)VirtualAlloc(NULL, 0x2000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    212     uint8_t *code = pDynData->code;
    213     ULONG_PTR p = 0;
    214     code[p++] = 0x56; //push rsi
    215     code[p++] = 0x57; //push rdi
    216     code[p++] = 0x48;
    217     code[p++] = 0x81;
    218     code[p++] = 0xec;
    219     code[p++] = 0x88;
    220     code[p++] = 0x07;
    221     code[p++] = 0x00;
    222     code[p++] = 0x00; //sub rsp,788h
    223     ULONG_PTR prologSize = p;
    224     code[p++] = 0x48;
    225     code[p++] = 0xb8; //mov rax, xxxxxxxh
    226     ULONG_PTR returnAddress = p;
    227     for (int i = 0; i < sizeof(ULONG_PTR); i++)
    228     {
    229         code[p++] = 0x00;
    230     }
    231     code[p++] = 0x50; //push rax
    232     code[p++] = 0x48;
    233     code[p++] = 0xb8; //mov rax, xxxxxxxh
    234     ULONG_PTR targetAddress = p;
    235     for (int i = 0; i < sizeof(ULONG_PTR); i++)
    236     {
    237         code[p++] = 0x00;
    238     }
    239     code[p++] = 0x50; //push rax
    240     code[p++] = 0xc3; //ret
    241     ULONG_PTR offsetRip = p;
    242     code[p++] = 0x48;
    243     code[p++] = 0x81;
    244     code[p++] = 0xc4;
    245     code[p++] = 0x88;
    246     code[p++] = 0x07;
    247     code[p++] = 0x00;
    248     code[p++] = 0x00; //add rsp,788h
    249     code[p++] = 0x5f; //pop rdi
    250     code[p++] = 0x5e; //pop rsi
    251     code[p++] = 0xc3; //ret
    252     ULONG_PTR trampoline = p;
    253 
    254     UNWIND_INFO *pUnwindInfo = pDynData->unwind_info;
    255     pUnwindInfo[0].Version = 1;
    256     pUnwindInfo[0].Flags = UNW_FLAG_NHANDLER;
    257     pUnwindInfo[0].SizeOfProlog = (UBYTE)prologSize;
    258     pUnwindInfo[0].CountOfCodes = 3;
    259     pUnwindInfo[0].FrameRegister = 0;
    260     pUnwindInfo[0].FrameOffset = 0;
    261 
    262     DWORD64 dyn_base = (DWORD64)pDynData;    
    263 
    264     UNWIND_CODE *pUnwindCode = pUnwindInfo->UnwindCode;
    265 
    266     //pUnwindCode[2].CodeOffset = 10;
    267     //pUnwindCode[2].UnwindOp = UWOP_ALLOC_LARGE;
    268     //pUnwindCode[2].OpInfo = 0;
    269     //
    270     //pUnwindCode[1].CodeOffset = 2;
    271     //pUnwindCode[1].UnwindOp = UWOP_PUSH_NONVOL;
    272     //pUnwindCode[1].OpInfo = 7;
    273     //
    274     //pUnwindCode[0].CodeOffset = 1;
    275     //pUnwindCode[0].UnwindOp = UWOP_PUSH_NONVOL;
    276     //pUnwindCode[0].OpInfo = 6;
    277         // push xxx must before sub rsp, xxx
    278         pUnwindCode[0].FrameOffset = 0x6001;
    279         pUnwindCode[1].FrameOffset = 0x7002;
    280     pUnwindCode[2].FrameOffset = 0x010a;
    281     
    282     
    283 
    284     RUNTIME_FUNCTION *function_table = pDynData->function_table;
    285     function_table[0].BeginAddress = (DWORD64)&code[0] - dyn_base; // set RVA of dynamic code start
    286     function_table[0].EndAddress = (DWORD64)&code[trampoline] - dyn_base; // RVA of dynamic code end
    287     function_table[0].UnwindInfoAddress = (DWORD64)pUnwindCode - dyn_base; // RVA of unwind info
    288 
    289     *(DWORD64*)&code[returnAddress] = (DWORD64)code + offsetRip;
    290     *(DWORD64*)&code[targetAddress] = (DWORD64)DumpCallStack; // VA of target
    291 
    292     if (!RtlAddFunctionTable(function_table, 1, dyn_base)) {
    293         printf("RtlAddFunctionTable() failed, exit.
    ");
    294         exit(EXIT_FAILURE);
    295     }
    296 
    297     void(*call)() = (void(*)())code;
    298     (*call)();
    299 
    300     return 0;
    301 }
    302 
    303 static EXCEPTION_DISPOSITION handler(
    304     PEXCEPTION_RECORD ExceptionRecord, 
    305     ULONG64 EstablisherFrame, 
    306     PCONTEXT ContextRecord, 
    307     PDISPATCHER_CONTEXT DispatcherContext
    308 ) 
    309 {
    310     printf("handler!
    ");
    311     ContextRecord->Rip += 3;
    312     return ExceptionContinueExecution;
    313 }
    314 
    315 DWORD TestForSehAddFunctionTable()
    316 {
    317     int ret;
    318     RUNTIME_FUNCTION *q;
    319     DYNSECTION *dynsection = (DYNSECTION*)VirtualAlloc(NULL, 0x2000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    320 
    321     uint8_t *code = dynsection->code;
    322     size_t p = 0;
    323     code[p++] = 0xb8; // mov rax, 42
    324     code[p++] = 0x2a;
    325     code[p++] = 0x00;
    326     code[p++] = 0x00;
    327     code[p++] = 0x00;
    328     code[p++] = 0xc6; // mov byte [rax], 0  -- raises exception!
    329     code[p++] = 0x00;
    330     code[p++] = 0x00;
    331     code[p++] = 0xc3; // ret
    332 
    333     size_t trampoline = p;
    334     code[p++] = 0x48; // mov rax, 
    335     code[p++] = 0xb8;
    336     size_t patch_handler_address = p;
    337     code[p++] = 0x00; // address to handler patched here
    338     code[p++] = 0x00;
    339     code[p++] = 0x00;
    340     code[p++] = 0x00;
    341     code[p++] = 0x00;
    342     code[p++] = 0x00;
    343     code[p++] = 0x00;
    344     code[p++] = 0x00;
    345     code[p++] = 0xff; // jmp rax
    346     code[p++] = 0xe0;
    347 
    348     DWORD64 dyn_base = 0;
    349     q = RtlLookupFunctionEntry((DWORD64)code, &dyn_base, NULL);
    350     printf("lookup 'code' %p %llx
    ", q, dyn_base); // no function table entry
    351 
    352     DWORD64 image_base = 0;
    353     q = RtlLookupFunctionEntry((DWORD64)main, &image_base, NULL);
    354     printf("lookup 'main' %p %llx
    ", q, image_base); // there is a function table entry
    355 
    356     dyn_base = (DWORD64)dynsection;
    357     UNWIND_INFO *unwind_info = dynsection->unwind_info;
    358     unwind_info[0].Version = 1;
    359     unwind_info[0].Flags = UNW_FLAG_EHANDLER;
    360     unwind_info[0].SizeOfProlog = 0;
    361     unwind_info[0].CountOfCodes = 0;
    362     unwind_info[0].FrameRegister = 0;
    363     unwind_info[0].FrameOffset = 0;
    364     *(DWORD *)&unwind_info[0].UnwindCode = (DWORD64)&code[trampoline] - dyn_base;
    365 
    366     RUNTIME_FUNCTION *function_table = dynsection->function_table;
    367     function_table[0].BeginAddress = (DWORD64)&code[0] - dyn_base; // set RVA of dynamic code start
    368     function_table[0].EndAddress = (DWORD64)&code[trampoline] - dyn_base; // RVA of dynamic code end
    369     function_table[0].UnwindInfoAddress = (DWORD64)unwind_info - dyn_base; // RVA of unwind info
    370 
    371     *(DWORD64 *)&code[patch_handler_address] = (DWORD64)handler; // VA of handler
    372 
    373     printf("main VA %016llx
    ", (DWORD64)main);
    374     printf("code VA %016llx
    ", (DWORD64)code);
    375     printf("function table VA %016llx
    ", (DWORD64)function_table);
    376     printf("unwind info VA %016llx
    ", (DWORD64)unwind_info);
    377     printf("handler VA %016llx
    ", (DWORD64)handler);
    378     printf("RUNTIME_FUNCTION begin RVA %08x, end RVA %08x, unwind RVA %08x
    ",
    379         function_table[0].BeginAddress, function_table[0].EndAddress,
    380         function_table[0].UnwindInfoAddress);
    381     printf("UNWIND_INFO handler RVA %08x
    ", *(DWORD *)&unwind_info[0].UnwindCode);
    382 
    383     if (!RtlAddFunctionTable(function_table, 1, dyn_base)) {
    384         printf("RtlAddFunctionTable() failed, exit.
    ");
    385         exit(EXIT_FAILURE);
    386     }
    387 
    388     q = RtlLookupFunctionEntry((DWORD64)code, &dyn_base, NULL);
    389     printf("lookup 'code' %p %llx
    ", q, dyn_base); // should return address of function table entry
    390 
    391     uint64_t(*call)() = (uint64_t(*)()) code;
    392     uint64_t result = (*call)();
    393     printf("result = %llx
    ", result);
    394 
    395     if (!RtlDeleteFunctionTable(function_table)) {
    396         printf("RtlDeleteFunctionTable() failed, exit.
    ");
    397         exit(EXIT_FAILURE);
    398     }
    399 
    400     return EXIT_SUCCESS;
    401 }
    402 #endif
  • 相关阅读:
    struts2中拦截器与过滤器之间的区别
    使用struts2中默认的拦截器以及自定义拦截器
    图解Tomcat类加载机制
    Eclipse项目中引用第三方jar包时将项目打包成jar文件的两种方式
    SQL中的四种连接方式
    My97datepicker日期控件
    Java中如何判断一个日期字符串是否是指定的格式
    jxl导入/导出excel
    优化myeclipse启动速度以及解决内存不足问题
    170726、常用 Git 命令清单
  • 原文地址:https://www.cnblogs.com/hanawasakuraki/p/13682306.html
Copyright © 2011-2022 走看看