From a93f0bf1097f1b37bccd120b121ae62ab7040135 Mon Sep 17 00:00:00 2001 From: tinysec Date: Thu, 23 Jul 2026 12:28:31 +0800 Subject: [PATCH] Add custom architecture function lifting --- .../CustomArchitectureAnalysisCallbacks.cs | 58 ++++ Architecture/FunctionLifterContext.cs | 267 ++++++++++++++++++ Struct/BNCustomArchitecture.cs | 19 ++ Struct/BNFunctionLifterContext.cs | 28 ++ 4 files changed, 372 insertions(+) create mode 100644 Architecture/FunctionLifterContext.cs create mode 100644 Struct/BNFunctionLifterContext.cs diff --git a/Architecture/CustomArchitectureAnalysisCallbacks.cs b/Architecture/CustomArchitectureAnalysisCallbacks.cs index eaa2ec3..5dcdcbb 100644 --- a/Architecture/CustomArchitectureAnalysisCallbacks.cs +++ b/Architecture/CustomArchitectureAnalysisCallbacks.cs @@ -11,10 +11,27 @@ private delegate void AnalyzeBasicBlocksCallback( IntPtr function, IntPtr analysisContext); + [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private delegate bool LiftFunctionCallback( + IntPtr context, + IntPtr function, + IntPtr lifterContext); + + [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)] + private delegate void FreeFunctionArchitectureContextCallback( + IntPtr context, + IntPtr functionContext); + private void AddAnalysisCallbacks(ref BNCustomArchitecture callbacks) { callbacks.analyzeBasicBlocks = UnsafeUtils.PinCallback( this.AnalyzeBasicBlocksAdapter); + callbacks.liftFunction = UnsafeUtils.PinCallback( + this.LiftFunctionAdapter); + callbacks.freeFunctionArchContext = + UnsafeUtils.PinCallback( + this.FreeFunctionArchitectureContextAdapter); } private void AnalyzeBasicBlocksAdapter( @@ -38,5 +55,46 @@ private void AnalyzeBasicBlocksAdapter( exception); } } + + private bool LiftFunctionAdapter( + IntPtr context, + IntPtr functionHandle, + IntPtr lifterContextHandle) + { + try + { + using (LowLevelILFunction function = + this.CreateCallbackLowLevelIL(functionHandle)) + { + FunctionLifterContext lifterContext = new FunctionLifterContext( + lifterContextHandle, + function); + return this.LiftFunction(function, lifterContext); + } + } + catch (Exception exception) + { + Core.LogError( + "Unhandled exception in CustomArchitecture.LiftFunction: {0}", + exception); + return false; + } + } + + private void FreeFunctionArchitectureContextAdapter( + IntPtr context, + IntPtr functionContext) + { + try + { + this.FreeFunctionArchContext(functionContext); + } + catch (Exception exception) + { + Core.LogError( + "Unhandled exception in CustomArchitecture.FreeFunctionArchContext: {0}", + exception); + } + } } } diff --git a/Architecture/FunctionLifterContext.cs b/Architecture/FunctionLifterContext.cs new file mode 100644 index 0000000..fa8a6f9 --- /dev/null +++ b/Architecture/FunctionLifterContext.cs @@ -0,0 +1,267 @@ +using System; +using System.Runtime.InteropServices; + +namespace BinaryNinja +{ + /// + /// Provides the native-owned context passed to custom architecture function lifting. + /// + public sealed class FunctionLifterContext + { + private readonly IntPtr handle; + private readonly LowLevelILFunction destination; + + internal FunctionLifterContext( + IntPtr handle, + LowLevelILFunction destination) + { + if (IntPtr.Zero == handle) + { + throw new ArgumentNullException(nameof(handle)); + } + + this.handle = handle; + this.destination = destination + ?? throw new ArgumentNullException(nameof(destination)); + } + + internal IntPtr DangerousGetHandle() + { + return this.handle; + } + + private BNFunctionLifterContext Native + { + get + { + return Marshal.PtrToStructure(this.handle); + } + } + + /// + /// Gets an independently referenced wrapper for the target platform. + /// + public Platform Platform + { + get + { + return BinaryNinja.Platform.MustNewFromHandle(this.Native.platform); + } + } + + /// + /// Gets an independently referenced wrapper for the analysis logger. + /// + public Logger Logger + { + get + { + return BinaryNinja.Logger.MustNewFromHandle(this.Native.logger); + } + } + + /// + /// Gets independently referenced wrappers for the function's basic blocks. + /// + public BasicBlock[] BasicBlocks + { + get + { + BNFunctionLifterContext native = this.Native; + return UnsafeUtils.ReadHandleArray( + native.basicBlocks, + native.basicBlockCount, + BasicBlock.MustNewFromHandle); + } + } + + public ArchitectureAndAddress[] InlinedRemappingKeys + { + get + { + BNFunctionLifterContext native = this.Native; + return this.ReadLocations( + native.inlinedRemappingKeys, + native.inlinedRemappingEntryCount); + } + } + + public ArchitectureAndAddress[] InlinedRemappingValues + { + get + { + BNFunctionLifterContext native = this.Native; + return this.ReadLocations( + native.inlinedRemappingValues, + native.inlinedRemappingEntryCount); + } + } + + public IndirectBranchInfo[] IndirectBranches + { + get + { + BNFunctionLifterContext native = this.Native; + return UnsafeUtils.ReadStructArray( + native.indirectBranches, + native.indirectBranchesCount, + IndirectBranchInfo.FromNative); + } + } + + public ArchitectureAndAddress[] NoReturnCalls + { + get + { + BNFunctionLifterContext native = this.Native; + return this.ReadLocations( + native.noReturnCalls, + native.noReturnCallsCount); + } + } + + public ArchitectureAndAddress[] ContextualFunctionReturnLocations + { + get + { + BNFunctionLifterContext native = this.Native; + return this.ReadLocations( + native.contextualFunctionReturnLocations, + native.contextualFunctionReturnCount); + } + } + + public bool[] ContextualFunctionReturnValues + { + get + { + BNFunctionLifterContext native = this.Native; + return UnsafeUtils.ReadBoolArray( + native.contextualFunctionReturnValues, + native.contextualFunctionReturnCount); + } + } + + public ulong[] InlinedCalls + { + get + { + BNFunctionLifterContext native = this.Native; + return UnsafeUtils.ReadNumberArray( + native.inlinedCalls, + native.inlinedCallsCount); + } + } + + public IntPtr FunctionArchitectureContext + { + get + { + return this.Native.functionArchContext; + } + } + + public bool ContainsInlinedFunctions + { + get + { + IntPtr value = this.Native.containsInlinedFunctions; + return IntPtr.Zero != value && UnsafeUtils.ReadBool(value); + } + set + { + IntPtr destination = this.Native.containsInlinedFunctions; + if (IntPtr.Zero == destination) + { + throw new InvalidOperationException( + "The lifter context does not provide an inline-result pointer."); + } + + Marshal.WriteByte(destination, value ? (byte)1 : (byte)0); + } + } + + public void PrepareBlockTranslation( + LowLevelILFunction function, + Architecture architecture, + ulong address) + { + if (null == function) + { + throw new ArgumentNullException(nameof(function)); + } + + if (null == architecture) + { + throw new ArgumentNullException(nameof(architecture)); + } + + NativeMethods.BNPrepareBlockTranslation( + function.DangerousGetHandle(), + architecture.DangerousGetHandle(), + address); + } + + public unsafe BasicBlock[] PrepareToCopyForeignFunction( + LowLevelILFunction source) + { + if (null == source) + { + throw new ArgumentNullException(nameof(source)); + } + + ulong count = 0; + IntPtr blocks = NativeMethods.BNPrepareToCopyForeignFunction( + this.destination.DangerousGetHandle(), + source.DangerousGetHandle(), + (IntPtr)(&count)); + try + { + return UnsafeUtils.ReadHandleArray( + blocks, + count, + BasicBlock.MustNewFromHandle); + } + finally + { + if (IntPtr.Zero != blocks) + { + NativeMethods.BNFreeBasicBlockList(blocks, count); + } + } + } + + public LowLevelILFunction? GetForeignFunctionLiftedIL(Function function) + { + if (null == function) + { + throw new ArgumentNullException(nameof(function)); + } + + BNFunctionLifterContext native = this.Native; + ulong[] inlinedCalls = this.InlinedCalls; + using (ScopedAllocator allocator = new ScopedAllocator()) + { + IntPtr result = NativeMethods.BNGetForeignFunctionLiftedIL( + function.DangerousGetHandle(), + native.logger, + new UIntPtr((ulong)inlinedCalls.Length), + allocator.AllocStructArray(inlinedCalls)); + return LowLevelILFunction.TakeHandle( + result, + false, + function.Architecture); + } + } + + private ArchitectureAndAddress[] ReadLocations(IntPtr locations, ulong count) + { + return UnsafeUtils.ReadStructArray< + BNArchitectureAndAddress, + ArchitectureAndAddress>( + locations, + count, + ArchitectureAndAddress.FromNative); + } + } +} diff --git a/Struct/BNCustomArchitecture.cs b/Struct/BNCustomArchitecture.cs index ccbc3ae..1d03ffa 100644 --- a/Struct/BNCustomArchitecture.cs +++ b/Struct/BNCustomArchitecture.cs @@ -683,6 +683,25 @@ public virtual void AnalyzeBasicBlocks( function.DangerousGetHandle(), context.DangerousGetHandle()); } + + /// + /// Lifts a function using the core-owned function-level lifting context. + /// + public virtual bool LiftFunction( + LowLevelILFunction function, + FunctionLifterContext context) + { + return NativeMethods.BNArchitectureDefaultLiftFunction( + function.DangerousGetHandle(), + context.DangerousGetHandle()); + } + + /// + /// Releases architecture-specific context created during basic-block analysis. + /// + public virtual void FreeFunctionArchContext(IntPtr context) + { + } public virtual string GetRegisterName( RegisterIndex reg diff --git a/Struct/BNFunctionLifterContext.cs b/Struct/BNFunctionLifterContext.cs new file mode 100644 index 0000000..81ea446 --- /dev/null +++ b/Struct/BNFunctionLifterContext.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.InteropServices; + +namespace BinaryNinja +{ + [StructLayout(LayoutKind.Sequential)] + internal struct BNFunctionLifterContext + { + internal IntPtr platform; + internal IntPtr logger; + internal ulong basicBlockCount; + internal IntPtr basicBlocks; + internal ulong inlinedRemappingEntryCount; + internal IntPtr inlinedRemappingKeys; + internal IntPtr inlinedRemappingValues; + internal ulong indirectBranchesCount; + internal IntPtr indirectBranches; + internal ulong noReturnCallsCount; + internal IntPtr noReturnCalls; + internal ulong contextualFunctionReturnCount; + internal IntPtr contextualFunctionReturnLocations; + internal IntPtr contextualFunctionReturnValues; + internal ulong inlinedCallsCount; + internal IntPtr inlinedCalls; + internal IntPtr functionArchContext; + internal IntPtr containsInlinedFunctions; + } +}