Skip to content

Commit

Permalink
Maintain minimal backwards compatability
Browse files Browse the repository at this point in the history
  • Loading branch information
mahaloz committed Sep 23, 2024
1 parent 665d817 commit 8ffc83f
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 24 deletions.
7 changes: 5 additions & 2 deletions examples/change_watcher_plugin/bs_change_watcher/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

__version__ = "0.0.1"


def create_plugin(*args, **kwargs):
"""
This is the entry point that all decompilers will call in various ways. To remain agnostic,
Expand Down Expand Up @@ -33,11 +32,15 @@ def create_plugin(*args, **kwargs):
)
}

def _start_watchers(*x, **y):
deci.start_artifact_watchers()
deci.info("Artifact watchers started!")

# register a menu to open when you right click on the psuedocode view
deci.gui_register_ctx_menu(
"StartArtifactChangeWatcher",
"Start watching artifact changes",
lambda *x, **y: deci.start_artifact_watchers(),
_start_watchers,
category="ArtifactChangeWatcher"
)

Expand Down
61 changes: 42 additions & 19 deletions libbs/decompilers/ida/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,32 @@
_l = logging.getLogger(__name__)
_IDA_VERSION = None

FORM_TYPE_TO_NAME = {
idaapi.BWN_PSEUDOCODE: "decompilation",
idaapi.BWN_DISASM: "disassembly",
idaapi.BWN_FUNCS: "functions",
# idaapi.BWN_STRINGS
0x1c: "structs",
# idaapi.BWN_ENUMS
0x1b: "enums",
# this is idaapi.BWN_TILIST, but made into a constant for 8.3 compatibility
0x3c: "types",
}

FORM_TYPE_TO_NAME = None
FUNC_FORMS = {"decompilation", "disassembly"}

def get_form_to_type_name():
global FORM_TYPE_TO_NAME
if FORM_TYPE_TO_NAME is None:
mapping = {
idaapi.BWN_PSEUDOCODE: "decompilation",
idaapi.BWN_DISASM: "disassembly",
idaapi.BWN_FUNCS: "functions",
idaapi.BWN_STRINGS: "strings"
}
if get_ida_version() >= Version("9.0"):
mapping.update({
idaapi.BWN_TILIST: "types"
})
else:
mapping.update({
idaapi.BWN_STRINGS: "structs",
idaapi.BWN_ENUMS: "enums",
0x3c: "types"
})
FORM_TYPE_TO_NAME = mapping

return FORM_TYPE_TO_NAME

#
# Wrappers for IDA Main thread r/w operations
# a special note about these functions:
Expand Down Expand Up @@ -168,7 +180,7 @@ def get_ida_version():


def new_ida_typing_system():
return get_ida_version() >= Version("9.0")
return get_ida_version() >= Version("8.4")


def get_ordinal_count():
Expand Down Expand Up @@ -692,6 +704,16 @@ def _deprecated_ida_to_bs_offset(func_addr, ida_stack_off):
bs_soff = ida_stack_off - frame_size + last_member_size
return bs_soff

def _deprecated_bs_to_ida_offset(func_addr, bs_stack_off):
frame = idaapi.get_frame(func_addr)
if not frame:
return bs_stack_off

frame_size = idc.get_struc_size(frame)
last_member_size = idaapi.get_member_size(frame.get_member(frame.memqty - 1))
ida_soff = bs_stack_off + frame_size - last_member_size
return ida_soff


def get_func_stack_tif(func):
if isinstance(func, int):
Expand Down Expand Up @@ -738,7 +760,7 @@ def get_frame_info(func_addr) -> typing.Tuple[int, int]:
return frame_size, last_member_size

def ida_to_bs_stack_offset(func_addr: int, ida_stack_off: int):
if not new_ida_typing_system():
if get_ida_version() < Version("9.0"):
return _deprecated_ida_to_bs_offset(func_addr, ida_stack_off)

frame_size, last_member_size = get_frame_info(func_addr)
Expand All @@ -749,9 +771,9 @@ def ida_to_bs_stack_offset(func_addr: int, ida_stack_off: int):
return bs_soff

def bs_to_ida_stack_offset(func_addr: int, bs_stack_off: int):
if not new_ida_typing_system():
if get_ida_version() < Version("9.0"):
# maintain backwards compatibility
return bs_stack_off
return _deprecated_bs_to_ida_offset(func_addr, bs_stack_off)

frame_size, last_member_size = get_frame_info(func_addr)
if frame_size is None or last_member_size is None:
Expand Down Expand Up @@ -1078,10 +1100,10 @@ def set_ida_struct(struct: Struct) -> bool:

# expand the struct to the desired size
# XXX: do not increment API here, why? Not sure, but you cant do it here.
if new_struct_system:
if get_ida_version() >= Version("9.0"):
expand_ida_struct(sid, struct.size)
else:
idc.expand_struc(struct_identifier, 0, struct.size)
idc.expand_struc(struct_identifier, 0, struct.size, False)

# add every member of the struct
for off, member in struct.members.items():
Expand Down Expand Up @@ -1586,7 +1608,8 @@ def view_to_bs_context(view, get_var=True) -> typing.Optional[Context]:
if form_type is None:
return None

view_name = FORM_TYPE_TO_NAME.get(form_type, "unknown")
form_to_type_name = get_form_to_type_name()
view_name = form_to_type_name.get(form_type, "unknown")
ctx = Context(screen_name=view_name)
if view_name in FUNC_FORMS:
ctx.addr = idaapi.get_screen_ea()
Expand Down
17 changes: 15 additions & 2 deletions libbs/decompilers/ida/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ def __init__(self, interface):
ida_idp.IDB_Hooks.__init__(self)
self.interface: "IDAInterface" = interface
self._seen_function_prototypes = {}
self._ver_9_or_higher = compat.get_ida_version() >= Version("9.0")

def bs_type_deleted(self, ordinal):
old_name, old_type = self.interface.cached_ord_to_type_names[ordinal]
Expand Down Expand Up @@ -423,8 +424,20 @@ def struc_member_changed(self, sptr, mptr):

return 0

def _valid_rename_event(self, ea):
if not self._ver_9_or_higher:
# ignore any changes landing here for structs and stack vars
import ida_struct, ida_enum
return not (ida_struct.is_member_id(ea) or ida_struct.get_struc(ea) or ida_enum.get_enum_name(ea))

# in version 9 and above, this event is not triggered by structs
return True

@while_should_watch
def renamed(self, ea, new_name, local_name):
if not self._valid_rename_event(ea):
return 0

ida_func = idaapi.get_func(ea)
# symbols changing without any corresponding func is assumed to be global var
if ida_func is None:
Expand Down Expand Up @@ -553,8 +566,8 @@ def lvar_name_changed(self, vdui, lvar, new_name, *args):
return 0

@while_should_watch
def lvar_type_changed(self, vdui, lvar, *args):
self.local_var_changed(vdui, lvar, reset_name=True)
def lvar_type_changed(self, vu: "vdui_t", v: "lvar_t", *args) -> int:
self.local_var_changed(vu, v, reset_name=True)
return 0

@while_should_watch
Expand Down
6 changes: 5 additions & 1 deletion libbs/decompilers/ida/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def __init__(self, **kwargs):
self._max_patch_size = 0xff
self._decompiler_available = None
self._dec_version = None
self._ida_analysis_finished = False

# GUI properties
self._updated_ctx = None
Expand Down Expand Up @@ -249,7 +250,10 @@ def gui_goto(self, func_addr) -> None:

def should_watch_artifacts(self) -> bool:
# never do hooks while IDA is in initial startup phase
return self._artifact_watchers_started and ida_auto.auto_is_ok()
if not self._ida_analysis_finished:
self._ida_analysis_finished = ida_auto.auto_is_ok()

return self._ida_analysis_finished and self._artifact_watchers_started

#
# Optional API
Expand Down

0 comments on commit 8ffc83f

Please sign in to comment.