using WhiteMagic.Hooking;
using Xunit;
namespace WhiteMagicTest.Hooking;
///
/// Tests for covering the accepted x86/x64 prologue
/// shapes and rejection of opcodes outside the covered set.
///
public class PrologueDecoderTests
{
[Theory]
[InlineData(false, new byte[] { 0x55 }, 1)] // push rbp
[InlineData(false, new byte[] { 0x53 }, 1)] // push rbx
[InlineData(false, new byte[] { 0x8B, 0xFF }, 2)] // mov edi, edi
[InlineData(false, new byte[] { 0x8B, 0xEC }, 2)] // mov ebp, esp
[InlineData(false, new byte[] { 0x83, 0xEC, 0x20 }, 3)] // sub esp, 0x20
[InlineData(false, new byte[] { 0x81, 0xEC, 0x00, 0x01, 0x00, 0x00 }, 6)] // sub esp, 0x100
[InlineData(true, new byte[] { 0x48, 0x8B, 0xEC }, 3)] // mov rbp, rsp
[InlineData(true, new byte[] { 0x48, 0x83, 0xEC, 0x28 }, 4)] // sub rsp, 0x28
[InlineData(true, new byte[] { 0x48, 0x81, 0xEC, 0x78, 0x56, 0x34, 0x12 }, 7)] // sub rsp, 0x12345678
public void Decodes_covered_prologue_shapes(bool is64Bit, byte[] bytes, int expectedLength)
{
int length = PrologueDecoder.GetInstructionLength(bytes, is64Bit);
Assert.Equal(expectedLength, length);
}
[Theory]
[InlineData(false, new byte[] { 0x83, 0x05, 0x39, 0x00, 0x00, 0x00, 0x01 })] // add [rip+0x39], 1 — wrong modrm
[InlineData(false, new byte[] { 0x83, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x01 })] // cmp [rip], 1 — wrong modrm
[InlineData(false, new byte[] { 0x81, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 })] // add [rip], 1 — wrong modrm
[InlineData(false, new byte[] { 0x0F, 0x05 })] // syscall
[InlineData(false, new byte[] { 0x90, 0x90 })] // nop
public void Rejects_unsafe_or_uncovered_shapes(bool is64Bit, byte[] bytes)
{
int length = PrologueDecoder.GetInstructionLength(bytes, is64Bit);
Assert.Equal(-1, length);
Assert.Throws(
() => PrologueDecoder.GetWholeInstructionLength(bytes, requiredBytes: 2, is64Bit));
}
}