From 3e3d37f0310ed57684ff00266a141fc559487b05 Mon Sep 17 00:00:00 2001 From: mahaloz Date: Mon, 13 Oct 2025 07:36:30 +0800 Subject: [PATCH 1/3] Fix issues with angr in arm --- libbs/__init__.py | 2 +- libbs/api/decompiler_interface.py | 9 ++++- libbs/decompilers/angr/artifact_lifter.py | 28 ++++++++++++++- libbs/decompilers/angr/interface.py | 42 +++++++++++++++++++++-- 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/libbs/__init__.py b/libbs/__init__.py index 076a2a3e..0ce16f58 100644 --- a/libbs/__init__.py +++ b/libbs/__init__.py @@ -1,4 +1,4 @@ -__version__ = "2.16.4" +__version__ = "2.16.5" import logging diff --git a/libbs/api/decompiler_interface.py b/libbs/api/decompiler_interface.py index 40275a50..daa5ab62 100644 --- a/libbs/api/decompiler_interface.py +++ b/libbs/api/decompiler_interface.py @@ -469,6 +469,13 @@ def should_watch_artifacts(self) -> bool: # These are API that provide extra introspection for plugins that may rely on LibBS Interface # + @property + def binary_arch(self) -> str: + """ + Returns a string of the currently loaded binary's architecture. + """ + raise NotImplementedError + @property def default_pointer_size(self) -> int: """ @@ -976,7 +983,7 @@ def find_current_decompiler(force: str = None) -> Optional[str]: except Exception: pass - if not force or has_bn_ui: + if has_bn_ui: return BINJA_DECOMPILER available.add(BINJA_DECOMPILER) # error can be thrown for an invalid license diff --git a/libbs/decompilers/angr/artifact_lifter.py b/libbs/decompilers/angr/artifact_lifter.py index 3ed58a24..ec04e411 100644 --- a/libbs/decompilers/angr/artifact_lifter.py +++ b/libbs/decompilers/angr/artifact_lifter.py @@ -1,13 +1,24 @@ +import typing + from libbs.api import ArtifactLifter +if typing.TYPE_CHECKING: + from .interface import AngrInterface class AngrArtifactLifter(ArtifactLifter): """ TODO: finish me """ - def __init__(self, interface): + def __init__(self, interface: "AngrInterface"): super(AngrArtifactLifter, self).__init__(interface) + @staticmethod + def is_arm() -> bool: + if interface.binary_arch is not None: + return "ARM" in interface.binary_arch + return False + + def lift_type(self, type_str: str) -> str: return type_str @@ -19,3 +30,18 @@ def lower_type(self, type_str: str) -> str: def lower_stack_offset(self, offset: int, func_addr: int) -> int: return offset + + def lower_addr(self, addr: int) -> int: + new_addr = super().lower_addr(addr) + if self.is_arm() and not deci.addr_starts_instruction(addr): + new_addr += 1 + + return new_addr + + + def lift_addr(self, addr: int) -> int: + new_addr = super().lift_addr(addr) + if self.is_arm() and new_addr % 2 == 1: + new_addr -= 1 + + return new_addr \ No newline at end of file diff --git a/libbs/decompilers/angr/interface.py b/libbs/decompilers/angr/interface.py index b5d27c37..9b162f52 100644 --- a/libbs/decompilers/angr/interface.py +++ b/libbs/decompilers/angr/interface.py @@ -1,6 +1,7 @@ import logging import os from collections import defaultdict +from functools import lru_cache from typing import Optional, Dict, List from pathlib import Path @@ -39,6 +40,7 @@ def __init__(self, workspace=None, **kwargs): self._ctx_menu_items = [] self._am_logger = None self._cfg = None + self._binary_arch = None super().__init__(name="angr", artifact_lifter=AngrArtifactLifter(self), **kwargs) def _init_headless_components(self, *args, **kwargs): @@ -186,6 +188,15 @@ def rename_local_variables_by_names(self, func: Function, name_map: Dict[str, st return self.refresh_decompilation(func.addr) + @property + def binary_arch(self) -> str | None: + if self._binary_arch is None: + if self.main_instance.project.arch: + self._binary_arch = self.main_instance.project.arch.name + + return self._binary_arch + + # # GUI API # @@ -210,7 +221,8 @@ def gui_register_ctx_menu(self, name, action_string, callback_func, category=Non def gui_active_context(self) -> Optional[Context]: curr_view = self.workspace.view_manager.current_tab - if not curr_view: + # current view is non-existent or does not support a "function" view type of context + if not curr_view or not hasattr(curr_view, "function"): return None try: @@ -293,6 +305,11 @@ def _functions(self) -> Dict[int, Function]: funcs = {} for addr, func in self.main_instance.project.kb.functions.items(): funcs[addr] = Function(addr, func.size) + + # syscalls and simprocedures are not real funcs to sync + if func.is_syscall or func.is_simprocedure or not func.name: + continue + funcs[addr].name = func.name return funcs @@ -395,12 +412,31 @@ def print(self, msg: str, **kwargs): # angr-management specific helpers # + # TODO: add LRU back one day + #@lru_cache(maxsize=1024) + def addr_starts_instruction(self, addr) -> bool: + """ + Returns True when the provided address maps to a valid instruction address in the binary and that address + is at the start of the instruction (not in the middle). Useful for checking if an instruction is + incorrectly computed due to ARM THUMB. + """ + cfg = self.project.kb.cfgs.get_most_accurate() + if cfg is None: + l.warning("Unable load CFG from angr. Other operations may be wrong.") + return False + + node = cfg.get_any_node(addr, anyaddr=True) + if node is None: + return False + + return addr in node.instruction_addrs + def refresh_decompilation(self, func_addr): if self.headless: return False - self.main_instance.workspace.jump_to(func_addr) - view = self.main_instance.workspace._get_or_create_view("pseudocode", CodeView) + self.workspace.jump_to(func_addr) + view = self.workspace._get_or_create_view("pseudocode", CodeView) view.codegen.am_event() view.focus() return True From afd8c20b4532f1865941f347ea21a86997cc73eb Mon Sep 17 00:00:00 2001 From: mahaloz Date: Mon, 13 Oct 2025 07:49:28 +0800 Subject: [PATCH 2/3] Fix --- libbs/decompilers/angr/artifact_lifter.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libbs/decompilers/angr/artifact_lifter.py b/libbs/decompilers/angr/artifact_lifter.py index ec04e411..044b751c 100644 --- a/libbs/decompilers/angr/artifact_lifter.py +++ b/libbs/decompilers/angr/artifact_lifter.py @@ -12,10 +12,9 @@ class AngrArtifactLifter(ArtifactLifter): def __init__(self, interface: "AngrInterface"): super(AngrArtifactLifter, self).__init__(interface) - @staticmethod - def is_arm() -> bool: - if interface.binary_arch is not None: - return "ARM" in interface.binary_arch + def is_arm(self) -> bool: + if self.deci.binary_arch is not None: + return "ARM" in self.deci.binary_arch return False @@ -33,7 +32,7 @@ def lower_stack_offset(self, offset: int, func_addr: int) -> int: def lower_addr(self, addr: int) -> int: new_addr = super().lower_addr(addr) - if self.is_arm() and not deci.addr_starts_instruction(addr): + if self.is_arm() and not self.deci.addr_starts_instruction(addr): new_addr += 1 return new_addr From a8214f1963a1a37356c8bebc3418331cc83c4a2d Mon Sep 17 00:00:00 2001 From: mahaloz Date: Mon, 13 Oct 2025 20:34:27 +0800 Subject: [PATCH 3/3] Finish fix --- libbs/decompilers/angr/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libbs/decompilers/angr/interface.py b/libbs/decompilers/angr/interface.py index 9b162f52..8b8452a3 100644 --- a/libbs/decompilers/angr/interface.py +++ b/libbs/decompilers/angr/interface.py @@ -420,7 +420,7 @@ def addr_starts_instruction(self, addr) -> bool: is at the start of the instruction (not in the middle). Useful for checking if an instruction is incorrectly computed due to ARM THUMB. """ - cfg = self.project.kb.cfgs.get_most_accurate() + cfg = self.main_instance.project.kb.cfgs.get_most_accurate() if cfg is None: l.warning("Unable load CFG from angr. Other operations may be wrong.") return False