diff --git a/Handle/BNHighLevelILFunction.cs b/Handle/BNHighLevelILFunction.cs index 3c268b3..d66290b 100644 --- a/Handle/BNHighLevelILFunction.cs +++ b/Handle/BNHighLevelILFunction.cs @@ -666,6 +666,55 @@ public void SetExpressionAttributes(HighLevelILExpressionIndex expression , uint attributes ); } + + /// + /// Attaches as the derived-string reference for , + /// mirroring Python HighLevelILFunction.set_derived_string_reference_for_expr + /// (highlevelil.py:3014). + /// + /// The expression to attach the derived string to. + /// The derived string to attach. Must not be null. + public void SetDerivedStringReferenceForExpr(HighLevelILExpressionIndex expression, DerivedString str) + { + if (null == str) + { + throw new ArgumentNullException(nameof(str)); + } + + using (ScopedAllocator allocator = new ScopedAllocator()) + { + // 1. Build the native struct; its value is a fresh BNStringRef* this method owns. + BNDerivedString native = str.ToNativeEx(); + + // 2. Pin the struct and hand it to the core. + IntPtr structPointer = allocator.AllocStruct(native); + UIntPtr exprIndex = (UIntPtr)(ulong)expression; + NativeMethods.BNSetHighLevelILDerivedStringReferenceForExpr(this.handle, exprIndex, structPointer); + + // 3. Free the BNStringRef* this method created. Core copies/add-refs the string + // (Python passes owned=False; its StringRef outlives the call), so freeing here + // balances this method's own allocation and is safe. + if (IntPtr.Zero != native.value) + { + NativeMethods.BNFreeStringRef(native.value); + } + } + } + + /// + /// Removes any derived-string reference attached to , mirroring + /// Python HighLevelILFunction.remove_derived_string_reference_for_expr + /// (highlevelil.py:3023). + /// + /// The expression whose derived-string reference to clear. + public void RemoveDerivedStringReferenceForExpr(HighLevelILExpressionIndex expression) + { + UIntPtr exprIndex = (UIntPtr)(ulong)expression; + NativeMethods.BNRemoveHighLevelILDerivedStringReferenceForExpr( + this.handle , + exprIndex + ); + } public LinearViewObject CreateLinearView( DisassemblySettings? settings = null) { diff --git a/NavigationExtensions.cs b/NavigationExtensions.cs index 4563a28..132993c 100644 --- a/NavigationExtensions.cs +++ b/NavigationExtensions.cs @@ -80,6 +80,17 @@ public sealed class DerivedString public ulong Length { get; } + /// + /// Constructs a value-only DerivedString with no location, mirroring Python + /// DerivedString(value=..., location=None, custom_type=None) for the common case of + /// attaching a derived string via + /// . + /// + public DerivedString(string value) + : this(value, false, 0, 0UL, 0UL) + { + } + private DerivedString( string value, bool locationValid, @@ -110,6 +121,37 @@ internal static DerivedString FromNative(BNDerivedString raw) ); } + // Re-materializes this projection as a native BNDerivedString for the Set path + // (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. + internal BNDerivedString ToNativeEx() + { + ulong byteCount = (ulong)Encoding.UTF8.GetByteCount(this.Value); + IntPtr stringRef = NativeMethods.BNCreateStringRefOfLength( + this.Value, + (UIntPtr)byteCount + ); + + BNDerivedString native = new BNDerivedString(); + native.value = stringRef; + native.locationValid = this.LocationValid; + + if (this.LocationValid) + { + native.location.locationType = (int)this.LocationType; + native.location.addr = this.Address; + native.location.len = this.Length; + } + + native.customType = IntPtr.Zero; + + return native; + } + // Decodes a BNStringRef* as a UTF-8 string. The core hands back a NUL-terminated C // string; UTF-8 decoding covers the non-ASCII case the rest of the binding targets. private static string ReadStringRef(IntPtr reference) diff --git a/Struct/BNHighLevelILInstruction.cs b/Struct/BNHighLevelILInstruction.cs index 5d87ef1..dc49da3 100644 --- a/Struct/BNHighLevelILInstruction.cs +++ b/Struct/BNHighLevelILInstruction.cs @@ -1496,6 +1496,49 @@ public RegisterValue Value return this.MediumLevelIL.Value; } } + + /// + /// The derived string this expression references, mirroring Python + /// HighLevelILInstruction.derived_string_reference (highlevelil.py:978). Returns + /// null when the expression carries no derived-string reference. + /// + public DerivedString? DerivedStringReference + { + get + { + // 1. Allocate the out-struct the core writes into. + using (ScopedAllocator allocator = new ScopedAllocator()) + { + IntPtr outPointer = allocator.AllocStruct(new BNDerivedString()); + + // 2. Ask the core for the reference; false means there is none. + UIntPtr exprIndex = (UIntPtr)(ulong)this.ExpressionIndex; + bool found = NativeMethods.BNGetHighLevelILDerivedStringReferenceForExpr( + this.ILFunction.DangerousGetHandle(), + exprIndex, + outPointer + ); + + if (!found) + { + return null; + } + + // 3. Project the struct, then free the owned BNStringRef*. Python's StringRef + // takes ownership (owned=True) and frees the handle on GC; this binding reads the + // text eagerly, so it must free the handle itself to avoid a leak per call. + BNDerivedString raw = Marshal.PtrToStructure(outPointer); + DerivedString result = DerivedString.FromNative(raw); + + if (IntPtr.Zero != raw.value) + { + NativeMethods.BNFreeStringRef(raw.value); + } + + return result; + } + } + } public PossibleValueSet PossibleValues {