From 87e0c152e20618b8702355e9a173cfc5ecdf8333 Mon Sep 17 00:00:00 2001 From: bird_egop Date: Sat, 12 Apr 2025 20:32:38 +0300 Subject: [PATCH] Fixed disassembler regression by adding handlers for TEST r/m8, r8 and TEST r/m8, imm8 instructions --- .../Handlers/Group3/TestImmWithRm8Handler.cs | 107 ++++++++++++++++++ .../X86/Handlers/InstructionHandlerFactory.cs | 4 +- .../X86/Handlers/TestRegMem8Handler.cs | 82 ++++++++++++++ 3 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 X86Disassembler/X86/Handlers/Group3/TestImmWithRm8Handler.cs create mode 100644 X86Disassembler/X86/Handlers/TestRegMem8Handler.cs diff --git a/X86Disassembler/X86/Handlers/Group3/TestImmWithRm8Handler.cs b/X86Disassembler/X86/Handlers/Group3/TestImmWithRm8Handler.cs new file mode 100644 index 0000000..e46a025 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Group3/TestImmWithRm8Handler.cs @@ -0,0 +1,107 @@ +namespace X86Disassembler.X86.Handlers.Group3; + +/// +/// Handler for TEST r/m8, imm8 instruction (0xF6 /0) +/// +public class TestImmWithRm8Handler : Group3BaseHandler +{ + /// + /// Initializes a new instance of the TestImmWithRm8Handler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public TestImmWithRm8Handler(byte[] codeBuffer, InstructionDecoder decoder, int length) + : base(codeBuffer, decoder, length) + { + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + if (opcode != 0xF6) + return false; + + // Check if the reg field of the ModR/M byte is 0 (TEST) + int position = Decoder.GetPosition(); + if (position >= Length) + return false; + + byte modRM = CodeBuffer[position]; + byte reg = (byte)((modRM & 0x38) >> 3); + + return reg == 0; // 0 = TEST + } + + /// + /// Decodes a TEST r/m8, imm8 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the mnemonic + instruction.Mnemonic = "test"; + + int position = Decoder.GetPosition(); + + if (position >= Length) + { + return false; + } + + // Read the ModR/M byte + byte modRM = CodeBuffer[position++]; + Decoder.SetPosition(position); + + // Extract the fields from the ModR/M byte + byte mod = (byte)((modRM & 0xC0) >> 6); + byte reg = (byte)((modRM & 0x38) >> 3); // Should be 0 for TEST + byte rm = (byte)(modRM & 0x07); + + // Decode the destination operand + string destOperand; + + // Special case for direct register addressing (mod == 3) + if (mod == 3) + { + // Get the register name based on the rm field + destOperand = GetRegister8(rm); + } + else + { + // Use the ModR/M decoder for memory addressing + destOperand = _modRMDecoder.DecodeModRM(mod, rm, true); + } + + // Read the immediate value + if (position >= Length) + { + return false; + } + + byte imm8 = CodeBuffer[position]; + Decoder.SetPosition(position + 1); + + // Set the operands + instruction.Operands = $"{destOperand}, 0x{imm8:X2}"; + + return true; + } + + /// + /// Gets the 8-bit register name for the given register index + /// + /// The register index + /// The register name + private static new string GetRegister8(byte reg) + { + string[] registerNames = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh" }; + return registerNames[reg & 0x07]; + } +} diff --git a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs index 3a22e8c..43f3dcb 100644 --- a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs +++ b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs @@ -41,6 +41,7 @@ public class InstructionHandlerFactory _handlers.Add(new JmpRel8Handler(_codeBuffer, _decoder, _length)); _handlers.Add(new CallRel32Handler(_codeBuffer, _decoder, _length)); _handlers.Add(new XorRegMemHandler(_codeBuffer, _decoder, _length)); + _handlers.Add(new TestRegMem8Handler(_codeBuffer, _decoder, _length)); _handlers.Add(new TestRegMemHandler(_codeBuffer, _decoder, _length)); _handlers.Add(new TestAlImmHandler(_codeBuffer, _decoder, _length)); _handlers.Add(new TestEaxImmHandler(_codeBuffer, _decoder, _length)); @@ -104,8 +105,9 @@ public class InstructionHandlerFactory /// private void RegisterGroup3Handlers() { - // TEST handler + // TEST handlers _handlers.Add(new TestImmWithRm32Handler(_codeBuffer, _decoder, _length)); + _handlers.Add(new TestImmWithRm8Handler(_codeBuffer, _decoder, _length)); // NOT handler _handlers.Add(new NotRm32Handler(_codeBuffer, _decoder, _length)); diff --git a/X86Disassembler/X86/Handlers/TestRegMem8Handler.cs b/X86Disassembler/X86/Handlers/TestRegMem8Handler.cs new file mode 100644 index 0000000..1f72c32 --- /dev/null +++ b/X86Disassembler/X86/Handlers/TestRegMem8Handler.cs @@ -0,0 +1,82 @@ +namespace X86Disassembler.X86.Handlers; + +/// +/// Handler for TEST r/m8, r8 instruction (0x84) +/// +public class TestRegMem8Handler : InstructionHandler +{ + // ModR/M decoder + private readonly ModRMDecoder _modRMDecoder; + + /// + /// Initializes a new instance of the TestRegMem8Handler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public TestRegMem8Handler(byte[] codeBuffer, InstructionDecoder decoder, int length) + : base(codeBuffer, decoder, length) + { + _modRMDecoder = new ModRMDecoder(codeBuffer, decoder, length); + } + + /// + /// Checks if this handler can decode the given opcode + /// + /// The opcode to check + /// True if this handler can decode the opcode + public override bool CanHandle(byte opcode) + { + return opcode == 0x84; + } + + /// + /// Decodes a TEST r/m8, r8 instruction + /// + /// The opcode of the instruction + /// The instruction object to populate + /// True if the instruction was successfully decoded + public override bool Decode(byte opcode, Instruction instruction) + { + // Set the mnemonic + instruction.Mnemonic = "test"; + + int position = Decoder.GetPosition(); + + if (position >= Length) + { + return false; + } + + // Read the ModR/M byte + byte modRM = CodeBuffer[position++]; + Decoder.SetPosition(position); + + // Extract the fields from the ModR/M byte + byte mod = (byte)((modRM & 0xC0) >> 6); + byte reg = (byte)((modRM & 0x38) >> 3); + byte rm = (byte)(modRM & 0x07); + + // Decode the destination operand + string destOperand = _modRMDecoder.DecodeModRM(mod, rm, true); + + // Get the source register + string srcReg = GetRegister8(reg); + + // Set the operands + instruction.Operands = $"{destOperand}, {srcReg}"; + + return true; + } + + /// + /// Gets the 8-bit register name for the given register index + /// + /// The register index + /// The register name + private static string GetRegister8(byte reg) + { + string[] registerNames = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh" }; + return registerNames[reg & 0x07]; + } +}