From 849669c375ed6dab0a36edf2fab338cc52c7cc0e Mon Sep 17 00:00:00 2001 From: tinysec Date: Thu, 23 Jul 2026 12:20:06 +0800 Subject: [PATCH] Add custom architecture basic block analysis --- Architecture/BasicBlockAnalysisContext.cs | 280 ++++++++++++++++++ .../CustomArchitectureAnalysisCallbacks.cs | 42 +++ .../CustomArchitectureRegistration.cs | 1 + Struct/BNBasicBlockAnalysisContext.cs | 262 +++------------- Struct/BNCustomArchitecture.cs | 12 + 5 files changed, 374 insertions(+), 223 deletions(-) create mode 100644 Architecture/BasicBlockAnalysisContext.cs create mode 100644 Architecture/CustomArchitectureAnalysisCallbacks.cs diff --git a/Architecture/BasicBlockAnalysisContext.cs b/Architecture/BasicBlockAnalysisContext.cs new file mode 100644 index 00000000..82b96280 --- /dev/null +++ b/Architecture/BasicBlockAnalysisContext.cs @@ -0,0 +1,280 @@ +using System; +using System.Runtime.InteropServices; + +namespace BinaryNinja +{ + /// + /// Provides the native-owned context passed to custom architecture basic-block analysis. + /// + public sealed class BasicBlockAnalysisContext + { + private readonly IntPtr handle; + + internal BasicBlockAnalysisContext(IntPtr handle) + { + if (IntPtr.Zero == handle) + { + throw new ArgumentNullException(nameof(handle)); + } + + this.handle = handle; + } + + internal IntPtr DangerousGetHandle() + { + return this.handle; + } + + private BNBasicBlockAnalysisContext Native + { + get + { + return Marshal.PtrToStructure(this.handle); + } + } + + /// + /// Gets an independently referenced wrapper for the function under analysis. + /// + public Function Function + { + get + { + return BinaryNinja.Function.MustNewFromHandle(this.Native.function); + } + } + + public FunctionAnalysisSkipOverride AnalysisSkipOverride + { + get + { + return this.Native.analysisSkipOverride; + } + } + + public bool GuidedAnalysisMode + { + get + { + return this.Native.guidedAnalysisMode; + } + } + + public bool TriggerGuidedOnInvalidInstruction + { + get + { + return this.Native.triggerGuidedOnInvalidInstruction; + } + } + + public bool TranslateTailCalls + { + get + { + return this.Native.translateTailCalls; + } + } + + public bool DisallowBranchToString + { + get + { + return this.Native.disallowBranchToString; + } + } + + public ulong MaxFunctionSize + { + get + { + return this.Native.maxFunctionSize; + } + } + + public bool MaxSizeReached + { + get + { + return this.Native.maxSizeReached; + } + set + { + BNBasicBlockAnalysisContext native = this.Native; + native.maxSizeReached = value; + Marshal.StructureToPtr(native, this.handle, false); + } + } + + public IndirectBranchInfo[] IndirectBranches + { + get + { + BNBasicBlockAnalysisContext native = this.Native; + return UnsafeUtils.ReadStructArray( + native.indirectBranches, + native.indirectBranchesCount, + IndirectBranchInfo.FromNative); + } + } + + public ArchitectureAndAddress[] IndirectNoReturnCalls + { + get + { + BNBasicBlockAnalysisContext native = this.Native; + return UnsafeUtils.ReadStructArray< + BNArchitectureAndAddress, + ArchitectureAndAddress>( + native.indirectNoReturnCalls, + native.indirectNoReturnCallsCount, + ArchitectureAndAddress.FromNative); + } + } + + public IntPtr FunctionArchitectureContext + { + get + { + return this.Native.functionArchContext; + } + set + { + BNBasicBlockAnalysisContext native = this.Native; + if (IntPtr.Zero != native.functionArchContext) + { + throw new InvalidOperationException( + "The function architecture context has already been set."); + } + + native.functionArchContext = value; + Marshal.StructureToPtr(native, this.handle, false); + } + } + + public BasicBlock? CreateBasicBlock(Architecture architecture, ulong address) + { + if (null == architecture) + { + throw new ArgumentNullException(nameof(architecture)); + } + + return AnalyzeBasicBlocksContext.CreateBasicBlock( + this.handle, + architecture, + address); + } + + public void AddBasicBlock(BasicBlock block) + { + if (null == block) + { + throw new ArgumentNullException(nameof(block)); + } + + AnalyzeBasicBlocksContext.AddBasicBlockToFunction(this.handle, block); + } + + public void AddTemporaryOutgoingReference(Function target) + { + if (null == target) + { + throw new ArgumentNullException(nameof(target)); + } + + AnalyzeBasicBlocksContext.AddTempReference(this.handle, target); + } + + public void SetContextualFunctionReturns( + ArchitectureAndAddress[] sources, + bool[] values) + { + if (null == sources) + { + throw new ArgumentNullException(nameof(sources)); + } + + if (null == values) + { + throw new ArgumentNullException(nameof(values)); + } + + if (sources.Length != values.Length) + { + throw new ArgumentException( + "Sources and values must contain the same number of entries."); + } + + AnalyzeBasicBlocksContext.SetContextualFunctionReturns( + this.handle, + sources, + values); + } + + public void SetDirectCodeReferences( + ArchitectureAndAddress[] sources, + ulong[] targets) + { + if (null == sources) + { + throw new ArgumentNullException(nameof(sources)); + } + + if (null == targets) + { + throw new ArgumentNullException(nameof(targets)); + } + + if (sources.Length != targets.Length) + { + throw new ArgumentException( + "Sources and targets must contain the same number of entries."); + } + + AnalyzeBasicBlocksContext.SetDirectCodeReferences( + this.handle, + sources, + targets); + } + + public void SetDirectNoReturnCalls(ArchitectureAndAddress[] sources) + { + if (null == sources) + { + throw new ArgumentNullException(nameof(sources)); + } + + AnalyzeBasicBlocksContext.SetDirectNoReturnCalls(this.handle, sources); + } + + public void SetHaltedDisassemblyAddresses(ArchitectureAndAddress[] sources) + { + if (null == sources) + { + throw new ArgumentNullException(nameof(sources)); + } + + AnalyzeBasicBlocksContext.SetHaltedDisassemblyAddresses(this.handle, sources); + } + + public void SetInlinedUnresolvedIndirectBranches( + ArchitectureAndAddress[] sourceAndDestinationPairs) + { + if (null == sourceAndDestinationPairs) + { + throw new ArgumentNullException(nameof(sourceAndDestinationPairs)); + } + + if (0 != sourceAndDestinationPairs.Length % 2) + { + throw new ArgumentException( + "Source and destination locations must be provided in pairs.", + nameof(sourceAndDestinationPairs)); + } + + AnalyzeBasicBlocksContext.SetInlinedUnresolvedIndirectBranches( + this.handle, + sourceAndDestinationPairs); + } + } +} diff --git a/Architecture/CustomArchitectureAnalysisCallbacks.cs b/Architecture/CustomArchitectureAnalysisCallbacks.cs new file mode 100644 index 00000000..eaa2ec32 --- /dev/null +++ b/Architecture/CustomArchitectureAnalysisCallbacks.cs @@ -0,0 +1,42 @@ +using System; +using System.Runtime.InteropServices; + +namespace BinaryNinja +{ + public abstract partial class CustomArchitecture + { + [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)] + private delegate void AnalyzeBasicBlocksCallback( + IntPtr context, + IntPtr function, + IntPtr analysisContext); + + private void AddAnalysisCallbacks(ref BNCustomArchitecture callbacks) + { + callbacks.analyzeBasicBlocks = UnsafeUtils.PinCallback( + this.AnalyzeBasicBlocksAdapter); + } + + private void AnalyzeBasicBlocksAdapter( + IntPtr context, + IntPtr functionHandle, + IntPtr analysisContextHandle) + { + try + { + using (Function function = Function.MustNewFromHandle(functionHandle)) + { + BasicBlockAnalysisContext analysisContext = + new BasicBlockAnalysisContext(analysisContextHandle); + this.AnalyzeBasicBlocks(function, analysisContext); + } + } + catch (Exception exception) + { + Core.LogError( + "Unhandled exception in CustomArchitecture.AnalyzeBasicBlocks: {0}", + exception); + } + } + } +} diff --git a/Architecture/CustomArchitectureRegistration.cs b/Architecture/CustomArchitectureRegistration.cs index 8936d4ec..ee848979 100644 --- a/Architecture/CustomArchitectureRegistration.cs +++ b/Architecture/CustomArchitectureRegistration.cs @@ -65,6 +65,7 @@ public Architecture Register(string name) this.AddAssemblyAndPatchCallbacks(ref callbacks); this.AddIntrinsicCallbacks(ref callbacks); this.AddFlagLoweringCallbacks(ref callbacks); + this.AddAnalysisCallbacks(ref callbacks); using (ScopedAllocator allocator = new ScopedAllocator()) { diff --git a/Struct/BNBasicBlockAnalysisContext.cs b/Struct/BNBasicBlockAnalysisContext.cs index 7294cbf8..51849f57 100644 --- a/Struct/BNBasicBlockAnalysisContext.cs +++ b/Struct/BNBasicBlockAnalysisContext.cs @@ -1,231 +1,47 @@ using System; -using System.Collections.Generic; using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; namespace BinaryNinja { [StructLayout(LayoutKind.Sequential)] - internal unsafe struct BNBasicBlockAnalysisContext + internal struct BNBasicBlockAnalysisContext { - /// - /// BNFunction* function - /// - public IntPtr function; - - /// - /// BNFunctionAnalysisSkipOverride analysisSkipOverride - /// - public FunctionAnalysisSkipOverride analysisSkipOverride; - - /// - /// bool guidedAnalysisMode - /// - [MarshalAs(UnmanagedType.I1)] public bool guidedAnalysisMode; - - /// - /// bool triggerGuidedOnInvalidInstruction - /// - [MarshalAs(UnmanagedType.I1)] public bool triggerGuidedOnInvalidInstruction; - - /// - /// bool translateTailCalls - /// - [MarshalAs(UnmanagedType.I1)] public bool translateTailCalls; - - /// - /// bool disallowBranchToString - /// - [MarshalAs(UnmanagedType.I1)] public bool disallowBranchToString; - - /// - /// uint64_t maxFunctionSize - /// - public ulong maxFunctionSize; - - /// - /// uint64_t indirectBranchesCount - /// - public ulong indirectBranchesCount; - - /// - /// BNIndirectBranchInfo* indirectBranches - /// - public IntPtr indirectBranches; - - /// - /// uint64_t indirectNoReturnCallsCount - /// - public ulong indirectNoReturnCallsCount; - - /// - /// BNArchitectureAndAddress* indirectNoReturnCalls - /// - public IntPtr indirectNoReturnCalls; - - /// - /// bool maxSizeReached - /// - [MarshalAs(UnmanagedType.I1)] public bool maxSizeReached; - - /// - /// uint64_t contextualFunctionReturnCount - /// - public ulong contextualFunctionReturnCount; - - /// - /// BNArchitectureAndAddress* contextualFunctionReturnLocations - /// - public IntPtr contextualFunctionReturnLocations; - - /// - /// bool* contextualFunctionReturnValues - /// - public IntPtr contextualFunctionReturnValues; - - /// - /// uint64_t directRefCount - /// - public ulong directRefCount; - - /// - /// BNArchitectureAndAddress* directRefSources - /// - public IntPtr directRefSources; - - /// - /// uint64_t* directRefTargets - /// - public IntPtr directRefTargets; - - /// - /// uint64_t directNoReturnCallsCount - /// - public ulong directNoReturnCallsCount; - - /// - /// BNArchitectureAndAddress* directNoReturnCalls - /// - public IntPtr directNoReturnCalls; - - /// - /// uint64_t haltedDisassemblyAddressesCount - /// - public ulong haltedDisassemblyAddressesCount; - - /// - /// BNArchitectureAndAddress* haltedDisassemblyAddresses - /// - public IntPtr haltedDisassemblyAddresses; - - /// - /// uint64_t inlinedUnresolvedIndirectBranchCount - /// - public ulong inlinedUnresolvedIndirectBranchCount; - - /// - /// BNArchitectureAndAddress* inlinedUnresolvedIndirectBranches - /// - public IntPtr inlinedUnresolvedIndirectBranches; - } + internal IntPtr function; + internal FunctionAnalysisSkipOverride analysisSkipOverride; + + [MarshalAs(UnmanagedType.I1)] + internal bool guidedAnalysisMode; + + [MarshalAs(UnmanagedType.I1)] + internal bool triggerGuidedOnInvalidInstruction; + + [MarshalAs(UnmanagedType.I1)] + internal bool translateTailCalls; - public class BasicBlockAnalysisContext - { - public Function? Function { get; set; } = null; - - public FunctionAnalysisSkipOverride AnalysisSkipOverride { get; set; } = - FunctionAnalysisSkipOverride.DefaultFunctionAnalysisSkip; - - public bool GuidedAnalysisMode { get; set; } = false; - - public bool TriggerGuidedOnInvalidInstruction { get; set; } = false; - - public bool TranslateTailCalls { get; set; } = false; - - public bool DisallowBranchToString { get; set; } = false; - - public ulong MaxFunctionSize { get; set; } = 0; - - public ulong IndirectBranchesCount { get; set; } = 0; - - /// - /// BNIndirectBranchInfo* indirectBranches - /// - public IntPtr IndirectBranches { get; set; } = IntPtr.Zero; - - /// - /// uint64_t indirectNoReturnCallsCount - /// - public ulong IndirectNoReturnCallsCount { get; set; } = 0; - - /// - /// BNArchitectureAndAddress* indirectNoReturnCalls - /// - public IntPtr IndirectNoReturnCalls { get; set; } = IntPtr.Zero; - - public bool MaxSizeReached { get; set; } = false; - - public ulong ContextualFunctionReturnCount { get; set; } = 0; - - /// - /// BNArchitectureAndAddress* contextualFunctionReturnLocations - /// - public IntPtr ContextualFunctionReturnLocations { get; set; } = IntPtr.Zero; - - /// - /// bool* contextualFunctionReturnValues - /// - public IntPtr ContextualFunctionReturnValues { get; set; } = IntPtr.Zero; - - /// - /// uint64_t directRefCount - /// - public ulong DirectRefCount { get; set; } = 0; - - /// - /// BNArchitectureAndAddress* directRefSources - /// - public IntPtr DirectRefSources { get; set; } = IntPtr.Zero; - - /// - /// uint64_t* directRefTargets - /// - public IntPtr DirectRefTargets { get; set; } = IntPtr.Zero; - - /// - /// uint64_t directNoReturnCallsCount - /// - public ulong DirectNoReturnCallsCount { get; set; } = 0; - - /// - /// BNArchitectureAndAddress* directNoReturnCalls - /// - public IntPtr DirectNoReturnCalls { get; set; } = IntPtr.Zero; - - /// - /// uint64_t haltedDisassemblyAddressesCount - /// - public ulong HaltedDisassemblyAddressesCount { get; set; } = 0; - - /// - /// BNArchitectureAndAddress* haltedDisassemblyAddresses - /// - public IntPtr HaltedDisassemblyAddresses { get; set; } = IntPtr.Zero; - - /// - /// uint64_t inlinedUnresolvedIndirectBranchCount - /// - public ulong InlinedUnresolvedIndirectBranchCount { get; set; } = 0; - - /// - /// BNArchitectureAndAddress* inlinedUnresolvedIndirectBranches - /// - public IntPtr InlinedUnresolvedIndirectBranches { get; set; } = IntPtr.Zero; - - - public BasicBlockAnalysisContext() - { - - } - } -} \ No newline at end of file + [MarshalAs(UnmanagedType.I1)] + internal bool disallowBranchToString; + + internal ulong maxFunctionSize; + internal ulong indirectBranchesCount; + internal IntPtr indirectBranches; + internal ulong indirectNoReturnCallsCount; + internal IntPtr indirectNoReturnCalls; + + [MarshalAs(UnmanagedType.I1)] + internal bool maxSizeReached; + + internal ulong contextualFunctionReturnCount; + internal IntPtr contextualFunctionReturnLocations; + internal IntPtr contextualFunctionReturnValues; + internal ulong directRefCount; + internal IntPtr directRefSources; + internal IntPtr directRefTargets; + internal ulong directNoReturnCallsCount; + internal IntPtr directNoReturnCalls; + internal ulong haltedDisassemblyAddressesCount; + internal IntPtr haltedDisassemblyAddresses; + internal ulong inlinedUnresolvedIndirectBranchCount; + internal IntPtr inlinedUnresolvedIndirectBranches; + internal IntPtr functionArchContext; + } +} diff --git a/Struct/BNCustomArchitecture.cs b/Struct/BNCustomArchitecture.cs index e5f629af..ccbc3aee 100644 --- a/Struct/BNCustomArchitecture.cs +++ b/Struct/BNCustomArchitecture.cs @@ -671,6 +671,18 @@ LowLevelILFunction il { return null; } + + /// + /// Analyzes a function's basic blocks using the core-owned analysis context. + /// + public virtual void AnalyzeBasicBlocks( + Function function, + BasicBlockAnalysisContext context) + { + NativeMethods.BNArchitectureDefaultAnalyzeBasicBlocks( + function.DangerousGetHandle(), + context.DangerousGetHandle()); + } public virtual string GetRegisterName( RegisterIndex reg