Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new Decompilation artifact with mappings #92

Merged
merged 13 commits into from
Jul 21, 2024
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
4 changes: 2 additions & 2 deletions .github/workflows/dec-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 15
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
Expand Down Expand Up @@ -48,4 +48,4 @@ jobs:
run: |
# these two test must be run in separate python environments, due to the way ghidra bridge works
# you also must run these tests in the exact order shown here
pytest ./tests/test_remote_ghidra.py && pytest ./tests/test_decompilers.py
pytest ./tests/test_remote_ghidra.py && pytest ./tests/test_decompilers.py -s
2 changes: 1 addition & 1 deletion libbs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "1.12.1"
__version__ = "1.13.0"


import logging
Expand Down
10 changes: 9 additions & 1 deletion libbs/api/artifact_lifter.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def lower_stack_offset(self, offset: int, func_addr: int) -> int:
#

def _lift_or_lower_artifact(self, artifact, mode):
target_attrs = ("type", "offset", "addr", "func_addr")
target_attrs = ("type", "offset", "addr", "func_addr", "line_map")
if mode not in ("lower", "lift"):
return None

Expand All @@ -81,6 +81,14 @@ def _lift_or_lower_artifact(self, artifact, mode):
continue
lifting_func = getattr(self, f"{mode}_stack_offset")
setattr(lifted_art, attr, lifting_func(curr_val, lifted_art.addr))
# special handling for decompilation
elif attr == "line_map":
lifted_line_map = {}
lift_or_lower_func = self.lift_addr if mode == "lift" else self.lower_addr
for k, v in curr_val.items():
lifted_line_map[k] = {lift_or_lower_func(_v) for _v in v}

setattr(lifted_art, attr, lifted_line_map)
else:
attr_func_name = attr if attr != "func_addr" else "addr"
lifting_func = getattr(self, f"{mode}_{attr_func_name}")
Expand Down
26 changes: 17 additions & 9 deletions libbs/api/decompiler_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
Artifact,
Function, FunctionHeader, StackVariable,
Comment, GlobalVariable, Patch,
Enum, Struct, FunctionArgument
Enum, Struct, FunctionArgument, Decompilation
)
from libbs.decompilers import SUPPORTED_DECOMPILERS, ANGR_DECOMPILER, \
BINJA_DECOMPILER, IDA_DECOMPILER, GHIDRA_DECOMPILER
Expand Down Expand Up @@ -110,6 +110,7 @@ def __init__(
else:
self._init_headless_components()

self.info(f"Using configuration file: {self.config.save_location}")
self.config.save()

def _init_headless_components(self, *args, check_dec_path=True, **kwargs):
Expand Down Expand Up @@ -270,30 +271,37 @@ def decompiler_available(self) -> bool:
"""
return True

def decompile(self, addr: int) -> Optional[str]:
def decompile(self, addr: int, map_lines=False, **kwargs) -> Optional[Decompilation]:
lowered_addr = self.art_lifter.lower_addr(addr)
if not self.decompiler_available:
_l.error("Decompiler is not available.")
return None

# TODO: make this a function call after transitioning decompiler artifacts to LiveState
sorted_funcs = sorted(self._functions().items(), key=lambda x: x[0])
for func_addr, func in sorted_funcs:
if func.addr <= lowered_addr < (func.addr + func.size):
break
func_by_addr = {_addr: func for _addr, func in sorted_funcs}
func = None
if lowered_addr in func_by_addr:
func = func_by_addr[lowered_addr]
else:
func = None
_l.debug(f"Address is not a function start, searching for function...")
for func_addr, _func in sorted_funcs:
if _func.addr <= lowered_addr < (_func.addr + _func.size):
func = _func
break

if func is None:
self.warning(f"Failed to find function for address {hex(lowered_addr)}")
return None

try:
decompilation = self._decompile(func)
decompilation = self._decompile(func, map_lines=map_lines, **kwargs)
except Exception as e:
self.warning(f"Failed to decompile function at {hex(lowered_addr)}: {e}")
decompilation = None

if decompilation is not None:
decompilation = self.art_lifter.lift(decompilation)

return decompilation

def xrefs_to(self, artifact: Artifact) -> List[Artifact]:
Expand All @@ -307,7 +315,7 @@ def xrefs_to(self, artifact: Artifact) -> List[Artifact]:
def get_func_containing(self, addr: int) -> Optional[Function]:
raise NotImplementedError

def _decompile(self, function: Function) -> Optional[str]:
def _decompile(self, function: Function, map_lines=False, **kwargs) -> Optional[Decompilation]:
raise NotImplementedError

def get_decompilation_object(self, function: Function, **kwargs) -> Optional[object]:
Expand Down
13 changes: 8 additions & 5 deletions libbs/artifacts/decompilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,27 @@
class Decompilation(Artifact):
__slots__ = Artifact.__slots__ + (
"addr",
"decompilation",
"text",
"line_map",
"decompiler",
)

def __init__(
self,
addr: int = None,
decompilation: str = None,
text: str = None,
line_map: dict = None,
decompiler: str = None,
**kwargs
):
super().__init__(**kwargs)
self.addr = addr
self.decompilation = decompilation
self.text = text
self.line_map = line_map or {}
self.decompiler = decompiler

def __str__(self):
return f"//ADDR: {hex(self.addr)}\n// SOURCE: {self.decompiler}\n{self.decompilation}"
return f"//ADDR: {hex(self.addr)}\n// SOURCE: {self.decompiler}\n{self.text}"

def __repr__(self):
return f"<Decompilation: {self.decompiler}@{hex(self.addr)} len={len(self.decompilation)}>"
return f"<Decompilation: {self.decompiler}@{hex(self.addr)} len={len(self.text)}>"
2 changes: 1 addition & 1 deletion libbs/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def save(self):
with open(self.save_location, "w") as fp:
toml.dump(dump_dict, fp)

_l.info(f"Saved config to {self.save_location}")
_l.debug(f"Saved config to %s", self.save_location)
return True

def load(self):
Expand Down
9 changes: 5 additions & 4 deletions libbs/decompilers/angr/compat.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# pylint: disable=wrong-import-position,wrong-import-order
import logging
from collections import defaultdict
from typing import Optional

from angrmanagement.plugins import BasePlugin
Expand Down Expand Up @@ -94,23 +95,23 @@ def handle_stack_var_renamed(self, func, offset, old_name, new_name):
if func is None:
return False

decompilation = self.interface.decompile_function(func)
decompilation = self.interface.decompile_function(func).codegen
stack_var = self.interface.find_stack_var_in_codegen(decompilation, offset)
print("handle_stack_var_renamed signal sent out to everyone")
self.interface.stack_variable_changed(StackVariable(offset, new_name, None, stack_var.size, func.addr))
return True

# pylint: disable=unused-argument
def handle_stack_var_retyped(self, func, offset, old_type, new_type):
decompilation = self.interface.decompile_function(func)
decompilation = self.interface.decompile_function(func).codegen
stack_var = self.interface.find_stack_var_in_codegen(decompilation, offset)
var_type = AngrInterface.stack_var_type_str(decompilation, stack_var)
self.interface.stack_variable_changed(StackVariable(offset, stack_var.name, var_type, stack_var.size, func.addr))
return True

# pylint: disable=unused-argument
def handle_func_arg_renamed(self, func, offset, old_name, new_name):
decompilation = self.interface.decompile_function(func)
decompilation = self.interface.decompile_function(func).codegen
func_args = AngrInterface.func_args_as_libbs_args(decompilation)
self.interface.function_header_changed(
FunctionHeader(
Expand All @@ -127,7 +128,7 @@ def handle_func_arg_renamed(self, func, offset, old_name, new_name):

# pylint: disable=unused-argument
def handle_func_arg_retyped(self, func, offset, old_type, new_type):
decompilation = self.interface.decompile_function(func)
decompilation = self.interface.decompile_function(func).codegen
func_args = AngrInterface.func_args_as_libbs_args(decompilation)
self.interface.function_header_changed(
FunctionHeader(
Expand Down
Loading
Loading