def context_ghidra(func=None, target=sys.stdout, with_banner=True, width=None, force_show=False): banner = [pwndbg.ui.banner("ghidra decompile", target=target, width=width)] if config_context_ghidra == "never" and not force_show: return [] elif config_context_ghidra == "if-no-source" and not force_show: try: with open(gdb.selected_frame().find_sal().symtab.fullname()) as _: pass except: # a lot can go wrong in search of source code. return [] # we don't care what, just that it did not work out well... filename = gdb.current_progspace().filename try: r2 = init_radare2(filename) # LD list supported decompilers (e cmd.pdc=?) # Outputs for example:: pdc\npdg if not "pdg" in r2.cmd("LD").split("\n"): return banner + ["radare2 plugin r2ghidra-dec must be installed and available from r2"] except ImportError: # no r2pipe present return banner + ["r2pipe not available, but required for r2->ghidra-bridge"] if func is None: try: func = hex(pwndbg.regs[pwndbg.regs.current.pc]) except: func = "main" src = r2.cmdj("pdgj @" + func) source = src.get("code", "") curline = None try: cur = pwndbg.regs[pwndbg.regs.current.pc] except AttributeError: cur = None # If not running there is no current.pc if cur is not None: closest = 0 for off in (a.get("offset", 0) for a in src.get("annotations", [])): if abs(cur - closest) > abs(cur - off): closest = off pos_annotations = sorted([a for a in src.get("annotations", []) if a.get("offset") == closest], key=lambda a: a["start"]) if pos_annotations: curline = source.count("\n", 0, pos_annotations[0]["start"]) source = source.split("\n") # Append --> for the current line if possible if curline is not None: line = source[curline] if line.startswith(' '): line = line[4:] source[curline] = '--> ' + line # Join the source for highlighting source = "\n".join(source) if pwndbg.config.syntax_highlight: # highlighting depends on the file extension to guess the language, so try to get one... try: # try to read the source filename from debug information src_filename = gdb.selected_frame().find_sal().symtab.fullname() except: # if non, take the original filename and maybe append .c (just assuming is was c) src_filename = filename+".c" if os.path.basename(filename).find(".") < 0 else filename source = H.syntax_highlight(source, src_filename) source = source.split("\n") return banner + source if with_banner else source
def decompile(func=None): """ Return the source of the given function decompiled by ghidra. If no function is given, decompile the function within the current pc. This function requires radare2, r2pipe and r2ghidra. Raises Exception if any fatal error occures. """ try: r2 = pwndbg.radare2.r2pipe() except ImportError: raise Exception('r2pipe not available, but required for r2->ghidra bridge') # LD -> list supported decompilers (e cmd.pdc=?) # Outputs for example: pdc\npdg if "pdg" not in r2.cmd("LD").split("\n"): raise Exception('radare2 plugin r2ghidra must be installed and available from r2') if not func: func = hex(pwndbg.regs[pwndbg.regs.current.pc]) if pwndbg.proc.alive else 'main' src = r2.cmdj("pdgj @" + func) if not src: raise Exception("Decompile command failed, check if '{}' is a valid target".format(func)) current_line_marker = '/*%%PWNDBG_CODE_MARKER%%*/' source = src.get("code", "") # If not running there is no current pc to mark if pwndbg.proc.alive: pc = pwndbg.regs[pwndbg.regs.current.pc] closest = 0 for off in (a.get("offset", 0) for a in src.get("annotations", [])): if abs(pc - closest) > abs(pc - off): closest = off pos_annotations = sorted([a for a in src.get("annotations", []) if a.get("offset") == closest], key=lambda a: a["start"]) # Append code prefix marker for the current line and replace it later if pos_annotations: curline = source.count("\n", 0, pos_annotations[0]["start"]) source = source.split("\n") line = source[curline] if line.startswith(' '): line = line[min(4, len(pwndbg.config.code_prefix) + 1):] source[curline] = current_line_marker + ' ' + line source = "\n".join(source) if pwndbg.config.syntax_highlight: # highlighting depends on the file extension to guess the language, so try to get one... src_filename = pwndbg.symbol.selected_frame_source_absolute_filename() if not src_filename: filename = gdb.current_progspace().filename src_filename = filename + ".c" if os.path.basename(filename).find(".") < 0 else filename source = H.syntax_highlight(source, src_filename) # Replace code prefix marker after syntax highlighting source = source.replace(current_line_marker, C.prefix(pwndbg.config.code_prefix), 1) return source
def get_highlight_source(filename): # Notice that the code is cached with open(filename, encoding='utf-8') as f: source = f.read() if pwndbg.config.syntax_highlight: source = H.syntax_highlight(source, filename) source_lines = source.splitlines() source_lines = tuple(line.rstrip() for line in source_lines) return source_lines
def syntax_highlight(ins): return H.syntax_highlight(ins, filename='.asm')
def enhance(value, code=True): """ Given the last pointer in a chain, attempt to characterize Note that 'the last pointer in a chain' may not at all actually be a pointer. Additionally, optimizations are made based on various sources of data for 'value'. For example, if it is set to RWX, we try to get information on whether it resides on the stack, or in a RW section that *happens* to be RWX, to determine which order to print the fields. Arguments: value(obj): Value to enhance code(bool): Hint that indicates the value may be an instruction """ value = int(value) name = pwndbg.symbol.get(value) or None page = pwndbg.vmmap.find(value) # If it's not in a page we know about, try to dereference # it anyway just to test. can_read = True if not page or None == pwndbg.memory.peek(value): can_read = False if not can_read: return E.integer(int_str(value)) # It's mapped memory, or we can at least read it. # Try to find out if it's a string. instr = None exe = page and page.execute rwx = page and page.rwx # For the purpose of following pointers, don't display # anything on the stack or heap as 'code' if '[stack' in page.objfile or '[heap' in page.objfile: rwx = exe = False # If IDA doesn't think it's in a function, don't display it as code. if pwndbg.ida.available() and not pwndbg.ida.GetFunctionName(value): rwx = exe = False if exe: instr = pwndbg.disasm.one(value) if instr: instr = "%-6s %s" % (instr.mnemonic, instr.op_str) if pwndbg.config.syntax_highlight: instr = syntax_highlight(instr) szval = pwndbg.strings.get(value) or None szval0 = szval if szval: szval = E.string(repr(szval)) intval = int(pwndbg.memory.poi(pwndbg.typeinfo.pvoid, value)) intval0 = intval if 0 <= intval < 10: intval = E.integer(str(intval)) else: intval = E.integer('%#x' % int(intval & pwndbg.arch.ptrmask)) retval = [] # print([instr,intval0,szval]) if not code: instr = None # If it's on the stack, don't display it as code in a chain. if instr and 'stack' in page.objfile: retval = [intval, szval] # If it's RWX but a small value, don't display it as code in a chain. elif instr and rwx and intval0 < 0x1000: retval = [intval, szval] # If it's an instruction and *not* RWX, display it unconditionally elif instr and exe: if not rwx: if szval: retval = [instr, szval] else: retval = [instr] else: retval = [instr, intval, szval] # Otherwise strings have preference elif szval: if len(szval0) < pwndbg.arch.ptrsize: retval = [intval, szval] else: retval = [szval] # And then integer else: return E.integer(int_str(intval0)) retval = tuple(filter(lambda x: x is not None, retval)) if len(retval) == 0: return E.unknown("???") if len(retval) == 1: return retval[0] return retval[0] + E.comment( color.strip(' /* {} */'.format('; '.join(retval[1:]))))
def enhance(value, code = True): """ Given the last pointer in a chain, attempt to characterize Note that 'the last pointer in a chain' may not at all actually be a pointer. Additionally, optimizations are made based on various sources of data for 'value'. For example, if it is set to RWX, we try to get information on whether it resides on the stack, or in a RW section that *happens* to be RWX, to determine which order to print the fields. Arguments: value(obj): Value to enhance code(bool): Hint that indicates the value may be an instruction """ value = int(value) name = pwndbg.symbol.get(value) or None page = pwndbg.vmmap.find(value) # If it's not in a page we know about, try to dereference # it anyway just to test. can_read = True if not page or None == pwndbg.memory.peek(value): can_read = False if not can_read: return E.integer(int_str(value)) # It's mapped memory, or we can at least read it. # Try to find out if it's a string. instr = None exe = page and page.execute rwx = page and page.rwx # For the purpose of following pointers, don't display # anything on the stack or heap as 'code' if '[stack' in page.objfile or '[heap' in page.objfile: rwx = exe = False # If IDA doesn't think it's in a function, don't display it as code. if pwndbg.ida.available() and not pwndbg.ida.GetFunctionName(value): rwx = exe = False if exe: instr = pwndbg.disasm.one(value) if instr: instr = "%-6s %s" % (instr.mnemonic, instr.op_str) if pwndbg.config.syntax_highlight: instr = syntax_highlight(instr) szval = pwndbg.strings.get(value) or None szval0 = szval if szval: szval = E.string(repr(szval)) intval = int(pwndbg.memory.poi(pwndbg.typeinfo.pvoid, value)) intval0 = intval if 0 <= intval < 10: intval = E.integer(str(intval)) else: intval = E.integer('%#x' % int(intval & pwndbg.arch.ptrmask)) retval = [] # print([instr,intval0,szval]) if not code: instr = None # If it's on the stack, don't display it as code in a chain. if instr and 'stack' in page.objfile: retval = [intval, szval] # If it's RWX but a small value, don't display it as code in a chain. elif instr and rwx and intval0 < 0x1000: retval = [intval, szval] # If it's an instruction and *not* RWX, display it unconditionally elif instr and exe: if not rwx: if szval: retval = [instr, szval] else: retval = [instr] else: retval = [instr, intval, szval] # Otherwise strings have preference elif szval: if len(szval0) < pwndbg.arch.ptrsize: retval = [intval, szval] else: retval = [szval] # And then integer else: return E.integer(int_str(intval0)) retval = tuple(filter(lambda x: x is not None, retval)) if len(retval) == 0: return E.unknown("???") if len(retval) == 1: return retval[0] return retval[0] + E.comment(color.strip(' /* {} */'.format('; '.join(retval[1:]))))