diff --git a/X86Disassembler/X86/Handlers/Add/AddR32Rm32Handler.cs b/X86Disassembler/X86/Handlers/Add/AddR32Rm32Handler.cs new file mode 100644 index 0000000..83b530e --- /dev/null +++ b/X86Disassembler/X86/Handlers/Add/AddR32Rm32Handler.cs @@ -0,0 +1,73 @@ +namespace X86Disassembler.X86.Handlers.Add; + +/// +/// Handler for ADD r32, r/m32 instruction (0x03) +/// +public class AddR32Rm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the AddR32Rm32Handler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public AddR32Rm32Handler(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 == 0x03; + } + + /// + /// Decodes an ADD 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 = "add"; + + // 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/Group5/CallRm32Handler.cs b/X86Disassembler/X86/Handlers/Group5/CallRm32Handler.cs new file mode 100644 index 0000000..6541980 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Group5/CallRm32Handler.cs @@ -0,0 +1,76 @@ +namespace X86Disassembler.X86.Handlers.Group5; + +/// +/// Handler for CALL r/m32 instruction (0xFF /2) +/// +public class CallRm32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the CallRm32Handler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public CallRm32Handler(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 == 0xFF; + } + + /// + /// Decodes a CALL 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); + + // CALL r/m32 is encoded as FF /2 + if (reg != 2) + { + return false; + } + + // Set the mnemonic + instruction.Mnemonic = "call"; + + // For memory operands, set the operand + if (mod != 3) // Memory operand + { + string operand = ModRMDecoder.DecodeModRM(mod, rm, false); + instruction.Operands = operand; + } + else // Register operand + { + string rmName = GetRegister32(rm); + instruction.Operands = rmName; + } + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs index 70457b8..0bc701a 100644 --- a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs +++ b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs @@ -1,9 +1,11 @@ +using X86Disassembler.X86.Handlers.Add; using X86Disassembler.X86.Handlers.ArithmeticImmediate; using X86Disassembler.X86.Handlers.ArithmeticUnary; using X86Disassembler.X86.Handlers.Call; using X86Disassembler.X86.Handlers.Cmp; using X86Disassembler.X86.Handlers.Dec; using X86Disassembler.X86.Handlers.FloatingPoint; +using X86Disassembler.X86.Handlers.Group5; using X86Disassembler.X86.Handlers.Jump; using X86Disassembler.X86.Handlers.Lea; using X86Disassembler.X86.Handlers.Mov; @@ -81,6 +83,12 @@ public class InstructionHandlerFactory // Register Dec handlers RegisterDecHandlers(); + // Register Add handlers + RegisterAddHandlers(); + + // Register Group5 handlers + RegisterGroup5Handlers(); + // Register Data Transfer handlers RegisterDataTransferHandlers(); @@ -251,6 +259,24 @@ public class InstructionHandlerFactory _handlers.Add(new DecRegHandler(_codeBuffer, _decoder, _length)); } + /// + /// Registers all Add instruction handlers + /// + private void RegisterAddHandlers() + { + // Add Add handlers + _handlers.Add(new AddR32Rm32Handler(_codeBuffer, _decoder, _length)); + } + + /// + /// Registers all Group5 instruction handlers + /// + private void RegisterGroup5Handlers() + { + // Add Group5 handlers + _handlers.Add(new CallRm32Handler(_codeBuffer, _decoder, _length)); + } + /// /// Registers all Data Transfer instruction handlers /// diff --git a/X86Disassembler/X86/OpcodeMap.cs b/X86Disassembler/X86/OpcodeMap.cs index 381f81e..628957b 100644 --- a/X86Disassembler/X86/OpcodeMap.cs +++ b/X86Disassembler/X86/OpcodeMap.cs @@ -72,6 +72,14 @@ public static class OpcodeMap OneByteOpcodes[0x4E] = "dec"; // DEC ESI OneByteOpcodes[0x4F] = "dec"; // DEC EDI + // ADD instructions + OneByteOpcodes[0x00] = "add"; // ADD r/m8, r8 + OneByteOpcodes[0x01] = "add"; // ADD r/m32, r32 + OneByteOpcodes[0x02] = "add"; // ADD r8, r/m8 + OneByteOpcodes[0x03] = "add"; // ADD r32, r/m32 + OneByteOpcodes[0x04] = "add"; // ADD AL, imm8 + OneByteOpcodes[0x05] = "add"; // ADD EAX, imm32 + // Group 1 instructions (ADD, OR, ADC, SBB, AND, SUB, XOR, CMP) OneByteOpcodes[0x80] = "group1b"; OneByteOpcodes[0x81] = "group1d"; @@ -81,6 +89,9 @@ public static class OpcodeMap OneByteOpcodes[0xF6] = "group3b"; // 8-bit operations OneByteOpcodes[0xF7] = "group3d"; // 32-bit operations + // Group 5 instructions (INC, DEC, CALL, CALL, JMP, JMP, PUSH) + OneByteOpcodes[0xFF] = "group5"; + // TEST instructions OneByteOpcodes[0x84] = "test"; // TEST r/m8, r8 OneByteOpcodes[0x85] = "test"; // TEST r/m32, r32 diff --git a/X86DisassemblerTests/AddInstructionTests.cs b/X86DisassemblerTests/AddInstructionTests.cs new file mode 100644 index 0000000..59835a0 --- /dev/null +++ b/X86DisassemblerTests/AddInstructionTests.cs @@ -0,0 +1,49 @@ +namespace X86DisassemblerTests; + +using System; +using Xunit; +using X86Disassembler.X86; + +/// +/// Tests for ADD instruction handlers +/// +public class AddInstructionTests +{ + /// + /// Tests the ADD r32, r/m32 instruction (0x03) with register operand + /// + [Fact] + public void TestAddR32Rm32_Register() + { + // Arrange + byte[] code = { 0x03, 0xF5 }; // ADD ESI, EBP + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("add", instructions[0].Mnemonic); + Assert.Equal("esi, ebp", instructions[0].Operands); + } + + /// + /// Tests the ADD r32, m32 instruction (0x03) with memory operand + /// + [Fact] + public void TestAddR32M32() + { + // Arrange + byte[] code = { 0x03, 0x00 }; // ADD EAX, DWORD PTR [EAX] + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("add", instructions[0].Mnemonic); + Assert.Equal("eax, dword ptr [eax]", instructions[0].Operands); + } +} diff --git a/X86DisassemblerTests/CallRm32Tests.cs b/X86DisassemblerTests/CallRm32Tests.cs new file mode 100644 index 0000000..1f59841 --- /dev/null +++ b/X86DisassemblerTests/CallRm32Tests.cs @@ -0,0 +1,49 @@ +namespace X86DisassemblerTests; + +using System; +using Xunit; +using X86Disassembler.X86; + +/// +/// Tests for CALL r/m32 instruction (0xFF /2) +/// +public class CallRm32Tests +{ + /// + /// Tests the CALL r32 instruction (0xFF /2) with register operand + /// + [Fact] + public void TestCallReg() + { + // Arrange + byte[] code = { 0xFF, 0xD3 }; // CALL EBX + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("call", instructions[0].Mnemonic); + Assert.Equal("ebx", instructions[0].Operands); + } + + /// + /// Tests the CALL m32 instruction (0xFF /2) with memory operand + /// + [Fact] + public void TestCallMem() + { + // Arrange + byte[] code = { 0xFF, 0x10 }; // CALL DWORD PTR [EAX] + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("call", instructions[0].Mnemonic); + Assert.Equal("dword ptr [eax]", instructions[0].Operands); + } +}