2025-04-12 19:18:52 +03:00
|
|
|
namespace X86Disassembler.X86;
|
|
|
|
|
2025-04-15 02:42:47 +03:00
|
|
|
using Operands;
|
2025-04-14 22:08:50 +03:00
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Handles decoding of ModR/M bytes in x86 instructions
|
|
|
|
/// </summary>
|
|
|
|
public class ModRMDecoder
|
|
|
|
{
|
|
|
|
// ModR/M byte masks
|
|
|
|
private const byte MOD_MASK = 0xC0; // 11000000b
|
|
|
|
private const byte REG_MASK = 0x38; // 00111000b
|
2025-04-14 01:15:26 +03:00
|
|
|
private const byte RM_MASK = 0x07; // 00000111b
|
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
// SIB byte masks
|
|
|
|
private const byte SIB_SCALE_MASK = 0xC0; // 11000000b
|
|
|
|
private const byte SIB_INDEX_MASK = 0x38; // 00111000b
|
2025-04-14 01:15:26 +03:00
|
|
|
private const byte SIB_BASE_MASK = 0x07; // 00000111b
|
|
|
|
|
2025-04-13 23:06:52 +03:00
|
|
|
// Register names for different sizes
|
2025-04-14 01:15:26 +03:00
|
|
|
private static readonly string[] RegisterNames16 = {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"};
|
|
|
|
private static readonly string[] RegisterNames32 = {"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"};
|
2025-04-13 23:06:52 +03:00
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
// The instruction decoder that owns this ModRM decoder
|
|
|
|
private readonly InstructionDecoder _decoder;
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the ModRMDecoder class
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="decoder">The instruction decoder that owns this ModRM decoder</param>
|
2025-04-14 22:08:50 +03:00
|
|
|
public ModRMDecoder(InstructionDecoder decoder)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
|
|
|
_decoder = decoder;
|
|
|
|
}
|
2025-04-15 00:14:28 +03:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Maps the register index from the ModR/M byte to the RegisterIndex enum value
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="modRMRegIndex">The register index from the ModR/M byte (0-7)</param>
|
|
|
|
/// <returns>The corresponding RegisterIndex enum value</returns>
|
|
|
|
private RegisterIndex MapModRMToRegisterIndex(int modRMRegIndex)
|
|
|
|
{
|
|
|
|
// The mapping from ModR/M register index to RegisterIndex enum is:
|
|
|
|
// 0 -> A (EAX)
|
|
|
|
// 1 -> C (ECX)
|
|
|
|
// 2 -> D (EDX)
|
|
|
|
// 3 -> B (EBX)
|
|
|
|
// 4 -> Sp (ESP)
|
|
|
|
// 5 -> Bp (EBP)
|
|
|
|
// 6 -> Si (ESI)
|
|
|
|
// 7 -> Di (EDI)
|
|
|
|
return modRMRegIndex switch
|
|
|
|
{
|
|
|
|
0 => RegisterIndex.A, // EAX
|
|
|
|
1 => RegisterIndex.C, // ECX
|
|
|
|
2 => RegisterIndex.D, // EDX
|
|
|
|
3 => RegisterIndex.B, // EBX
|
|
|
|
4 => RegisterIndex.Sp, // ESP
|
|
|
|
5 => RegisterIndex.Bp, // EBP
|
|
|
|
6 => RegisterIndex.Si, // ESI
|
|
|
|
7 => RegisterIndex.Di, // EDI
|
|
|
|
_ => RegisterIndex.A // Default to EAX
|
|
|
|
};
|
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-15 02:29:32 +03:00
|
|
|
/// <summary>
|
2025-04-16 01:10:33 +03:00
|
|
|
/// Maps the register index from the ModR/M byte to the RegisterIndex8 enum value
|
2025-04-15 02:29:32 +03:00
|
|
|
/// </summary>
|
|
|
|
/// <param name="modRMRegIndex">The register index from the ModR/M byte (0-7)</param>
|
2025-04-16 01:10:33 +03:00
|
|
|
/// <returns>The corresponding RegisterIndex8 enum value</returns>
|
|
|
|
private RegisterIndex8 MapModRMToRegisterIndex8(int modRMRegIndex)
|
2025-04-15 02:29:32 +03:00
|
|
|
{
|
2025-04-16 01:10:33 +03:00
|
|
|
// The mapping from ModR/M register index to RegisterIndex8 enum is direct:
|
|
|
|
// 0 -> AL, 1 -> CL, 2 -> DL, 3 -> BL, 4 -> AH, 5 -> CH, 6 -> DH, 7 -> BH
|
|
|
|
return (RegisterIndex8)modRMRegIndex;
|
2025-04-15 02:29:32 +03:00
|
|
|
}
|
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
/// <summary>
|
2025-04-14 22:08:50 +03:00
|
|
|
/// Decodes a ModR/M byte to get the operand
|
2025-04-12 19:18:52 +03:00
|
|
|
/// </summary>
|
|
|
|
/// <param name="mod">The mod field (2 bits)</param>
|
2025-04-13 23:06:52 +03:00
|
|
|
/// <param name="rmIndex">The r/m field as RegisterIndex</param>
|
2025-04-12 19:18:52 +03:00
|
|
|
/// <param name="is64Bit">True if the operand is 64-bit</param>
|
2025-04-14 22:08:50 +03:00
|
|
|
/// <returns>The operand object</returns>
|
|
|
|
public Operand DecodeModRM(byte mod, RegisterIndex rmIndex, bool is64Bit)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 22:08:50 +03:00
|
|
|
int operandSize = is64Bit ? 64 : 32;
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
switch (mod)
|
|
|
|
{
|
|
|
|
case 0: // [reg] or disp32
|
2025-04-13 23:06:52 +03:00
|
|
|
// Special case: [EBP] is encoded as disp32 with no base register
|
2025-04-15 02:29:32 +03:00
|
|
|
if (rmIndex == RegisterIndex.Bp) // disp32 (was EBP/BP)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 01:15:26 +03:00
|
|
|
if (_decoder.CanReadUInt())
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 01:15:26 +03:00
|
|
|
uint disp32 = _decoder.ReadUInt32();
|
2025-04-14 22:08:50 +03:00
|
|
|
return OperandFactory.CreateDirectMemoryOperand(disp32, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-14 22:08:50 +03:00
|
|
|
// Fallback for incomplete data
|
|
|
|
return OperandFactory.CreateDirectMemoryOperand(0, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-13 23:06:52 +03:00
|
|
|
// Special case: [ESP] is encoded with SIB byte
|
2025-04-15 00:14:28 +03:00
|
|
|
if (rmIndex == RegisterIndex.Sp) // SIB (was ESP/SP)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
|
|
|
// Handle SIB byte
|
2025-04-14 01:15:26 +03:00
|
|
|
if (_decoder.CanReadByte())
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 01:15:26 +03:00
|
|
|
byte sib = _decoder.ReadByte();
|
2025-04-12 19:18:52 +03:00
|
|
|
return DecodeSIB(sib, 0, is64Bit);
|
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-14 22:08:50 +03:00
|
|
|
// Fallback for incomplete data
|
2025-04-15 00:14:28 +03:00
|
|
|
return OperandFactory.CreateBaseRegisterMemoryOperand(RegisterIndex.Sp, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
|
|
|
// Regular case: [reg]
|
2025-04-14 22:08:50 +03:00
|
|
|
return OperandFactory.CreateBaseRegisterMemoryOperand(rmIndex, operandSize);
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
case 1: // [reg + disp8]
|
2025-04-15 00:14:28 +03:00
|
|
|
if (rmIndex == RegisterIndex.Sp) // SIB + disp8 (ESP/SP)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
|
|
|
// Handle SIB byte
|
2025-04-14 01:15:26 +03:00
|
|
|
if (_decoder.CanReadByte())
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 01:15:26 +03:00
|
|
|
byte sib = _decoder.ReadByte();
|
2025-04-14 22:08:50 +03:00
|
|
|
sbyte disp8 = (sbyte)(_decoder.CanReadByte() ? _decoder.ReadByte() : 0);
|
|
|
|
return DecodeSIB(sib, (uint)disp8, is64Bit);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-14 22:08:50 +03:00
|
|
|
// Fallback for incomplete data
|
2025-04-15 00:14:28 +03:00
|
|
|
return OperandFactory.CreateBaseRegisterMemoryOperand(RegisterIndex.Sp, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2025-04-14 01:15:26 +03:00
|
|
|
if (_decoder.CanReadByte())
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 22:08:50 +03:00
|
|
|
sbyte disp8 = (sbyte)_decoder.ReadByte();
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-15 00:14:28 +03:00
|
|
|
// For EBP (BP), always create a displacement memory operand, even if displacement is 0
|
|
|
|
// This is because [EBP] with no displacement is encoded as [EBP+0]
|
|
|
|
if (disp8 == 0 && rmIndex != RegisterIndex.Bp)
|
2025-04-13 23:06:52 +03:00
|
|
|
{
|
2025-04-14 22:08:50 +03:00
|
|
|
return OperandFactory.CreateBaseRegisterMemoryOperand(rmIndex, operandSize);
|
2025-04-13 23:06:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-14 22:08:50 +03:00
|
|
|
return OperandFactory.CreateDisplacementMemoryOperand(rmIndex, disp8, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-14 22:08:50 +03:00
|
|
|
// Fallback for incomplete data
|
|
|
|
return OperandFactory.CreateBaseRegisterMemoryOperand(rmIndex, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
case 2: // [reg + disp32]
|
2025-04-15 00:14:28 +03:00
|
|
|
if (rmIndex == RegisterIndex.Sp) // SIB + disp32 (ESP/SP)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
|
|
|
// Handle SIB byte
|
2025-04-14 01:15:26 +03:00
|
|
|
if (_decoder.CanReadUInt())
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 01:15:26 +03:00
|
|
|
byte sib = _decoder.ReadByte();
|
|
|
|
uint disp32 = _decoder.ReadUInt32();
|
2025-04-12 19:18:52 +03:00
|
|
|
return DecodeSIB(sib, disp32, is64Bit);
|
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-14 22:08:50 +03:00
|
|
|
// Fallback for incomplete data
|
2025-04-15 00:14:28 +03:00
|
|
|
return OperandFactory.CreateBaseRegisterMemoryOperand(RegisterIndex.Sp, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2025-04-14 01:15:26 +03:00
|
|
|
if (_decoder.CanReadUInt())
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 01:15:26 +03:00
|
|
|
uint disp32 = _decoder.ReadUInt32();
|
|
|
|
|
2025-04-15 00:14:28 +03:00
|
|
|
// For EBP (BP), always create a displacement memory operand, even if displacement is 0
|
|
|
|
// This is because [EBP] with no displacement is encoded as [EBP+disp]
|
|
|
|
if (rmIndex == RegisterIndex.Bp)
|
|
|
|
{
|
|
|
|
return OperandFactory.CreateDisplacementMemoryOperand(rmIndex, (int)disp32, operandSize);
|
|
|
|
}
|
|
|
|
|
2025-04-13 23:06:52 +03:00
|
|
|
// Only show displacement if it's not zero
|
|
|
|
if (disp32 == 0)
|
|
|
|
{
|
2025-04-14 22:08:50 +03:00
|
|
|
return OperandFactory.CreateBaseRegisterMemoryOperand(rmIndex, operandSize);
|
2025-04-13 23:06:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-14 22:08:50 +03:00
|
|
|
return OperandFactory.CreateDisplacementMemoryOperand(rmIndex, (int)disp32, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-14 22:08:50 +03:00
|
|
|
// Fallback for incomplete data
|
|
|
|
return OperandFactory.CreateBaseRegisterMemoryOperand(rmIndex, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-13 23:06:52 +03:00
|
|
|
case 3: // reg (direct register access)
|
2025-04-14 22:08:50 +03:00
|
|
|
return OperandFactory.CreateRegisterOperand(rmIndex, operandSize);
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
default:
|
2025-04-14 22:08:50 +03:00
|
|
|
// Fallback for invalid mod value
|
|
|
|
return OperandFactory.CreateRegisterOperand(RegisterIndex.A, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-15 00:14:28 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Peaks a ModR/M byte and returns the raw field values, without advancing position
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>A tuple containing the raw mod, reg, and rm fields from the ModR/M byte</returns>
|
2025-04-15 02:42:47 +03:00
|
|
|
public byte PeakModRMReg()
|
2025-04-15 00:14:28 +03:00
|
|
|
{
|
|
|
|
if (!_decoder.CanReadByte())
|
|
|
|
{
|
2025-04-15 02:42:47 +03:00
|
|
|
return 0;
|
2025-04-15 00:14:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
byte modRM = _decoder.PeakByte();
|
|
|
|
|
|
|
|
// Extract fields from ModR/M byte
|
|
|
|
byte regIndex = (byte)((modRM & REG_MASK) >> 3); // Middle 3 bits (bits 3-5)
|
|
|
|
|
2025-04-15 02:42:47 +03:00
|
|
|
return regIndex;
|
2025-04-15 00:14:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Reads a ModR/M byte and returns the raw field values
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>A tuple containing the raw mod, reg, and rm fields from the ModR/M byte</returns>
|
|
|
|
public (byte mod, byte reg, byte rm) ReadModRMRaw()
|
|
|
|
{
|
|
|
|
if (!_decoder.CanReadByte())
|
|
|
|
{
|
|
|
|
return (0, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
byte modRM = _decoder.ReadByte();
|
|
|
|
|
|
|
|
// Extract fields from ModR/M byte
|
|
|
|
byte mod = (byte)((modRM & MOD_MASK) >> 6); // Top 2 bits (bits 6-7)
|
|
|
|
byte regIndex = (byte)((modRM & REG_MASK) >> 3); // Middle 3 bits (bits 3-5)
|
|
|
|
byte rmIndex = (byte)(modRM & RM_MASK); // Bottom 3 bits (bits 0-2)
|
|
|
|
|
|
|
|
return (mod, regIndex, rmIndex);
|
|
|
|
}
|
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
/// <summary>
|
2025-04-15 02:29:32 +03:00
|
|
|
/// Reads and decodes a ModR/M byte for standard 32-bit operands
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>A tuple containing the mod, reg, rm fields and the decoded operand</returns>
|
|
|
|
public (byte mod, RegisterIndex reg, RegisterIndex rm, Operand operand) ReadModRM()
|
|
|
|
{
|
2025-04-16 01:10:33 +03:00
|
|
|
return ReadModRMInternal(false);
|
2025-04-15 02:29:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Reads and decodes a ModR/M byte for 64-bit operands
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>A tuple containing the mod, reg, rm fields and the decoded operand</returns>
|
|
|
|
public (byte mod, RegisterIndex reg, RegisterIndex rm, Operand operand) ReadModRM64()
|
|
|
|
{
|
2025-04-16 01:10:33 +03:00
|
|
|
return ReadModRMInternal(true);
|
2025-04-15 02:29:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Reads and decodes a ModR/M byte for 8-bit operands
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>A tuple containing the mod, reg, rm fields and the decoded operand</returns>
|
2025-04-16 01:10:33 +03:00
|
|
|
public (byte mod, RegisterIndex8 reg, RegisterIndex8 rm, Operand operand) ReadModRM8()
|
2025-04-15 02:29:32 +03:00
|
|
|
{
|
2025-04-16 01:10:33 +03:00
|
|
|
return ReadModRM8Internal();
|
2025-04-15 02:29:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
2025-04-16 01:10:33 +03:00
|
|
|
/// Internal implementation for reading and decoding a ModR/M byte for standard 32-bit or 64-bit operands
|
2025-04-12 19:18:52 +03:00
|
|
|
/// </summary>
|
|
|
|
/// <param name="is64Bit">True if the operand is 64-bit</param>
|
2025-04-14 22:08:50 +03:00
|
|
|
/// <returns>A tuple containing the mod, reg, rm fields and the decoded operand</returns>
|
2025-04-16 01:10:33 +03:00
|
|
|
private (byte mod, RegisterIndex reg, RegisterIndex rm, Operand operand) ReadModRMInternal(bool is64Bit)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 01:15:26 +03:00
|
|
|
if (!_decoder.CanReadByte())
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-16 01:10:33 +03:00
|
|
|
return (0, RegisterIndex.A, RegisterIndex.A, OperandFactory.CreateRegisterOperand(RegisterIndex.A, is64Bit ? 64 : 32));
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
|
|
|
byte modRM = _decoder.ReadByte();
|
|
|
|
|
2025-04-13 23:06:52 +03:00
|
|
|
// Extract fields from ModR/M byte
|
2025-04-14 22:08:50 +03:00
|
|
|
byte mod = (byte)((modRM & MOD_MASK) >> 6);
|
2025-04-15 00:14:28 +03:00
|
|
|
byte regIndex = (byte)((modRM & REG_MASK) >> 3);
|
|
|
|
byte rmIndex = (byte)(modRM & RM_MASK);
|
|
|
|
|
|
|
|
// Map the ModR/M register indices to RegisterIndex enum values
|
2025-04-16 01:10:33 +03:00
|
|
|
RegisterIndex reg = MapModRMToRegisterIndex(regIndex);
|
|
|
|
RegisterIndex rm = MapModRMToRegisterIndex(rmIndex);
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-15 02:29:32 +03:00
|
|
|
// Create the operand based on the mod and rm fields
|
2025-04-14 22:08:50 +03:00
|
|
|
Operand operand = DecodeModRM(mod, rm, is64Bit);
|
2025-04-16 01:10:33 +03:00
|
|
|
|
|
|
|
return (mod, reg, rm, operand);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Internal implementation for reading and decoding a ModR/M byte for 8-bit operands
|
|
|
|
/// </summary>
|
|
|
|
/// <returns>A tuple containing the mod, reg, rm fields and the decoded operand</returns>
|
|
|
|
private (byte mod, RegisterIndex8 reg, RegisterIndex8 rm, Operand operand) ReadModRM8Internal()
|
|
|
|
{
|
|
|
|
if (!_decoder.CanReadByte())
|
|
|
|
{
|
|
|
|
return (0, RegisterIndex8.AL, RegisterIndex8.AL, OperandFactory.CreateRegisterOperand8(RegisterIndex8.AL));
|
|
|
|
}
|
|
|
|
|
|
|
|
byte modRM = _decoder.ReadByte();
|
|
|
|
|
|
|
|
// Extract fields from ModR/M byte
|
|
|
|
byte mod = (byte)((modRM & MOD_MASK) >> 6);
|
|
|
|
byte regIndex = (byte)((modRM & REG_MASK) >> 3);
|
|
|
|
byte rmIndex = (byte)(modRM & RM_MASK);
|
|
|
|
|
|
|
|
// Map the ModR/M register indices to RegisterIndex8 enum values
|
|
|
|
RegisterIndex8 reg = MapModRMToRegisterIndex8(regIndex);
|
|
|
|
RegisterIndex8 rm = MapModRMToRegisterIndex8(rmIndex);
|
|
|
|
|
|
|
|
// Create the operand based on the mod and rm fields
|
|
|
|
Operand operand;
|
2025-04-15 02:29:32 +03:00
|
|
|
|
2025-04-16 01:10:33 +03:00
|
|
|
if (mod == 3) // Register operand
|
2025-04-15 02:29:32 +03:00
|
|
|
{
|
2025-04-16 01:10:33 +03:00
|
|
|
// For register operands, create an 8-bit register operand
|
|
|
|
operand = OperandFactory.CreateRegisterOperand8(rm);
|
|
|
|
}
|
|
|
|
else // Memory operand
|
|
|
|
{
|
|
|
|
// For memory operands, we need to map the RegisterIndex8 to RegisterIndex for base registers
|
|
|
|
RegisterIndex rmRegIndex = MapRegister8ToBaseRegister(rm);
|
|
|
|
operand = DecodeModRM(mod, rmRegIndex, false);
|
|
|
|
operand.Size = 8; // Set size to 8 bits
|
2025-04-15 02:29:32 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
return (mod, reg, rm, operand);
|
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Decodes a SIB byte
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="sib">The SIB byte</param>
|
|
|
|
/// <param name="displacement">The displacement value</param>
|
|
|
|
/// <param name="is64Bit">True if the operand is 64-bit</param>
|
2025-04-14 22:08:50 +03:00
|
|
|
/// <returns>The decoded SIB operand</returns>
|
|
|
|
private Operand DecodeSIB(byte sib, uint displacement, bool is64Bit)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 22:08:50 +03:00
|
|
|
int operandSize = is64Bit ? 64 : 32;
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-13 23:06:52 +03:00
|
|
|
// Extract fields from SIB byte
|
2025-04-14 22:08:50 +03:00
|
|
|
byte scale = (byte)((sib & SIB_SCALE_MASK) >> 6);
|
2025-04-15 00:14:28 +03:00
|
|
|
int indexIndex = (sib & SIB_INDEX_MASK) >> 3;
|
|
|
|
int baseIndex = sib & SIB_BASE_MASK;
|
|
|
|
|
|
|
|
// Map the SIB register indices to RegisterIndex enum values
|
|
|
|
RegisterIndex index = MapModRMToRegisterIndex(indexIndex);
|
|
|
|
RegisterIndex @base = MapModRMToRegisterIndex(baseIndex);
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-13 23:06:52 +03:00
|
|
|
// Special case: ESP/SP (4) in index field means no index register
|
2025-04-15 00:14:28 +03:00
|
|
|
if (index == RegisterIndex.Sp)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-13 23:06:52 +03:00
|
|
|
// Special case: EBP/BP (5) in base field with no displacement means disp32 only
|
2025-04-15 00:14:28 +03:00
|
|
|
if (@base == RegisterIndex.Bp && displacement == 0)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 01:15:26 +03:00
|
|
|
if (_decoder.CanReadUInt())
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 01:15:26 +03:00
|
|
|
uint disp32 = _decoder.ReadUInt32();
|
2025-04-15 02:29:32 +03:00
|
|
|
|
2025-04-15 23:54:51 +03:00
|
|
|
// When both index is ESP (no index) and base is EBP with disp32,
|
|
|
|
// this is a direct memory reference [disp32]
|
|
|
|
return OperandFactory.CreateDirectMemoryOperand(disp32, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-14 22:08:50 +03:00
|
|
|
// Fallback for incomplete data
|
2025-04-15 23:54:51 +03:00
|
|
|
return OperandFactory.CreateDirectMemoryOperand(0, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-15 23:54:51 +03:00
|
|
|
// When index is ESP (no index), we just have a base register with optional displacement
|
2025-04-14 01:15:26 +03:00
|
|
|
if (displacement == 0)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
2025-04-14 22:08:50 +03:00
|
|
|
return OperandFactory.CreateBaseRegisterMemoryOperand(@base, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-14 22:08:50 +03:00
|
|
|
return OperandFactory.CreateDisplacementMemoryOperand(@base, (int)displacement, operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-15 02:29:32 +03:00
|
|
|
// Special case: EBP/BP (5) in base field with no displacement means disp32 only
|
|
|
|
if (@base == RegisterIndex.Bp && displacement == 0)
|
|
|
|
{
|
|
|
|
if (_decoder.CanReadUInt())
|
|
|
|
{
|
|
|
|
uint disp32 = _decoder.ReadUInt32();
|
|
|
|
int scaleValue = 1 << scale; // 1, 2, 4, or 8
|
|
|
|
|
2025-04-15 23:54:51 +03:00
|
|
|
// If we have a direct memory reference with a specific displacement,
|
|
|
|
// use a direct memory operand instead of a scaled index memory operand
|
|
|
|
if (disp32 > 0 && index == RegisterIndex.Sp)
|
|
|
|
{
|
|
|
|
return OperandFactory.CreateDirectMemoryOperand(disp32, operandSize);
|
|
|
|
}
|
|
|
|
|
2025-04-15 02:29:32 +03:00
|
|
|
// Create a scaled index memory operand with displacement but no base register
|
|
|
|
return OperandFactory.CreateScaledIndexMemoryOperand(
|
|
|
|
index,
|
|
|
|
scaleValue,
|
|
|
|
null,
|
|
|
|
(int)disp32,
|
|
|
|
operandSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fallback for incomplete data
|
|
|
|
return OperandFactory.CreateScaledIndexMemoryOperand(
|
|
|
|
index,
|
|
|
|
1 << scale,
|
|
|
|
null,
|
|
|
|
0,
|
|
|
|
operandSize);
|
|
|
|
}
|
|
|
|
|
2025-04-13 23:06:52 +03:00
|
|
|
// Normal case with base and index registers
|
2025-04-12 19:18:52 +03:00
|
|
|
int scaleFactor = 1 << scale; // 1, 2, 4, or 8
|
2025-04-14 01:15:26 +03:00
|
|
|
|
2025-04-14 22:08:50 +03:00
|
|
|
// Create a scaled index memory operand
|
|
|
|
return OperandFactory.CreateScaledIndexMemoryOperand(
|
|
|
|
index,
|
|
|
|
scaleFactor,
|
|
|
|
@base,
|
|
|
|
(int)displacement,
|
|
|
|
operandSize);
|
2025-04-12 19:18:52 +03:00
|
|
|
}
|
2025-04-13 23:06:52 +03:00
|
|
|
|
2025-04-12 19:18:52 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Gets the register name based on the register index and size
|
|
|
|
/// </summary>
|
2025-04-13 23:06:52 +03:00
|
|
|
/// <param name="regIndex">The register index as RegisterIndex enum</param>
|
2025-04-16 01:10:33 +03:00
|
|
|
/// <param name="size">The register size (16 or 32 bits)</param>
|
2025-04-12 19:18:52 +03:00
|
|
|
/// <returns>The register name</returns>
|
2025-04-13 23:06:52 +03:00
|
|
|
public static string GetRegisterName(RegisterIndex regIndex, int size)
|
2025-04-12 19:18:52 +03:00
|
|
|
{
|
|
|
|
return size switch
|
|
|
|
{
|
2025-04-15 23:54:51 +03:00
|
|
|
16 => RegisterNames16[(int)regIndex],
|
|
|
|
32 => RegisterNames32[(int)regIndex],
|
|
|
|
64 => RegisterNames32[(int)regIndex], // For now, reuse 32-bit names for 64-bit
|
|
|
|
_ => "unknown"
|
2025-04-12 19:18:52 +03:00
|
|
|
};
|
|
|
|
}
|
2025-04-16 01:10:33 +03:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the 8-bit register name based on the RegisterIndex8 enum value
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="regIndex8">The register index as RegisterIndex8 enum</param>
|
|
|
|
/// <returns>The 8-bit register name</returns>
|
|
|
|
public static string GetRegisterName(RegisterIndex8 regIndex8)
|
|
|
|
{
|
|
|
|
return regIndex8.ToString().ToLower();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Maps a RegisterIndex8 enum value to the corresponding RegisterIndex enum value for base registers
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="regIndex8">The RegisterIndex8 enum value</param>
|
|
|
|
/// <returns>The corresponding RegisterIndex enum value</returns>
|
|
|
|
private RegisterIndex MapRegister8ToBaseRegister(RegisterIndex8 regIndex8)
|
|
|
|
{
|
|
|
|
// Map 8-bit register indices to their corresponding 32-bit register indices
|
|
|
|
return regIndex8 switch
|
|
|
|
{
|
|
|
|
RegisterIndex8.AL => RegisterIndex.A,
|
|
|
|
RegisterIndex8.CL => RegisterIndex.C,
|
|
|
|
RegisterIndex8.DL => RegisterIndex.D,
|
|
|
|
RegisterIndex8.BL => RegisterIndex.B,
|
|
|
|
RegisterIndex8.AH => RegisterIndex.A,
|
|
|
|
RegisterIndex8.CH => RegisterIndex.C,
|
|
|
|
RegisterIndex8.DH => RegisterIndex.D,
|
|
|
|
RegisterIndex8.BH => RegisterIndex.B,
|
|
|
|
_ => RegisterIndex.A // Default to EAX
|
|
|
|
};
|
|
|
|
}
|
2025-04-14 01:15:26 +03:00
|
|
|
}
|