From 9518d3f373d1760e657d17533e171ed7785b63d9 Mon Sep 17 00:00:00 2001 From: tinysec Date: Wed, 8 Jul 2026 14:04:48 +0800 Subject: [PATCH] fix: HLIL member_index masking + operand-count table (root cause) Python's _get_member_index masks the sentinel (1<<63) and returns None when there is no named member; the binding returned the raw ulong and never returned null. Investigation showed the deeper root cause: the OperationOperands table undercounted the three field instructions by omitting the member_index slot, so RawOperands was one short and ANY operand access on them (including the existing Offset) threw IndexOutOfRangeException. member_index was therefore never readable at all. - OperationOperands: HLIL_STRUCT_FIELD 2->3, HLIL_DEREF_FIELD 2->3, HLIL_DEREF_FIELD_SSA 3->4 (source, offset/srcMem, member_index). Growing the count only adds the member_index slot; existing operand indices are unchanged. - HighLevelILInstruction.MemberIndexFromRawOperand: masks the (1<<63) sentinel to null, mirroring Python _get_member_index. - HLILStructField.MemberIndex, HLILDerefField.MemberIndex, HLILDerefFieldSSA.MemberIndex: long? via the helper. The two Deref members are renamed from MemoryIndex for parity with StructField (no internal callers). --- HighLevelIL/HLILDerefField.cs | 9 +++++++-- HighLevelIL/HLILDerefFieldSSA.cs | 9 +++++++-- HighLevelIL/HLILStructField.cs | 8 ++++++-- Struct/BNHighLevelILInstruction.cs | 19 ++++++++++++++++--- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/HighLevelIL/HLILDerefField.cs b/HighLevelIL/HLILDerefField.cs index cdabe09..b67faf3 100644 --- a/HighLevelIL/HLILDerefField.cs +++ b/HighLevelIL/HLILDerefField.cs @@ -27,11 +27,16 @@ public long Offset } } - public ulong MemoryIndex + /// + /// The named-member index, or null when there is no named member. Mirrors Python + /// _get_member_index (renamed from MemoryIndex for parity with + /// and ). + /// + public long? MemberIndex { get { - return this.RawOperands[2]; + return HighLevelILInstruction.MemberIndexFromRawOperand(this.RawOperands[2]); } } } diff --git a/HighLevelIL/HLILDerefFieldSSA.cs b/HighLevelIL/HLILDerefFieldSSA.cs index 8e37d01..9be041f 100644 --- a/HighLevelIL/HLILDerefFieldSSA.cs +++ b/HighLevelIL/HLILDerefFieldSSA.cs @@ -35,11 +35,16 @@ public long Offset } } - public ulong MemoryIndex + /// + /// The named-member index, or null when there is no named member. Mirrors Python + /// _get_member_index (renamed from MemoryIndex for parity with + /// and ). + /// + public long? MemberIndex { get { - return this.RawOperands[3]; + return HighLevelILInstruction.MemberIndexFromRawOperand(this.RawOperands[3]); } } } diff --git a/HighLevelIL/HLILStructField.cs b/HighLevelIL/HLILStructField.cs index 9728ff7..e1fd892 100644 --- a/HighLevelIL/HLILStructField.cs +++ b/HighLevelIL/HLILStructField.cs @@ -27,11 +27,15 @@ public long Offset } } - public ulong MemberIndex + /// + /// The named-member index, or null when there is no named member. Mirrors Python + /// _get_member_index: the sentinel (1<<63) means no named member. + /// + public long? MemberIndex { get { - return this.RawOperands[2]; + return HighLevelILInstruction.MemberIndexFromRawOperand(this.RawOperands[2]); } } } diff --git a/Struct/BNHighLevelILInstruction.cs b/Struct/BNHighLevelILInstruction.cs index 43d75ed..60c91b0 100644 --- a/Struct/BNHighLevelILInstruction.cs +++ b/Struct/BNHighLevelILInstruction.cs @@ -94,11 +94,11 @@ public abstract class HighLevelILInstruction : { HighLevelILOperation.HLIL_ASSERT, 1 }, // cond { HighLevelILOperation.HLIL_VAR, 1 }, // var - { HighLevelILOperation.HLIL_STRUCT_FIELD, 2 }, // base, fieldOffset/index + { HighLevelILOperation.HLIL_STRUCT_FIELD, 3 }, // base, fieldOffset, memberIndex { HighLevelILOperation.HLIL_ARRAY_INDEX, 2 }, // base, index { HighLevelILOperation.HLIL_SPLIT, 2 }, // hi, lo { HighLevelILOperation.HLIL_DEREF, 1 }, // addr - { HighLevelILOperation.HLIL_DEREF_FIELD, 2 }, // addr, fieldOffset/index + { HighLevelILOperation.HLIL_DEREF_FIELD, 3 }, // addr, fieldOffset, memberIndex { HighLevelILOperation.HLIL_ADDRESS_OF, 1 }, // var/expr { HighLevelILOperation.HLIL_CONST, 1 }, // value @@ -201,7 +201,7 @@ public abstract class HighLevelILInstruction : { HighLevelILOperation.HLIL_VAR_SSA, 2 }, // var, version { HighLevelILOperation.HLIL_ARRAY_INDEX_SSA, 3 }, // base, index, srcMem { HighLevelILOperation.HLIL_DEREF_SSA, 2 }, // addr, srcMem - { HighLevelILOperation.HLIL_DEREF_FIELD_SSA, 3 }, // addr, field, srcMem + { HighLevelILOperation.HLIL_DEREF_FIELD_SSA, 4 }, // addr, srcMem, fieldOffset, memberIndex { HighLevelILOperation.HLIL_CALL_SSA, 4 }, // dest, params(list), outputs(list), srcMem { HighLevelILOperation.HLIL_SYSCALL_SSA, 3 }, // params(list), outputs(list), srcMem @@ -986,6 +986,19 @@ public double GetOperandAsDouble(OperandIndex operand) { return BitConverter.UInt64BitsToDouble(this.RawOperands[(ulong)operand]); } + + // member_index mask: mirrors Python HighLevelILInstruction._get_member_index. The sentinel + // (1<<63) means "no named member" and is returned as null; otherwise the (non-negative) + // named-member index. Callers pass the already-extracted raw operand value. + internal static long? MemberIndexFromRawOperand(ulong rawOperand) + { + if (0UL != (rawOperand & (1UL << 63))) + { + return null; + } + + return (long)rawOperand; + } public RegisterValue GetOperandAsConstantData( OperandIndex operand1 ,