def _dump_debug_frames_interp(self): """ Dump the interpreted (decoded) frame information from .debug_frame """ if not self._dwarfinfo.has_CFI(): return self._emitline('Contents of the .debug_frame section:') for entry in self._dwarfinfo.CFI_entries(): if isinstance(entry, CIE): self._emitline('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (entry.offset, entry['length'], entry['CIE_id'], bytes2str(entry['augmentation']), entry['code_alignment_factor'], entry['data_alignment_factor'], entry['return_address_register'])) ra_regnum = entry['return_address_register'] else: # FDE self._emitline( '\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (entry.offset, entry['length'], entry['CIE_pointer'], entry.cie.offset, entry['initial_location'], entry['initial_location'] + entry['address_range'])) ra_regnum = entry.cie['return_address_register'] # Print the heading row for the decoded table self._emit(' LOC') self._emit(' ' if entry.structs.address_size == 4 else ' ') self._emit(' CFA ') # Decode the table nad look at the registers it describes. # We build reg_order here to match readelf's order. In particular, # registers are sorted by their number, and the register matching # ra_regnum is always listed last with a special heading. decoded_table = entry.get_decoded() reg_order = sorted( ifilter(lambda r: r != ra_regnum, decoded_table.reg_order)) # Headings for the registers for regnum in reg_order: self._emit('%-6s' % describe_reg_name(regnum)) self._emitline('ra ') # Now include ra_regnum in reg_order to print its values similarly # to the other registers. reg_order.append(ra_regnum) for line in decoded_table.table: self._emit( self._format_hex(line['pc'], fullhex=True, lead0x=False)) self._emit(' %-9s' % describe_CFI_CFA_rule(line['cfa'])) for regnum in reg_order: if regnum in line: s = describe_CFI_register_rule(line[regnum]) else: s = 'u' self._emit('%-6s' % s) self._emitline() self._emitline()
def _process_subprogram_tag(die, section_offset, M, global_var_data): if die.tag != 'DW_TAG_subprogram': return F = M.funcs.add() F.ea = 0 F.name = get_name(die) F.is_entrypoint = 0 has_frame = False frame_regname = "" if 'DW_AT_frame_base' in die.attributes: frame_attr = die.attributes['DW_AT_frame_base'] has_frame = True loc_expr = "{}".format( describe_DWARF_expr(frame_attr.value, die.cu.structs)).split(' ') if loc_expr[0][1:][:-1] == "DW_OP_call_frame_cfa": lowpc_attr = die.attributes['DW_AT_low_pc'] #DEBUG("loc_expr {0} {1:x}".format(loc_expr, lowpc_attr.value)) frame = EH_FRAMES[ lowpc_attr.value] if lowpc_attr.value in EH_FRAMES else None if frame: DEBUG("{0:x}, {1}".format(frame['initial_location'], frame)) for instr in frame.instructions: name = instruction_name(instr.opcode) if name == 'DW_CFA_def_cfa_register': frame_regname = describe_reg_name( instr.args[0], None, False) for child in die.iter_children(): if child.tag != 'DW_TAG_variable': continue stackvar = F.stack_vars.add() stackvar.name = get_name(child) stackvar.sp_offset = 0 stackvar.has_frame = has_frame stackvar.reg_name = frame_regname (type, size, offset) = get_types(child) stackvar.size = size if size > 0 else 0 if 'DW_AT_location' in child.attributes: attr = child.attributes['DW_AT_location'] if attr.form not in ('DW_FORM_data4', 'DW_FORM_data8', 'DW_FORM_sec_offset'): loc_expr = "{}".format( describe_DWARF_expr(attr.value, child.cu.structs)).split(' ') if loc_expr[0][1:][:-1] == 'DW_OP_fbreg': offset = int(loc_expr[1][:-1]) stackvar.sp_offset = offset
def _process_subprogram_tag(die, section_offset, M, global_var_data): if die.tag != 'DW_TAG_subprogram': return F = M.funcs.add() F.ea = 0 F.name = get_name(die) F.is_entrypoint = 0 has_frame = False frame_regname = "" if 'DW_AT_frame_base' in die.attributes: frame_attr = die.attributes['DW_AT_frame_base'] has_frame = True loc_expr = "{}".format(describe_DWARF_expr(frame_attr.value, die.cu.structs)).split(' ') if loc_expr[0][1:][:-1] == "DW_OP_call_frame_cfa": lowpc_attr = die.attributes['DW_AT_low_pc'] #DEBUG("loc_expr {0} {1:x}".format(loc_expr, lowpc_attr.value)) frame = EH_FRAMES[lowpc_attr.value] if lowpc_attr.value in EH_FRAMES else None if frame: DEBUG("{0:x}, {1}".format(frame['initial_location'], frame)) for instr in frame.instructions: name = instruction_name(instr.opcode) if name == 'DW_CFA_def_cfa_register': frame_regname = describe_reg_name(instr.args[0], None, False) for child in die.iter_children(): if child.tag != 'DW_TAG_variable': continue stackvar = F.stack_vars.add() stackvar.name = get_name(child) stackvar.sp_offset = 0 stackvar.has_frame = has_frame stackvar.reg_name = frame_regname (type, size, offset) = get_types(child) stackvar.size = size if size > 0 else 0 if 'DW_AT_location' in child.attributes: attr = child.attributes['DW_AT_location'] if attr.form not in ('DW_FORM_data4', 'DW_FORM_data8', 'DW_FORM_sec_offset'): loc_expr = "{}".format(describe_DWARF_expr(attr.value, child.cu.structs)).split(' ') if loc_expr[0][1:][:-1] == 'DW_OP_fbreg': offset = int(loc_expr[1][:-1]) stackvar.sp_offset = offset
def _full_reg_name(regnum): regname = describe_reg_name(regnum, None, False) if regname: return 'r%s (%s)' % (regnum, regname) else: return 'r%s' % regnum
def _dump_debug_frames_interp(self): """ Dump the interpreted (decoded) frame information from .debug_frame """ if not self._dwarfinfo.has_CFI(): return self._emitline('Contents of the .debug_frame section:') for entry in self._dwarfinfo.CFI_entries(): if isinstance(entry, CIE): self._emitline('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % ( entry.offset, entry['length'], entry['CIE_id'], bytes2str(entry['augmentation']), entry['code_alignment_factor'], entry['data_alignment_factor'], entry['return_address_register'])) ra_regnum = entry['return_address_register'] else: # FDE self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % ( entry.offset, entry['length'], entry['CIE_pointer'], entry.cie.offset, entry['initial_location'], entry['initial_location'] + entry['address_range'])) ra_regnum = entry.cie['return_address_register'] # Print the heading row for the decoded table self._emit(' LOC') self._emit(' ' if entry.structs.address_size == 4 else ' ') self._emit(' CFA ') # Decode the table nad look at the registers it describes. # We build reg_order here to match readelf's order. In particular, # registers are sorted by their number, and the register matching # ra_regnum is always listed last with a special heading. decoded_table = entry.get_decoded() reg_order = sorted(ifilter( lambda r: r != ra_regnum, decoded_table.reg_order)) # Headings for the registers for regnum in reg_order: self._emit('%-6s' % describe_reg_name(regnum)) self._emitline('ra ') # Now include ra_regnum in reg_order to print its values similarly # to the other registers. reg_order.append(ra_regnum) for line in decoded_table.table: self._emit(self._format_hex( line['pc'], fullhex=True, lead0x=False)) self._emit(' %-9s' % describe_CFI_CFA_rule(line['cfa'])) for regnum in reg_order: if regnum in line: s = describe_CFI_register_rule(line[regnum]) else: s = 'u' self._emit('%-6s' % s) self._emitline() self._emitline()
def dump_eh_frame_table(entry): """ dumps an interpreted EH_CFI entry """ if isinstance(entry, CIE): emitline( '\n%08x %s %s CIE "%s" cf=%d df=%d ra=%d' % (entry.offset, format_hex(entry['length'], fullhex=True, lead0x=False), format_hex(entry['CIE_id'], fullhex=True, lead0x=False), bytes2str(entry['augmentation']), entry['code_alignment_factor'], entry['data_alignment_factor'], entry['return_address_register'])) ra_regnum = entry['return_address_register'] else: # FDE emitline( '\n%08x %s %s FDE cie=%08x pc=%s..%s' % (entry.offset, format_hex(entry['length'], fullhex=True, lead0x=False), format_hex(entry['CIE_pointer'], fullhex=True, lead0x=False), entry.cie.offset, format_hex(entry['initial_location'], fullhex=True, lead0x=False), format_hex(entry['initial_location'] + entry['address_range'], fullhex=True, lead0x=False))) ra_regnum = entry.cie['return_address_register'] # Print the heading row for the decoded table emit(' LOC') emit(' ' if entry.structs.address_size == 4 else ' ') emit(' CFA ') # Decode the table nad look at the registers it describes. # We build reg_order here to match readelf's order. In particular, # registers are sorted by their number, and the register matching # ra_regnum is always listed last with a special heading. decoded_table = entry.get_decoded() # print ("\n\nDecoded table:\n"+(str(decoded_table))+"\n\n") reg_order = sorted( ifilter(lambda r: r != ra_regnum, decoded_table.reg_order)) if len(decoded_table.reg_order): # Headings for the registers for regnum in reg_order: emit('%-6s' % describe_reg_name(regnum)) emitline('ra ') # Now include ra_regnum in reg_order to print its values similarly # to the other registers. reg_order.append(ra_regnum) else: emitline() for line in decoded_table.table: emit(format_hex(line['pc'], fullhex=True, lead0x=False)) emit(' %-9s' % describe_CFI_CFA_rule(line['cfa'])) for regnum in reg_order: if regnum in line: s = describe_CFI_register_rule(line[regnum]) else: s = 'u' emit('%-6s' % s) emitline() emitline()
def _handle_stack(self, top, stack): opcode_name = top.opcode_name args = top.args # register location if opcode_name[:9] == "DW_OP_reg": regnum = opcode_name[9:] if opcode_name[9:] != 'x' else args[0] regname = describe_reg_name(int(regnum), _MACHINE_ARCH) stack.append(OpPiece(REG, regname, regname, None)) # register + computation elif opcode_name[:10] == "DW_OP_breg": regnum = opcode_name[10:] if opcode_name[10:] != 'x' else args[0] offset = args[0] if regnum != 'x' else args[1] regname = describe_reg_name(int(regnum), _MACHINE_ARCH) separator = "+" if offset >= 0 else "" stack.append( OpPiece(REG, regname, regname + separator + hex(offset), None)) # frame base offset elif opcode_name == "DW_OP_fbreg": offset = args[0] separator = "+" if offset >= 0 else "" stack.append( OpPiece(ADDR, "frame base", "frame base" + separator + hex(offset), None)) # address location elif opcode_name[:10] == "DW_OP_addr": addr = args[0] stack.append(OpPiece(ADDR, hex(addr), hex(addr), None)) # composite locations elif opcode_name == "DW_OP_piece": size = args[0] prev_piece = stack.pop() new_piece = OpPiece(prev_piece.format, prev_piece.key, prev_piece.value, size) stack.append(new_piece) elif opcode_name == "DW_OP_bit_piece": return None, True # known value, not location elif opcode_name == "DW_OP_implicit_value": length = args[0] for i in range(length): stack.push(OpPiece(VALUE, args[i + 1], args[i + 1], None)) elif opcode_name == "DW_OP_stack_value" or opcode_name == "OP:0x9f": prev_piece = stack.pop() # prev_piece.format = VALUE stack.append(prev_piece) return stack, True # terminate ## literal encodings; DWARF4 2.5.1.1 elif opcode_name[:9] == "DW_OP_lit": stack.append(opcode_name[9]) elif opcode_name[:11] == "DW_OP_const": const = args[0] stack.append(const) ## stack ops; 2.5.1.3 elif opcode_name == "DW_OP_dup": stack.append(stack[-1]) elif opcode_name == "DW_OP_drop": stack.pop() elif opcode_name == "DW_OP_pick": index = args[0] stack.append(stack[index]) elif opcode_name == "DW_OP_over": stack.append(stack[-2]) elif opcode_name == "DW_OP_swap": old_order = stack[-2:] for piece in old_order[::-1]: stack.append(piece) elif opcode_name == "DW_OP_rot": old_order = stack[-3:] for piece in old_order[::-1]: stack.append(piece) elif opcode_name == "DW_OP_deref": return None, True elif opcode_name == "DW_OP_deref_size": return None, True elif opcode_name == "DW_OP_xderef": return None, True elif opcode_name == "DW_OP_xderef_size": return None, True elif opcode_name == "DW_OP_push_object_address": return None, True elif opcode_name == "DW_OP_form_tls_address": return None, True elif opcode_name == "DW_OP_call_frame_cfa": return None, True # DW_OP_GNU_entry_value # pyelftools doesn't know how to parse its args elif opcode_name == "OP:0xf3": return None, True ## arithmetic/logical ops elif opcode_name == "DW_OP_abs": old = stack.pop() try: stack.append(abs(old)) except: stack.append( OpPiece(old.format, old.key, "|" + old.value + "|", old.size)) elif opcode_name == "DW_OP_and": first = stack.pop() second = stack.pop() try: stack.append(first & second) except: new = self._handle_2arg_exception(opcode_name, first, second) stack.append(new) elif opcode_name == "DW_OP_div": first = stack.pop() second = stack.pop() try: stack.append(second / first) except: new = self._handle_2arg_exception(opcode_name, second, first) stack.append(new) elif opcode_name == "DW_OP_minus": first = stack.pop() second = stack.pop() try: stack.append(second - first) except: new = self._handle_2arg_exception(opcode_name, second, first) stack.append(new) elif opcode_name == "DW_OP_mod": first = stack.pop() second = stack.pop() try: stack.append(second % first) except: new = self._handle_2arg_exception(opcode_name, second, first) stack.append(new) elif opcode_name == "DW_OP_mul": first = stack.pop() second = stack.pop() try: stack.append(second * first) except: new = self._handle_2arg_exception(opcode_name, first, second) stack.append(new) elif opcode_name == "DW_OP_neg": first = stack.pop() try: stack.append(-first) except: stack.append( OpPiece(first.format, first.key, '-' + first.value, first.size)) elif opcode_name == "DW_OP_not": first = stack.pop() try: stack.append(~first) except: stack.append( OpPiece(first.format, first.key, '~' + first.value, first.size)) elif opcode_name == "DW_OP_or": first = stack.pop() second = stack.pop() try: stack.append(second | first) except: new = self._handle_2arg_exception(opcode_name, first, second) stack.append(new) elif opcode_name == "DW_OP_plus": first = stack.pop() second = stack.pop() try: stack.append(second + first) except: new = self._handle_2arg_exception(opcode_name, first, second) stack.append(new) elif opcode_name == "DW_OP_plus_uconst": first = stack.pop() second = args[0] try: stack.append(first + second) except: stack.append( OpPiece(first.format, first.key, first.value + "+" + second, first.size)) elif opcode_name == "DW_OP_shl": return None, True elif opcode_name == "DW_OP_shr": return None, True elif opcode_name == "DW_OP_shra": return None, True elif opcode_name == "DW_OP_xor": first = stack.pop() second = stack.pop() try: stack.append(second ^ first) except: new = self._handle_2arg_exception(opcode_name, first, second) stack.append(new) ## control flow ops elif opcode_name == "DW_OP_le": first = stack.pop() second = stack.pop() if second <= first: stack.append(1) else: stack.append(0) elif opcode_name == "DW_OP_ge": first = stack.pop() second = stack.pop() if second >= first: stack.append(1) else: stack.append(0) elif opcode_name == "DW_OP_eq": first = stack.pop() second = stack.pop() if second == first: stack.append(1) else: stack.append(0) elif opcode_name == "DW_OP_lt": first = stack.pop() second = stack.pop() if second < first: stack.append(1) else: stack.append(0) elif opcode_name == "DW_OP_gt": first = stack.pop() second = stack.pop() if second > first: stack.append(1) else: stack.append(0) elif opcode_name == "DW_OP_ne": first = stack.pop() second = stack.pop() if second != first: stack.append(1) else: stack.append(0) elif opcode_name == "DW_OP_skip": return None, True elif opcode_name == "DW_OP_bra": return None, True # OP:0xf3 else: return None, True return stack, False
def _after_visit(self, opcode, args) -> Optional[bool]: if opcode == DW_OP_stack_value: # The value on the stack is the actual value, not the location. self._is_stack_value = True return False elif opcode in self._const_ops: self._stack.append(args[0]) elif DW_OP_lit0 <= opcode and opcode <= DW_OP_lit31: self._stack.append(opcode - DW_OP_lit0) elif DW_OP_reg0 <= opcode and opcode <= DW_OP_reg31: regnum = opcode - DW_OP_reg0 self._stack.append(describe_reg_name(regnum, self._arch)) elif DW_OP_breg0 <= opcode and opcode <= DW_OP_breg31: regnum = opcode - DW_OP_breg0 regname = describe_reg_name(regnum, self._arch) self._stack.extend([regname, args[0], ExprOp.ADD]) self._stack.append(ExprOp.DYNAMIC) return False elif opcode == DW_OP_fbreg and isinstance(self._frame_base, ExprOp): self._stack.extend([self._frame_base, args[0], ExprOp.ADD]) elif opcode == DW_OP_fbreg and self._frame_base is None: self._stack.extend([ExprOp.CFA, args[0], ExprOp.ADD]) elif opcode == DW_OP_regx: regnum = args[0] if regnum < len(_REG_NAMES_x64): regname = describe_reg_name(regnum, self._arch) self._stack.append(regname) else: print('Unsupported reg num:', regnum) elif opcode == DW_OP_piece and len(self._stack) == 0: # if you run into a DW_OP_piece and the expression stack is # empty, then the bytes for the piece are optimized out. pass elif opcode == DW_OP_piece and len(self._stack) > 0 and isinstance( self.stack[-1], str): # if you run into a DW_OP_piece and it refers to a register # then select the subrange. self._stack.extend([args[0], ExprOp.VAR_FIELD]) elif opcode == DW_OP_bit_piece and len(self._stack) == 0: # if you run into a DW_OP_bit_piece and the expression stack is # empty, then the bits for the piece are optimized out. pass elif opcode == DW_OP_bit_piece and len(self._stack) > 0 and isinstance( self.stack[-1], str) and args[1] == 0: # the location is only the lower `args[0]` bits of the register. pass elif opcode == DW_OP_call_frame_cfa: # assert(self._is_setting_frame_base) self._stack.append(ExprOp.CFA) elif opcode in self._dynamic_ops: self._stack.append(ExprOp.DYNAMIC) return False elif opcode == DW_OP_plus: self._stack.append(ExprOp.ADD) elif opcode == DW_OP_not: self._stack.append(ExprOp.NOT) elif opcode == DW_OP_neg: self._stack.append(ExprOp.NEG) elif opcode == DW_OP_or: self._stack.append(ExprOp.OR) elif opcode == DW_OP_ne: self._stack.append(ExprOp.NE) elif opcode == DW_OP_eq: self._stack.append(ExprOp.EQ) elif opcode == DW_OP_le: self._stack.append(ExprOp.LE) elif opcode == DW_OP_ge: self._stack.append(ExprOp.GE) elif opcode == DW_OP_gt: self._stack.append(ExprOp.GT) elif opcode == DW_OP_lt: self._stack.append(ExprOp.LT) elif opcode == DW_OP_and: self._stack.append(ExprOp.AND) elif opcode == DW_OP_minus: self._stack.append(ExprOp.MINUS) elif opcode == DW_OP_shra: self._stack.append(ExprOp.ASHR) elif opcode == DW_OP_xor: self._stack.append(ExprOp.XOR) elif opcode == DW_OP_mul: self._stack.append(ExprOp.MUL) elif opcode == DW_OP_mod: self._stack.append(ExprOp.MOD) elif opcode == DW_OP_div: self._stack.append(ExprOp.DIV) elif opcode == DW_OP_shr: self._stack.append(ExprOp.SHR) elif opcode == DW_OP_plus_uconst: self._stack.append(ExprOp.PLUS_IMM) elif opcode == DW_OP_over: self._stack.append(ExprOp.OVER) elif opcode == DW_OP_implicit_value: v = int.from_bytes(args[0], 'little') self._stack.append(v) self._is_stack_value = True elif DW_OP_lo_user <= opcode and opcode <= DW_OP_hi_user: self._stack.append(ExprOp.UNSUPPORTED) return False elif opcode in self._unsupported_ops: self._stack.append(ExprOp.UNSUPPORTED) return False else: if not self._is_setting_frame_base: print('Expr:', [hex(x) for x in self.save_expr]) print('Args:', args) print('Stack:', self._stack) print('Frame:', self._frame_base) raise Exception( f'unimplemented opcode: {hex(opcode)} {DW_OP_opcode2name.get(opcode,"UNK")}\nFrame: {self._frame_base}' )