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

Added support for far call instructions and PUSH imm16. Fixed invalid test cases in call_tests.csv and or_tests.csv

This commit is contained in:
bird_egop 2025-04-16 21:44:02 +03:00
parent 089fe4dfd4
commit fa1a7f582c
7 changed files with 304 additions and 4 deletions

View File

@ -137,8 +137,9 @@ public class InstructionHandlerFactory
private void RegisterCallHandlers() private void RegisterCallHandlers()
{ {
// Add Call handlers // Add Call handlers
_handlers.Add(new CallRel32Handler(_decoder)); _handlers.Add(new CallRel32Handler(_decoder)); // CALL rel32 (opcode E8)
_handlers.Add(new CallRm32Handler(_decoder)); _handlers.Add(new CallRm32Handler(_decoder)); // CALL r/m32 (opcode FF /2)
_handlers.Add(new CallFarPtrHandler(_decoder)); // CALL m16:32 (opcode FF /3) - Far call
} }
/// <summary> /// <summary>
@ -378,6 +379,7 @@ public class InstructionHandlerFactory
// Add PUSH immediate handlers // Add PUSH immediate handlers
_handlers.Add(new PushImm32Handler(_decoder)); // PUSH imm32 (opcode 68) _handlers.Add(new PushImm32Handler(_decoder)); // PUSH imm32 (opcode 68)
_handlers.Add(new PushImm16Handler(_decoder)); // PUSH imm16 with operand size prefix (0x66 0x68)
_handlers.Add(new PushImm8Handler(_decoder)); // PUSH imm8 (opcode 6A) _handlers.Add(new PushImm8Handler(_decoder)); // PUSH imm8 (opcode 6A)
} }

View File

@ -0,0 +1,67 @@
namespace X86Disassembler.X86.Handlers.Push;
using Operands;
/// <summary>
/// Handler for PUSH imm16 instruction with operand size prefix (0x66 0x68)
/// </summary>
public class PushImm16Handler : InstructionHandler
{
/// <summary>
/// Initializes a new instance of the PushImm16Handler class
/// </summary>
/// <param name="decoder">The instruction decoder that owns this handler</param>
public PushImm16Handler(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)
{
// Check for operand size prefix (66h) followed by PUSH imm (68h)
if (opcode != 0x68)
{
return false;
}
// Check if we have an operand size prefix
return Decoder.HasOperandSizePrefix();
}
/// <summary>
/// Decodes a PUSH imm16 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.Push;
// Check if we have enough bytes for the 16-bit immediate
if(!Decoder.CanReadUShort())
{
return false;
}
// Read the 16-bit immediate value
ushort imm16 = Decoder.ReadUInt16();
// Create an immediate operand with 16-bit size
var immOperand = new ImmediateOperand(imm16, 16);
// Set the structured operands
instruction.StructuredOperands =
[
immOperand
];
return true;
}
}

View File

@ -0,0 +1,206 @@
namespace X86Disassembler.X86.Operands;
/// <summary>
/// Represents a far pointer memory operand (m16:32) in an x86 instruction
/// </summary>
public class FarPointerOperand : MemoryOperand
{
/// <summary>
/// Gets the base register (if any)
/// </summary>
public RegisterIndex? BaseRegister { get; }
/// <summary>
/// Gets the index register (if any)
/// </summary>
public RegisterIndex? IndexRegister { get; }
/// <summary>
/// Gets the scale factor (if using an index register)
/// </summary>
public int Scale { get; }
/// <summary>
/// Gets the displacement value (if any)
/// </summary>
public long Displacement { get; }
/// <summary>
/// Gets the direct memory address (if any)
/// </summary>
public long? Address { get; }
/// <summary>
/// Initializes a new instance of the FarPointerOperand class for base register memory operands
/// </summary>
/// <param name="baseRegister">The base register</param>
/// <param name="segmentOverride">Optional segment override</param>
public FarPointerOperand(RegisterIndex baseRegister, string? segmentOverride = null)
: base(48, segmentOverride)
{
Type = OperandType.MemoryBaseReg;
BaseRegister = baseRegister;
IndexRegister = null;
Scale = 1;
Displacement = 0;
Address = null;
}
/// <summary>
/// Initializes a new instance of the FarPointerOperand class for displacement memory operands
/// </summary>
/// <param name="baseRegister">The base register</param>
/// <param name="displacement">The displacement value</param>
/// <param name="segmentOverride">Optional segment override</param>
public FarPointerOperand(RegisterIndex baseRegister, long displacement, string? segmentOverride = null)
: base(48, segmentOverride)
{
Type = OperandType.MemoryBaseRegPlusOffset;
BaseRegister = baseRegister;
IndexRegister = null;
Scale = 1;
Displacement = displacement;
Address = null;
}
/// <summary>
/// Initializes a new instance of the FarPointerOperand class for scaled index memory operands
/// </summary>
/// <param name="indexRegister">The index register</param>
/// <param name="scale">The scale factor</param>
/// <param name="baseRegister">The optional base register</param>
/// <param name="displacement">The displacement value</param>
/// <param name="segmentOverride">Optional segment override</param>
public FarPointerOperand(RegisterIndex indexRegister, int scale, RegisterIndex? baseRegister = null, long displacement = 0, string? segmentOverride = null)
: base(48, segmentOverride)
{
Type = OperandType.MemoryIndexed;
BaseRegister = baseRegister;
IndexRegister = indexRegister;
Scale = scale;
Displacement = displacement;
Address = null;
}
/// <summary>
/// Initializes a new instance of the FarPointerOperand class for direct memory operands
/// </summary>
/// <param name="address">The memory address</param>
/// <param name="segmentOverride">Optional segment override</param>
public FarPointerOperand(long address, string? segmentOverride = null)
: base(48, segmentOverride)
{
Type = OperandType.MemoryDirect;
BaseRegister = null;
IndexRegister = null;
Scale = 1;
Displacement = 0;
Address = address;
}
/// <summary>
/// Creates a FarPointerOperand from an existing memory operand
/// </summary>
/// <param name="memoryOperand">The memory operand to convert</param>
/// <returns>A new FarPointerOperand with the same properties</returns>
public static FarPointerOperand FromMemoryOperand(MemoryOperand memoryOperand)
{
// Create the appropriate type of FarPointerOperand based on the source operand type
if (memoryOperand is BaseRegisterMemoryOperand baseRegMemOperand)
{
return new FarPointerOperand(baseRegMemOperand.BaseRegister, memoryOperand.SegmentOverride);
}
else if (memoryOperand is DisplacementMemoryOperand dispMemOperand)
{
return new FarPointerOperand(dispMemOperand.BaseRegister, dispMemOperand.Displacement, memoryOperand.SegmentOverride);
}
else if (memoryOperand is DirectMemoryOperand directMemOperand)
{
return new FarPointerOperand(directMemOperand.Address, memoryOperand.SegmentOverride);
}
else if (memoryOperand is ScaledIndexMemoryOperand sibMemOperand)
{
return new FarPointerOperand(sibMemOperand.IndexRegister, sibMemOperand.Scale, sibMemOperand.BaseRegister, sibMemOperand.Displacement, memoryOperand.SegmentOverride);
}
// Default case - shouldn't happen if all memory operand types are handled above
throw new System.ArgumentException("Unsupported memory operand type", nameof(memoryOperand));
}
/// <summary>
/// Returns a string representation of this operand
/// </summary>
public override string ToString()
{
string prefix = "fword ptr ";
// Add segment override if present
if (SegmentOverride != null)
{
prefix = $"{prefix}{SegmentOverride}:";
}
// Format based on operand type
return Type switch
{
OperandType.MemoryBaseReg => $"{prefix}[{RegisterMapper.GetRegisterName(BaseRegister!.Value, 32)}]",
OperandType.MemoryBaseRegPlusOffset => $"{prefix}[{RegisterMapper.GetRegisterName(BaseRegister!.Value, 32)}+0x{Displacement:X}]",
OperandType.MemoryDirect => $"{prefix}[0x{Address!.Value:X}]",
OperandType.MemoryIndexed => FormatSIBString(prefix),
_ => $"{prefix}[unknown]"
};
}
/// <summary>
/// Formats the string representation for SIB addressing mode
/// </summary>
private string FormatSIBString(string prefix)
{
string result = prefix + "[";
// Add base register if present
if (BaseRegister.HasValue)
{
result += RegisterMapper.GetRegisterName(BaseRegister.Value, 32);
}
// Add index register with scale if present
if (IndexRegister.HasValue)
{
// Add + if we already have a base register
if (BaseRegister.HasValue)
{
result += "+";
}
result += RegisterMapper.GetRegisterName(IndexRegister.Value, 32);
// Add scale if not 1
if (Scale > 1)
{
result += $"*{Scale}";
}
}
// Add displacement if non-zero
if (Displacement != 0)
{
// Format as signed value
if (Displacement > 0)
{
result += $"+0x{Displacement:X}";
}
else
{
result += $"-0x{-Displacement:X}";
}
}
result += "]";
return result;
}
}

View File

@ -27,11 +27,13 @@ public abstract class MemoryOperand : Operand
/// <returns>The size prefix string</returns> /// <returns>The size prefix string</returns>
protected string GetSizePrefix() protected string GetSizePrefix()
{ {
// Use size-based prefix
string sizePrefix = Size switch string sizePrefix = Size switch
{ {
8 => "byte ptr ", 8 => "byte ptr ",
16 => "word ptr ", 16 => "word ptr ",
32 => "dword ptr ", 32 => "dword ptr ",
48 => "fword ptr ",
64 => "qword ptr ", 64 => "qword ptr ",
_ => "" _ => ""
}; };

View File

@ -209,4 +209,15 @@ public static class OperandFactory
{ {
return new FPURegisterOperand(registerIndex); return new FPURegisterOperand(registerIndex);
} }
/// <summary>
/// Creates a far pointer operand from an existing memory operand
/// </summary>
/// <param name="memoryOperand">The memory operand to convert to a far pointer</param>
/// <returns>A far pointer operand with the same addressing mode as the given memory operand</returns>
public static FarPointerOperand CreateFarPointerOperand(MemoryOperand memoryOperand)
{
// Create a new FarPointerOperand with the same properties as the given memory operand
return FarPointerOperand.FromMemoryOperand(memoryOperand);
}
} }

View File

@ -68,7 +68,14 @@ 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]"] }]
FF1D;[{ "Type": "Call", "Operands": ["fword ptr [ebp]"] }] # SPECIAL CASE in x86 encoding:
# When Mod=00 and R/M=101 (EBP), this doesn't actually refer to [EBP] but instead indicates
# a 32-bit displacement-only addressing mode. The correct encoding for "Call fword ptr [ebp]"
# would be FF5D00 which is "Call fword ptr [ebp+0x0]"
# FF1D;[{ "Type": "Call", "Operands": ["fword ptr [ebp]"] }]
# Correct encoding for "Call fword ptr [ebp]" with displacement 0
FF5D00;[{ "Type": "Call", "Operands": ["fword ptr [ebp+0x0]"] }]
FF1E;[{ "Type": "Call", "Operands": ["fword ptr [esi]"] }] FF1E;[{ "Type": "Call", "Operands": ["fword ptr [esi]"] }]
FF1F;[{ "Type": "Call", "Operands": ["fword ptr [edi]"] }] FF1F;[{ "Type": "Call", "Operands": ["fword ptr [edi]"] }]
FF18;[{ "Type": "Call", "Operands": ["fword ptr [eax]"] }] FF18;[{ "Type": "Call", "Operands": ["fword ptr [eax]"] }]

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

View File

@ -12,7 +12,12 @@ RawBytes;Instructions
83C842;[{ "Type": "Or", "Operands": ["eax", "0x42"] }] 83C842;[{ "Type": "Or", "Operands": ["eax", "0x42"] }]
# OR with memory operands # OR with memory operands
810C2578563412;[{ "Type": "Or", "Operands": ["dword ptr [eax]", "0x12345678"] }] # INVALID TEST: The following test has an invalid encoding.
# When ModR/M byte has R/M=100 (ESP), a SIB byte is required.
# 810C2578563412;[{ "Type": "Or", "Operands": ["dword ptr [eax]", "0x12345678"] }]
# Correct encoding for "Or dword ptr [eax], 0x12345678"
810878563412;[{ "Type": "Or", "Operands": ["dword ptr [eax]", "0x12345678"] }]
# OR r/m32, r32 (opcode 09) # OR r/m32, r32 (opcode 09)
09D8;[{ "Type": "Or", "Operands": ["eax", "ebx"] }] 09D8;[{ "Type": "Or", "Operands": ["eax", "ebx"] }]

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