From bead9d1010e4e876f39d4c20fd9d1ed798a93bf8 Mon Sep 17 00:00:00 2001 From: jonschz Date: Sun, 16 Jun 2024 08:14:26 +0200 Subject: [PATCH] feature: Import stub functions but don't overwrite their argument list Ghidra might have auto-detected some arguments, so we don't want to overwrite that if the stub's argument list has not been verified Closes #1009 --- .../lego_util/function_importer.py | 28 ++++++++++++++----- .../lego_util/pdb_extraction.py | 8 +----- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/tools/ghidra_scripts/lego_util/function_importer.py b/tools/ghidra_scripts/lego_util/function_importer.py index 55d3c5b2..bf99f7f8 100644 --- a/tools/ghidra_scripts/lego_util/function_importer.py +++ b/tools/ghidra_scripts/lego_util/function_importer.py @@ -88,7 +88,10 @@ def matches_ghidra_function(self, ghidra_function: Function) -> bool: self.signature.call_type == ghidra_function.getCallingConventionName() ) - if thiscall_matches: + if self.is_stub: + # We do not import the argument list for stubs, so it should be excluded in matches + args_match = True + elif thiscall_matches: if self.signature.call_type == "__thiscall": args_match = self._matches_thiscall_parameters(ghidra_function) else: @@ -102,7 +105,7 @@ def matches_ghidra_function(self, ghidra_function: Function) -> bool: name_match, return_type_match, thiscall_matches, - args_match, + "ignored" if self.is_stub else args_match, ) return ( @@ -163,16 +166,25 @@ def overwrite_ghidra_function(self, ghidra_function: Function): ghidra_function.setReturnType(self.return_type, SourceType.USER_DEFINED) ghidra_function.setCallingConvention(self.call_type) + if self.is_stub: + logger.debug( + "%s is a stub, skipping parameter import", self.get_full_name() + ) + return + ghidra_function.replaceParameters( Function.FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, - True, + True, # force SourceType.USER_DEFINED, self.arguments, ) - # When we set the parameters, Ghidra will generate the layout. - # Now we read them again and match them against the stack layout in the PDB, - # both to verify and to set the parameter names. + self._import_parameter_names(ghidra_function) + + def _import_parameter_names(self, ghidra_function: Function): + # When we call `ghidra_function.replaceParameters`, Ghidra will generate the layout. + # Now we read the parameters again and match them against the stack layout in the PDB, + # both to verify the layout and to set the parameter names. ghidra_parameters: list[Parameter] = ghidra_function.getParameters() # Try to add Ghidra function names @@ -186,7 +198,9 @@ def overwrite_ghidra_function(self, ghidra_function: Function): # Appears to never happen - could in theory be relevant to __fastcall__ functions, # which we haven't seen yet - logger.warning("Unhandled register variable in %s", self.get_full_name) + logger.warning( + "Unhandled register variable in %s", self.get_full_name() + ) continue def _rename_stack_parameter(self, index: int, param: Parameter): diff --git a/tools/ghidra_scripts/lego_util/pdb_extraction.py b/tools/ghidra_scripts/lego_util/pdb_extraction.py index d36f287a..0c2ef7dc 100644 --- a/tools/ghidra_scripts/lego_util/pdb_extraction.py +++ b/tools/ghidra_scripts/lego_util/pdb_extraction.py @@ -135,9 +135,7 @@ def get_function_list(self) -> list[PdbFunction]: ) return [signature for signature in handled if signature is not None] - def handle_matched_function( - self, match_info: MatchInfo - ) -> Optional[PdbFunction]: + def handle_matched_function(self, match_info: MatchInfo) -> Optional[PdbFunction]: assert match_info.orig_addr is not None match_options = self.compare.get_match_options(match_info.orig_addr) assert match_options is not None @@ -172,8 +170,4 @@ def handle_matched_function( is_stub = match_options.get("stub", False) - # TODO: Remove when implementing stubs - if is_stub: - return None - return PdbFunction(match_info, function_signature, is_stub)