Skip to content

Commit 4518013

Browse files
committed
feat: expose HLIL derived-string-reference (get/set/remove) parity
Closes HIGH missing-capi gap #16. The binding declared the three derived-string P/Invokes (BNGet/BNSet/BNRemoveHighLevelILDerivedStringReferenceForExpr) but exposed no managed surface, so HLIL derived strings were unreachable vs Python (highlevelil.py:978 derived_string_reference, :3014 set_derived_string_reference_for_expr, :3023 remove_derived_string_reference_for_expr). Add: - HighLevelILInstruction.DerivedStringReference property (DerivedString?, null when the expression carries no reference). The getter owns the BNStringRef* core returns (Python uses owned=True; its StringRef frees on GC), so the binding frees it after the eager read. - HighLevelILFunction.SetDerivedStringReferenceForExpr(expr, DerivedString), rebuilding the value as a fresh BNStringRef* (the projection reads eagerly and does not retain the core handle) and freeing it after the call; core copies/add-refs the string, matching Python's owned=False borrow. - HighLevelILFunction.RemoveDerivedStringReferenceForExpr(expr). - DerivedString.ToNativeEx() + a public value-only ctor so callers can construct a reference to attach.
1 parent e017b2d commit 4518013

3 files changed

Lines changed: 134 additions & 0 deletions

File tree

‎Handle/BNHighLevelILFunction.cs‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,55 @@ public void SetExpressionAttributes(HighLevelILExpressionIndex expression , uint
666666
attributes
667667
);
668668
}
669+
670+
/// <summary>
671+
/// Attaches <paramref name="str"/> as the derived-string reference for <paramref name="expression"/>,
672+
/// mirroring Python <c>HighLevelILFunction.set_derived_string_reference_for_expr</c>
673+
/// (highlevelil.py:3014).
674+
/// </summary>
675+
/// <param name="expression">The expression to attach the derived string to.</param>
676+
/// <param name="str">The derived string to attach. Must not be null.</param>
677+
public void SetDerivedStringReferenceForExpr(HighLevelILExpressionIndex expression, DerivedString str)
678+
{
679+
if (null == str)
680+
{
681+
throw new ArgumentNullException(nameof(str));
682+
}
683+
684+
using (ScopedAllocator allocator = new ScopedAllocator())
685+
{
686+
// 1. Build the native struct; its value is a fresh BNStringRef* this method owns.
687+
BNDerivedString native = str.ToNativeEx();
688+
689+
// 2. Pin the struct and hand it to the core.
690+
IntPtr structPointer = allocator.AllocStruct(native);
691+
UIntPtr exprIndex = (UIntPtr)(ulong)expression;
692+
NativeMethods.BNSetHighLevelILDerivedStringReferenceForExpr(this.handle, exprIndex, structPointer);
693+
694+
// 3. Free the BNStringRef* this method created. Core copies/add-refs the string
695+
// (Python passes owned=False; its StringRef outlives the call), so freeing here
696+
// balances this method's own allocation and is safe.
697+
if (IntPtr.Zero != native.value)
698+
{
699+
NativeMethods.BNFreeStringRef(native.value);
700+
}
701+
}
702+
}
703+
704+
/// <summary>
705+
/// Removes any derived-string reference attached to <paramref name="expression"/>, mirroring
706+
/// Python <c>HighLevelILFunction.remove_derived_string_reference_for_expr</c>
707+
/// (highlevelil.py:3023).
708+
/// </summary>
709+
/// <param name="expression">The expression whose derived-string reference to clear.</param>
710+
public void RemoveDerivedStringReferenceForExpr(HighLevelILExpressionIndex expression)
711+
{
712+
UIntPtr exprIndex = (UIntPtr)(ulong)expression;
713+
NativeMethods.BNRemoveHighLevelILDerivedStringReferenceForExpr(
714+
this.handle ,
715+
exprIndex
716+
);
717+
}
669718

670719
public LinearViewObject CreateLinearView( DisassemblySettings? settings = null)
671720
{

‎NavigationExtensions.cs‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ public sealed class DerivedString
8080

8181
public ulong Length { get; }
8282

83+
/// <summary>
84+
/// Constructs a value-only DerivedString with no location, mirroring Python
85+
/// <c>DerivedString(value=..., location=None, custom_type=None)</c> for the common case of
86+
/// attaching a derived string via
87+
/// <see cref="HighLevelILFunction.SetDerivedStringReferenceForExpr"/>.
88+
/// </summary>
89+
public DerivedString(string value)
90+
: this(value, false, 0, 0UL, 0UL)
91+
{
92+
}
93+
8394
private DerivedString(
8495
string value,
8596
bool locationValid,
@@ -110,6 +121,37 @@ internal static DerivedString FromNative(BNDerivedString raw)
110121
);
111122
}
112123

124+
// Re-materializes this projection as a native BNDerivedString for the Set path
125+
// (BNSetHighLevelILDerivedStringReferenceForExpr). The Value text is rebuilt as a fresh
126+
// BNStringRef* because this projection reads eagerly and does not retain the original core
127+
// handle. The caller owns that BNStringRef* and must free it (BNFreeStringRef) once the
128+
// core has consumed the struct: core copies/add-refs the string, mirroring Python's
129+
// owned=False borrow. custom_type is not surfaced by this read-only projection (Python
130+
// CustomStringType handle is dropped), so it is left null.
131+
internal BNDerivedString ToNativeEx()
132+
{
133+
ulong byteCount = (ulong)Encoding.UTF8.GetByteCount(this.Value);
134+
IntPtr stringRef = NativeMethods.BNCreateStringRefOfLength(
135+
this.Value,
136+
(UIntPtr)byteCount
137+
);
138+
139+
BNDerivedString native = new BNDerivedString();
140+
native.value = stringRef;
141+
native.locationValid = this.LocationValid;
142+
143+
if (this.LocationValid)
144+
{
145+
native.location.locationType = (int)this.LocationType;
146+
native.location.addr = this.Address;
147+
native.location.len = this.Length;
148+
}
149+
150+
native.customType = IntPtr.Zero;
151+
152+
return native;
153+
}
154+
113155
// Decodes a BNStringRef* as a UTF-8 string. The core hands back a NUL-terminated C
114156
// string; UTF-8 decoding covers the non-ASCII case the rest of the binding targets.
115157
private static string ReadStringRef(IntPtr reference)

‎Struct/BNHighLevelILInstruction.cs‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,49 @@ public RegisterValue Value
14961496
return this.MediumLevelIL.Value;
14971497
}
14981498
}
1499+
1500+
/// <summary>
1501+
/// The derived string this expression references, mirroring Python
1502+
/// <c>HighLevelILInstruction.derived_string_reference</c> (highlevelil.py:978). Returns
1503+
/// <c>null</c> when the expression carries no derived-string reference.
1504+
/// </summary>
1505+
public DerivedString? DerivedStringReference
1506+
{
1507+
get
1508+
{
1509+
// 1. Allocate the out-struct the core writes into.
1510+
using (ScopedAllocator allocator = new ScopedAllocator())
1511+
{
1512+
IntPtr outPointer = allocator.AllocStruct(new BNDerivedString());
1513+
1514+
// 2. Ask the core for the reference; false means there is none.
1515+
UIntPtr exprIndex = (UIntPtr)(ulong)this.ExpressionIndex;
1516+
bool found = NativeMethods.BNGetHighLevelILDerivedStringReferenceForExpr(
1517+
this.ILFunction.DangerousGetHandle(),
1518+
exprIndex,
1519+
outPointer
1520+
);
1521+
1522+
if (!found)
1523+
{
1524+
return null;
1525+
}
1526+
1527+
// 3. Project the struct, then free the owned BNStringRef*. Python's StringRef
1528+
// takes ownership (owned=True) and frees the handle on GC; this binding reads the
1529+
// text eagerly, so it must free the handle itself to avoid a leak per call.
1530+
BNDerivedString raw = Marshal.PtrToStructure<BNDerivedString>(outPointer);
1531+
DerivedString result = DerivedString.FromNative(raw);
1532+
1533+
if (IntPtr.Zero != raw.value)
1534+
{
1535+
NativeMethods.BNFreeStringRef(raw.value);
1536+
}
1537+
1538+
return result;
1539+
}
1540+
}
1541+
}
14991542

15001543
public PossibleValueSet PossibleValues
15011544
{

0 commit comments

Comments
 (0)