From 7d23af32fae00e5f5220b005ff9ff11da034e6f7 Mon Sep 17 00:00:00 2001 From: bird_egop Date: Sun, 13 Apr 2025 00:50:23 +0300 Subject: [PATCH] Added support for MOV r/m8, imm8 (0xC6) and ADD r/m32, r32 (0x01) instructions with tests --- .../X86/Handlers/Add/AddRm32R32Handler.cs | 73 ++++++++++ .../X86/Handlers/InstructionHandlerFactory.cs | 2 + .../X86/Handlers/Mov/MovRm8Imm8Handler.cs | 125 ++++++++++++++++++ X86DisassemblerTests/AddRm32R32Tests.cs | 49 +++++++ X86DisassemblerTests/MovRm8Imm8Tests.cs | 49 +++++++ 5 files changed, 298 insertions(+) create mode 100644 X86Disassembler/X86/Handlers/Add/AddRm32R32Handler.cs create mode 100644 X86Disassembler/X86/Handlers/Mov/MovRm8Imm8Handler.cs create mode 100644 X86DisassemblerTests/AddRm32R32Tests.cs create mode 100644 X86DisassemblerTests/MovRm8Imm8Tests.cs diff --git a/X86Disassembler/X86/Handlers/Add/AddRm32R32Handler.cs b/X86Disassembler/X86/Handlers/Add/AddRm32R32Handler.cs new file mode 100644 index 0000000..cd0ada2 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Add/AddRm32R32Handler.cs @@ -0,0 +1,73 @@ +namespace X86Disassembler.X86.Handlers.Add; + +/// +/// Handler for ADD r/m32, r32 instruction (0x01) +/// +public class AddRm32R32Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the AddRm32R32Handler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public AddRm32R32Handler(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 == 0x01; + } + + /// + /// Decodes an ADD r/m32, r32 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 = $"{operand}, {regName}"; + } + else // Register operand + { + string rmName = GetRegister32(rm); + instruction.Operands = $"{rmName}, {regName}"; + } + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs index 0bc701a..7cc3169 100644 --- a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs +++ b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs @@ -266,6 +266,7 @@ public class InstructionHandlerFactory { // Add Add handlers _handlers.Add(new AddR32Rm32Handler(_codeBuffer, _decoder, _length)); + _handlers.Add(new AddRm32R32Handler(_codeBuffer, _decoder, _length)); } /// @@ -290,6 +291,7 @@ public class InstructionHandlerFactory _handlers.Add(new MovEaxMoffsHandler(_codeBuffer, _decoder, _length)); _handlers.Add(new MovMoffsEaxHandler(_codeBuffer, _decoder, _length)); _handlers.Add(new MovRm32Imm32Handler(_codeBuffer, _decoder, _length)); + _handlers.Add(new MovRm8Imm8Handler(_codeBuffer, _decoder, _length)); // Add PUSH handlers _handlers.Add(new PushRegHandler(_codeBuffer, _decoder, _length)); diff --git a/X86Disassembler/X86/Handlers/Mov/MovRm8Imm8Handler.cs b/X86Disassembler/X86/Handlers/Mov/MovRm8Imm8Handler.cs new file mode 100644 index 0000000..cf0a23b --- /dev/null +++ b/X86Disassembler/X86/Handlers/Mov/MovRm8Imm8Handler.cs @@ -0,0 +1,125 @@ +namespace X86Disassembler.X86.Handlers.Mov; + +/// +/// Handler for MOV r/m8, imm8 instruction (0xC6) +/// +public class MovRm8Imm8Handler : InstructionHandler +{ + /// + /// Initializes a new instance of the MovRm8Imm8Handler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public MovRm8Imm8Handler(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 == 0xC6; + } + + /// + /// Decodes a MOV 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) + { + 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/m8, imm8 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 byte + if (position >= Length) + { + return false; + } + + byte imm8 = CodeBuffer[position++]; + Decoder.SetPosition(position); + + // Set the mnemonic + instruction.Mnemonic = "mov"; + + // Get the operand string + string operand; + if (mod != 3) // Memory operand + { + string memOperand = ModRMDecoder.DecodeModRM(mod, rm, true); + + // Replace the size prefix with "byte ptr" + if (memOperand.StartsWith("qword ptr ")) + { + operand = memOperand.Replace("qword ptr ", "byte ptr "); + } + else if (memOperand.StartsWith("dword ptr ")) + { + operand = memOperand.Replace("dword ptr ", "byte ptr "); + } + else + { + operand = $"byte ptr {memOperand}"; + } + } + else // Register operand + { + operand = GetRegister8(rm); + } + + // Set the operands + instruction.Operands = $"{operand}, 0x{imm8:X2}"; + + return true; + } +} diff --git a/X86DisassemblerTests/AddRm32R32Tests.cs b/X86DisassemblerTests/AddRm32R32Tests.cs new file mode 100644 index 0000000..11f10d2 --- /dev/null +++ b/X86DisassemblerTests/AddRm32R32Tests.cs @@ -0,0 +1,49 @@ +namespace X86DisassemblerTests; + +using System; +using Xunit; +using X86Disassembler.X86; + +/// +/// Tests for ADD r/m32, r32 instruction (0x01) +/// +public class AddRm32R32Tests +{ + /// + /// Tests the ADD r32, r32 instruction (0x01) with register operand + /// + [Fact] + public void TestAddR32R32() + { + // Arrange + byte[] code = { 0x01, 0xC1 }; // ADD ECX, EAX + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("add", instructions[0].Mnemonic); + Assert.Equal("ecx, eax", instructions[0].Operands); + } + + /// + /// Tests the ADD m32, r32 instruction (0x01) with memory operand + /// + [Fact] + public void TestAddM32R32() + { + // Arrange + byte[] code = { 0x01, 0x01 }; // ADD DWORD PTR [ECX], EAX + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("add", instructions[0].Mnemonic); + Assert.Equal("dword ptr [ecx], eax", instructions[0].Operands); + } +} diff --git a/X86DisassemblerTests/MovRm8Imm8Tests.cs b/X86DisassemblerTests/MovRm8Imm8Tests.cs new file mode 100644 index 0000000..2dcb078 --- /dev/null +++ b/X86DisassemblerTests/MovRm8Imm8Tests.cs @@ -0,0 +1,49 @@ +namespace X86DisassemblerTests; + +using System; +using Xunit; +using X86Disassembler.X86; + +/// +/// Tests for MOV r/m8, imm8 instruction (0xC6) +/// +public class MovRm8Imm8Tests +{ + /// + /// Tests the MOV r8, imm8 instruction (0xC6) with register operand + /// + [Fact] + public void TestMovR8Imm8() + { + // Arrange + byte[] code = { 0xC6, 0xC0, 0x42 }; // MOV AL, 0x42 + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("mov", instructions[0].Mnemonic); + Assert.Equal("al, 0x42", instructions[0].Operands); + } + + /// + /// Tests the MOV m8, imm8 instruction (0xC6) with memory operand + /// + [Fact] + public void TestMovM8Imm8() + { + // Arrange + byte[] code = { 0xC6, 0x01, 0x01 }; // MOV BYTE PTR [ECX], 0x01 + + // Act + Disassembler disassembler = new Disassembler(code, 0x1000); + var instructions = disassembler.Disassemble(); + + // Assert + Assert.Single(instructions); + Assert.Equal("mov", instructions[0].Mnemonic); + Assert.Equal("byte ptr [ecx], 0x01", instructions[0].Operands); + } +}