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
原文地址:https://www.cnblogs.com/hanawasakuraki/p/13682306.html