Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

<img src="https://puu.sh/thLAo/491ac39e58.PNG" width="600">
Expand All @@ -43,6 +44,10 @@ After packaging, a table will appear listing all of the "packages" you have crea

<img src="https://puu.sh/tnz8C/d0f5141f43.PNG" width="600">

##### 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.

Expand Down
20 changes: 12 additions & 8 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -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 *

Expand All @@ -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()
Expand All @@ -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()
Expand Down
67 changes: 43 additions & 24 deletions analysis_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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!")



Expand All @@ -54,6 +54,7 @@ def get_arch(self):
'x86'
'x64'
'arm'
'arm64'
'mips'
'''
pass
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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):
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)

Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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)))

2 changes: 1 addition & 1 deletion cli_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading