From 3f9ff945c63b67dde73e498d9eb5a14c1a77684a Mon Sep 17 00:00:00 2001 From: tinysec Date: Thu, 23 Jul 2026 02:56:12 +0800 Subject: [PATCH] Add memory map navigation --- Function/BNGetMemoryRegions.cs | 2 +- Function/BNGetResolvedMemoryRanges.cs | 2 +- Handle/BNBinaryView.cs | 5 + Handle/BNMemoryMap.cs | 371 ++++++++++++++++++++++++++ Struct/BNMemoryRegionInfo.cs | 82 ++++++ Struct/BNResolvedMemoryRange.cs | 75 ++++++ 6 files changed, 535 insertions(+), 2 deletions(-) create mode 100644 Handle/BNMemoryMap.cs create mode 100644 Struct/BNMemoryRegionInfo.cs create mode 100644 Struct/BNResolvedMemoryRange.cs diff --git a/Function/BNGetMemoryRegions.cs b/Function/BNGetMemoryRegions.cs index cf0a1d33..9e4c1ba0 100644 --- a/Function/BNGetMemoryRegions.cs +++ b/Function/BNGetMemoryRegions.cs @@ -22,7 +22,7 @@ internal static extern IntPtr BNGetMemoryRegions( IntPtr view , // size_t* count - IntPtr count + out UIntPtr count ); } } diff --git a/Function/BNGetResolvedMemoryRanges.cs b/Function/BNGetResolvedMemoryRanges.cs index 2321c329..d83215e1 100644 --- a/Function/BNGetResolvedMemoryRanges.cs +++ b/Function/BNGetResolvedMemoryRanges.cs @@ -22,7 +22,7 @@ internal static extern IntPtr BNGetResolvedMemoryRanges( IntPtr view , // size_t* count - IntPtr count + out UIntPtr count ); } } diff --git a/Handle/BNBinaryView.cs b/Handle/BNBinaryView.cs index eb22495e..640b4979 100644 --- a/Handle/BNBinaryView.cs +++ b/Handle/BNBinaryView.cs @@ -5884,6 +5884,11 @@ public bool MemoryMapActivated } } + public MemoryMap MemoryMap + { + get { return new MemoryMap(this); } + } + public bool AddMemoryRegion( string name, ulong start, diff --git a/Handle/BNMemoryMap.cs b/Handle/BNMemoryMap.cs new file mode 100644 index 00000000..312a1f5f --- /dev/null +++ b/Handle/BNMemoryMap.cs @@ -0,0 +1,371 @@ +using System; +using System.Runtime.InteropServices; + +namespace BinaryNinja +{ + /// Live proxy for a binary view's configured and resolved memory map. + public sealed class MemoryMap + { + private readonly BinaryView view; + + internal MemoryMap(BinaryView view) + { + this.view = view; + } + + public bool IsActivated + { + get { return NativeMethods.BNIsMemoryMapActivated(this.view.DangerousGetHandle()); } + } + + public MemoryRegionInfo[] Regions + { + get { return this.GetMemoryRegions(); } + } + + public ResolvedMemoryRange[] Ranges + { + get { return this.GetResolvedRanges(); } + } + + public ResolvedMemoryRange[] ResolvedRanges + { + get { return this.GetResolvedRanges(); } + } + + public string Description + { + get { return this.view.MemoryMapDescription; } + } + + public string BaseDescription + { + get { return this.view.BaseMemoryMapDescription; } + } + + public void SetLogicalMemoryMapEnabled(bool enabled) + { + NativeMethods.BNSetLogicalMemoryMapEnabled(this.view.DangerousGetHandle(), enabled); + } + + public bool AddBinaryMemoryRegion( + string name, + ulong start, + BinaryView source, + uint flags = 0 + ) + { + return this.view.AddMemoryRegion(name, start, source, flags); + } + + public bool AddMemoryRegion( + string name, + ulong start, + BinaryView source, + uint flags = 0 + ) + { + return this.AddBinaryMemoryRegion(name, start, source, flags); + } + + public bool AddDataMemoryRegion( + string name, + ulong start, + DataBuffer source, + uint flags = 0 + ) + { + return this.view.AddMemoryRegion(name, start, source, flags); + } + + public bool AddMemoryRegion( + string name, + ulong start, + DataBuffer source, + uint flags = 0 + ) + { + return this.AddDataMemoryRegion(name, start, source, flags); + } + + public bool AddRemoteMemoryRegion( + string name, + ulong start, + FileAccessor source, + uint flags = 0 + ) + { + return this.view.AddMemoryRegion(name, start, source, flags); + } + + public bool AddMemoryRegion( + string name, + ulong start, + FileAccessor source, + uint flags = 0 + ) + { + return this.AddRemoteMemoryRegion(name, start, source, flags); + } + + public bool AddUnbackedMemoryRegion( + string name, + ulong start, + ulong length, + uint flags = 0, + byte fill = 0 + ) + { + return this.view.AddMemoryRegion(name, start, length, flags, fill); + } + + public bool AddMemoryRegion( + string name, + ulong start, + ulong length, + uint flags = 0, + byte fill = 0 + ) + { + return this.AddUnbackedMemoryRegion(name, start, length, flags, fill); + } + + public bool RemoveMemoryRegion(string name) + { + return this.view.RemoveMemoryRegion(name); + } + + public string GetActiveMemoryRegionAt(ulong address) + { + return this.view.GetActiveMemoryRegionAt(address); + } + + public uint GetMemoryRegionFlags(string name) + { + return this.view.GetMemoryRegionFlags(name); + } + + public bool SetMemoryRegionFlags(string name, uint flags) + { + return this.view.SetMemoryRegionFlags(name, flags); + } + + public bool IsMemoryRegionEnabled(string name) + { + return this.view.IsMemoryRegionEnabled(name); + } + + public bool SetMemoryRegionEnabled(string name, bool enabled) + { + return this.view.SetMemoryRegionEnabled(name, enabled); + } + + public bool IsMemoryRegionRebaseable(string name) + { + return this.view.IsMemoryRegionRebaseable(name); + } + + public bool SetMemoryRegionRebaseable(string name, bool rebaseable) + { + return this.view.SetMemoryRegionRebaseable(name, rebaseable); + } + + public byte GetMemoryRegionFill(string name) + { + return this.view.GetMemoryRegionFill(name); + } + + public bool SetMemoryRegionFill(string name, byte fill) + { + return this.view.SetMemoryRegionFill(name, fill); + } + + public bool IsMemoryRegionLocal(string name) + { + return this.view.IsMemoryRegionLocal(name); + } + + public string GetMemoryRegionDisplayName(string name) + { + return UnsafeUtils.TakeUtf8String( + NativeMethods.BNGetMemoryRegionDisplayName(this.view.DangerousGetHandle(), name) + ); + } + + public bool SetMemoryRegionDisplayName(string name, string displayName) + { + return NativeMethods.BNSetMemoryRegionDisplayName( + this.view.DangerousGetHandle(), + name, + displayName + ); + } + + public MemoryRegionInfo? GetMemoryRegionInfo(string name) + { + using (ScopedAllocator allocator = new ScopedAllocator()) + { + IntPtr result = allocator.AllocHGlobal(Marshal.SizeOf()); + if (!NativeMethods.BNGetMemoryRegionInfo( + this.view.DangerousGetHandle(), + name, + result + )) + { + return null; + } + + try + { + return MemoryRegionInfo.FromNative( + Marshal.PtrToStructure(result) + ); + } + finally + { + NativeMethods.BNFreeMemoryRegionInfo(result); + } + } + } + + public MemoryRegionInfo? GetRegion(string name) + { + return this.GetMemoryRegionInfo(name); + } + + public MemoryRegionInfo? GetActiveMemoryRegionInfoAt(ulong address) + { + using (ScopedAllocator allocator = new ScopedAllocator()) + { + IntPtr result = allocator.AllocHGlobal(Marshal.SizeOf()); + if (!NativeMethods.BNGetActiveMemoryRegionInfoAt( + this.view.DangerousGetHandle(), + address, + result + )) + { + return null; + } + + try + { + return MemoryRegionInfo.FromNative( + Marshal.PtrToStructure(result) + ); + } + finally + { + NativeMethods.BNFreeMemoryRegionInfo(result); + } + } + } + + public MemoryRegionInfo? GetActiveRegionAt(ulong address) + { + return this.GetActiveMemoryRegionInfoAt(address); + } + + public ResolvedMemoryRange? GetResolvedMemoryRangeAt(ulong address) + { + using (ScopedAllocator allocator = new ScopedAllocator()) + { + IntPtr result = allocator.AllocHGlobal(Marshal.SizeOf()); + if (!NativeMethods.BNGetResolvedMemoryRangeAt( + this.view.DangerousGetHandle(), + address, + result + )) + { + return null; + } + + try + { + return ResolvedMemoryRange.FromNative( + Marshal.PtrToStructure(result) + ); + } + finally + { + NativeMethods.BNFreeResolvedMemoryRange(result); + } + } + } + + public ResolvedMemoryRange? GetResolvedRangeAt(ulong address) + { + return this.GetResolvedMemoryRangeAt(address); + } + + public MemoryRegionInfo[] GetMemoryRegions() + { + IntPtr regions = NativeMethods.BNGetMemoryRegions( + this.view.DangerousGetHandle(), + out UIntPtr countValue + ); + ulong count = countValue.ToUInt64(); + if (IntPtr.Zero == regions) + { + return Array.Empty(); + } + + try + { + MemoryRegionInfo[] result = new MemoryRegionInfo[checked((int)count)]; + int nativeSize = Marshal.SizeOf(); + for (ulong i = 0; i < count; i++) + { + int offset = checked((int)(i * (ulong)nativeSize)); + BNMemoryRegionInfo item = Marshal.PtrToStructure( + IntPtr.Add(regions, offset) + ); + result[checked((int)i)] = MemoryRegionInfo.FromNative(item); + } + + return result; + } + finally + { + NativeMethods.BNFreeMemoryRegions(regions, countValue); + } + } + + public ResolvedMemoryRange[] GetResolvedRanges() + { + IntPtr ranges = NativeMethods.BNGetResolvedMemoryRanges( + this.view.DangerousGetHandle(), + out UIntPtr countValue + ); + ulong count = countValue.ToUInt64(); + if (IntPtr.Zero == ranges) + { + return Array.Empty(); + } + + try + { + ResolvedMemoryRange[] result = new ResolvedMemoryRange[checked((int)count)]; + int nativeSize = Marshal.SizeOf(); + for (ulong i = 0; i < count; i++) + { + int offset = checked((int)(i * (ulong)nativeSize)); + BNResolvedMemoryRange item = Marshal.PtrToStructure( + IntPtr.Add(ranges, offset) + ); + result[checked((int)i)] = ResolvedMemoryRange.FromNative(item); + } + + return result; + } + finally + { + NativeMethods.BNFreeResolvedMemoryRanges(ranges, countValue); + } + } + + public void Reset() + { + this.view.ResetMemoryMap(); + } + } +} diff --git a/Struct/BNMemoryRegionInfo.cs b/Struct/BNMemoryRegionInfo.cs new file mode 100644 index 00000000..a65ee599 --- /dev/null +++ b/Struct/BNMemoryRegionInfo.cs @@ -0,0 +1,82 @@ +using System; +using System.Runtime.InteropServices; + +namespace BinaryNinja +{ + [StructLayout(LayoutKind.Sequential)] + internal struct BNMemoryRegionInfo + { + internal IntPtr name; + internal IntPtr displayName; + internal ulong start; + internal ulong length; + internal uint flags; + [MarshalAs(UnmanagedType.I1)] internal bool enabled; + [MarshalAs(UnmanagedType.I1)] internal bool rebaseable; + internal byte fill; + [MarshalAs(UnmanagedType.I1)] internal bool hasTarget; + [MarshalAs(UnmanagedType.I1)] internal bool absoluteAddressMode; + [MarshalAs(UnmanagedType.I1)] internal bool local; + } + + /// Immutable snapshot of a configured memory region. + public sealed class MemoryRegionInfo + { + public string Name { get; } + public string DisplayName { get; } + public ulong Start { get; } + public ulong Length { get; } + public ulong End { get { return checked(this.Start + this.Length); } } + public SegmentFlag Flags { get; } + public bool Enabled { get; } + public bool Rebaseable { get; } + public byte Fill { get; } + public bool HasTarget { get; } + public bool AbsoluteAddressMode { get; } + public bool Local { get; } + + internal MemoryRegionInfo( + string name, + string displayName, + ulong start, + ulong length, + SegmentFlag flags, + bool enabled, + bool rebaseable, + byte fill, + bool hasTarget, + bool absoluteAddressMode, + bool local + ) + { + this.Name = name; + this.DisplayName = displayName; + this.Start = start; + this.Length = length; + this.Flags = flags; + this.Enabled = enabled; + this.Rebaseable = rebaseable; + this.Fill = fill; + this.HasTarget = hasTarget; + this.AbsoluteAddressMode = absoluteAddressMode; + this.Local = local; + } + + internal static MemoryRegionInfo FromNative(BNMemoryRegionInfo native) + { + return new MemoryRegionInfo( + UnsafeUtils.ReadUtf8String(native.name), + UnsafeUtils.ReadUtf8String(native.displayName), + native.start, + native.length, + (SegmentFlag)native.flags, + native.enabled, + native.rebaseable, + native.fill, + native.hasTarget, + native.absoluteAddressMode, + native.local + ); + } + } +} diff --git a/Struct/BNResolvedMemoryRange.cs b/Struct/BNResolvedMemoryRange.cs new file mode 100644 index 00000000..5143fd92 --- /dev/null +++ b/Struct/BNResolvedMemoryRange.cs @@ -0,0 +1,75 @@ +using System; +using System.Runtime.InteropServices; + +namespace BinaryNinja +{ + [StructLayout(LayoutKind.Sequential)] + internal struct BNResolvedMemoryRange + { + internal ulong start; + internal ulong length; + internal IntPtr regions; + internal UIntPtr regionCount; + } + + /// Immutable non-overlapping interval in the resolved memory map. + public sealed class ResolvedMemoryRange + { + public ulong Start { get; } + public ulong Length { get; } + public ulong End { get { return checked(this.Start + this.Length); } } + public MemoryRegionInfo[] Regions { get; } + + public MemoryRegionInfo? ActiveRegion + { + get { return 0 == this.Regions.Length ? null : this.Regions[0]; } + } + + public string? Name + { + get + { + MemoryRegionInfo? active = this.ActiveRegion; + return null == active ? null : active.Name; + } + } + + public SegmentFlag Flags + { + get + { + MemoryRegionInfo? active = this.ActiveRegion; + return null == active ? (SegmentFlag)0 : active.Flags; + } + } + + internal ResolvedMemoryRange( + ulong start, + ulong length, + MemoryRegionInfo[] regions + ) + { + this.Start = start; + this.Length = length; + this.Regions = regions; + } + + internal static ResolvedMemoryRange FromNative(BNResolvedMemoryRange native) + { + ulong count = native.regionCount.ToUInt64(); + MemoryRegionInfo[] regions = new MemoryRegionInfo[checked((int)count)]; + int nativeSize = Marshal.SizeOf(); + + for (ulong i = 0; i < count; i++) + { + int offset = checked((int)(i * (ulong)nativeSize)); + BNMemoryRegionInfo item = Marshal.PtrToStructure( + IntPtr.Add(native.regions, offset) + ); + regions[checked((int)i)] = MemoryRegionInfo.FromNative(item); + } + + return new ResolvedMemoryRange(native.start, native.length, regions); + } + } +}