diff --git a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs index afd0689..fa1c3dd 100644 --- a/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs +++ b/X86Disassembler/X86/Handlers/InstructionHandlerFactory.cs @@ -10,6 +10,7 @@ using X86Disassembler.X86.Handlers.Inc; using X86Disassembler.X86.Handlers.Jump; using X86Disassembler.X86.Handlers.Lea; using X86Disassembler.X86.Handlers.Mov; +using X86Disassembler.X86.Handlers.Nop; using X86Disassembler.X86.Handlers.Or; using X86Disassembler.X86.Handlers.Pop; using X86Disassembler.X86.Handlers.Push; @@ -78,6 +79,7 @@ public class InstructionHandlerFactory RegisterStringHandlers(); RegisterMovHandlers(); RegisterSubHandlers(); // Register SUB handlers + RegisterNopHandlers(); // Register NOP handlers } /// @@ -396,6 +398,17 @@ public class InstructionHandlerFactory _handlers.Add(new SubImmFromRm8Handler(_codeBuffer, _decoder, _length)); } + /// + /// Registers all NOP instruction handlers + /// + private void RegisterNopHandlers() + { + // Register NOP handlers + _handlers.Add(new NopHandler(_codeBuffer, _decoder, _length)); + _handlers.Add(new TwoByteNopHandler(_codeBuffer, _decoder, _length)); + _handlers.Add(new MultiByteNopHandler(_codeBuffer, _decoder, _length)); + } + /// /// Gets the handler that can decode the given opcode /// diff --git a/X86Disassembler/X86/Handlers/Nop/MultiByteNopHandler.cs b/X86Disassembler/X86/Handlers/Nop/MultiByteNopHandler.cs new file mode 100644 index 0000000..fb9a5a7 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Nop/MultiByteNopHandler.cs @@ -0,0 +1,99 @@ +namespace X86Disassembler.X86.Handlers.Nop; + +/// +/// Handler for multi-byte NOP instructions (0x0F 0x1F ...) +/// These are used for alignment and are encoded as NOP operations with specific memory operands +/// +public class MultiByteNopHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the MultiByteNopHandler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public MultiByteNopHandler(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) + { + // Multi-byte NOPs start with 0x0F + if (opcode != 0x0F) + { + return false; + } + + int position = Decoder.GetPosition(); + + // Check if we have enough bytes to read the second opcode + if (position >= Length) + { + return false; + } + + // Check if the second byte is 0x1F (part of the multi-byte NOP encoding) + byte secondByte = CodeBuffer[position]; + return secondByte == 0x1F; + } + + /// + /// Decodes a multi-byte NOP 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 = "nop"; + + int position = Decoder.GetPosition(); + + // Skip the second byte (0x1F) + position++; + + // Check if we have enough bytes to read the ModR/M byte + 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); + + // Update the decoder position + Decoder.SetPosition(position); + + // Decode the memory operand + string memOperand; + + if (mod == 3) + { + // This is a register operand, which is not a valid multi-byte NOP + // But we'll handle it anyway + memOperand = ModRMDecoder.GetRegisterName(rm, 32); + } + else + { + // Get the memory operand string + memOperand = ModRMDecoder.DecodeModRM(mod, rm, false); + } + + // Set the operands + instruction.Operands = memOperand; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Nop/NopHandler.cs b/X86Disassembler/X86/Handlers/Nop/NopHandler.cs new file mode 100644 index 0000000..577ff93 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Nop/NopHandler.cs @@ -0,0 +1,45 @@ +namespace X86Disassembler.X86.Handlers.Nop; + +/// +/// Handler for the NOP instruction (opcode 0x90) +/// +public class NopHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the NopHandler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public NopHandler(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 == 0x90; + } + + /// + /// Decodes a NOP 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 = "nop"; + + // NOP has no operands + instruction.Operands = ""; + + return true; + } +} diff --git a/X86Disassembler/X86/Handlers/Nop/TwoByteNopHandler.cs b/X86Disassembler/X86/Handlers/Nop/TwoByteNopHandler.cs new file mode 100644 index 0000000..45ec369 --- /dev/null +++ b/X86Disassembler/X86/Handlers/Nop/TwoByteNopHandler.cs @@ -0,0 +1,47 @@ +namespace X86Disassembler.X86.Handlers.Nop; + +/// +/// Handler for the 2-byte NOP instruction (0x66 0x90) +/// This is a NOP with an operand size prefix +/// +public class TwoByteNopHandler : InstructionHandler +{ + /// + /// Initializes a new instance of the TwoByteNopHandler class + /// + /// The buffer containing the code to decode + /// The instruction decoder that owns this handler + /// The length of the buffer + public TwoByteNopHandler(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) + { + // Check if the opcode is 0x90 and we have a 0x66 prefix + return opcode == 0x90 && Decoder.HasOperandSizeOverridePrefix(); + } + + /// + /// Decodes a 2-byte NOP 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 = "nop"; + + // NOP has no operands, even with the operand size prefix + instruction.Operands = ""; + + return true; + } +} diff --git a/X86DisassemblerTests/TestData/nop_tests.csv b/X86DisassemblerTests/TestData/nop_tests.csv index 2a57f98..406e306 100644 --- a/X86DisassemblerTests/TestData/nop_tests.csv +++ b/X86DisassemblerTests/TestData/nop_tests.csv @@ -1,2 +1,31 @@ +# NOP instruction tests +# Format: RawBytes;Instructions RawBytes;Instructions + +# Basic NOP instruction (1-byte) 90;[{ "Mnemonic": "nop", "Operands": "" }] + +# Multi-byte NOP instructions (used for alignment) +# 2-byte NOP +6690;[{ "Mnemonic": "nop", "Operands": "" }] + +# 3-byte NOP (XCHG EAX, EAX) +0F1F00;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax]" }] + +# 4-byte NOP +0F1F4000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+0x00]" }] + +# 5-byte NOP +0F1F440000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax]" }] + +# 6-byte NOP +660F1F440000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax]" }] + +# 7-byte NOP +0F1F8000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+0x00000000]" }] + +# 8-byte NOP +0F1F840000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax]" }] + +# 9-byte NOP +660F1F840000000000;[{ "Mnemonic": "nop", "Operands": "dword ptr [eax+eax]" }]