mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2025-05-18 19:31: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:
parent
089fe4dfd4
commit
fa1a7f582c
@ -137,8 +137,9 @@ public class InstructionHandlerFactory
|
||||
private void RegisterCallHandlers()
|
||||
{
|
||||
// Add Call handlers
|
||||
_handlers.Add(new CallRel32Handler(_decoder));
|
||||
_handlers.Add(new CallRm32Handler(_decoder));
|
||||
_handlers.Add(new CallRel32Handler(_decoder)); // CALL rel32 (opcode E8)
|
||||
_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>
|
||||
@ -378,6 +379,7 @@ public class InstructionHandlerFactory
|
||||
|
||||
// Add PUSH immediate handlers
|
||||
_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)
|
||||
}
|
||||
|
||||
|
67
X86Disassembler/X86/Handlers/Push/PushImm16Handler.cs
Normal file
67
X86Disassembler/X86/Handlers/Push/PushImm16Handler.cs
Normal 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;
|
||||
}
|
||||
}
|
206
X86Disassembler/X86/Operands/FarPointerOperand.cs
Normal file
206
X86Disassembler/X86/Operands/FarPointerOperand.cs
Normal 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;
|
||||
}
|
||||
}
|
@ -27,11 +27,13 @@ public abstract class MemoryOperand : Operand
|
||||
/// <returns>The size prefix string</returns>
|
||||
protected string GetSizePrefix()
|
||||
{
|
||||
// Use size-based prefix
|
||||
string sizePrefix = Size switch
|
||||
{
|
||||
8 => "byte ptr ",
|
||||
16 => "word ptr ",
|
||||
32 => "dword ptr ",
|
||||
48 => "fword ptr ",
|
||||
64 => "qword ptr ",
|
||||
_ => ""
|
||||
};
|
||||
|
@ -209,4 +209,15 @@ public static class OperandFactory
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
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]"] }]
|
||||
FF1F;[{ "Type": "Call", "Operands": ["fword ptr [edi]"] }]
|
||||
FF18;[{ "Type": "Call", "Operands": ["fword ptr [eax]"] }]
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 15.
|
@ -12,7 +12,12 @@ RawBytes;Instructions
|
||||
83C842;[{ "Type": "Or", "Operands": ["eax", "0x42"] }]
|
||||
|
||||
# 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)
|
||||
09D8;[{ "Type": "Or", "Operands": ["eax", "ebx"] }]
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 11.
|
Loading…
x
Reference in New Issue
Block a user