Created
May 20, 2020 16:32
-
-
Save Pamplemousse/e199cf63d314e8b6bce6a09dce1ff48b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Backward slice from a given sink. | |
Unfortunately, the BB definition used by Radare2 is too limiting: | |
https://github.com/radareorg/radare2/issues/7170#issuecomment-631579110 | |
""" | |
from functools import reduce | |
import r2pipe | |
import sys | |
# | |
# Utils | |
# | |
def _predecessors_from_codexrefs(codexrefs): | |
def _block_top_address(address): | |
result = r2.cmdj("abj %s" % address) | |
assert len(result) == 1, "A `call` instruction should belong to a single block" | |
return result[0]['addr'] | |
# TODO: | |
# if len(codexrefs) != 0: | |
# address = codexrefs[0]['addr'] | |
# if _block_top_address(address) == 0x401140: | |
# calls = list(filter( | |
# # Let's get the `call`s instructions in the node of the callsite, located *before* the current callsite. | |
# lambda x: x['type'] == 'CALL' and x['at'] < address, | |
# r2.cmdj("axffj @%s" % address) | |
# )) | |
# sorted_calls = sorted(calls, key=lambda e: e['at'], reverse=True) | |
# import ipdb; ipdb.set_trace() | |
# pass | |
return list(map( | |
lambda p: _block_top_address(p['addr']), | |
codexrefs | |
)) | |
def _codexrefs_from_function_address(address): | |
result = r2.cmdj("afij %s" % address) | |
assert len(result) == 1, "Function <%s> should have one set of info" % address | |
return result[0]["codexrefs"] | |
PREDECESSORS_PER_ADDRESS = dict() | |
def _predecessors_from_address(address): | |
if address in PREDECESSORS_PER_ADDRESS.keys(): | |
return PREDECESSORS_PER_ADDRESS[address] | |
codexrefs = _codexrefs_from_function_address(address) | |
return _predecessors_from_codexrefs(codexrefs) | |
def all_predecessors_from_address(address, callback): | |
""" | |
:param int address: | |
:param Function callback: A function to call for on each predecessor. | |
""" | |
predecessors = _predecessors_from_address(address) | |
if len(predecessors) == 0: | |
return [] | |
for p in predecessors: | |
_add_to_graph(address, p) | |
return list(reduce( | |
lambda acc, p: acc + all_predecessors_from_address(p, callback), | |
predecessors, | |
[address] | |
)) | |
# | |
# Main | |
# | |
try: | |
binary = sys.argv[1] | |
sink_name = sys.argv[2] | |
r2 = r2pipe.open(binary) | |
r2.cmd('aaa') | |
sink_address = int(r2.cmd("f~%s[0]" % sink_name)[:-1], 16) | |
except IndexError: | |
exit("usage: %s <binary> <function_address>" % sys.argv[0]) | |
except BrokenPipeError: | |
exit("%s failed to open. Is this really an existing binary?" % binary) | |
except ValueError: | |
exit("There is no function '%s' in the binary." % sys.argv[2]) | |
def _add_to_graph(a, p): | |
(a, p) = (hex(a), hex(p)) | |
r2.cmd("agn %s" % p) | |
r2.cmd("age %s %s" % (p, a)) | |
r2.cmd("agn %s" % hex(sink_address)) | |
all_predecessors_from_address(sink_address, _add_to_graph) | |
result = r2.cmd('agg') | |
print(result) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment