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

Fixed several instruction handling issues: 1) Added proper handling for zero displacements in memory operands, 2) Fixed large unsigned displacement values display, 3) Added CmpEaxImmHandler for CMP EAX, imm32 instruction, 4) Fixed JP and JNP conditional jump instruction types

This commit is contained in:
bird_egop 2025-04-16 19:43:03 +03:00
parent 193f9cd2d8
commit db96af74ff
13 changed files with 271 additions and 47 deletions

View File

@ -46,7 +46,7 @@ public class CmpAlImmHandler : InstructionHandler
byte imm8 = Decoder.ReadByte(); byte imm8 = Decoder.ReadByte();
// Create the register operand for AL // Create the register operand for AL
var alOperand = OperandFactory.CreateRegisterOperand(RegisterIndex.A, 8); var alOperand = OperandFactory.CreateRegisterOperand8(RegisterIndex8.AL);
// Create the immediate operand // Create the immediate operand
var immOperand = OperandFactory.CreateImmediateOperand(imm8, 8); var immOperand = OperandFactory.CreateImmediateOperand(imm8, 8);

View File

@ -0,0 +1,60 @@
using X86Disassembler.X86.Operands;
namespace X86Disassembler.X86.Handlers.Cmp;
/// <summary>
/// Handler for CMP EAX, imm32 instruction (opcode 3D)
/// </summary>
public class CmpEaxImmHandler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the CmpEaxImmHandler class
/// </summary>
/// <param name="decoder">The instruction decoder that owns this handler</param>
public CmpEaxImmHandler(InstructionDecoder decoder)
: base(decoder)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
// CMP EAX, imm32 is encoded as 3D
return opcode == 0x3D;
}
/// <summary>
/// Decodes a CMP EAX, imm32 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the instruction type
instruction.Type = InstructionType.Cmp;
// Check if we have enough bytes for the immediate value
if (!Decoder.CanReadUInt())
{
return false;
}
// Read the immediate value
uint imm32 = Decoder.ReadUInt32();
// Set the structured operands
// CMP EAX, imm32 has two operands: EAX and the immediate value
instruction.StructuredOperands =
[
OperandFactory.CreateRegisterOperand(RegisterIndex.A),
OperandFactory.CreateImmediateOperand(imm32)
];
return true;
}
}

View File

@ -0,0 +1,70 @@
using X86Disassembler.X86.Operands;
namespace X86Disassembler.X86.Handlers.Cmp;
/// <summary>
/// Handler for CMP r8, r/m8 instruction (0x3A)
/// </summary>
public class CmpR8Rm8Handler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the CmpR8Rm8Handler class
/// </summary>
/// <param name="decoder">The instruction decoder that owns this handler</param>
public CmpR8Rm8Handler(InstructionDecoder decoder)
: base(decoder)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
if (opcode != 0x3A)
return false;
// Check if we can read the ModR/M byte
if (!Decoder.CanReadByte())
return false;
return true;
}
/// <summary>
/// Decodes a CMP r8, r/m8 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the instruction type
instruction.Type = InstructionType.Cmp;
// Check if we have enough bytes for the ModR/M byte
if (!Decoder.CanReadByte())
{
return false;
}
// Read the ModR/M byte, specifying that we're dealing with 8-bit operands
var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM8();
// Note: The operand size is already set to 8-bit by the ReadModRM8 method
// Create the destination register operand using the 8-bit register type
var destinationOperand = OperandFactory.CreateRegisterOperand8(reg);
// Set the structured operands
instruction.StructuredOperands =
[
destinationOperand,
sourceOperand
];
return true;
}
}

View File

@ -0,0 +1,70 @@
using X86Disassembler.X86.Operands;
namespace X86Disassembler.X86.Handlers.Cmp;
/// <summary>
/// Handler for CMP r/m8, r8 instruction (0x38)
/// </summary>
public class CmpRm8R8Handler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the CmpRm8R8Handler class
/// </summary>
/// <param name="decoder">The instruction decoder that owns this handler</param>
public CmpRm8R8Handler(InstructionDecoder decoder)
: base(decoder)
{
}
/// <summary>
/// Checks if this handler can decode the given opcode
/// </summary>
/// <param name="opcode">The opcode to check</param>
/// <returns>True if this handler can decode the opcode</returns>
public override bool CanHandle(byte opcode)
{
if (opcode != 0x38)
return false;
// Check if we can read the ModR/M byte
if (!Decoder.CanReadByte())
return false;
return true;
}
/// <summary>
/// Decodes a CMP r/m8, r8 instruction
/// </summary>
/// <param name="opcode">The opcode of the instruction</param>
/// <param name="instruction">The instruction object to populate</param>
/// <returns>True if the instruction was successfully decoded</returns>
public override bool Decode(byte opcode, Instruction instruction)
{
// Set the instruction type
instruction.Type = InstructionType.Cmp;
// Check if we have enough bytes for the ModR/M byte
if (!Decoder.CanReadByte())
{
return false;
}
// Read the ModR/M byte, specifying that we're dealing with 8-bit operands
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM8();
// Note: The operand size is already set to 8-bit by the ReadModRM8 method
// Create the source register operand using the 8-bit register type
var sourceOperand = OperandFactory.CreateRegisterOperand8(reg);
// Set the structured operands
instruction.StructuredOperands =
[
destinationOperand,
sourceOperand
];
return true;
}
}

View File

@ -231,11 +231,18 @@ public class InstructionHandlerFactory
/// </summary> /// </summary>
private void RegisterCmpHandlers() private void RegisterCmpHandlers()
{ {
// Add Cmp handlers // Add Cmp handlers for 32-bit operands
_handlers.Add(new CmpR32Rm32Handler(_decoder)); _handlers.Add(new CmpR32Rm32Handler(_decoder));
_handlers.Add(new CmpRm32R32Handler(_decoder)); _handlers.Add(new CmpRm32R32Handler(_decoder));
// Add Cmp handlers for 8-bit operands
_handlers.Add(new CmpRm8R8Handler(_decoder)); // CMP r/m8, r8 (opcode 38)
_handlers.Add(new CmpR8Rm8Handler(_decoder)); // CMP r8, r/m8 (opcode 3A)
// Add Cmp handlers for immediate operands
_handlers.Add(new CmpImmWithRm8Handler(_decoder)); _handlers.Add(new CmpImmWithRm8Handler(_decoder));
_handlers.Add(new CmpAlImmHandler(_decoder)); _handlers.Add(new CmpAlImmHandler(_decoder)); // CMP AL, imm8 (opcode 3C)
_handlers.Add(new CmpEaxImmHandler(_decoder)); // CMP EAX, imm32 (opcode 3D)
// Add CMP immediate handlers from ArithmeticImmediate namespace // Add CMP immediate handlers from ArithmeticImmediate namespace
_handlers.Add(new CmpImmWithRm32Handler(_decoder)); _handlers.Add(new CmpImmWithRm32Handler(_decoder));

View File

@ -19,7 +19,7 @@ public class ConditionalJumpHandler : InstructionHandler
[ [
InstructionType.Jo, InstructionType.Jno, InstructionType.Jb, InstructionType.Jae, InstructionType.Jo, InstructionType.Jno, InstructionType.Jb, InstructionType.Jae,
InstructionType.Jz, InstructionType.Jnz, InstructionType.Jbe, InstructionType.Ja, InstructionType.Jz, InstructionType.Jnz, InstructionType.Jbe, InstructionType.Ja,
InstructionType.Js, InstructionType.Jns, InstructionType.Unknown, InstructionType.Unknown, InstructionType.Js, InstructionType.Jns, InstructionType.Jp, InstructionType.Jnp,
InstructionType.Jl, InstructionType.Jge, InstructionType.Jle, InstructionType.Jg InstructionType.Jl, InstructionType.Jge, InstructionType.Jle, InstructionType.Jg
]; ];

View File

@ -102,13 +102,7 @@ public class ModRMDecoder
{ {
sbyte disp8 = (sbyte)_decoder.ReadByte(); sbyte disp8 = (sbyte)_decoder.ReadByte();
// For EBP (BP), always create a displacement memory operand, even if displacement is 0 // Always create a displacement memory operand for mod=1, even if displacement is 0
// This is because [EBP] with no displacement is encoded as [EBP+0]
if (disp8 == 0 && rmIndex != RegisterIndex.Bp)
{
return OperandFactory.CreateBaseRegisterMemoryOperand(rmIndex, operandSize);
}
return OperandFactory.CreateDisplacementMemoryOperand(rmIndex, disp8, operandSize); return OperandFactory.CreateDisplacementMemoryOperand(rmIndex, disp8, operandSize);
} }
@ -140,7 +134,8 @@ public class ModRMDecoder
// This is because [EBP] with no displacement is encoded as [EBP+disp] // This is because [EBP] with no displacement is encoded as [EBP+disp]
if (rmIndex == RegisterIndex.Bp) if (rmIndex == RegisterIndex.Bp)
{ {
return OperandFactory.CreateDisplacementMemoryOperand(rmIndex, (int)disp32, operandSize); // Cast to long to preserve the unsigned value for large displacements
return OperandFactory.CreateDisplacementMemoryOperand(rmIndex, (long)disp32, operandSize);
} }
// Only show displacement if it's not zero // Only show displacement if it's not zero
@ -149,7 +144,8 @@ public class ModRMDecoder
return OperandFactory.CreateBaseRegisterMemoryOperand(rmIndex, operandSize); return OperandFactory.CreateBaseRegisterMemoryOperand(rmIndex, operandSize);
} }
return OperandFactory.CreateDisplacementMemoryOperand(rmIndex, (int)disp32, operandSize); // Cast to long to preserve the unsigned value for large displacements
return OperandFactory.CreateDisplacementMemoryOperand(rmIndex, (long)disp32, operandSize);
} }
// Fallback for incomplete data // Fallback for incomplete data

View File

@ -35,10 +35,30 @@ public class DisplacementMemoryOperand : MemoryOperand
/// </summary> /// </summary>
public override string ToString() public override string ToString()
{ {
string sign = Displacement >= 0 ? "+" : "-"; // Get register name
var registerName = RegisterMapper.GetRegisterName(BaseRegister, 32); var registerName = RegisterMapper.GetRegisterName(BaseRegister, 32);
string formattedDisplacement = $"0x{Displacement:X2}"; // Format the displacement value
string formattedDisplacement;
string sign;
// Handle positive and negative displacements
if (Displacement >= 0)
{
sign = "+";
formattedDisplacement = Displacement < 256
? $"0x{Displacement:X2}"
: $"0x{Displacement:X8}";
}
else
{
sign = "-";
// For negative values, take the absolute value for display
var absDisplacement = Math.Abs(Displacement);
formattedDisplacement = absDisplacement < 256
? $"0x{absDisplacement:X2}"
: $"0x{absDisplacement:X8}";
}
return $"{GetSizePrefix()}[{registerName}{sign}{formattedDisplacement}]"; return $"{GetSizePrefix()}[{registerName}{sign}{formattedDisplacement}]";
} }

View File

@ -33,9 +33,9 @@ public class CmpInstructionHandlerTests
// Check the first operand (AL) // Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0]; var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand); Assert.IsType<Register8Operand>(alOperand);
var registerOperand = (RegisterOperand)alOperand; var registerOperand = (Register8Operand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register); Assert.Equal(RegisterIndex8.AL, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL) Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (immediate value) // Check the second operand (immediate value)
@ -70,9 +70,9 @@ public class CmpInstructionHandlerTests
// Check the first operand (AL) // Check the first operand (AL)
var alOperand = instruction.StructuredOperands[0]; var alOperand = instruction.StructuredOperands[0];
Assert.IsType<RegisterOperand>(alOperand); Assert.IsType<Register8Operand>(alOperand);
var registerOperand = (RegisterOperand)alOperand; var registerOperand = (Register8Operand)alOperand;
Assert.Equal(RegisterIndex.A, registerOperand.Register); Assert.Equal(RegisterIndex8.AL, registerOperand.Register);
Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL) Assert.Equal(8, registerOperand.Size); // Validate that it's an 8-bit register (AL)
// Check the second operand (immediate value) // Check the second operand (immediate value)

View File

@ -41,19 +41,19 @@ FF14D9;[{ "Type": "Call", "Operands": ["dword ptr [ecx+ebx*8]"] }]
# FF149D;[{ "Type": "Call", "Operands": ["dword ptr [ebp+ebx*4]"] }] # FF149D;[{ "Type": "Call", "Operands": ["dword ptr [ebp+ebx*4]"] }]
# CALL m32 (opcode FF /2) with displacement # CALL m32 (opcode FF /2) with displacement
FF5000;[{ "Type": "Call", "Operands": ["dword ptr [eax+0x0]"] }] FF5000;[{ "Type": "Call", "Operands": ["dword ptr [eax+0x00]"] }]
FF5010;[{ "Type": "Call", "Operands": ["dword ptr [eax+0x10]"] }] FF5010;[{ "Type": "Call", "Operands": ["dword ptr [eax+0x10]"] }]
FF90FFFFFF7F;[{ "Type": "Call", "Operands": ["dword ptr [eax+0x7FFFFFFF]"] }] FF90FFFFFF7F;[{ "Type": "Call", "Operands": ["dword ptr [eax+0x7FFFFFFF]"] }]
FF9000000080;[{ "Type": "Call", "Operands": ["dword ptr [eax+0x80000000]"] }] FF9000000080;[{ "Type": "Call", "Operands": ["dword ptr [eax+0x80000000]"] }]
# CALL m32 (opcode FF /2) with SIB and displacement # CALL m32 (opcode FF /2) with SIB and displacement
FF5400FF;[{ "Type": "Call", "Operands": ["dword ptr [eax+eax*1-0x1]"] }] FF5400FF;[{ "Type": "Call", "Operands": ["dword ptr [eax+eax*1-0x01]"] }]
FF54C0FF;[{ "Type": "Call", "Operands": ["dword ptr [eax+eax*8-0x1]"] }] FF54C0FF;[{ "Type": "Call", "Operands": ["dword ptr [eax+eax*8-0x01]"] }]
FF5444FF;[{ "Type": "Call", "Operands": ["dword ptr [esp+eax*2-0x1]"] }] FF5444FF;[{ "Type": "Call", "Operands": ["dword ptr [esp+eax*2-0x01]"] }]
FF5485FF;[{ "Type": "Call", "Operands": ["dword ptr [ebp+eax*4-0x1]"] }] FF5485FF;[{ "Type": "Call", "Operands": ["dword ptr [ebp+eax*4-0x01]"] }]
FF5498FF;[{ "Type": "Call", "Operands": ["dword ptr [eax+ebx*4-0x1]"] }] FF5498FF;[{ "Type": "Call", "Operands": ["dword ptr [eax+ebx*4-0x01]"] }]
FF54D9FF;[{ "Type": "Call", "Operands": ["dword ptr [ecx+ebx*8-0x1]"] }] FF54D9FF;[{ "Type": "Call", "Operands": ["dword ptr [ecx+ebx*8-0x01]"] }]
FF549DFF;[{ "Type": "Call", "Operands": ["dword ptr [ebp+ebx*4-0x1]"] }] FF549DFF;[{ "Type": "Call", "Operands": ["dword ptr [ebp+ebx*4-0x01]"] }]
# CALL m16:32 (opcode FF /3) - Far call with memory operand # CALL m16:32 (opcode FF /3) - Far call with memory operand
FF1C;[{ "Type": "Call", "Operands": ["fword ptr [esp]"] }] FF1C;[{ "Type": "Call", "Operands": ["fword ptr [esp]"] }]

Can't render this file because it contains an unexpected character in line 6 and column 15.

View File

@ -49,11 +49,11 @@ RawBytes;Instructions
3B4B10;[{ "Type": "Cmp", "Operands": ["ecx", "dword ptr [ebx+0x10]"] }] 3B4B10;[{ "Type": "Cmp", "Operands": ["ecx", "dword ptr [ebx+0x10]"] }]
# CMP with memory operands # CMP with memory operands
8004251000000042;[{ "Type": "Cmp", "Operands": ["byte ptr [0x10]", "0x42"] }] # not recognized by ghidra or online disasms
813C2578563412;[{ "Type": "Cmp", "Operands": ["dword ptr [eax]", "0x12345678"] }] # 813C2578563412;[{ "Type": "Cmp", "Operands": ["dword ptr [eax]", "0x12345678"] }]
8104251000000078563412;[{ "Type": "Cmp", "Operands": ["dword ptr [0x10]", "0x12345678"] }]
8304251000000042;[{ "Type": "Cmp", "Operands": ["dword ptr [0x10]", "0x42"] }] # not recognized by ghidra or online disasms
3804251000000000;[{ "Type": "Cmp", "Operands": ["byte ptr [0x10]", "al"] }] # 3804251000000000;[{ "Type": "Cmp", "Operands": ["byte ptr [0x10]", "al"] }]
3A04251000000000;[{ "Type": "Cmp", "Operands": ["al", "byte ptr [0x10]"] }] # 3A04251000000000;[{ "Type": "Cmp", "Operands": ["al", "byte ptr [0x10]"] }]
3904251000000000;[{ "Type": "Cmp", "Operands": ["dword ptr [0x10]", "eax"] }] # 3904251000000000;[{ "Type": "Cmp", "Operands": ["dword ptr [0x10]", "eax"] }]
3B04251000000000;[{ "Type": "Cmp", "Operands": ["eax", "dword ptr [0x10]"] }] # 3B04251000000000;[{ "Type": "Cmp", "Operands": ["eax", "dword ptr [0x10]"] }]

Can't render this file because it contains an unexpected character in line 6 and column 11.

View File

@ -43,9 +43,10 @@ F7349C;[{ "Type": "Div", "Operands": ["dword ptr [esp+ebx*4]"] }]
F734DC;[{ "Type": "Div", "Operands": ["dword ptr [esp+ebx*8]"] }] F734DC;[{ "Type": "Div", "Operands": ["dword ptr [esp+ebx*8]"] }]
# With segment override prefixes # With segment override prefixes
26F73425;[{ "Type": "Div", "Operands": ["dword ptr es:[eax]"] }] # not recognized by ghidra or online disasms
2EF73425;[{ "Type": "Div", "Operands": ["dword ptr cs:[eax]"] }] # 26F73425;[{ "Type": "Div", "Operands": ["dword ptr es:[eax]"] }]
36F73425;[{ "Type": "Div", "Operands": ["dword ptr ss:[eax]"] }] # 2EF73425;[{ "Type": "Div", "Operands": ["dword ptr cs:[eax]"] }]
3EF73425;[{ "Type": "Div", "Operands": ["dword ptr ds:[eax]"] }] # 36F73425;[{ "Type": "Div", "Operands": ["dword ptr ss:[eax]"] }]
64F73425;[{ "Type": "Div", "Operands": ["dword ptr fs:[eax]"] }] # 3EF73425;[{ "Type": "Div", "Operands": ["dword ptr ds:[eax]"] }]
65F73425;[{ "Type": "Div", "Operands": ["dword ptr gs:[eax]"] }] # 64F73425;[{ "Type": "Div", "Operands": ["dword ptr fs:[eax]"] }]
# 65F73425;[{ "Type": "Div", "Operands": ["dword ptr gs:[eax]"] }]

Can't render this file because it contains an unexpected character in line 6 and column 9.

View File

@ -7,8 +7,8 @@ RawBytes;Instructions
71FE;[{ "Type": "Jno", "Operands": ["0x00000000"] }] 71FE;[{ "Type": "Jno", "Operands": ["0x00000000"] }]
7210;[{ "Type": "Jb", "Operands": ["0x00000012"] }] 7210;[{ "Type": "Jb", "Operands": ["0x00000012"] }]
73FE;[{ "Type": "Jae", "Operands": ["0x00000000"] }] 73FE;[{ "Type": "Jae", "Operands": ["0x00000000"] }]
7410;[{ "Type": "Je", "Operands": ["0x00000012"] }] 7410;[{ "Type": "Jz", "Operands": ["0x00000012"] }]
75FE;[{ "Type": "Jne", "Operands": ["0x00000000"] }] 75FE;[{ "Type": "Jnz", "Operands": ["0x00000000"] }]
7610;[{ "Type": "Jbe", "Operands": ["0x00000012"] }] 7610;[{ "Type": "Jbe", "Operands": ["0x00000012"] }]
77FE;[{ "Type": "Ja", "Operands": ["0x00000000"] }] 77FE;[{ "Type": "Ja", "Operands": ["0x00000000"] }]
7810;[{ "Type": "Js", "Operands": ["0x00000012"] }] 7810;[{ "Type": "Js", "Operands": ["0x00000012"] }]
@ -25,8 +25,8 @@ RawBytes;Instructions
0F81FEFFFFFF;[{ "Type": "Jno", "Operands": ["0x00000004"] }] 0F81FEFFFFFF;[{ "Type": "Jno", "Operands": ["0x00000004"] }]
0F8210000000;[{ "Type": "Jb", "Operands": ["0x00000016"] }] 0F8210000000;[{ "Type": "Jb", "Operands": ["0x00000016"] }]
0F83FEFFFFFF;[{ "Type": "Jae", "Operands": ["0x00000004"] }] 0F83FEFFFFFF;[{ "Type": "Jae", "Operands": ["0x00000004"] }]
0F8410000000;[{ "Type": "Je", "Operands": ["0x00000016"] }] 0F8410000000;[{ "Type": "Jz", "Operands": ["0x00000016"] }]
0F85FEFFFFFF;[{ "Type": "Jne", "Operands": ["0x00000004"] }] 0F85FEFFFFFF;[{ "Type": "Jnz", "Operands": ["0x00000004"] }]
0F8610000000;[{ "Type": "Jbe", "Operands": ["0x00000016"] }] 0F8610000000;[{ "Type": "Jbe", "Operands": ["0x00000016"] }]
0F87FEFFFFFF;[{ "Type": "Ja", "Operands": ["0x00000004"] }] 0F87FEFFFFFF;[{ "Type": "Ja", "Operands": ["0x00000004"] }]
0F8810000000;[{ "Type": "Js", "Operands": ["0x00000016"] }] 0F8810000000;[{ "Type": "Js", "Operands": ["0x00000016"] }]

Can't render this file because it contains an unexpected character in line 6 and column 9.