From c1c1c23d7ad12b0031a117e363fb606f70207c04 Mon Sep 17 00:00:00 2001 From: tinysec Date: Wed, 8 Jul 2026 13:46:45 +0800 Subject: [PATCH] fix: EnumerationType member signedness (Type.Signed + ConvertInteger) Python's EnumerationType.members reinterprets each member's raw u64 per the type's signedness and width (binaryninja.types.convert_integer), so a signed enum reports -1 for 0xFFFFFFFFFFFFFFFF. The C# binding only exposed the raw EnumerationMember.Value (ulong), so signed enumerations appeared as huge unsigned numbers. - Type.Signed: wrap BNIsTypeSigned (BoolWithConfidence). Width already existed. - EnumerationType.GetMemberSignedValues(): return each member value run through the width-mask + signed-reinterpret conversion that mirrors convert_integer. The raw EnumerationMember.Value stays ulong (the low-level C value); the signed view is opt-in via the parity method. --- Handle/BNType.cs | 12 ++++++++ Type/EnumerationType.cs | 67 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/Handle/BNType.cs b/Handle/BNType.cs index a5bbd97..968c0fd 100644 --- a/Handle/BNType.cs +++ b/Handle/BNType.cs @@ -203,6 +203,18 @@ public ulong Width } } + /// + /// Whether this integer-like type is signed (wraps BNIsTypeSigned). + /// + public BoolWithConfidence Signed + { + get + { + return BoolWithConfidence.FromNative(NativeMethods.BNIsTypeSigned(this.handle)); + } + } + + public ulong Alignment { get diff --git a/Type/EnumerationType.cs b/Type/EnumerationType.cs index f3bc8c2..55c328a 100644 --- a/Type/EnumerationType.cs +++ b/Type/EnumerationType.cs @@ -36,5 +36,72 @@ public EnumerationMember[] Members return this.Enumeration.Members; } } + + /// + /// The member values reinterpreted per this enumeration's signedness and width, mirroring + /// Python's EnumerationType.members (which applies + /// binaryninja.types.convert_integer to each raw u64). A signed enumeration reports + /// -1 for 0xFFFFFFFFFFFFFFFF; alone returns the + /// raw unsigned value, so signed enumerations previously appeared as huge unsigned numbers. + /// + /// + /// For an unsigned enumeration whose value exceeds , the + /// authoritative value is (ulong); the long returned + /// here wraps. Signed enumerations — the case this method exists for — are exact. + /// + public long[] GetMemberSignedValues() + { + EnumerationMember[] members = this.Members; + BoolWithConfidence signed = this.Signed; + ulong width = this.Width; + + long[] result = new long[members.Length]; + + for (int i = 0; i < members.Length; i++) + { + result[i] = ConvertInteger(members[i].Value, signed.Value, width); + } + + return result; + } + + // Reinterpret a raw 64-bit value as a signed or unsigned integer of the given byte width, + // mirroring Python binaryninja.types.convert_integer (types.py:68). Mask to the width first, + // then sign-extend when signed. + private static long ConvertInteger(ulong value, bool signed, ulong width) + { + ulong masked; + + switch (width) + { + case 1: + masked = value & 0xFFUL; + if (signed) + { + return (long)(sbyte)masked; + } + return (long)(byte)masked; + + case 2: + masked = value & 0xFFFFUL; + if (signed) + { + return (long)(short)masked; + } + return (long)(ushort)masked; + + case 4: + masked = value & 0xFFFFFFFFUL; + if (signed) + { + return (long)(int)masked; + } + return (long)(uint)masked; + + default: + // Width 8 (and any other value): the full 64 bits. + return (long)value; + } + } } }