From 6e50ceda4e65e5952e59449fad4953ea6c5aaf37 Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Tue, 18 Nov 2025 15:45:15 -0800 Subject: Allow controlling which address is used for instructions created when inlining during analysis Previously the address of the instruction in the function being inlined was used as the new instruction's address when copying it during inlining. Now there is an additional option: use the address of the call instruction that is being replaced as the new instruction's address. This new mode is useful when inlining thunks or stub functions, but care must be taken if using it beyond that. The benefit is that it ensures that when a function contains multiple calls to the same stub function, each inlined copy ends up with distinct addresses. This ensures that call type adjustments and other overrides that are stored on the function and keyed by address can be applied independently to each callsite that was inlined. The trade-off is that if the function being inlined contains non-trivial logic, all of the inlined instructions sharing an address will limit what type of adjustments can be applied to them. The Objective-C and shared cache workflows are updated to take advantage of this new mode when they enable inlining of stub functions. This will make it possible for multiple calls to the same runtime function within a single function to have separate call type adjustments applied in the future. --- python/function.py | 41 ++++++++++++++++++++++------------------- python/types.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 20 deletions(-) (limited to 'python') diff --git a/python/function.py b/python/function.py index c328ca77..29527f4d 100644 --- a/python/function.py +++ b/python/function.py @@ -1667,13 +1667,13 @@ class Function: return self.view.get_function_parent_components(self) @property - def inline_during_analysis(self) -> 'types.BoolWithConfidence': + def inline_during_analysis(self) -> 'types.InlineDuringAnalysisWithConfidence': """Whether the function's IL should be inlined into all callers' IL""" - result = core.BNIsFunctionInlinedDuringAnalysis(self.handle) - return types.BoolWithConfidence(result.value, confidence=result.confidence) + result = core.BNGetFunctionInlinedDuringAnalysis(self.handle) + return types.InlineDuringAnalysisWithConfidence(result.value, confidence=result.confidence) @inline_during_analysis.setter - def inline_during_analysis(self, value: Union[bool, 'types.BoolWithConfidence']): + def inline_during_analysis(self, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']): self.set_user_inline_during_analysis(value) def mark_recent_use(self) -> None: @@ -3553,23 +3553,26 @@ class Function: """ core.BNUnsplitVariable(self.handle, var.to_BNVariable()) - def set_auto_inline_during_analysis(self, value: Union[bool, 'types.BoolWithConfidence']): - bc = core.BNBoolWithConfidence() - bc.value = bool(value) - if isinstance(value, types.BoolWithConfidence): - bc.confidence = value.confidence - else: - bc.confidence = core.max_confidence - core.BNSetAutoFunctionInlinedDuringAnalysis(self.handle, bc) + @classmethod + def _inline_during_analysis_with_confidence(cls, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']) -> 'types.InlineDuringAnalysisWithConfidence': + if isinstance(value, types.InlineDuringAnalysisWithConfidence): + return value - def set_user_inline_during_analysis(self, value: Union[bool, 'types.BoolWithConfidence']): - bc = core.BNBoolWithConfidence() - bc.value = bool(value) if isinstance(value, types.BoolWithConfidence): - bc.confidence = value.confidence - else: - bc.confidence = core.max_confidence - core.BNSetUserFunctionInlinedDuringAnalysis(self.handle, bc) + return core.BNInlineDuringAnalysisWithConfidence(int(value.value), value.confidence) + + if isinstance(value, bool): + return core.BNInlineDuringAnalysisWithConfidence(int(value), core.max_confidence) + + return core.BNInlineDuringAnalysisWithConfidence(value, core.max_confidence) + + def set_auto_inline_during_analysis(self, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']): + value = self._inline_during_analysis_with_confidence(value) + core.BNSetAutoFunctionInlinedDuringAnalysis(self.handle, value) + + def set_user_inline_during_analysis(self, value: Union['types.InlineDuringAnalysis', 'types.InlineDuringAnalysisWithConfidence', bool, 'types.BoolWithConfidence']): + value = self._inline_during_analysis_with_confidence(value) + core.BNSetUserFunctionInlinedDuringAnalysis(self.handle, value) def toggle_region(self, hash): """ diff --git a/python/types.py b/python/types.py index a47f531d..44583c4d 100644 --- a/python/types.py +++ b/python/types.py @@ -27,7 +27,7 @@ import uuid # Binary Ninja components from . import _binaryninjacore as core from .enums import ( - StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, + InlineDuringAnalysis, StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, ReferenceType, VariableSourceType, TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType, TokenEscapingType, @@ -528,6 +528,39 @@ class BoolWithConfidence: return BoolWithConfidence(value, confidence)._to_core_struct() +@dataclass(frozen=True) +class InlineDuringAnalysisWithConfidence: + """Represents an InlineDuringAnalysis value with an associated confidence level.""" + value: InlineDuringAnalysis + confidence: int = core.max_confidence + + def __eq__(self, other): + if not isinstance(other, self.__class__): + # For backward compatibility, allow comparison with bool + if isinstance(other, bool): + return bool(self.value) == other + # Allow comparison with enum value directly + return self.value == other + else: + return (self.value, self.confidence) == (other.value, other.confidence) + + def __ne__(self, other): + return not (self == other) + + def __bool__(self): + return bool(self.value) + + def _to_core_struct(self) -> core.BNInlineDuringAnalysisWithConfidence: + result = core.BNInlineDuringAnalysisWithConfidence() + result.value = self.value + result.confidence = self.confidence + return result + + @classmethod + def from_core_struct(cls, core_struct: core.BNInlineDuringAnalysisWithConfidence) -> 'InlineDuringAnalysisWithConfidence': + return cls(InlineDuringAnalysis(core_struct.value), core_struct.confidence) + + @dataclass class MutableTypeBuilder(Generic[TB]): type: TB -- cgit v1.3.1