0
mirror of https://github.com/sampletext32/ParkanPlayground.git synced 2025-05-19 11:51:17 +03:00

Refactored instruction decoder to improve modularity. Created StringInstructionDecoder and updated PrefixDecoder. Fixed handler registration in InstructionHandlerFactory.

This commit is contained in:
bird_egop 2025-04-13 02:16:12 +03:00
parent c14a92bf04
commit 9dfa559045
4 changed files with 362 additions and 147 deletions

View File

@ -13,6 +13,7 @@ using X86Disassembler.X86.Handlers.Or;
using X86Disassembler.X86.Handlers.Pop; using X86Disassembler.X86.Handlers.Pop;
using X86Disassembler.X86.Handlers.Push; using X86Disassembler.X86.Handlers.Push;
using X86Disassembler.X86.Handlers.Ret; using X86Disassembler.X86.Handlers.Ret;
using X86Disassembler.X86.Handlers.String;
using X86Disassembler.X86.Handlers.Test; using X86Disassembler.X86.Handlers.Test;
using X86Disassembler.X86.Handlers.Xchg; using X86Disassembler.X86.Handlers.Xchg;
using X86Disassembler.X86.Handlers.Xor; using X86Disassembler.X86.Handlers.Xor;
@ -41,32 +42,36 @@ public class InstructionHandlerFactory
_decoder = decoder; _decoder = decoder;
_length = length; _length = length;
RegisterHandlers(); RegisterAllHandlers();
} }
/// <summary> /// <summary>
/// Registers all handlers /// Registers all handlers
/// </summary> /// </summary>
private void RegisterHandlers() private void RegisterAllHandlers()
{ {
// Register specific instruction handlers // Register specific instruction handlers
_handlers.Add(new Int3Handler(_codeBuffer, _decoder, _length)); _handlers.Add(new Int3Handler(_codeBuffer, _decoder, _length));
RegisterArithmeticUnaryHandlers();
RegisterArithmeticImmediateHandlers(); RegisterArithmeticImmediateHandlers();
RegisterReturnHandlers(); RegisterArithmeticUnaryHandlers();
RegisterCallHandlers(); RegisterAddHandlers();
RegisterJumpHandlers(); RegisterCmpHandlers();
RegisterTestHandlers();
RegisterXorHandlers(); RegisterXorHandlers();
RegisterOrHandlers(); RegisterOrHandlers();
RegisterLeaHandlers(); RegisterTestHandlers();
RegisterCmpHandlers(); RegisterDataTransferHandlers();
RegisterJumpHandlers();
RegisterCallHandlers();
RegisterReturnHandlers();
RegisterDecHandlers(); RegisterDecHandlers();
RegisterIncHandlers(); RegisterIncHandlers();
RegisterAddHandlers(); RegisterPushHandlers();
RegisterDataTransferHandlers(); RegisterPopHandlers();
RegisterLeaHandlers();
RegisterFloatingPointHandlers(); RegisterFloatingPointHandlers();
RegisterStringHandlers();
RegisterMovHandlers();
} }
/// <summary> /// <summary>
@ -295,6 +300,51 @@ public class InstructionHandlerFactory
_handlers.Add(new LoadStoreInt16Handler(_codeBuffer, _decoder, _length)); _handlers.Add(new LoadStoreInt16Handler(_codeBuffer, _decoder, _length));
} }
/// <summary>
/// Registers all String instruction handlers
/// </summary>
private void RegisterStringHandlers()
{
// Add String instruction handlers
_handlers.Add(new RepMovsHandler(_codeBuffer, _decoder, _length));
}
/// <summary>
/// Registers all MOV instruction handlers
/// </summary>
private void RegisterMovHandlers()
{
// Add MOV handlers
_handlers.Add(new MovRegMemHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new MovMemRegHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new MovRegImm32Handler(_codeBuffer, _decoder, _length));
_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));
_handlers.Add(new MovRm8Imm8Handler(_codeBuffer, _decoder, _length));
}
/// <summary>
/// Registers all PUSH instruction handlers
/// </summary>
private void RegisterPushHandlers()
{
// Add PUSH handlers
_handlers.Add(new PushRegHandler(_codeBuffer, _decoder, _length));
_handlers.Add(new PushImm32Handler(_codeBuffer, _decoder, _length));
_handlers.Add(new PushImm8Handler(_codeBuffer, _decoder, _length));
}
/// <summary>
/// Registers all POP instruction handlers
/// </summary>
private void RegisterPopHandlers()
{
// Add POP handlers
_handlers.Add(new PopRegHandler(_codeBuffer, _decoder, _length));
}
/// <summary> /// <summary>
/// Gets the handler that can decode the given opcode /// Gets the handler that can decode the given opcode
/// </summary> /// </summary>

View File

@ -19,13 +19,10 @@ public class InstructionDecoder
// The instruction handler factory // The instruction handler factory
private readonly InstructionHandlerFactory _handlerFactory; private readonly InstructionHandlerFactory _handlerFactory;
// Instruction prefixes // Specialized decoders
private bool _operandSizePrefix; private readonly PrefixDecoder _prefixDecoder;
private bool _addressSizePrefix; private readonly ModRMDecoder _modRMDecoder;
private bool _segmentOverridePrefix; private readonly StringInstructionDecoder _stringDecoder;
private bool _lockPrefix;
private bool _repPrefix;
private string _segmentOverride;
/// <summary> /// <summary>
/// Initializes a new instance of the InstructionDecoder class /// Initializes a new instance of the InstructionDecoder class
@ -37,7 +34,11 @@ public class InstructionDecoder
_codeBuffer = codeBuffer; _codeBuffer = codeBuffer;
_length = length; _length = length;
_position = 0; _position = 0;
_segmentOverride = "";
// Create specialized decoders
_prefixDecoder = new PrefixDecoder();
_modRMDecoder = new ModRMDecoder(codeBuffer, this, length);
_stringDecoder = new StringInstructionDecoder(codeBuffer, length);
// Create the instruction handler factory // Create the instruction handler factory
_handlerFactory = new InstructionHandlerFactory(_codeBuffer, this, _length); _handlerFactory = new InstructionHandlerFactory(_codeBuffer, this, _length);
@ -55,12 +56,7 @@ public class InstructionDecoder
} }
// Reset prefix flags // Reset prefix flags
_operandSizePrefix = false; _prefixDecoder.Reset();
_addressSizePrefix = false;
_segmentOverridePrefix = false;
_lockPrefix = false;
_repPrefix = false;
_segmentOverride = string.Empty;
// Save the start position of the instruction // Save the start position of the instruction
int startPosition = _position; int startPosition = _position;
@ -76,54 +72,21 @@ public class InstructionDecoder
{ {
byte prefix = _codeBuffer[_position]; byte prefix = _codeBuffer[_position];
if (prefix == 0x66) // Operand size prefix if (_prefixDecoder.DecodePrefix(prefix))
{ {
_operandSizePrefix = true;
_position++;
}
else if (prefix == 0x67) // Address size prefix
{
_addressSizePrefix = true;
_position++;
}
else if ((prefix >= 0x26 && prefix <= 0x3E && (prefix & 0x7) == 0x6) || prefix == 0x64 || prefix == 0x65) // Segment override prefix
{
_segmentOverridePrefix = true;
switch (prefix)
{
case 0x26: _segmentOverride = "es"; break;
case 0x2E: _segmentOverride = "cs"; break;
case 0x36: _segmentOverride = "ss"; break;
case 0x3E: _segmentOverride = "ds"; break;
case 0x64: _segmentOverride = "fs"; break;
case 0x65: _segmentOverride = "gs"; break;
}
_position++;
}
else if (prefix == 0xF0) // LOCK prefix
{
_lockPrefix = true;
_position++;
}
else if (prefix == 0xF2 || prefix == 0xF3) // REP/REPNE prefix
{
_repPrefix = true;
_position++; _position++;
// Special case for string instructions // Special case for REP/REPNE prefix followed by string instruction
if (_position < _length) if ((prefix == 0xF2 || prefix == 0xF3) && _position < _length)
{ {
byte stringOp = _codeBuffer[_position]; byte nextByte = _codeBuffer[_position];
if (stringOp == 0xA4 || stringOp == 0xA5 || // MOVS if (_stringDecoder.IsStringInstruction(nextByte))
stringOp == 0xAA || stringOp == 0xAB || // STOS
stringOp == 0xAC || stringOp == 0xAD || // LODS
stringOp == 0xAE || stringOp == 0xAF) // SCAS
{ {
// Skip the string operation opcode // Skip the string operation opcode
_position++; _position++;
// Handle REP string instruction // Handle REP string instruction
return CreateStringInstruction(prefix, stringOp, startPosition); return _stringDecoder.CreateStringInstruction(prefix, nextByte, startPosition, _position);
} }
} }
} }
@ -137,9 +100,9 @@ public class InstructionDecoder
{ {
// If we reached the end of the buffer while processing prefixes, // If we reached the end of the buffer while processing prefixes,
// create an instruction with just the prefix information // create an instruction with just the prefix information
if (_segmentOverridePrefix) if (_prefixDecoder.HasSegmentOverridePrefix())
{ {
instruction.Mnemonic = _segmentOverride; instruction.Mnemonic = _prefixDecoder.GetSegmentOverride();
instruction.Operands = ""; instruction.Operands = "";
// Set the raw bytes // Set the raw bytes
@ -174,22 +137,9 @@ public class InstructionDecoder
instruction.Operands = "??"; instruction.Operands = "??";
} }
// Add REP prefix to the instruction if present // Apply prefixes to the instruction
if (_repPrefix && !instruction.Mnemonic.StartsWith("rep")) instruction.Mnemonic = _prefixDecoder.ApplyRepPrefix(instruction.Mnemonic);
{ instruction.Operands = _prefixDecoder.ApplySegmentOverride(instruction.Operands);
instruction.Mnemonic = $"rep {instruction.Mnemonic}";
}
// Add segment override prefix to the instruction if present
if (_segmentOverridePrefix && !string.IsNullOrEmpty(instruction.Operands))
{
// If the instruction has memory operands, add the segment override
if (instruction.Operands.Contains("["))
{
// Replace the first '[' with the segment override
instruction.Operands = instruction.Operands.Replace("[", $"{_segmentOverride}:[" );
}
}
// Set the raw bytes // Set the raw bytes
int bytesLength = _position - startPosition; int bytesLength = _position - startPosition;
@ -199,62 +149,6 @@ public class InstructionDecoder
return instruction; return instruction;
} }
/// <summary>
/// Creates an instruction for a string operation with REP/REPNE prefix
/// </summary>
/// <param name="prefix">The REP/REPNE prefix (0xF2 or 0xF3)</param>
/// <param name="stringOp">The string operation opcode</param>
/// <param name="startPosition">The start position of the instruction</param>
/// <returns>The created instruction</returns>
private Instruction CreateStringInstruction(byte prefix, byte stringOp, int startPosition)
{
// Create a new instruction
Instruction instruction = new Instruction
{
Address = (uint)startPosition,
};
// Get the mnemonic for the string operation
string mnemonic = OpcodeMap.GetMnemonic(stringOp);
instruction.Mnemonic = prefix == 0xF3 ? $"rep {mnemonic}" : $"repne {mnemonic}";
// Set operands based on the string operation
switch (stringOp)
{
case 0xA4: // MOVSB
instruction.Operands = "byte ptr [edi], byte ptr [esi]";
break;
case 0xA5: // MOVSD
instruction.Operands = "dword ptr [edi], dword ptr [esi]";
break;
case 0xAA: // STOSB
instruction.Operands = "byte ptr [edi], al";
break;
case 0xAB: // STOSD
instruction.Operands = "dword ptr [edi], eax";
break;
case 0xAC: // LODSB
instruction.Operands = "al, byte ptr [esi]";
break;
case 0xAD: // LODSD
instruction.Operands = "eax, dword ptr [esi]";
break;
case 0xAE: // SCASB
instruction.Operands = "al, byte ptr [edi]";
break;
case 0xAF: // SCASD
instruction.Operands = "eax, dword ptr [edi]";
break;
}
// Set the raw bytes
int length = _position - startPosition;
instruction.RawBytes = new byte[length];
Array.Copy(_codeBuffer, startPosition, instruction.RawBytes, 0, length);
return instruction;
}
/// <summary> /// <summary>
/// Gets the current position in the buffer /// Gets the current position in the buffer
/// </summary> /// </summary>
@ -279,7 +173,7 @@ public class InstructionDecoder
/// <returns>True if the operand size prefix is present</returns> /// <returns>True if the operand size prefix is present</returns>
public bool HasOperandSizePrefix() public bool HasOperandSizePrefix()
{ {
return _operandSizePrefix; return _prefixDecoder.HasOperandSizePrefix();
} }
/// <summary> /// <summary>
@ -288,7 +182,7 @@ public class InstructionDecoder
/// <returns>True if the address size prefix is present</returns> /// <returns>True if the address size prefix is present</returns>
public bool HasAddressSizePrefix() public bool HasAddressSizePrefix()
{ {
return _addressSizePrefix; return _prefixDecoder.HasAddressSizePrefix();
} }
/// <summary> /// <summary>
@ -297,7 +191,7 @@ public class InstructionDecoder
/// <returns>True if a segment override prefix is present</returns> /// <returns>True if a segment override prefix is present</returns>
public bool HasSegmentOverridePrefix() public bool HasSegmentOverridePrefix()
{ {
return _segmentOverridePrefix; return _prefixDecoder.HasSegmentOverridePrefix();
} }
/// <summary> /// <summary>
@ -306,7 +200,7 @@ public class InstructionDecoder
/// <returns>The segment override prefix, or an empty string if none is present</returns> /// <returns>The segment override prefix, or an empty string if none is present</returns>
public string GetSegmentOverride() public string GetSegmentOverride()
{ {
return _segmentOverride; return _prefixDecoder.GetSegmentOverride();
} }
/// <summary> /// <summary>
@ -315,7 +209,7 @@ public class InstructionDecoder
/// <returns>True if the LOCK prefix is present</returns> /// <returns>True if the LOCK prefix is present</returns>
public bool HasLockPrefix() public bool HasLockPrefix()
{ {
return _lockPrefix; return _prefixDecoder.HasLockPrefix();
} }
/// <summary> /// <summary>
@ -324,7 +218,7 @@ public class InstructionDecoder
/// <returns>True if the REP/REPNE prefix is present</returns> /// <returns>True if the REP/REPNE prefix is present</returns>
public bool HasRepPrefix() public bool HasRepPrefix()
{ {
return _repPrefix; return _prefixDecoder.HasRepPrefix();
} }
/// <summary> /// <summary>
@ -352,7 +246,7 @@ public class InstructionDecoder
return 0; return 0;
} }
ushort value = BitConverter.ToUInt16(_codeBuffer, _position); ushort value = (ushort)(_codeBuffer[_position] | (_codeBuffer[_position + 1] << 8));
_position += 2; _position += 2;
return value; return value;
} }
@ -368,7 +262,10 @@ public class InstructionDecoder
return 0; return 0;
} }
uint value = BitConverter.ToUInt32(_codeBuffer, _position); uint value = (uint)(_codeBuffer[_position] |
(_codeBuffer[_position + 1] << 8) |
(_codeBuffer[_position + 2] << 16) |
(_codeBuffer[_position + 3] << 24));
_position += 4; _position += 4;
return value; return value;
} }

View File

@ -0,0 +1,170 @@
namespace X86Disassembler.X86;
/// <summary>
/// Handles decoding of instruction prefixes
/// </summary>
public class PrefixDecoder
{
// Prefix flags
private bool _operandSizePrefix;
private bool _addressSizePrefix;
private bool _segmentOverridePrefix;
private bool _lockPrefix;
private bool _repPrefix;
private string _segmentOverride = string.Empty;
/// <summary>
/// Initializes a new instance of the PrefixDecoder class
/// </summary>
public PrefixDecoder()
{
Reset();
}
/// <summary>
/// Resets all prefix flags
/// </summary>
public void Reset()
{
_operandSizePrefix = false;
_addressSizePrefix = false;
_segmentOverridePrefix = false;
_lockPrefix = false;
_repPrefix = false;
_segmentOverride = string.Empty;
}
/// <summary>
/// Decodes a prefix byte
/// </summary>
/// <param name="prefix">The prefix byte</param>
/// <returns>True if the byte was a prefix, false otherwise</returns>
public bool DecodePrefix(byte prefix)
{
if (prefix == 0x66) // Operand size prefix
{
_operandSizePrefix = true;
return true;
}
else if (prefix == 0x67) // Address size prefix
{
_addressSizePrefix = true;
return true;
}
else if ((prefix >= 0x26 && prefix <= 0x3E && (prefix & 0x7) == 0x6) || prefix == 0x64 || prefix == 0x65) // Segment override prefix
{
_segmentOverridePrefix = true;
switch (prefix)
{
case 0x26: _segmentOverride = "es"; break;
case 0x2E: _segmentOverride = "cs"; break;
case 0x36: _segmentOverride = "ss"; break;
case 0x3E: _segmentOverride = "ds"; break;
case 0x64: _segmentOverride = "fs"; break;
case 0x65: _segmentOverride = "gs"; break;
}
return true;
}
else if (prefix == 0xF0) // LOCK prefix
{
_lockPrefix = true;
return true;
}
else if (prefix == 0xF2 || prefix == 0xF3) // REP/REPNE prefix
{
_repPrefix = true;
return true;
}
return false;
}
/// <summary>
/// Checks if the operand size prefix is present
/// </summary>
/// <returns>True if the operand size prefix is present</returns>
public bool HasOperandSizePrefix()
{
return _operandSizePrefix;
}
/// <summary>
/// Checks if the address size prefix is present
/// </summary>
/// <returns>True if the address size prefix is present</returns>
public bool HasAddressSizePrefix()
{
return _addressSizePrefix;
}
/// <summary>
/// Checks if a segment override prefix is present
/// </summary>
/// <returns>True if a segment override prefix is present</returns>
public bool HasSegmentOverridePrefix()
{
return _segmentOverridePrefix;
}
/// <summary>
/// Gets the segment override prefix
/// </summary>
/// <returns>The segment override prefix, or an empty string if none is present</returns>
public string GetSegmentOverride()
{
return _segmentOverride;
}
/// <summary>
/// Checks if the LOCK prefix is present
/// </summary>
/// <returns>True if the LOCK prefix is present</returns>
public bool HasLockPrefix()
{
return _lockPrefix;
}
/// <summary>
/// Checks if the REP/REPNE prefix is present
/// </summary>
/// <returns>True if the REP/REPNE prefix is present</returns>
public bool HasRepPrefix()
{
return _repPrefix;
}
/// <summary>
/// Applies the segment override prefix to the operands string if applicable
/// </summary>
/// <param name="operands">The operands string</param>
/// <returns>The operands string with segment override applied</returns>
public string ApplySegmentOverride(string operands)
{
if (_segmentOverridePrefix && !string.IsNullOrEmpty(operands))
{
// If the instruction has memory operands, add the segment override
if (operands.Contains("["))
{
// Replace the first '[' with the segment override
return operands.Replace("[", $"{_segmentOverride}:[" );
}
}
return operands;
}
/// <summary>
/// Applies the REP prefix to the mnemonic if applicable
/// </summary>
/// <param name="mnemonic">The mnemonic</param>
/// <returns>The mnemonic with REP prefix applied</returns>
public string ApplyRepPrefix(string mnemonic)
{
if (_repPrefix && !mnemonic.StartsWith("rep"))
{
return $"rep {mnemonic}";
}
return mnemonic;
}
}

View File

@ -0,0 +1,98 @@
namespace X86Disassembler.X86;
/// <summary>
/// Handles decoding of string instructions
/// </summary>
public class StringInstructionDecoder
{
// The buffer containing the code to decode
private readonly byte[] _codeBuffer;
// The length of the buffer
private readonly int _length;
/// <summary>
/// Initializes a new instance of the StringInstructionDecoder class
/// </summary>
/// <param name="codeBuffer">The buffer containing the code to decode</param>
/// <param name="length">The length of the buffer</param>
public StringInstructionDecoder(byte[] codeBuffer, int length)
{
_codeBuffer = codeBuffer;
_length = length;
}
/// <summary>
/// Checks if the opcode is a string instruction
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if the opcode is a string instruction</returns>
public bool IsStringInstruction(byte opcode)
{
return opcode == 0xA4 || opcode == 0xA5 || // MOVS
opcode == 0xAA || opcode == 0xAB || // STOS
opcode == 0xAC || opcode == 0xAD || // LODS
opcode == 0xAE || opcode == 0xAF; // SCAS
}
/// <summary>
/// Creates an instruction for a string operation with REP/REPNE prefix
/// </summary>
/// <param name="prefix">The REP/REPNE prefix (0xF2 or 0xF3)</param>
/// <param name="stringOp">The string operation opcode</param>
/// <param name="startPosition">The start position of the instruction</param>
/// <param name="currentPosition">The current position after reading the string opcode</param>
/// <returns>The created instruction</returns>
public Instruction CreateStringInstruction(byte prefix, byte stringOp, int startPosition, int currentPosition)
{
// Create a new instruction
Instruction instruction = new Instruction
{
Address = (uint)startPosition,
};
// Get the mnemonic for the string operation
string mnemonic = OpcodeMap.GetMnemonic(stringOp);
instruction.Mnemonic = prefix == 0xF3 ? $"rep {mnemonic}" : $"repne {mnemonic}";
// Set operands based on the string operation
instruction.Operands = GetStringOperands(stringOp);
// Set the raw bytes
int length = currentPosition - startPosition;
instruction.RawBytes = new byte[length];
Array.Copy(_codeBuffer, startPosition, instruction.RawBytes, 0, length);
return instruction;
}
/// <summary>
/// Gets the operands for a string instruction
/// </summary>
/// <param name="stringOp">The string operation opcode</param>
/// <returns>The operands string</returns>
private string GetStringOperands(byte stringOp)
{
switch (stringOp)
{
case 0xA4: // MOVSB
return "byte ptr [edi], byte ptr [esi]";
case 0xA5: // MOVSD
return "dword ptr [edi], dword ptr [esi]";
case 0xAA: // STOSB
return "byte ptr [edi], al";
case 0xAB: // STOSD
return "dword ptr [edi], eax";
case 0xAC: // LODSB
return "al, byte ptr [esi]";
case 0xAD: // LODSD
return "eax, dword ptr [esi]";
case 0xAE: // SCASB
return "al, byte ptr [edi]";
case 0xAF: // SCASD
return "eax, dword ptr [edi]";
default:
return "??";
}
}
}