Rizin
unix-like reverse engineering framework and cli tools
windows-x64.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2022 GustavoLCR <gugulcr@gmail.com>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
4 #include <rz_debug.h>
5 #include <mdmp_windefs.h>
7 
8 static int is_pc_inside_module(const void *value, const void *list_data) {
9  const ut64 pc = *(const ut64 *)value;
10  const RzDebugMap *module = list_data;
11  return !(pc >= module->addr && pc < module->addr_end);
12 }
13 
14 #define CMP(x, y) (st64)((st64)x - ((PE64_RUNTIME_FUNCTION *)y)->EndAddress)
15 #define READ_AT(address, buf, size) dbg->iob.read_at(dbg->iob.io, address, buf, size)
16 
17 static inline bool init_module_runtime_functions(RzDebug *dbg, RzVector *functions, ut64 module_base) {
18  ut8 buf[4];
19 
20  const ut64 lfanew_offset = module_base + rz_offsetof(Pe64_image_dos_header, e_lfanew);
21  READ_AT(lfanew_offset, buf, sizeof(buf));
22  const ut64 pe_offset = module_base + rz_read_le32(buf);
23 
24  const ut64 exception_entry_offset = pe_offset + rz_offsetof(Pe64_image_nt_headers, optional_header.DataDirectory[PE_IMAGE_DIRECTORY_ENTRY_EXCEPTION]);
25  READ_AT(exception_entry_offset, buf, sizeof(buf));
26  const ut64 exception_table_va = module_base + rz_read_le32(buf);
27 
28  READ_AT(exception_entry_offset + 4, buf, sizeof(buf));
29  const ut32 exception_table_size = rz_read_le32(buf);
30  if (!exception_table_size || exception_table_size == UT32_MAX) {
31  return false;
32  }
33 
34  ut8 *section = malloc(exception_table_size);
35  if (!section) {
36  return false;
37  }
38 
39  rz_vector_fini(functions);
40  rz_vector_init(functions, sizeof(PE64_RUNTIME_FUNCTION), NULL, NULL);
41  if (!rz_vector_reserve(functions, exception_table_size / sizeof(PE64_RUNTIME_FUNCTION))) {
42  rz_vector_fini(functions);
43  free(section);
44  return false;
45  }
46 
47  if (!READ_AT(exception_table_va, section, exception_table_size)) {
48  rz_vector_fini(functions);
49  free(section);
50  return false;
51  }
52 
53  ut64 offset;
54  for (offset = 0; offset < exception_table_size; offset += sizeof(PE64_RUNTIME_FUNCTION)) {
59  if (!rfcn.BeginAddress) {
60  break;
61  }
62  rz_vector_push(functions, &rfcn);
63  }
64  free(section);
65  return true;
66 }
67 
68 static inline ut64 read_register(RzDebug *dbg, ut64 at) {
69  ut8 buf[8];
70  dbg->iob.read_at(dbg->iob.io, at, buf, sizeof(buf));
71  return rz_read_le64(buf);
72 }
73 
74 static inline ut32 read_slot32(RzDebug *dbg, PE64_UNWIND_INFO *info, int *index) {
75  ut32 ret = rz_read_le32(&info->UnwindCode[*index]);
76  *index += 2;
77  return ret;
78 }
79 
80 static inline ut16 read_slot16(RzDebug *dbg, PE64_UNWIND_INFO *info, int *index) {
81  ut16 ret = rz_read_le16(&info->UnwindCode[*index]);
82  *index += 1;
83  return ret;
84 }
85 
86 // Decides if current rsp or another register is used as the frame base (eg. rbp, etc)
87 static inline ut64 get_frame_base(const PE64_UNWIND_INFO *info, const struct context_type_amd64 *context, const ut64 function_address) {
88  const ut64 rip_offset_to_function = context->rip - function_address;
89  const ut64 *integer_registers = &context->rax;
90  if (!info->FrameRegister) {
91  // There is no register being used as the frame base, use rsp
92  return context->rsp;
93  } else if ((rip_offset_to_function >= info->SizeOfProlog) || ((info->Flags & PE64_UNW_FLAG_CHAININFO) != 0)) {
94  // There is a register being used as a frame base, and we definetly already set it
95  return integer_registers[info->FrameRegister] - info->FrameOffset * 16;
96  } else {
97  // There is a register being used as a frame base, unknown if we set it yet
98  int i;
99  // Find unwind of where frame register is being set
100  for (i = 0; i < info->CountOfCodes; i++) {
101  if (info->UnwindCode[i].UnwindOp == UWOP_SET_FPREG) {
102  break;
103  }
104  }
105  // Check if we already set the frame register
106  if (rip_offset_to_function >= info->UnwindCode[i].CodeOffset) {
107  return integer_registers[info->FrameRegister] - info->FrameOffset * 16;
108  } else {
109  return context->rsp;
110  }
111  }
112 }
113 
116  if (!info) {
117  return NULL;
118  }
119  READ_AT(at, (ut8 *)info, sizeof(*info));
120  const size_t unwind_code_array_sz = info->CountOfCodes * sizeof(PE64_UNWIND_CODE);
121  void *tmp = realloc(info, sizeof(PE64_UNWIND_INFO) + unwind_code_array_sz);
122  if (!tmp) {
123  free(info);
124  return NULL;
125  }
126  info = tmp;
127  READ_AT(at + rz_offsetof(PE64_UNWIND_INFO, UnwindCode), (ut8 *)info->UnwindCode, unwind_code_array_sz);
128  return info;
129 }
130 
131 static inline bool unwind_function(
132  RzDebug *dbg,
133  RzDebugFrame *frame,
134  PE64_RUNTIME_FUNCTION *rfcn,
135  struct context_type_amd64 *context,
136  const ut64 module_address,
137  const ut64 function_address) {
138 
139  bool is_chained = false;
140  ut64 *integer_registers = &context->rax;
141  ut64 machine_frame_start;
142  bool is_machine_frame = false;
143 
144  ut64 unwind_info_address = module_address + rfcn->UnwindInfoAddress;
145 
146  // Read initial unwind info structure
147  PE64_UNWIND_INFO *info = read_unwind_info(dbg, unwind_info_address);
148  if (!info) {
149  return false;
150  }
151 
152  // Get address that is used as the base for stack accesses
153  ut64 frame_base = get_frame_base(info, context, function_address);
154 
155 process_chained_info:
156  if (info->Version != 1 && info->Version != 2) {
157  // Version 1 found in user-space, version 2 in kernel
158  RZ_LOG_ERROR("Unwind info version (%" PFMT32d ") for function 0x%" PFMT64x " is not recognized\n",
159  (ut32)info->Version, function_address);
160  free(info);
161  return false;
162  }
163  int i = 0;
164  while (i < info->CountOfCodes) {
165  const PE64_UNWIND_CODE code = info->UnwindCode[i];
166  i++;
167  // Check if we are already past the prolog instruction
168  // If we are processing a chained scope, always process all of them
169  if (!is_chained && context->rip < function_address + code.CodeOffset) {
170  // Skip, as it wasn't executed yet
171  switch (code.UnwindOp) {
172  case UWOP_ALLOC_LARGE:
173  i++;
174  if (code.OpInfo) {
175  i++;
176  }
177  break;
178  case UWOP_SAVE_XMM128:
179  case UWOP_SAVE_NONVOL:
180  case UWOP_UNKNOWN1:
181  i++;
182  break;
185  case UWOP_UNKNOWN2:
186  i += 2;
187  break;
188  default:
189  break;
190  }
191  continue;
192  }
193  ut16 offset;
194  switch (code.UnwindOp) {
195  case UWOP_PUSH_NONVOL: /* info == register number */
196  integer_registers[code.OpInfo] = read_register(dbg, context->rsp);
197  context->rsp += 8;
198  break;
199  case UWOP_ALLOC_LARGE: /* info == unscaled or scaled, alloc size in next 1 or 2 slots */
200  if (code.OpInfo) {
201  context->rsp += read_slot32(dbg, info, &i);
202  } else {
203  context->rsp += read_slot16(dbg, info, &i) * 8;
204  }
205  break;
206  case UWOP_ALLOC_SMALL: /* info == size of allocation / 8 - 1 */
207  context->rsp += code.OpInfo * 8 + 8;
208  break;
209  case UWOP_SET_FPREG: /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
210  frame->bp = integer_registers[info->FrameRegister];
211  context->rsp = integer_registers[info->FrameRegister] - info->FrameOffset * 16;
212  break;
213  case UWOP_SAVE_NONVOL: /* info == register number, offset in next slot */
214  offset = read_slot16(dbg, info, &i) * 8;
215  integer_registers[code.OpInfo] = read_register(dbg, frame_base + offset);
216  break;
217  case UWOP_SAVE_XMM128: /* info == XMM reg number, offset in next slot */
218  case UWOP_UNKNOWN1: /* 1 extra slot */
219  i++;
220  break;
221  case UWOP_SAVE_NONVOL_FAR: /* info == register number, offset in next 2 slots */
222  offset = read_slot32(dbg, info, &i);
223  integer_registers[code.OpInfo] = read_register(dbg, frame_base + offset);
224  break;
225  case UWOP_SAVE_XMM128_FAR: /* info == XMM reg number, offset in next 2 slots */
226  case UWOP_UNKNOWN2: /* 2 extra slots */
227  i += 2;
228  break;
229  case UWOP_PUSH_MACHFRAME: /* info == 0: no error-code, 1: error-code */
230  if (code.OpInfo) {
231  context->rsp += 8;
232  }
233  is_machine_frame = true;
234  machine_frame_start = context->rsp + 40;
235  context->rip = read_register(dbg, context->rsp);
236  context->rsp = read_register(dbg, context->rsp + 24);
237  break;
238  }
239  }
240  if (info->Flags & PE64_UNW_FLAG_CHAININFO) {
241  if (i % 2) {
242  i++;
243  }
244 
245  // Read chained RUNTIME_FUNCTION
246  const ut64 chained_fcn_address = unwind_info_address + rz_offsetof(PE64_UNWIND_INFO, UnwindCode[i]);
247  ut8 buf[sizeof(PE64_RUNTIME_FUNCTION)];
248  READ_AT(chained_fcn_address, buf, sizeof(buf));
249 
250  // Get unwind info from the chained RUNTIME_FUNCTION
251  unwind_info_address = module_address + rz_read_le32(buf + rz_offsetof(PE64_RUNTIME_FUNCTION, UnwindInfoAddress));
252  free(info);
253  info = read_unwind_info(dbg, unwind_info_address);
254  if (!info) {
255  return false;
256  }
257 
258  // Make sure we process all the chained unwind ops
259  is_chained = true;
260  goto process_chained_info;
261  }
262  if (is_machine_frame) {
263  frame->size = machine_frame_start - frame->sp;
264  } else {
265  frame->size = context->rsp - frame->sp;
266  context->rip = read_register(dbg, context->rsp);
267  context->rsp += 8;
268  }
269  free(info);
270  return true;
271 }
272 
274  RzList *frames = *out_frames ? *out_frames : rz_list_newf(free);
275  *out_frames = frames;
276  if (!frames) {
277  return true;
278  }
280  if (!modules) {
281  return true;
282  }
283  if (!context->rsp) {
284  int arena_size;
285  ut8 *arena = rz_reg_get_bytes(dbg->reg, RZ_REG_TYPE_GPR, &arena_size);
286  if (!arena || arena_size < sizeof(*context)) {
288  free(arena);
289  return true;
290  }
291  memcpy(context, arena, sizeof(*context));
292  free(arena);
293  }
294  RzDebugMap *last_module = NULL;
295  RzVector functions;
296  rz_vector_init(&functions, 0, NULL, NULL);
297  bool ret = true;
298  while (true) {
300  if (!frame) {
301  break;
302  }
303  frame->addr = context->rip;
304  frame->sp = context->rsp;
305  rz_list_append(frames, frame);
306 
307  // Find in which module current rip is
309  if (!it) {
310  // Either broken stack or module info not avalable (PEB paged out, etc)
311  break;
312  }
314  if (!module) {
315  // Should never happen
316  break;
317  }
318  if (module != last_module) {
319  // Read runtime function entries for module
320  if (!init_module_runtime_functions(dbg, &functions, module->addr)) {
321  ret = false;
322  break;
323  }
324  last_module = module;
325  }
326  int index;
327  const ut64 offset_from_base = context->rip - module->addr;
328  rz_vector_upper_bound(&functions, offset_from_base, index, CMP);
329  if (index == rz_vector_len(&functions)) {
330  // Leaf function
331  frame->size = 0;
332  context->rip = read_register(dbg, context->rsp);
333  context->rsp += 8;
334  } else {
335  // Not a leaf function
336  PE64_RUNTIME_FUNCTION *rfcn = rz_vector_index_ptr(&functions, index);
337  ut64 function_address = module->addr + rfcn->BeginAddress;
338  for (index--; function_address > context->rip && index >= 0; index--) {
339  rfcn = rz_vector_index_ptr(&functions, index);
340  function_address = module->addr + rfcn->BeginAddress;
341  }
342  if (index < 0 && function_address > context->rip) {
343  ret = false;
344  break;
345  }
346  if (!unwind_function(dbg, frame, rfcn, context, module->addr, function_address)) {
347  break;
348  }
349  }
350  }
351  if (!ret) {
352  free(rz_list_pop(frames));
353  }
355  rz_vector_fini(&functions);
356  return ret;
357 }
lzma_index ** i
Definition: index.h:629
RZ_API ut8 * rz_reg_get_bytes(RzReg *reg, int type, int *size)
Definition: arena.c:8
RzBinInfo * info(RzBinFile *bf)
Definition: bin_ne.c:86
static int value
Definition: cmd_api.c:93
#define NULL
Definition: cris-opc.c:27
uint16_t ut16
uint32_t ut32
RzDebug * dbg
Definition: desil.c:30
RZ_API RzList * rz_debug_modules_list(RzDebug *dbg)
Definition: dmap.c:29
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
voidpf uLong offset
Definition: ioapi.h:144
voidpf void * buf
Definition: ioapi.h:138
uint8_t ut8
Definition: lh5801.h:11
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
RZ_API RZ_BORROW RzListIter * rz_list_find(RZ_NONNULL const RzList *list, const void *p, RZ_NONNULL RzListComparator cmp)
Returns RzListIter element which matches via the RzListComparator.
Definition: list.c:620
RZ_API RZ_OWN RzList * rz_list_newf(RzListFree f)
Returns a new initialized RzList pointer and sets the free method.
Definition: list.c:248
RZ_API void * rz_list_iter_get_data(RzListIter *list)
returns the value stored in the list element
Definition: list.c:42
RZ_API RZ_OWN void * rz_list_pop(RZ_NONNULL RzList *list)
Removes and returns the last element of the list.
Definition: list.c:376
RZ_API RZ_BORROW RzListIter * rz_list_append(RZ_NONNULL RzList *list, void *data)
Appends at the end of the list a new element.
Definition: list.c:288
RZ_API void rz_list_free(RZ_NONNULL RzList *list)
Empties the list and frees the list pointer.
Definition: list.c:137
void * realloc(void *ptr, size_t size)
Definition: malloc.c:144
void * malloc(size_t size)
Definition: malloc.c:123
modules
Definition: regress.py:20
#define PE_IMAGE_DIRECTORY_ENTRY_EXCEPTION
Definition: pe_specs.h:146
@ UWOP_SET_FPREG
Definition: pe_specs.h:690
@ UWOP_UNKNOWN2
Definition: pe_specs.h:694
@ UWOP_SAVE_XMM128_FAR
Definition: pe_specs.h:696
@ UWOP_ALLOC_LARGE
Definition: pe_specs.h:688
@ UWOP_PUSH_MACHFRAME
Definition: pe_specs.h:697
@ UWOP_SAVE_XMM128
Definition: pe_specs.h:695
@ UWOP_SAVE_NONVOL_FAR
Definition: pe_specs.h:692
@ UWOP_PUSH_NONVOL
Definition: pe_specs.h:687
@ UWOP_ALLOC_SMALL
Definition: pe_specs.h:689
@ UWOP_SAVE_NONVOL
Definition: pe_specs.h:691
@ UWOP_UNKNOWN1
Definition: pe_specs.h:693
#define PE64_UNW_FLAG_CHAININFO
Definition: pe_specs.h:703
static ut16 rz_read_le16(const void *src)
Definition: rz_endian.h:206
static ut32 rz_read_le32(const void *src)
Definition: rz_endian.h:239
static ut64 rz_read_le64(const void *src)
Definition: rz_endian.h:266
#define RZ_LOG_ERROR(fmtstr,...)
Definition: rz_log.h:58
@ RZ_REG_TYPE_GPR
Definition: rz_reg.h:21
#define RZ_IN
Definition: rz_types.h:50
#define RZ_NEW0(x)
Definition: rz_types.h:284
#define RZ_INOUT
Definition: rz_types.h:52
#define rz_offsetof(type, member)
Definition: rz_types.h:360
#define PFMT64x
Definition: rz_types.h:393
#define PFMT32d
Definition: rz_types.h:408
#define UT32_MAX
Definition: rz_types_base.h:99
static void * rz_vector_index_ptr(RzVector *vec, size_t index)
Definition: rz_vector.h:88
#define rz_vector_upper_bound(vec, x, i, cmp)
Definition: rz_vector.h:203
RZ_API void * rz_vector_reserve(RzVector *vec, size_t capacity)
Definition: vector.c:214
RZ_API void * rz_vector_push(RzVector *vec, void *x)
Definition: vector.c:197
RZ_API void rz_vector_fini(RzVector *vec)
Definition: vector.c:61
static size_t rz_vector_len(const RzVector *vec)
Definition: rz_vector.h:82
RZ_API void rz_vector_init(RzVector *vec, size_t elem_size, RzVectorFree free, void *free_user)
Definition: vector.c:33
Definition: inftree9.h:24
Definition: sftypes.h:77
RzReg * reg
Definition: rz_debug.h:286
RzIOBind iob
Definition: rz_debug.h:293
RzIOReadAt read_at
Definition: rz_io.h:240
RzIO * io
Definition: rz_io.h:232
static ut64 get_frame_base(const PE64_UNWIND_INFO *info, const struct context_type_amd64 *context, const ut64 function_address)
Definition: windows-x64.c:87
static ut32 read_slot32(RzDebug *dbg, PE64_UNWIND_INFO *info, int *index)
Definition: windows-x64.c:74
#define READ_AT(address, buf, size)
Definition: windows-x64.c:15
static bool backtrace_windows_x64(RZ_IN RzDebug *dbg, RZ_INOUT RzList **out_frames, RZ_INOUT struct context_type_amd64 *context)
Definition: windows-x64.c:273
static ut16 read_slot16(RzDebug *dbg, PE64_UNWIND_INFO *info, int *index)
Definition: windows-x64.c:80
static int is_pc_inside_module(const void *value, const void *list_data)
Definition: windows-x64.c:8
static bool init_module_runtime_functions(RzDebug *dbg, RzVector *functions, ut64 module_base)
Definition: windows-x64.c:17
static PE64_UNWIND_INFO * read_unwind_info(RzDebug *dbg, ut64 at)
Definition: windows-x64.c:114
static ut64 read_register(RzDebug *dbg, ut64 at)
Definition: windows-x64.c:68
static bool unwind_function(RzDebug *dbg, RzDebugFrame *frame, PE64_RUNTIME_FUNCTION *rfcn, struct context_type_amd64 *context, const ut64 module_address, const ut64 function_address)
Definition: windows-x64.c:131
#define CMP(x, y)
Definition: windows-x64.c:14
ut64(WINAPI *w32_GetEnabledXStateFeatures)()