using System; namespace WhiteMagic.Hooking; /// /// Minimal instruction-length decoder for common x86/x64 prologue shapes. /// The set is intentionally small: any opcode outside the covered set is rejected /// rather than guessed. Full arbitrary-prologue validation is provided by the /// optional Iced backend (Phase 8). /// /// /// Covered shapes: /// /// push reg: 0x50-0x57 (1 byte), including REX-prefixed forms. /// push ebp/rbp: 0x55 (1 byte). /// mov edi, edi: 8B FF (2 bytes). /// mov ebp/rbp, esp/rsp: 8B EC / 48 8B EC (2/3 bytes). /// sub esp/rsp, imm8: 83 EC imm8 / 48 83 EC imm8 (3/4 bytes). /// sub esp/rsp, imm32: 81 EC imm32 / 48 81 EC imm32 (6/7 bytes). /// /// internal static class PrologueDecoder { /// /// Returns the length of the first instruction in /// if it matches a covered shape; otherwise returns -1. /// public static int GetInstructionLength(ReadOnlySpan bytes, bool is64Bit) { if (bytes.Length == 0) return 0; int i = 0; if (is64Bit && bytes[i] >= 0x40 && bytes[i] <= 0x4F) { // REX prefix. i++; if (bytes.Length <= i) return -1; } byte op = bytes[i]; // push reg / push rbp. if ((op & 0xF8) == 0x50 || op == 0x55) return i + 1; // mov r32/64, r/m32/64. Recognize only the specific forms listed above. if (op == 0x8B && bytes.Length > i + 1) { byte modrm = bytes[i + 1]; if (modrm == 0xFF || modrm == 0xEC) return i + 2; } // sub r/m32/64, imm8. if (op == 0x83 && bytes.Length > i + 2) return i + 3; // sub r/m32/64, imm32. if (op == 0x81 && bytes.Length > i + 5) return i + 6; return -1; } /// /// Walks prologue instructions until at least /// have been covered, returning the total length of whole instructions that must /// be preserved in the trampoline. /// /// An opcode is outside the covered set. public static int GetWholeInstructionLength(byte[] prologue, int requiredBytes, bool is64Bit) { int total = 0; while (total < requiredBytes) { int len = GetInstructionLength(prologue.AsSpan(total), is64Bit); if (len <= 0) { throw new InvalidOperationException( "The target prologue contains an instruction outside the covered opcode set. " + "Install the optional Iced backend for full instruction-boundary validation."); } total += len; } return total; } }