diff --git a/Function/BNGetStringRecognizerList.cs b/Function/BNGetStringRecognizerList.cs
index 62e3e3e..33c4a72 100644
--- a/Function/BNGetStringRecognizerList.cs
+++ b/Function/BNGetStringRecognizerList.cs
@@ -19,7 +19,7 @@ internal static partial class NativeMethods
internal static extern IntPtr BNGetStringRecognizerList(
// size_t* count
- IntPtr count
+ out ulong count
);
}
}
diff --git a/Function/BNRegisterStringRecognizer.cs b/Function/BNRegisterStringRecognizer.cs
index e3f7434..7a383a0 100644
--- a/Function/BNRegisterStringRecognizer.cs
+++ b/Function/BNRegisterStringRecognizer.cs
@@ -23,7 +23,7 @@ internal static extern IntPtr BNRegisterStringRecognizer(
[MarshalAs(UnmanagedType.LPUTF8Str)] string name ,
// BNCustomStringRecognizer* recognizer
- IntPtr recognizer
+ in BNCustomStringRecognizer recognizer
);
}
}
diff --git a/Function/BNStringRecognizerRecognizeConstant.cs b/Function/BNStringRecognizerRecognizeConstant.cs
index c6636f7..144306c 100644
--- a/Function/BNStringRecognizerRecognizeConstant.cs
+++ b/Function/BNStringRecognizerRecognizeConstant.cs
@@ -35,7 +35,7 @@ internal static extern bool BNStringRecognizerRecognizeConstant(
long val ,
// BNDerivedString* out
- IntPtr @out
+ out BNDerivedString @out
);
}
}
diff --git a/Function/BNStringRecognizerRecognizeConstantPointer.cs b/Function/BNStringRecognizerRecognizeConstantPointer.cs
index 5e1414e..6210a79 100644
--- a/Function/BNStringRecognizerRecognizeConstantPointer.cs
+++ b/Function/BNStringRecognizerRecognizeConstantPointer.cs
@@ -35,7 +35,7 @@ internal static extern bool BNStringRecognizerRecognizeConstantPointer(
long val ,
// BNDerivedString* out
- IntPtr @out
+ out BNDerivedString @out
);
}
}
diff --git a/Function/BNStringRecognizerRecognizeExternPointer.cs b/Function/BNStringRecognizerRecognizeExternPointer.cs
index dbf0b8e..f40d3bb 100644
--- a/Function/BNStringRecognizerRecognizeExternPointer.cs
+++ b/Function/BNStringRecognizerRecognizeExternPointer.cs
@@ -38,7 +38,7 @@ internal static extern bool BNStringRecognizerRecognizeExternPointer(
ulong offset ,
// BNDerivedString* out
- IntPtr @out
+ out BNDerivedString @out
);
}
}
diff --git a/Function/BNStringRecognizerRecognizeImport.cs b/Function/BNStringRecognizerRecognizeImport.cs
index bb2a9e6..52061c6 100644
--- a/Function/BNStringRecognizerRecognizeImport.cs
+++ b/Function/BNStringRecognizerRecognizeImport.cs
@@ -35,7 +35,7 @@ internal static extern bool BNStringRecognizerRecognizeImport(
long val ,
// BNDerivedString* out
- IntPtr @out
+ out BNDerivedString @out
);
}
}
diff --git a/NavigationExtensions.cs b/NavigationExtensions.cs
index 8875071..689f35f 100644
--- a/NavigationExtensions.cs
+++ b/NavigationExtensions.cs
@@ -80,6 +80,8 @@ public sealed class DerivedString
public ulong Length { get; }
+ public CustomStringType? CustomType { get; }
+
///
/// Constructs a value-only DerivedString with no location, mirroring Python
/// DerivedString(value=..., location=None, custom_type=None) for the common case of
@@ -87,7 +89,24 @@ public sealed class DerivedString
/// .
///
public DerivedString(string value)
- : this(value, false, 0, 0UL, 0UL)
+ : this(value, false, 0, 0UL, 0UL, null)
+ {
+ }
+
+ /// Constructs a derived string with an explicit source location and type.
+ public DerivedString(
+ string value,
+ DerivedStringLocationType locationType,
+ ulong address,
+ ulong length,
+ CustomStringType? customType = null)
+ : this(value, true, (int)locationType, address, length, customType)
+ {
+ }
+
+ /// Constructs a value-only derived string with an explicit custom type.
+ public DerivedString(string value, CustomStringType customType)
+ : this(value, false, 0, 0UL, 0UL, customType)
{
}
@@ -96,13 +115,15 @@ private DerivedString(
bool locationValid,
int locationType,
ulong addr,
- ulong len)
+ ulong len,
+ CustomStringType? customType)
{
this.Value = value;
this.LocationValid = locationValid;
this.LocationType = (DerivedStringLocationType)locationType;
this.Address = addr;
this.Length = len;
+ this.CustomType = customType;
}
// Reads one BNDerivedString out of a by-value native array. Must run before the array
@@ -117,7 +138,8 @@ internal static DerivedString FromNative(BNDerivedString raw)
raw.locationValid,
raw.location.locationType,
raw.location.addr,
- raw.location.len
+ raw.location.len,
+ CustomStringType.FromHandle(raw.customType)
);
}
@@ -125,9 +147,9 @@ internal static DerivedString FromNative(BNDerivedString raw)
// (BNSetHighLevelILDerivedStringReferenceForExpr). The Value text is rebuilt as a fresh
// BNStringRef* because this projection reads eagerly and does not retain the original core
// handle. The caller owns that BNStringRef* and must free it (BNFreeStringRef) once the
- // core has consumed the struct: core copies/add-refs the string, mirroring Python's
- // owned=False borrow. custom_type is not surfaced by this read-only projection (Python
- // CustomStringType handle is dropped), so it is left null.
+ // core has consumed the struct: core copies/add-refs the string for setter calls, while
+ // recognizer callbacks transfer the owned reference to the core caller. The custom type
+ // is a static core handle and is passed through without an additional reference.
internal BNDerivedString ToNativeEx()
{
ulong byteCount = (ulong)Encoding.UTF8.GetByteCount(this.Value);
@@ -147,7 +169,9 @@ internal BNDerivedString ToNativeEx()
native.location.len = this.Length;
}
- native.customType = IntPtr.Zero;
+ native.customType = null == this.CustomType
+ ? IntPtr.Zero
+ : this.CustomType.DangerousGetHandle();
return native;
}
diff --git a/StringRecognizer.cs b/StringRecognizer.cs
new file mode 100644
index 0000000..d58e8e9
--- /dev/null
+++ b/StringRecognizer.cs
@@ -0,0 +1,631 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace BinaryNinja
+{
+ internal static partial class NativeDelegates
+ {
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ internal delegate bool BNStringRecognizerIsValidForType(
+ IntPtr context,
+ IntPtr function,
+ IntPtr type
+ );
+
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ internal delegate bool BNStringRecognizerRecognizeConstant(
+ IntPtr context,
+ IntPtr function,
+ UIntPtr expressionIndex,
+ IntPtr type,
+ long value,
+ IntPtr result
+ );
+
+ [UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ internal delegate bool BNStringRecognizerRecognizeExternPointer(
+ IntPtr context,
+ IntPtr function,
+ UIntPtr expressionIndex,
+ IntPtr type,
+ long value,
+ ulong offset,
+ IntPtr result
+ );
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct BNCustomStringRecognizer
+ {
+ internal IntPtr context;
+
+ internal IntPtr isValidForType;
+
+ internal IntPtr recognizeConstant;
+
+ internal IntPtr recognizeConstantPointer;
+
+ internal IntPtr recognizeExternPointer;
+
+ internal IntPtr recognizeImport;
+ }
+
+ ///
+ /// Recognizes derived strings represented by high-level IL constants and pointer expressions.
+ ///
+ public class StringRecognizer : AbstractSafeHandle
+ {
+ private enum RecognitionKind
+ {
+ Constant,
+ ConstantPointer,
+ ExternPointer,
+ Import
+ }
+
+ private static readonly object registrationLock = new object();
+
+ private static readonly List registeredRecognizers =
+ new List();
+
+ private readonly string? registrationName;
+
+ private bool isRegistered;
+
+ private NativeDelegates.BNStringRecognizerIsValidForType? isValidForTypeCallback;
+
+ private NativeDelegates.BNStringRecognizerRecognizeConstant? recognizeConstantCallback;
+
+ private NativeDelegates.BNStringRecognizerRecognizeConstant? recognizeConstantPointerCallback;
+
+ private NativeDelegates.BNStringRecognizerRecognizeExternPointer? recognizeExternPointerCallback;
+
+ private NativeDelegates.BNStringRecognizerRecognizeConstant? recognizeImportCallback;
+
+ /// Creates an unregistered custom string recognizer.
+ protected StringRecognizer(string name)
+ : base(false)
+ {
+ if (null == name)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ this.registrationName = name;
+ }
+
+ private StringRecognizer(IntPtr handle)
+ : base(handle, false)
+ {
+ }
+
+ /// Gets the registered recognizer name.
+ public string Name
+ {
+ get
+ {
+ if (this.IsInvalid)
+ {
+ return this.registrationName ?? string.Empty;
+ }
+
+ return UnsafeUtils.TakeUtf8String(
+ NativeMethods.BNGetStringRecognizerName(this.handle)
+ );
+ }
+ }
+
+ /// Registers this custom recognizer and roots its callbacks for core use.
+ public void Register()
+ {
+ if (this.isRegistered || !this.IsInvalid)
+ {
+ throw new InvalidOperationException("The string recognizer is already registered.");
+ }
+
+ this.isValidForTypeCallback =
+ new NativeDelegates.BNStringRecognizerIsValidForType(this.InvokeIsValidForType);
+ this.recognizeConstantCallback =
+ new NativeDelegates.BNStringRecognizerRecognizeConstant(this.InvokeRecognizeConstant);
+ this.recognizeConstantPointerCallback =
+ new NativeDelegates.BNStringRecognizerRecognizeConstant(
+ this.InvokeRecognizeConstantPointer
+ );
+ this.recognizeExternPointerCallback =
+ new NativeDelegates.BNStringRecognizerRecognizeExternPointer(
+ this.InvokeRecognizeExternPointer
+ );
+ this.recognizeImportCallback =
+ new NativeDelegates.BNStringRecognizerRecognizeConstant(this.InvokeRecognizeImport);
+
+ BNCustomStringRecognizer callbacks = new BNCustomStringRecognizer();
+ callbacks.context = IntPtr.Zero;
+ callbacks.isValidForType = Marshal.GetFunctionPointerForDelegate(
+ this.isValidForTypeCallback
+ );
+ callbacks.recognizeConstant = Marshal.GetFunctionPointerForDelegate(
+ this.recognizeConstantCallback
+ );
+ callbacks.recognizeConstantPointer = Marshal.GetFunctionPointerForDelegate(
+ this.recognizeConstantPointerCallback
+ );
+ callbacks.recognizeExternPointer = Marshal.GetFunctionPointerForDelegate(
+ this.recognizeExternPointerCallback
+ );
+ callbacks.recognizeImport = Marshal.GetFunctionPointerForDelegate(
+ this.recognizeImportCallback
+ );
+
+ IntPtr handle = NativeMethods.BNRegisterStringRecognizer(
+ this.registrationName ?? string.Empty,
+ in callbacks
+ );
+ if (IntPtr.Zero == handle)
+ {
+ throw new InvalidOperationException("The core rejected the string recognizer.");
+ }
+
+ this.SetHandle(handle);
+ this.isRegistered = true;
+ lock (StringRecognizer.registrationLock)
+ {
+ StringRecognizer.registeredRecognizers.Add(this);
+ }
+ }
+
+ /// Finds a registered recognizer by name.
+ public static StringRecognizer? FromName(string name)
+ {
+ if (null == name)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ IntPtr handle = NativeMethods.BNGetStringRecognizerByName(name);
+ if (IntPtr.Zero == handle)
+ {
+ return null;
+ }
+
+ return new CoreStringRecognizer(handle);
+ }
+
+ /// Gets every recognizer registered with the core.
+ public static StringRecognizer[] GetRecognizers()
+ {
+ IntPtr recognizers = NativeMethods.BNGetStringRecognizerList(out ulong count);
+ return UnsafeUtils.TakeHandleArray(
+ recognizers,
+ count,
+ StringRecognizer.CreateCoreRecognizer,
+ NativeMethods.BNFreeStringRecognizerList
+ );
+ }
+
+ private static StringRecognizer CreateCoreRecognizer(IntPtr handle)
+ {
+ if (IntPtr.Zero == handle)
+ {
+ throw new ArgumentNullException(nameof(handle));
+ }
+
+ return new CoreStringRecognizer(handle);
+ }
+
+ /// Determines whether this recognizer accepts expressions of the given type.
+ public virtual bool IsValidForType(HighLevelILFunction function, BinaryNinja.Type type)
+ {
+ return true;
+ }
+
+ public virtual DerivedString? RecognizeConstant(
+ HighLevelILInstruction instruction,
+ BinaryNinja.Type type,
+ long value
+ )
+ {
+ return null;
+ }
+
+ public virtual DerivedString? RecognizeConstantPointer(
+ HighLevelILInstruction instruction,
+ BinaryNinja.Type type,
+ long value
+ )
+ {
+ return null;
+ }
+
+ public virtual DerivedString? RecognizeExternPointer(
+ HighLevelILInstruction instruction,
+ BinaryNinja.Type type,
+ long value,
+ ulong offset
+ )
+ {
+ return null;
+ }
+
+ public virtual DerivedString? RecognizeImport(
+ HighLevelILInstruction instruction,
+ BinaryNinja.Type type,
+ long value
+ )
+ {
+ return null;
+ }
+
+ private bool InvokeIsValidForType(IntPtr context, IntPtr functionHandle, IntPtr typeHandle)
+ {
+ try
+ {
+ using (HighLevelILFunction function =
+ HighLevelILFunction.MustNewFromHandle(functionHandle))
+ using (BinaryNinja.Type type = BinaryNinja.Type.MustNewFromHandle(typeHandle))
+ {
+ return this.IsValidForType(function, type);
+ }
+ }
+ catch (Exception exception)
+ {
+ Core.LogError("Unhandled exception in StringRecognizer.IsValidForType: {0}", exception);
+ return false;
+ }
+ }
+
+ private bool InvokeRecognizeConstant(
+ IntPtr context,
+ IntPtr function,
+ UIntPtr expressionIndex,
+ IntPtr type,
+ long value,
+ IntPtr result
+ )
+ {
+ return this.InvokeRecognition(
+ RecognitionKind.Constant,
+ function,
+ expressionIndex,
+ type,
+ value,
+ 0,
+ result
+ );
+ }
+
+ private bool InvokeRecognizeConstantPointer(
+ IntPtr context,
+ IntPtr function,
+ UIntPtr expressionIndex,
+ IntPtr type,
+ long value,
+ IntPtr result
+ )
+ {
+ return this.InvokeRecognition(
+ RecognitionKind.ConstantPointer,
+ function,
+ expressionIndex,
+ type,
+ value,
+ 0,
+ result
+ );
+ }
+
+ private bool InvokeRecognizeExternPointer(
+ IntPtr context,
+ IntPtr function,
+ UIntPtr expressionIndex,
+ IntPtr type,
+ long value,
+ ulong offset,
+ IntPtr result
+ )
+ {
+ return this.InvokeRecognition(
+ RecognitionKind.ExternPointer,
+ function,
+ expressionIndex,
+ type,
+ value,
+ offset,
+ result
+ );
+ }
+
+ private bool InvokeRecognizeImport(
+ IntPtr context,
+ IntPtr function,
+ UIntPtr expressionIndex,
+ IntPtr type,
+ long value,
+ IntPtr result
+ )
+ {
+ return this.InvokeRecognition(
+ RecognitionKind.Import,
+ function,
+ expressionIndex,
+ type,
+ value,
+ 0,
+ result
+ );
+ }
+
+ private bool InvokeRecognition(
+ RecognitionKind kind,
+ IntPtr functionHandle,
+ UIntPtr expressionIndex,
+ IntPtr typeHandle,
+ long value,
+ ulong offset,
+ IntPtr resultPointer
+ )
+ {
+ try
+ {
+ using (HighLevelILFunction function =
+ HighLevelILFunction.MustNewFromHandle(functionHandle))
+ using (BinaryNinja.Type type = BinaryNinja.Type.MustNewFromHandle(typeHandle))
+ {
+ HighLevelILInstruction instruction = function.MustGetExpression(
+ (HighLevelILExpressionIndex)expressionIndex.ToUInt64()
+ );
+ DerivedString? recognized;
+
+ switch (kind)
+ {
+ case RecognitionKind.Constant:
+ recognized = this.RecognizeConstant(instruction, type, value);
+ break;
+ case RecognitionKind.ConstantPointer:
+ recognized = this.RecognizeConstantPointer(instruction, type, value);
+ break;
+ case RecognitionKind.ExternPointer:
+ recognized = this.RecognizeExternPointer(
+ instruction,
+ type,
+ value,
+ offset
+ );
+ break;
+ case RecognitionKind.Import:
+ recognized = this.RecognizeImport(instruction, type, value);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(kind));
+ }
+
+ return StringRecognizer.WriteResult(recognized, resultPointer);
+ }
+ }
+ catch (Exception exception)
+ {
+ Core.LogError("Unhandled exception in StringRecognizer recognition callback: {0}", exception);
+ return false;
+ }
+ }
+
+ private static bool WriteResult(DerivedString? result, IntPtr resultPointer)
+ {
+ if (null == result)
+ {
+ return false;
+ }
+
+ BNDerivedString native = result.ToNativeEx();
+ try
+ {
+ Marshal.StructureToPtr(native, resultPointer, false);
+ return true;
+ }
+ catch
+ {
+ if (IntPtr.Zero != native.value)
+ {
+ NativeMethods.BNFreeStringRef(native.value);
+ }
+
+ throw;
+ }
+ }
+
+ private sealed class CoreStringRecognizer : StringRecognizer
+ {
+ internal CoreStringRecognizer(IntPtr handle)
+ : base(handle)
+ {
+ }
+
+ public override bool IsValidForType(
+ HighLevelILFunction function,
+ BinaryNinja.Type type
+ )
+ {
+ StringRecognizer.ValidateArguments(function, type);
+ return NativeMethods.BNIsStringRecognizerValidForType(
+ this.handle,
+ function.DangerousGetHandle(),
+ type.DangerousGetHandle()
+ );
+ }
+
+ public override DerivedString? RecognizeConstant(
+ HighLevelILInstruction instruction,
+ BinaryNinja.Type type,
+ long value
+ )
+ {
+ return this.Recognize(
+ RecognitionKind.Constant,
+ instruction,
+ type,
+ value,
+ 0
+ );
+ }
+
+ public override DerivedString? RecognizeConstantPointer(
+ HighLevelILInstruction instruction,
+ BinaryNinja.Type type,
+ long value
+ )
+ {
+ return this.Recognize(
+ RecognitionKind.ConstantPointer,
+ instruction,
+ type,
+ value,
+ 0
+ );
+ }
+
+ public override DerivedString? RecognizeExternPointer(
+ HighLevelILInstruction instruction,
+ BinaryNinja.Type type,
+ long value,
+ ulong offset
+ )
+ {
+ return this.Recognize(
+ RecognitionKind.ExternPointer,
+ instruction,
+ type,
+ value,
+ offset
+ );
+ }
+
+ public override DerivedString? RecognizeImport(
+ HighLevelILInstruction instruction,
+ BinaryNinja.Type type,
+ long value
+ )
+ {
+ return this.Recognize(
+ RecognitionKind.Import,
+ instruction,
+ type,
+ value,
+ 0
+ );
+ }
+
+ private DerivedString? Recognize(
+ RecognitionKind kind,
+ HighLevelILInstruction instruction,
+ BinaryNinja.Type type,
+ long value,
+ ulong offset
+ )
+ {
+ StringRecognizer.ValidateArguments(instruction, type);
+ UIntPtr expressionIndex = (UIntPtr)(ulong)instruction.ExpressionIndex;
+ BNDerivedString result;
+ bool recognized;
+
+ switch (kind)
+ {
+ case RecognitionKind.Constant:
+ recognized = NativeMethods.BNStringRecognizerRecognizeConstant(
+ this.handle,
+ instruction.Function.DangerousGetHandle(),
+ expressionIndex,
+ type.DangerousGetHandle(),
+ value,
+ out result
+ );
+ break;
+ case RecognitionKind.ConstantPointer:
+ recognized = NativeMethods.BNStringRecognizerRecognizeConstantPointer(
+ this.handle,
+ instruction.Function.DangerousGetHandle(),
+ expressionIndex,
+ type.DangerousGetHandle(),
+ value,
+ out result
+ );
+ break;
+ case RecognitionKind.ExternPointer:
+ recognized = NativeMethods.BNStringRecognizerRecognizeExternPointer(
+ this.handle,
+ instruction.Function.DangerousGetHandle(),
+ expressionIndex,
+ type.DangerousGetHandle(),
+ value,
+ offset,
+ out result
+ );
+ break;
+ case RecognitionKind.Import:
+ recognized = NativeMethods.BNStringRecognizerRecognizeImport(
+ this.handle,
+ instruction.Function.DangerousGetHandle(),
+ expressionIndex,
+ type.DangerousGetHandle(),
+ value,
+ out result
+ );
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(kind));
+ }
+
+ if (!recognized)
+ {
+ return null;
+ }
+
+ try
+ {
+ return DerivedString.FromNative(result);
+ }
+ finally
+ {
+ if (IntPtr.Zero != result.value)
+ {
+ NativeMethods.BNFreeStringRef(result.value);
+ }
+ }
+ }
+ }
+
+ private static void ValidateArguments(
+ HighLevelILFunction function,
+ BinaryNinja.Type type
+ )
+ {
+ if (null == function)
+ {
+ throw new ArgumentNullException(nameof(function));
+ }
+
+ if (null == type)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+ }
+
+ private static void ValidateArguments(
+ HighLevelILInstruction instruction,
+ BinaryNinja.Type type
+ )
+ {
+ if (null == instruction)
+ {
+ throw new ArgumentNullException(nameof(instruction));
+ }
+
+ if (null == type)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+ }
+ }
+}