Home > database >  Find Two-byte Illegal Opcodes for x86-64
Find Two-byte Illegal Opcodes for x86-64

Time:01-02

My goal is to find what two-byte opcodes generate an illegal instruction exception. For example, opcodes 0F 0B UD2 raises an invalid opcode exception. The UD2 instruction is provided for software testing to explicitly generate an invalid opcode.

Warning Snake oil code ahead as I'm not familiar with Windows internals.

The code below allocates a 4K page with read/write/execute permissions and using UD2 as a starting point it tries to determine all the possible two-byte opcodes.

First, it copies the two-byte opcodes to the last two bytes of the 4K page

opcodes

then executes them and checks for the exception code.

I figured that executing the last two page bytes would either

  1. Generate an illegal exception EXCEPTION_ILLEGAL_INSTRUCTION with exactly two bytes.
  2. Generate an access violation EXCEPTION_ACCESS_VIOLATION when extending beyond the 4K page.

Running the code below shows interesting instructions plus many unknowns too:

Illegal opcodes 0x0f 0x0b (error 0xc000001d)
ud2 - Generates an invalid opcode. 
   
Illegal opcodes 0x0f 0x37 (error 0xc000001d)
getsec - Exit authenticated code execution mode.

Illegal opcodes 0x0f 0xaa (error 0xc000001d)
rsm - Resume operation of interrupted program.

Question

The hack'ish code runs fine in this opcode range

Executing opcodes 0x0f 0x0b ... Executing opcodes 0x0f 0xcb

until it encounters these two opcodes

0x0f 0xcc bswap esp

It seems anything that manipulates the stack pointer causes issues whereby it's stuck at this point (clicking Continue just repeats the message)

stuck

I've tried moving the opcode execution into its own thread since they have their own stack, but that didn't help!

Is there a way to preserve the stack pointers RSP and RBP or maybe there's a simple fix to resolve it?

(Built using M$ Visual C 2019)

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <intrin.h>

// The UD2 (0x0F, 0x0B) instruction is guaranteed to generate an invalid opcode exception.

DWORD InstructionResult;

void ExecuteOpcodes(LPVOID mem)
{
    __try
    {
        // Execute opcodes...
        ((void(*)())((unsigned char*)mem   0xFFE))();
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        InstructionResult = GetExceptionCode();
    }
}

int main()
{
    LPVOID mem = VirtualAlloc(NULL, 2, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    DWORD oldProtect = VirtualProtect(mem, 2, PAGE_EXECUTE_READWRITE, &oldProtect);

    // Start searching at the UD2 (0x0F, 0x0B) instruction which is guaranteed to generate an invalid opcode exception.
    for (int i = 15; i <= 255; i  )
    {
        for (int j = 11; j <= 255; j  )
        {
            // Write two byte opcodes at the 4K page end.
            *((unsigned char*)mem   0xFFE) = i;
            *((unsigned char*)mem   0xFFF) = j;

            printf("Executing opcodes 0xx 0xx\n",i,j);
            HANDLE hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ExecuteOpcodes, mem, 0, 0);
            WaitForSingleObject(hThread, INFINITE);
            CloseHandle(hThread);
            if (InstructionResult == EXCEPTION_ILLEGAL_INSTRUCTION)
            {
                printf("Illegal opcodes 0xx 0xx (error 0xx)\n", i, j, InstructionResult);
            }
        }
    }

    VirtualFree(mem, 0, MEM_RELEASE);

    return 0;
}

CodePudding user response:

maybe there's a simple fix to resolve it?

The UNIX-standard way to resolve this is to do all the test execution in a child process.

When I last worked on Windows 15 years ago, creating a child process was very expensive (slow). But since you have fewer that 64K byte combinations to try, even a slow mechanism will get you all the answers in at most a few hours.

  • Related