diff --git a/X86Disassembler/X86/Handlers/Cmp/CmpR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Cmp/CmpR32Rm32Handler.cs new file mode 100644 index 0000000..1e0eaec --- /dev/null +++ b/X86Disassembler/X86/Handlers/Cmp/CmpR32Rm32Handler.cs @@ -0,0 +1,73 @@ +namespace X86Disassembler.X86.Handlers.Cmp; + +/// +/// Handler for CMP r32, r/m32 instruction (0x3B) +/// +public class CmpR32Rm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the CmpR32Rm32Handler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public CmpR32Rm32Handler(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) + { + return opcode == 0x3B; + } + + /// + /// Decodes a CMP r32, r/m32 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) + { + 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); + + // Set the mnemonic + instruction.Mnemonic = "cmp"; + + // Get the register name + string regName = GetRegister32(reg); + + // For memory operands, set the operand + if (mod != 3) // Memory operand + { + string operand = ModRMDecoder.DecodeModRM(mod, rm, false); + instruction.Operands = $"{regName}, {operand}"; + } + else // Register operand + { + string rmName = GetRegister32(rm); + instruction.Operands = $"{regName}, {rmName}"; + } + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs index e9c4bc5..2b945a0 100644 --- a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs +++ b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs @@ -1,6 +1,7 @@ using X86Disassembler.X86.Handlers.ArithmeticImmediate; using X86Disassembler.X86.Handlers.ArithmeticUnary; using X86Disassembler.X86.Handlers.Call; +using X86Disassembler.X86.Handlers.Cmp; using X86Disassembler.X86.Handlers.FloatingPoint; using X86Disassembler.X86.Handlers.Jump; using X86Disassembler.X86.Handlers.Lea; @@ -10,8 +11,8 @@ using X86Disassembler.X86.Handlers.Pop; using X86Disassembler.X86.Handlers.Push; using X86Disassembler.X86.Handlers.Ret; using X86Disassembler.X86.Handlers.Test; -using X86Disassembler.X86.Handlers.Xor; using X86Disassembler.X86.Handlers.Xchg; +using X86Disassembler.X86.Handlers.Xor; namespace X86Disassembler.X86.Handlers; @@ -73,6 +74,9 @@ public class InstructionHandlerFactory // Register Lea handlers RegisterLeaHandlers(); + // Register Cmp handlers + RegisterCmpHandlers(); + // Register Data Transfer handlers RegisterDataTransferHandlers(); @@ -225,6 +229,15 @@ public class InstructionHandlerFactory _handlers.Add(new LeaR32MHandler(_codeBuffer, _decoder, _length)); } + /// + /// Registers all Cmp instruction handlers + /// + private void RegisterCmpHandlers() + { + // Add Cmp handlers + _handlers.Add(new CmpR32Rm32Handler(_codeBuffer, _decoder, _length)); + } + /// /// Registers all Data Transfer instruction handlers /// @@ -237,6 +250,7 @@ public class InstructionHandlerFactory _handlers.Add(new MovRegImm8Handler(_codeBuffer, _decoder, _length)); _handlers.Add(new MovEaxMoffsHandler(_codeBuffer, _decoder, _length)); _handlers.Add(new MovMoffsEaxHandler(_codeBuffer, _decoder, _length)); + _handlers.Add(new MovRm32Imm32Handler(_codeBuffer, _decoder, _length)); // Add PUSH handlers _handlers.Add(new PushRegHandler(_codeBuffer, _decoder, _length)); diff --git a/X86Disassembler/X86/Handlers/Mov/MovRm32Imm32Handler.cs b/X86Disassembler/X86/Handlers/Mov/MovRm32Imm32Handler.cs new file mode 100644 index 0000000..fd18a50 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Mov/MovRm32Imm32Handler.cs @@ -0,0 +1,115 @@ +namespace X86Disassembler.X86.Handlers.Mov; + +/// +/// Handler for MOV r/m32, imm32 instruction (0xC7) +/// +public class MovRm32Imm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the MovRm32Imm32Handler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public MovRm32Imm32Handler(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) + { + return opcode == 0xC7; + } + + /// + /// Decodes a MOV r/m32, imm32 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) + { + int position = Decoder.GetPosition(); + + if (position >= Length) + { + return false; + } + + // Read the ModR/M byte + byte modRM = CodeBuffer[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); + + // MOV r/m32, imm32 only uses reg=0 + if (reg != 0) + { + return false; + } + + // Process SIB and displacement bytes if needed + if (mod != 3 && rm == 4) // SIB byte present + { + if (position >= Length) + { + return false; + } + position++; // Skip SIB byte + } + + // Handle displacement + if ((mod == 1 && position >= Length) || (mod == 2 && position + 3 >= Length)) + { + return false; + } + + if (mod == 1) // 8-bit displacement + { + position++; + } + else if (mod == 2) // 32-bit displacement + { + position += 4; + } + + // Read the immediate dword + if (position + 3 >= Length) + { + return false; + } + + byte b0 = CodeBuffer[position++]; + byte b1 = CodeBuffer[position++]; + byte b2 = CodeBuffer[position++]; + byte b3 = CodeBuffer[position++]; + uint imm32 = (uint)(b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)); + Decoder.SetPosition(position); + + // Set the mnemonic + instruction.Mnemonic = "mov"; + + // Get the operand string + string operand; + if (mod != 3) // Memory operand + { + operand = ModRMDecoder.DecodeModRM(mod, rm, false); + } + else // Register operand + { + operand = GetRegister32(rm); + } + + // Set the operands + instruction.Operands = $"{operand}, 0x{imm32:X8}"; + + return true; + } +} diff --git a/X86Disassembler/X86/OpcodeMap.cs b/X86Disassembler/X86/OpcodeMap.cs index 6e72cd9..ba54a65 100644 --- a/X86Disassembler/X86/OpcodeMap.cs +++ b/X86Disassembler/X86/OpcodeMap.cs @@ -50,6 +50,18 @@ public static class OpcodeMap // LEA instruction OneByteOpcodes[0x8D] = "lea"; // LEA r32, m + // CMP instructions + OneByteOpcodes[0x38] = "cmp"; // CMP r/m8, r8 + OneByteOpcodes[0x39] = "cmp"; // CMP r/m32, r32 + OneByteOpcodes[0x3A] = "cmp"; // CMP r8, r/m8 + OneByteOpcodes[0x3B] = "cmp"; // CMP r32, r/m32 + OneByteOpcodes[0x3C] = "cmp"; // CMP AL, imm8 + OneByteOpcodes[0x3D] = "cmp"; // CMP EAX, imm32 + + // MOV instructions + OneByteOpcodes[0xC6] = "mov"; // MOV r/m8, imm8 + OneByteOpcodes[0xC7] = "mov"; // MOV r/m32, imm32 + // Group 1 instructions (ADD, OR, ADC, SBB, AND, SUB, XOR, CMP) OneByteOpcodes[0x80] = "group1b"; OneByteOpcodes[0x81] = "group1d"; diff --git a/X86DisassemblerTests/CmpInstructionTests.cs b/X86DisassemblerTests/CmpInstructionTests.cs new file mode 100644 index 0000000..d6b46e6 --- /dev/null +++ b/X86DisassemblerTests/CmpInstructionTests.cs @@ -0,0 +1,49 @@ +namespace X86DisassemblerTests; + +using System; +using Xunit; +using X86Disassembler.X86; + +/// +/// Tests for CMP instruction handlers +/// +public class CmpInstructionTests +{ + /// + /// Tests the CMP r32, r/m32 instruction (0x3B) with register operand + /// + [Fact] + public void TestCmpR32Rm32_Register() + { + // Arrange + byte[] code = { 0x3B, 0xC7 }; // CMP EAX, EDI + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("cmp", instructions[0].Mnemonic); + Assert.Equal("eax, edi", instructions[0].Operands); + } + + /// + /// Tests the CMP r32, m32 instruction (0x3B) with memory operand + /// + [Fact] + public void TestCmpR32M32() + { + // Arrange + byte[] code = { 0x3B, 0x00 }; // CMP EAX, DWORD PTR [EAX] + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("cmp", instructions[0].Mnemonic); + Assert.Equal("eax, dword ptr [eax]", instructions[0].Operands); + } +} diff --git a/X86DisassemblerTests/MovRm32Imm32Tests.cs b/X86DisassemblerTests/MovRm32Imm32Tests.cs new file mode 100644 index 0000000..c22ba58 --- /dev/null +++ b/X86DisassemblerTests/MovRm32Imm32Tests.cs @@ -0,0 +1,49 @@ +namespace X86DisassemblerTests; + +using System; +using Xunit; +using X86Disassembler.X86; + +/// +/// Tests for MOV r/m32, imm32 instruction (0xC7) +/// +public class MovRm32Imm32Tests +{ + /// + /// Tests the MOV r32, imm32 instruction (0xC7) with register operand + /// + [Fact] + public void TestMovR32Imm32() + { + // Arrange + byte[] code = { 0xC7, 0xC0, 0x78, 0x56, 0x34, 0x12 }; // MOV EAX, 0x12345678 + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("mov", instructions[0].Mnemonic); + Assert.Equal("eax, 0x12345678", instructions[0].Operands); + } + + /// + /// Tests the MOV m32, imm32 instruction (0xC7) with memory operand + /// + [Fact] + public void TestMovM32Imm32() + { + // Arrange + byte[] code = { 0xC7, 0x00, 0x78, 0x56, 0x34, 0x12 }; // MOV DWORD PTR [EAX], 0x12345678 + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("mov", instructions[0].Mnemonic); + Assert.Equal("dword ptr [eax], 0x12345678", instructions[0].Operands); + } +}