diff --git a/README.md b/README.md index cab3e67..5a3c671 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Installation on Windows typically requires installing PyQt5. --- #### Packaging a Function +##### Binary Ninja From within Binary Ninja, right click anywhere inside of a function and select `[ripr] Package Function`. @@ -43,6 +44,10 @@ After packaging, a table will appear listing all of the "packages" you have crea +##### Radare2 +If you've followed step 3 in the installation instructions, run `.(ripr 0x1234)` (with 0x1234 replaced by the address of the function). +Otherwise, you can manually invoke ripr with `#!pipe python /absolute/path/to/ripr/r2pipe_run.py 0x1234`. + #### Packaging Specific Basic Blocks You can also choose to only package specific basic blocks rather than the entire function. diff --git a/__init__.py b/__init__.py index 4291997..2beb12e 100644 --- a/__init__.py +++ b/__init__.py @@ -1,6 +1,10 @@ import sys + if (sys.platform == 'win32'): - sys.path.append("C:\\Python27\\lib\\site-packages\\PyQt5") + if sys.version_info[0] >= 3: + sys.path.append("C:\\Python37\\lib\\site-packages\\PyQt5") + else: + sys.path.append("C:\\Python27\\lib\\site-packages\\PyQt5") try: from binaryninja import * @@ -9,10 +13,10 @@ raise ImportError # ripr imports - from analysis_engine import * - from codegen import * - from packager import * - import gui + from .analysis_engine import * + from .codegen import * + from .packager import * + from .gui import * ui = gui.riprWidget() @@ -22,19 +26,19 @@ def packageFunction(view, fobj): pkg.package_function() def packageRegion(view, start, length): - print "[ripr] Packaging 0x%x - 0x%x" % (start, start+length) + print ("[ripr] Packaging 0x%x - 0x%x" % (start, start+length)) engine = get_engine(view) pkg = Packager(isFunc=False, address=start, engine=engine, length=length, ui=ui) pkg.package_region() def packageBasicBlock(view, addr): - print "[ripr] Adding Basic Block containing %x " % addr + print ("[ripr] Adding Basic Block containing %x " % addr) engine = get_engine(view) pkg = Packager(isFunc=False, address=addr, engine=engine, ui=ui) pkg.package_bb() def generate_basicBlocks(view, fobj): - print "[ripr] Generating code from currently selected basic blocks" + print ("[ripr] Generating code from currently selected basic blocks") engine = get_engine(view) pkg = Packager(isFunc=False, address=fobj.start, engine=engine, ui=ui) pkg.generate_bb_code() diff --git a/analysis_engine.py b/analysis_engine.py index 59e30d5..878aeec 100644 --- a/analysis_engine.py +++ b/analysis_engine.py @@ -7,17 +7,17 @@ try: from binaryninja import * except: - print "[!!] Not running in Binary Ninja" + print ("[!!] Not running in Binary Ninja") try: import r2pipe except: - print "[!!] Not running in Radare2" + print ("[!!] Not running in Radare2") import json import re import sys -import codegen +from .codegen import * from binascii import unhexlify def get_engine(*args): @@ -31,7 +31,7 @@ def get_engine(*args): return bn_engine(args[0]) - raise ValueError, "No analysis engine found!" + raise (ValueError, "No analysis engine found!") @@ -54,6 +54,7 @@ def get_arch(self): 'x86' 'x64' 'arm' + 'arm64' 'mips' ''' pass @@ -160,18 +161,20 @@ def get_arch(self): These can be different from Binary Ninja names, so they are explicitly mapped into the ripr names, even if they are the same in some cases. ''' - print self.bv.arch.name + print (self.bv.arch.name) if (self.bv.arch.name == 'x86'): return 'x86' elif (self.bv.arch.name == 'x86_64'): return 'x64' elif (self.bv.arch.name == 'armv7'): return 'arm' + elif (self.bv.arch.name == 'aarch64'): + return 'arm64' def mark_gathered_basic_block(self, address): fobj = self.bv.get_functions_containing(address)[0] if (fobj == None): - print "FOBJ IS NONE" + print ("FOBJ IS NONE") bb = fobj.get_basic_block_at(address) bb.highlight = HighlightStandardColor.BlackHighlightColor @@ -187,25 +190,30 @@ def clean_gathered_basic_block(self, address): def get_basic_block_bytes(self, address): bb = self.bv.get_basic_blocks_at(address) if len(bb) != 1: - print "[ripr] Address belongs to more than one basic block!" + print ("[ripr] Address belongs to more than one basic block!") bb = bb[0] - return {bb.start: codegen.codeSlice(self.read_bytes(bb.start, bb.end-bb.start), bb.start)} + return {bb.start: codeSlice(self.read_bytes(bb.start, bb.end-bb.start), bb.start)} def get_function_bytes(self, address=None, name=None): ''' Binary Ninja does not seem to assume Functions are contiguous; rather they are treated as a collection of basic blocks. ''' - print "[ripr] Inside get_function_bytes()" + print ("[ripr] Inside get_function_bytes()") if (address != None): fobj = self.bv.get_function_at(address) elif (name != None): - print "[ripr] TODO" + print ("[ripr] TODO") return else: - print "[ripr] No arguments supplied to get_function_bytes" + print ("[ripr] No arguments supplied to get_function_bytes") return None + + if self.bv.get_function_at(address)==None: + print ("[ripr] Couldn't get function binary view. Maybe code arch is thumb2?") + return None + # Sort the basic blocks in ascending order bblist = sorted(fobj.basic_blocks, key=lambda x: x.start) map(lambda bb: bb.set_user_highlight(HighlightStandardColor.BlackHighlightColor), bblist) @@ -218,10 +226,10 @@ def get_function_bytes(self, address=None, name=None): clist.append([bb]) # Print out the list if the function is not contiguous if (len(clist) > 1): - print clist + print (clist) # Create a return list in the expected format from the contiguous units. - retdir = {unit[0].start : codegen.codeSlice(self.read_bytes(unit[0].start, unit[-1].start - unit[0].start + unit[-1].length), unit[0].start) for unit in clist} + retdir = {unit[0].start : codeSlice(self.read_bytes(unit[0].start, unit[-1].start - unit[0].start + unit[-1].length), unit[0].start) for unit in clist} return retdir def get_page_bytes(self, address): @@ -264,7 +272,7 @@ def get_instruction_length(self, address): def find_llil_block_from_addr(self, address): fobj = self.bv.get_functions_containing(address) if len(fobj) > 1: - print "[ripr] Multiple Functions contain this address!!" + print ("[ripr] Multiple Functions contain this address!!") return None fobj = fobj[0] bbindex = fobj.get_basic_block_at(address).index @@ -273,7 +281,7 @@ def find_llil_block_from_addr(self, address): def find_mlil_block_from_addr(self, address): fobj = self.bv.get_functions_containing(address) if len(fobj) > 1: - print "[ripr] Multiple Functions contain this address!!" + print ("[ripr] Multiple Functions contain this address!!") return None fobj = fobj[0] bbindex = fobj.get_basic_block_at(address).index @@ -297,6 +305,9 @@ def branches_from_block(self, block, callCallback, branchCallback): def branches_from_func(self, address, callCallback, branchCallback): fobj = self.bv.get_function_at(address) + if (fobj==None): + return + for block in fobj.low_level_il: self.branches_from_block(block, callCallback, branchCallback) @@ -316,6 +327,8 @@ def get_refs_to(self, address): def function_contains_addr(self, func_addr, testAddr): fobj = self.bv.get_function_at(func_addr) + if (fobj==None): + return False return (fobj.get_basic_block_at(testAddr) != None) def scan_potential_pointers_bb(self, il_block, fobj): @@ -381,7 +394,7 @@ def highlight_instr(self, func_addr, instrAddr, color): elif color == "orange": bn_color = HighlightStandardColor.OrangeHighlightColor else: - raise ValueError, "Unsupported color" + raise (ValueError, "Unsupported color") fobj.set_user_instr_highlight(instrAddr, bn_color) def add_comment(self, func_addr, instrAddr, comment): @@ -442,29 +455,35 @@ def get_arch(self): elif arch == "x86" and bits == 64: return 'x64' else: - raise NotImplementedError, "Only tested witn x86 & x86_64" + raise (NotImplementedError, "Only tested with x86 & x86_64") + ''' + elif arch == "arm" and bits == 32: + return 'arm' + elif arch == "arm" and bits == 64: + return 'arm64' + ''' def get_function_bytes(self, address=None, name=None): if (address != None): funcInfo = self.r2.cmd("afij {}".format(hex(address))) elif (name != None): - print "[ripr] TODO" + print ("[ripr] TODO") return else: - print "[ripr] No arguments supplied to get_function_bytes" + print ("[ripr] No arguments supplied to get_function_bytes") return None if funcInfo.strip() == "": - raise ValueError, "Function not found at {}".format(address) + raise (ValueError, "Function not found at {}".format(address)) funcInfo = json.loads(funcInfo, strict=False) if len(funcInfo) == 0: - raise ValueError, "Function not found at {}".format(address) - print funcInfo + raise (ValueError, "Function not found at {}".format(address)) + print (funcInfo) offset = funcInfo[0]["offset"] size = funcInfo[0]["size"] bytes = self.read_bytes(offset, size) - retdir = {offset: codegen.codeSlice(bytes, offset)} + retdir = {offset: codeSlice(bytes, offset)} return retdir def get_page_bytes(self, address): @@ -560,7 +579,7 @@ def highlight_instr(self, func_addr, instrAddr, color): def add_comment(self, func_addr, instrAddr, comment): if not re.compile("^[a-z0-9 !\\-\\_]+$", re.IGNORECASE).match(comment): # Don't send arbitrary contents to radare pipe - print "Ignoring malformed comment: {}".format(comment) + print ("Ignoring malformed comment: {}".format(comment)) else: self.r2.cmd("CC [ripr] {} @{}".format(comment, hex(instrAddr))) diff --git a/cli_ui.py b/cli_ui.py index 39900ac..6255ab6 100644 --- a/cli_ui.py +++ b/cli_ui.py @@ -23,7 +23,7 @@ def text_input_box(self, promptText): def impCallsOptions(self): options = ["nop", "hook", "cancel"] - print "Code contains calls to imported functions. How should this be handled?", + print ("Code contains calls to imported functions. How should this be handled?") while True: selection = raw_input("({})?".format(", ".join(options))).strip() if selection in options: diff --git a/codegen.py b/codegen.py index 07f12ae..04d4e4f 100644 --- a/codegen.py +++ b/codegen.py @@ -3,11 +3,16 @@ ''' import sys import os +import binascii + +python2=False +if sys.version_info[0] < 3: + python2=True try: from binaryninja import * except: - print "[+] Not running in BinaryNinja" + print ("[+] Not running in BinaryNinja") class callConv(object): def __init__(self, name, arch): @@ -17,15 +22,26 @@ def __init__(self, name, arch): def gen_arg_number(self, argno): pass + def genPointer(self, arg, regs, indent): + pass + + def dumpContext(self, indent): + pass + class x64callConv(callConv): # TODO Stack based arguments def __init__(self, name, arch): self.name = name self.arch = arch self.platform = '' - + self.regs = ["UC_X86_REG_RAX", "UC_X86_REG_RBP", "UC_X86_REG_RBX", "UC_X86_REG_RCX",\ + "UC_X86_REG_RDI", "UC_X86_REG_RDX", "UC_X86_REG_RSI", "UC_X86_REG_RSP",\ + "UC_X86_REG_RIP", "UC_X86_REG_R8", "UC_X86_REG_R9", "UC_X86_REG_R10",\ + "UC_X86_REG_R11", "UC_X86_REG_R12", "UC_X86_REG_R13", "UC_X86_REG_R14",\ + "UC_X86_REG_R15"] + def gen_arg_number(self, argno, indent=1): - print "X64" + print ("X64") if self.platform == "win": return self.msft(argno, indent) return self.systemV(argno, indent) @@ -52,10 +68,19 @@ def systemV(self, arg, indent): return ' ' * (indent*4) + "self.mu.reg_write(%s, arg_%x)\n" % (regs[arg.num], arg.num) return self.genPointer(arg, regs, indent) + def dumpContext(self, indent): + ret = ' ' * (indent * 4) + ("print ('[!] Exception occured - Emulator state (x64):')\n") + for r in self.regs: + ret += ' ' * (indent * 4) + ("print (\"%s : %%016X\" %% (self.mu.reg_read(%s)))\n" % (r,r)) + return ret + class x86callConv(callConv): def __init__(self, name, arch): self.name = name self.arch = arch + self.regs = ["UC_X86_REG_EAX", "UC_X86_REG_EBP", "UC_X86_REG_EBX", "UC_X86_REG_ECX",\ + "UC_X86_REG_EDI", "UC_X86_REG_EDX", "UC_X86_REG_ESI", "UC_X86_REG_ESP",\ + "UC_X86_REG_EIP"] def genPointer(self, arg, indent): ret = ' ' * (indent * 4) + "argAddr_%x = (%d * 0x1000)\n" % (arg.num, arg.num + 1) @@ -70,10 +95,20 @@ def gen_arg_number(self, arg, indent): return ' ' * (indent * 4) + "self.mu.mem_write(self.mu.reg_read(UC_X86_REG_ESP) + %d, struct.pack(' 1: return ' ' * (indent *4 ) + "self.mu.reg_write(%s, arg_%x)\n" % (regs[arg.num], arg.num) return self.genPointer(arg, regs, indent) - + + def dumpContext(self, indent): + ret = ' ' * (indent * 4) + ("print ('[!] Exception occured - Emulator state (arm):')\n") + for r in self.regs: + ret += ' ' * (indent * 4) + ("print (\"%s : %%X\" %% (self.mu.reg_read(%s)))\n" % (r,r)) + return ret + +class arm64callConv(callConv): + def __init__(self, name, arch): + self.name = name + self.arch = arch + self.regs = ["UC_ARM64_REG_X0", "UC_ARM64_REG_X1", "UC_ARM64_REG_X2", "UC_ARM64_REG_X3",\ + "UC_ARM64_REG_X4", "UC_ARM64_REG_X5", "UC_ARM64_REG_X6", "UC_ARM64_REG_X7",\ + "UC_ARM64_REG_X8", "UC_ARM64_REG_X9", "UC_ARM64_REG_X10", "UC_ARM64_REG_X11",\ + "UC_ARM64_REG_X12", "UC_ARM64_REG_X13", "UC_ARM64_REG_X14", "UC_ARM64_REG_X15",\ + "UC_ARM64_REG_X16", "UC_ARM64_REG_X17", "UC_ARM64_REG_X18", "UC_ARM64_REG_X19",\ + "UC_ARM64_REG_X20", "UC_ARM64_REG_X21", "UC_ARM64_REG_X22", "UC_ARM64_REG_X23",\ + "UC_ARM64_REG_X24", "UC_ARM64_REG_X25", "UC_ARM64_REG_X26", "UC_ARM64_REG_X27",\ + "UC_ARM64_REG_X28", "UC_ARM64_REG_X29", "UC_ARM64_REG_X30"] + + def genPointer(self, arg, regs, indent): + ret = ' ' * (indent * 4) + "argAddr_%x = (%d * 0x1000)\n" % (arg.num, arg.num+1) + ret += ' ' * (indent * 4) + "self.mu.mem_write(argAddr_%x, arg_%x)\n" % (arg.num, arg.num) + ret += ' ' * (indent * 4) + "self.mu.reg_write(%s, argAddr_%x)\n" % (regs[arg.num], arg.num) + return ret + + def gen_arg_number(self, arg, indent): + regs = ["UC_ARM64_REG_X0", "UC_ARM64_REG_X1", "UC_ARM64_REG_X2", "UC_ARM64_REG_X3"] + if arg.num < len(regs): + if arg.pointerDepth == 0 or arg.pointerDepth > 1: + return ' ' * (indent *4 ) + "self.mu.reg_write(%s, arg_%x)\n" % (regs[arg.num], arg.num) + return self.genPointer(arg, regs, indent) + + def dumpContext(self, indent): + ret = ' ' * (indent * 4) + ("print ('[!] Exception occured - Emulator state (arm64):')\n") + for r in self.regs: + ret += ' ' * (indent * 4) + ("print (\"%s : %%X\" %% (self.mu.reg_read(%s)))\n" % (r,r)) + return ret class codeSlice(object): ''' @@ -150,6 +222,17 @@ def __init__(self, name, isFunc=True): self.isFunc = isFunc + def setArch(self,a): + self.arch=a + if self.arch == 'x64': + self.callConv = x64callConv("linux", "x64") + elif self.arch == 'x86': + self.callConv = x86callConv("linux", "x86") + elif self.arch == 'arm': + self.callConv =armcallConv("linux", "arm") + elif self.arch == 'arm64': + self.callConv =armcallConv("linux", "arm64") + def data_saved(self, addr): return any(lowaddr <= addr <= highaddr for (lowaddr, highaddr) in self.saved_ranges) @@ -159,14 +242,14 @@ def add_mmap(self, addr, len=0x4000): def add_data(self, data, addr): if (self.data_saved(addr)): - print "[Warning] Trying to map data twice!" + print ("[Warning] Trying to map data twice!") return self.data.append((addr, data)) self.saved_ranges.append((addr, addr + len(data))) def add_code(self, cSlice): if (self.data_saved(cSlice.address)): - print "[Warning] Trying to map data twice" + print ("[Warning] Trying to map data twice") self.code.append(cSlice) self.saved_ranges.append((cSlice.address, cSlice.address + len(cSlice.code_bytes))) @@ -199,16 +282,26 @@ def generate_mmap(self, indent = 1): return out def generate_data_vars(self, indent = 1): + global python2 out = '' for i in range(0, len(self.data)): - out += ' ' * (indent * 4) + "self.data_%s = '%s'.decode('hex') \n" % (str(i), self.data[i][1].encode('hex')) + if python2==True: + cc=binascii.hexlify(self.data[i][1]) + else: + cc=binascii.hexlify(self.data[i][1]).decode('utf-8') + out += ' ' * (indent * 4) + "self.data_%s = binascii.unhexlify('%s') \n" % (str(i), cc) return out def generate_code_vars(self, indent = 1): + global python2 out = '' i = 0 for cSlice in self.code: - out += ' ' * (indent * 4) + "self.code_%s = '%s'.decode('hex') \n" % (str(i), cSlice.code_bytes.encode('hex')) + if python2==True: + cc=binascii.hexlify(cSlice.code_bytes) + else: + cc=binascii.hexlify(cSlice.code_bytes).decode('utf-8') + out += ' ' * (indent * 4) + "self.code_%s = binascii.unhexlify('%s') \n" % (str(i), cc) i += 1 return out @@ -225,9 +318,14 @@ def generate_stack_initialization(self, indent = 1): elif self.arch == 'arm': self.add_mmap(0x7ffff000, 1024*1024 * 2) out = ' ' * (indent * 4) + "self.mu.reg_write(UC_ARM_REG_SP, 0x7fffff00)\n" + + elif self.arch == 'arm64': + self.add_mmap(0x7ffff000, 1024*1024 * 2) + out = ' ' * (indent * 4) + "self.mu.reg_write(UC_ARM64_REG_SP, 0x7fffff00)\n" + ## TODO Add support for other architectures supported by Unicorn and Binja else: - print "[ripr] Error, Unsupported Architecture" + print ("[ripr] Error, Unsupported Architecture") return out @@ -274,7 +372,7 @@ def generate_start_unicorn_func(self, indent = 1): ''' decl = ' ' * (indent * 4) + 'def _start_unicorn(self, startaddr):\n' body = self.generate_emustart(indent=2) - return decl+body + return decl+body+"\n" def generate_return_guard_marker(self, indent=1): ''' @@ -287,8 +385,10 @@ def generate_return_guard_marker(self, indent=1): out += ' ' * (indent *4) + "self.mu.mem_write(0x7fffff00, '\\x01\\x00\\x00\\x00')\n" elif self.arch == 'arm': out += ' ' * (indent *4) + "self.mu.reg_write(UC_ARM_REG_LR, 0x4)\n" + elif self.arch == 'arm64': + out += ' ' * (indent *4) + "self.mu.reg_write(UC_ARM64_REG_LR, 0x4)\n" else: - print "Unsupported Arch" + print ("Unsupported Arch") return out def generate_restore_exec(self, indent=1): @@ -306,8 +406,11 @@ def generate_restore_exec(self, indent=1): elif self.arch == 'arm': out += ' ' * (indent * 4) + "self._start_unicorn(retAddr)\n" pass + elif self.arch == 'arm64': + out += ' ' * (indent * 4) + "self._start_unicorn(retAddr)\n" + pass else: - print "Unsupported Arch" + print ("Unsupported Arch") return out @@ -318,9 +421,11 @@ def generate_hook_lookup(self, indent=1): retAddr = ' ' * (indent * 4) + "retAddr = struct.unpack(\" 1: @@ -58,7 +66,7 @@ def argIdent(self, addr, isFunc): def uninit_vars(self, bbs): for bb in bbs: - mlb = self.engine.find_mlil_block_from_addr(bb.keys()[0]) + mlb = self.engine.find_mlil_block_from_addr(list(bb.keys())[0]) if mlb == None: continue set_vars = [] @@ -72,6 +80,6 @@ def uninit_vars(self, bbs): unset_vars = set(unset_vars) for uVar in unset_vars: - self.engine.highlight_instr(bb.keys()[0], uVar.mil.address, "orange") + self.engine.highlight_instr(list(bb.keys())[0], uVar.mil.address, "orange") return unset_vars diff --git a/defunct/.widgets.py.swp b/defunct/.widgets.py.swp deleted file mode 100644 index b03c295..0000000 Binary files a/defunct/.widgets.py.swp and /dev/null differ diff --git a/dependency.py b/dependency.py index 25ea443..7f26b46 100644 --- a/dependency.py +++ b/dependency.py @@ -4,8 +4,17 @@ properly. ''' -import analysis_engine as ae -from binaryninja import * +from .analysis_engine import aengine as ae +# Try to import stuff. +try: + from binaryninja import * +except: + print ("[!!] Not running in Binary Ninja") +try: + import r2pipe +except: + print ("[!!] Not running in Radare2") + class ImportedCall(object): ''' @@ -71,7 +80,7 @@ def branchScan(self, address, isFunc=True): Function is responsible for mapping calls and jumps that are outside the current selection's bounds, if possible. ''' - print "[ripr] Inside branchScan" + print ("[ripr] Inside branchScan") def callCallback(dest, instr_addr): if type(dest) != int: try: @@ -79,17 +88,17 @@ def callCallback(dest, instr_addr): except: return if (dest in self.imports): - print "[ripr] Found imported Call target..." + print ("[ripr] Found imported Call target...") self._mark_imported_call(address, instr_addr, dest) elif (self.codeobj.data_saved(dest) == False): - print "[ripr] Found LLIL CALL instruction" + print ("[ripr] Found LLIL CALL instruction") self._mark_additional_branch(address, instr_addr, dest, "call") else: - print "[ripr] Target address already mapped" + print ("[ripr] Target address already mapped") def jumpCallback(dest, instr_addr): - print "[ripr] JUMP TARGET: %s" % (dest) + print ("[ripr] JUMP TARGET: %s" % (dest)) if isFunc: self.engine.branches_from_func(address, callCallback, jumpCallback) @@ -109,7 +118,7 @@ def _find_stringRefs(self, address): for stringStart,stringLength in self.engine.get_strings(): for refAddress in self.engine.get_refs_to(stringStart): # Ignored the length if (self.engine.function_contains_addr(address, refAddress)): - print "[ripr] Found string reference: 0x%x" % (refAddress) + print ("[ripr] Found string reference: 0x%x" % (refAddress)) self._mark_identified_data(address, refAddress) dref = riprDataRef(stringStart, stringLength, 'str') self.dataRefs.append(dref) @@ -124,7 +133,7 @@ def _find_symbolRefs(self, address): for symStart in symbols: for refAddress in self.engine.get_refs_to(symStart): if self.engine.function_contains_addr(address, refAddress): - print "[ripr] Found Symbol Reference: 0x%x references 0x%x" % (refAddress, symStart) + print ("[ripr] Found Symbol Reference: 0x%x references 0x%x" % (refAddress, symStart)) self._mark_identified_data(address, refAddress) dref = riprDataRef(symStart, -1, 'sym') self.dataRefs.append(dref) @@ -142,7 +151,7 @@ def dataScan(self, address): Function is responsible for finding data the target code needs in order to run correctly. ''' - print "[ripr] Inside dataScan" + print ("[ripr] Inside dataScan") ret = [] # Find the low-hanging fruit @@ -151,7 +160,7 @@ def dataScan(self, address): # Iterate over all instructions for potential pointers for target, instrAddr in self.engine.scan_potential_pointers(address): if self.engine.is_plausible_pointer(target): - print "Found Potential Pointer: %s instaddr %s" % (hex(target), hex(instrAddr)) + print ("Found Potential Pointer: %s instaddr %s" % (hex(target), hex(instrAddr))) self._mark_identified_data(address, instrAddr) dref = riprDataRef(target, -1, 'ptr') self.dataRefs.append(dref) diff --git a/gui.py b/gui.py index bf88ce2..d623dc4 100644 --- a/gui.py +++ b/gui.py @@ -3,12 +3,15 @@ ''' import sys if (sys.platform == 'win32'): - sys.path.append("C:\\Python27\\lib\\site-packages") + if sys.version_info[0] >= 3: + sys.path.append("C:\\Python37\\lib\\site-packages\\PyQt5") + else: + sys.path.append("C:\\Python27\\lib\\site-packages\\PyQt5") try: from PyQt5 import QtWidgets, QtGui, QtCore from PyQt5.QtCore import Qt - from defunct.widgets import BinjaWidget - import defunct.widgets + from .defunct.widgets import BinjaWidget + from .defunct.widgets import * qtAvailable = True except: qtAvailable = False @@ -42,7 +45,7 @@ def _get_selected_codeobj(self): # Find the Class name from the index name = self.item(item.row(), item.column()).text() # Open the Editor - print "Selected Codeobj: %s" % name + print ("Selected Codeobj: %s" % name) return self.emuchunks[name] def contextMenuEvent(self, event): @@ -183,7 +186,10 @@ def yes_no_box(self, msg): if choice == enums.MessageBoxButtonResult.YesButton: return True return False - + + def msgBox(self, msg): + interaction.show_message_box("Binary Ninja - ripr", msg, enums.MessageBoxButtonSet.OKButtonSet) + def text_input_box(self,msg): text = interaction.get_text_line_input(msg, "Binary Ninja - ripr") return text diff --git a/packager.py b/packager.py index 5a6e5fa..f9a059d 100644 --- a/packager.py +++ b/packager.py @@ -1,11 +1,11 @@ -from codegen import * -import analysis_engine as ae -import dependency as dep -import conScan as con +from .codegen import * +from .analysis_engine import aengine as ae +from .dependency import depScanner +from .conScan import convenienceScanner ### Global for listing all chunks of code for which we have tried to create a python wrapper. emuchunks = {} - + # List of basic block chunks to package for BB mode bbChunks = [] class Packager(object): @@ -26,7 +26,7 @@ def __init__(self, isFunc, address, engine, ui = None, length=None): self.codeobj = genwrapper('', isFunc) self.arch = self.engine.get_arch() - self.codeobj.arch = self.arch + self.codeobj.setArch(self.arch) self.impCallStrategy = None self.dataStrategy = None @@ -40,7 +40,7 @@ def convenience_passes(self): This function is a wrapper for determining which convenience features can be enabled during code generation. ''' - c = con.convenienceScanner(self.engine) + c = convenienceScanner(self.engine) if (self.isFunc == True and self.codeobj.arch in ['x64', 'x86', 'arm']): self.codeobj.conPass['ret'] = True @@ -63,14 +63,17 @@ def minimal_package_function(self, address=None): address = self.address # Get the code to be emulated localCode = self.engine.get_function_bytes(address=address) - + if (localCode == None): + self.ui.msgBox("[ripr] Couldn't get function binary view. Maybe code arch is thumb2?") + return False # Add each contiguous chunk of code to codeobj and make sure # it will be mapped. - for startAddr in localCode.keys(): + for startAddr in list(localCode.keys()): self.codeobj.add_mmap(startAddr) self.targetCode.append(localCode) - + return True + def minimal_package_region(self): targetCode = self.engine.get_region_bytes(address=self.address) self.codeobj.add_data(targetCode[0], targetCode[1]) @@ -94,7 +97,8 @@ def package_function(self): if not self.codeobj.name: return # Get the bare minimum required information. - self.minimal_package_function() + if (self.minimal_package_function()==False): + return # Try to find dependencies of our code. self.resolve_dependencies() @@ -132,7 +136,7 @@ def generate_bb_code(self): if not self.codeobj.name: return # Set starting address to first basic block selected - self.codeobj.startaddr = bbChunks[0].keys()[0] + self.codeobj.startaddr = list(bbChunks[0].keys())[0] self.targetCode = bbChunks @@ -154,7 +158,7 @@ def generate_bb_code(self): def cleanup_basic_blocks(self): global bbChunks for bb in bbChunks: - self.engine.clean_gathered_basic_block(bb.keys()[0]) + self.engine.clean_gathered_basic_block(list(bb.keys())[0]) def package_region(self): @@ -176,14 +180,14 @@ def _find_code_unit(self, faddr): def _nop_impFunc(self, impCalls): for impCall in impCalls: - print "[ripr] Nopping out Imported Call: 0x%x" % (impCall.address) + print ("[ripr] Nopping out Imported Call: 0x%x" % (impCall.address)) cSlice = self._find_code_unit(impCall.address) codeLen = len(cSlice.code_bytes) nop = self.engine.get_nop_opcode() if (impCall.inst_len % len(nop) != 0): - print "[ripr] Cannot NOP out instruction..." + print ("[ripr] Cannot NOP out instruction...") return # Create string of NOP opcodes and calculate where to place it @@ -200,15 +204,15 @@ def update_codeobj(self): # in the case of others being automatically mapped as dependencies localCode = self.targetCode[0] - print self.targetCode - self.codeobj.codelen = sum([len(localCode[x].code_bytes) for x in localCode.keys()]) + print (self.targetCode) + self.codeobj.codelen = sum([len(localCode[x].code_bytes) for x in list(localCode.keys())]) for found_code in self.targetCode: for addr in found_code: self.codeobj.add_code(found_code[addr]) def resolve_imported_calls(self, resolv): - print "[ripr] Selection includes calls to imported Functions!" + print ("[ripr] Selection includes calls to imported Functions!") if self.impCallStrategy == None: self.impCallStrategy = self.ui.impCallsOptions() @@ -236,12 +240,15 @@ def map_dependent_sections(self, dataRefs): ''' Map any sections the target code touches. ''' - print "Mapping Sections" + print ("Mapping Sections") pagesize = self.engine.get_page_size() secs = [] - for ref in dataRefs: - sections = [self.engine.find_section(ref.address)] - secs += sections + + for ref in dataRefs: + section=self.engine.find_section(ref.address) + if section!=-1: + secs += [section] + for sec_start, sec_end, sec_name in secs: self.codeobj.add_data(self.engine.read_bytes(sec_start, sec_end - sec_start), sec_start) self.codeobj.add_mmap(sec_start) @@ -269,23 +276,24 @@ def resolve_data_dependencies(self, dataRefs): def resolve_codeRefs(self, coderefs): for ref in coderefs: - print "Found CodeRef: %x::%s" % (ref.address, ref.type) + print ("Found CodeRef: %x::%s" % (ref.address, ref.type)) if (ref.type == 'call'): - self.minimal_package_function(address=ref.address) + if (self.minimal_package_function(address=ref.address)==False): + continue self.resolve_dependencies(address=ref.address, isFunc=True) def resolve_dependencies(self, address=None, isFunc=None): ''' This method is a high-level wrapper for finding data our target code depends on. ''' - resolv = dep.depScanner(self.engine, self.codeobj) + resolv = depScanner(self.engine, self.codeobj) if (address == None): address = self.address if (isFunc == None): isFunc = self.isFunc - print "Resolving Dependencies for %x" % address + print ("Resolving Dependencies for %x" % address) if isFunc: coderefs = resolv.branchScan(address, self.isFunc) datarefs = resolv.dataScan(address) @@ -293,22 +301,22 @@ def resolve_dependencies(self, address=None, isFunc=None): datarefs = resolv.dataScan(address) coderefs = [] for bb in bbChunks: - coderefs += resolv.branchScan(bb.keys()[0], self.isFunc) + coderefs += resolv.branchScan(list(bb.keys())[0], self.isFunc) if (resolv.impCalls != []): self.resolve_imported_calls(resolv) if (coderefs != []): if (self.ui.yes_no_box("Target code may depend on outside code, attempt to map automatically?") == True): - print "[ripr] Performing analysis on code dependencies..." + print ("[ripr] Performing analysis on code dependencies...") self.resolve_codeRefs(coderefs) else: pass if (resolv.dataRefs != []): # Try to map these automatically - print "[ripr] Found these potential Data References" + print ("[ripr] Found these potential Data References") for ref in resolv.dataRefs: - print "Data Referenced: 0x%x" % (ref.address) + print ("Data Referenced: 0x%x" % (ref.address)) self.resolve_data_dependencies(resolv.dataRefs) pass diff --git a/r2pipe_run.py b/r2pipe_run.py index 762e3e0..376e64a 100644 --- a/r2pipe_run.py +++ b/r2pipe_run.py @@ -1,13 +1,13 @@ import sys # ripr imports -from analysis_engine import * -from codegen import * -from packager import * -import cli_ui +from .analysis_engine import * +from .codegen import * +from .packager import * +from .cli_ui import * def packageFunction(addr): - print "[ripr] Packaging function {}".format(hex(addr)) + print ("[ripr] Packaging function {}".format(hex(addr))) engine = get_engine(addr) ui = cli_ui.cli_ui() pkg = Packager(isFunc=True, address=addr, engine=engine, ui=ui) diff --git a/sample/impCall/ripr_arm_impCall.py b/sample/impCall/ripr_arm_impCall.py index 34e6021..e250473 100644 --- a/sample/impCall/ripr_arm_impCall.py +++ b/sample/impCall/ripr_arm_impCall.py @@ -17,15 +17,15 @@ def __init__(self): self.hookdict = {66688L: 'hook_puts', 66696L: 'hook_puts', 66704L: 'hook_malloc'} def hook_puts(self): - print "===========In Puts==========" + print ("===========In Puts==========") arg = self.mu.reg_read(UC_ARM_REG_R0) mem = self.mu.mem_read(arg, 0x200) - print "Puts would have Printed: %s" % (mem.split("\x00")[0]) - print "===========Leaving Puts==========" + print ("Puts would have Printed: %s" % (mem.split("\x00")[0])) + print ("===========Leaving Puts==========") def hook_malloc(self): - print "===========In Malloc==========" - print "===========Leaving Malloc===========" + print ("===========In Malloc==========") + print ("===========Leaving Malloc===========") def _start_unicorn(self, startaddr): try: @@ -46,4 +46,4 @@ def run(self): return self.mu.reg_read(UC_ARM_REG_R0) x = arm_test() -print x.run() +print (x.run()) diff --git a/sample/impCall/ripr_x64_impCall.py b/sample/impCall/ripr_x64_impCall.py index ead6eba..685208b 100644 --- a/sample/impCall/ripr_x64_impCall.py +++ b/sample/impCall/ripr_x64_impCall.py @@ -18,15 +18,15 @@ def __init__(self): self.hookdict = {4195721L: 'hook_puts', 4195731L: 'hook_malloc', 4195711L: 'hook_puts'} # Do whatever we want in hooked functions def hook_puts(self): - print "===========In Puts==========" + print ("===========In Puts==========") arg = self.mu.reg_read(UC_X86_REG_RDI) mem = self.mu.mem_read(arg, 0x200) - print "Puts would have Printed: %s" % (mem.split("\x00")[0]) - print "===========Leaving Puts==========" + print ("Puts would have Printed: %s" % (mem.split("\x00")[0])) + print ("===========Leaving Puts==========") def hook_malloc(self): - print "===========In Malloc==========" - print "===========Leaving Malloc===========" + print ("===========In Malloc==========") + print ("===========Leaving Malloc===========") def _start_unicorn(self, startaddr): @@ -49,4 +49,4 @@ def run(self): return self.mu.reg_read(UC_X86_REG_RAX) x = x64_test() -print x.run() +print (x.run()) diff --git a/sample/impCall/ripr_x86_impCall.py b/sample/impCall/ripr_x86_impCall.py index 18d3218..07138be 100644 --- a/sample/impCall/ripr_x86_impCall.py +++ b/sample/impCall/ripr_x86_impCall.py @@ -17,17 +17,17 @@ def __init__(self): self.hookdict = {134513753L: 'hook_puts', 134513782L: 'hook_malloc', 134513769L: 'hook_puts'} def hook_puts(self): - print "===========In Puts==========" + print ("===========In Puts==========") arg = self.mu.reg_read(UC_X86_REG_ESP) arg2 = self.mu.mem_read(arg+4, 0x4) arg2 = struct.unpack(" " % (sys.argv[0]) + print ("Usage: %s <key> <plaintext>" % (sys.argv[0])) exit() key=sys.argv[1] @@ -137,4 +137,4 @@ def run(self, arg_0, arg_1): ksa=KSA() S=str(ksa.run(key,S)) prga=PRGA() -print str(prga.run(S,plain)).encode('hex') +print (str(prga.run(S,plain)).encode('hex')) diff --git a/sample/rc4/tester.py b/sample/rc4/tester.py index 155195e..4258014 100755 --- a/sample/rc4/tester.py +++ b/sample/rc4/tester.py @@ -12,7 +12,7 @@ if args.seed != None: random.seed(args.seed) -print "Running %d testcases..." % (args.num) +print ("Running %d testcases..." % (args.num)) for i in xrange(0,args.num): key_len = random.randint(1,16) @@ -22,7 +22,7 @@ ripr = subprocess.check_output(["python", "myrc4.py", key, plain]).strip().upper() orig = subprocess.check_output(["./a.out", key, plain]).strip().upper() if ripr != orig: - print "FAIL:\n'%s' vs. '%s'" % (orig, ripr) - print "Key: %s\nPlain:\n'%s'" % (key, plain) + print ("FAIL:\n'%s' vs. '%s'" % (orig, ripr)) + print ("Key: %s\nPlain:\n'%s'" % (key, plain)) exit() print "Everything is OK!"