#include "disasm.h" #include "utils.h" #include "win_funcs.h" // ======================= // ====== windows.h ====== // ======================= #define PAGE_NOACCESS 0x01 #define PAGE_READONLY 0x02 #define PAGE_READWRITE 0x04 #define PAGE_WRITECOPY 0x08 #define PAGE_EXECUTE 0x10 #define PAGE_EXECUTE_READ 0x20 #define PAGE_EXECUTE_READWRITE 0x40 #define PAGE_EXECUTE_WRITECOPY 0x80 #define PAGE_GUARD 0x100 #define PAGE_NOCACHE 0x200 #define PAGE_WRITECOMBINE 0x400 #define PAGE_GRAPHICS_NOACCESS 0x0800 #define PAGE_GRAPHICS_READONLY 0x1000 #define PAGE_GRAPHICS_READWRITE 0x2000 #define PAGE_GRAPHICS_EXECUTE 0x4000 #define PAGE_GRAPHICS_EXECUTE_READ 0x8000 #define PAGE_GRAPHICS_EXECUTE_READWRITE 0x10000 #define PAGE_GRAPHICS_COHERENT 0x20000 #define PAGE_GRAPHICS_NOCACHE 0x40000 #define PAGE_ENCLAVE_THREAD_CONTROL 0x80000000 #define PAGE_REVERT_TO_FILE_MAP 0x80000000 #define PAGE_TARGETS_NO_UPDATE 0x40000000 #define PAGE_TARGETS_INVALID 0x40000000 #define PAGE_ENCLAVE_UNVALIDATED 0x20000000 #define PAGE_ENCLAVE_MASK 0x10000000 #define PAGE_ENCLAVE_DECOMMIT (PAGE_ENCLAVE_MASK | 0) #define PAGE_ENCLAVE_SS_FIRST (PAGE_ENCLAVE_MASK | 1) #define PAGE_ENCLAVE_SS_REST (PAGE_ENCLAVE_MASK | 2) #define MEM_COMMIT 0x00001000 #define MEM_RESERVE 0x00002000 #define MEM_REPLACE_PLACEHOLDER 0x00004000 #define MEM_RESERVE_PLACEHOLDER 0x00040000 #define MEM_RESET 0x00080000 #define MEM_TOP_DOWN 0x00100000 #define MEM_WRITE_WATCH 0x00200000 #define MEM_PHYSICAL 0x00400000 #define MEM_ROTATE 0x00800000 #define MEM_DIFFERENT_IMAGE_BASE_OK 0x00800000 #define MEM_RESET_UNDO 0x01000000 #define MEM_LARGE_PAGES 0x20000000 #define MEM_4MB_PAGES 0x80000000 #define MEM_64K_PAGES (MEM_LARGE_PAGES | MEM_PHYSICAL) #define MEM_UNMAP_WITH_TRANSIENT_BOOST 0x00000001 #define MEM_COALESCE_PLACEHOLDERS 0x00000001 #define MEM_PRESERVE_PLACEHOLDER 0x00000002 #define MEM_DECOMMIT 0x00004000 #define MEM_RELEASE 0x00008000 #define MEM_FREE 0x00010000 #define MEM_EXTENDED_PARAMETER_GRAPHICS 0x00000001 #define MEM_EXTENDED_PARAMETER_NONPAGED 0x00000002 #define MEM_EXTENDED_PARAMETER_ZERO_PAGES_OPTIONAL 0x00000004 #define MEM_EXTENDED_PARAMETER_NONPAGED_LARGE 0x00000008 #define MEM_EXTENDED_PARAMETER_NONPAGED_HUGE 0x00000010 #define MEM_EXTENDED_PARAMETER_SOFT_FAULT_PAGES 0x00000020 #define MEM_EXTENDED_PARAMETER_EC_CODE 0x00000040 #define MEM_EXTENDED_PARAMETER_NUMA_NODE_MANDATORY MINLONG64 // ======================== // ====== !windows.h ====== // ======================== const u8 x64_max_inst_size = 16U; const u8 MAX_LEGACY_PREFIXES = 4U; const u8 legacy_prefixes[] = { // Operand-size override 0x66, // Address-size override 0x67, // Segment override 0x2E, 0x3E, 0x26, 0x64, 0x65, 0x36, // Lock 0xF0, // Repeat 0xF2, 0xF3, }; const u8 REX_prefixes[] = { 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, }; const char* registers[] = { "AL", "AH", "AX", "EAX", "RAX", "BL", "BH", "BX", "EBX", "RBX", "CL", "CH", "CX", "ECX", "RCX", "DL", "DH", "DX", "EDX", "RDX", "SIL", "SI", "ESI", "RSI", "DIL", "DI", "EDI", "RDI", "BPL", "BP", "EBP", "RBP", "SPL", "SP", "ESP", "RSP", "R8B", "R8W", "R8D", "R8", "R9B", "R9W", "R9D", "R9", "R10B", "R10W", "R10D", "R10", "R11B", "R11W", "R11D", "R11", "R12B", "R12W", "R12D", "R12", "R13B", "R13W", "R13D", "R13", "R14B", "R14W", "R14D", "R14", "R15B", "R15W", "R15D", "R15", "RIP", // Segments "CS", "DS", // Ignored in 64 mode "ES", // Ignored in 64 mode "FS", "GS", "SS", // Ignored in 64 mode }; const char* ModRM_registers[8U][5U] = { { NULL, "RAX", "MMX0", "XMM0", "YMM0" }, { NULL, "RCX", "MMX1", "XMM1", "YMM1" }, { NULL, "RDX", "MMX2", "XMM2", "YMM2" }, { NULL, "RBX", "MMX3", "XMM3", "YMM3" }, { "AH", "RSP", "MMX4", "XMM4", "YMM4" }, { "CH", "RBP", "MMX5", "XMM5", "YMM5" }, { "DH", "RSI", "MMX6", "XMM6", "YMM6" }, { "BH", "RDI", "MMX7", "XMM7", "YMM7" } }; const char* SIB_index_registers[8U] = { "RAX", "RCX", "RDX", "RBX", "", "RBP", "RSI", "RDI" }; const char* SIB_base_registers[8U] = { "RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI" }; struct operand { // Default operand size in 64bit mode is 32bit. // If 66h legacy prefix is present 16bit override the default operand size // If REX.W prefix is present 64bit override the default operand size // In some cases instruction forces 64bit operand size enum OPERAND_TYPE { None, Inst_FAR, // A TYPE_E, // E ModRM_Reg, // G Imm, // I TYPE_J, // Relative offset added to RIP ModRM_MEM, // M, mod != 0b11 Vector_REG, // L ModRM_Seg_Reg, // S TYPE_R, // R mod != 0b11 DS_rSI, // X ES_rSI, // Y Reg, } src; // Handle eventual SIB byte following ModRM byte union { enum DATA_TYPE { TYPE_NONE = 0x0, TYPE_BYTE = 0x1, // b TYPE_WORD = 0x2, // w TYPE_DWORD = 0x4, TYPE_QWORD = 0x8, TYPE_FP = 0x10, TYPE_v = 0x1f, // v TYPE_p = 0x20, // 32 or 48 bit depending on 16 or 32 bit operand size TYPE_z = 0x1000, // z TYPE_1 = 0x2000, // Hardcoded 1 } type; enum REGISTER { AL, AH, AX, EAX, RAX, BL, BH, BX, EBX, RBX, CL, CH, CX, ECX, RCX, DL, DH, DX, EDX, RDX, SIL, SI, ESI, RSI, DIL, DI, EDI, RDI, BPL, BP, EBP, RBP, SPL, SP, ESP, RSP, R8B, R8W, R8D, R8, R9B, R9W, R9D, R9, R10B, R10W, R10D, R10, R11B, R11W, R11D, R11, R12B, R12W, R12D, R12, R13B, R13W, R13D, R13, R14B, R14W, R14D, R14, R15B, R15W, R15D, R15, RIP, // Segments CS, DS, // Ignored in 64 mode ES, // Ignored in 64 mode FS, GS, SS, // Ignored in 64 mode } reg; }; // SSE stuff }; struct opcode { const char* name; struct operand src_operand; struct operand dst_operand; }; const struct opcode rep_f3_opcode_map[] = { { "PAUSE", { .src = None }, { .src = None }}, // 0x90 }; const struct opcode primary_opcode_map[256U] = { { "ADD", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, { "ADD", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, { "ADD", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, { "ADD", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, { "ADD", { .src = Reg, .reg = AL }, { .src = Imm, .type = TYPE_BYTE }}, { "ADD", { .src = Reg, .reg = RAX }, { .src = Imm, .type = TYPE_z }}, { "PUSH", { .src = Reg, .reg = ES }, { .src = None }}, { "POP", { .src = Reg, .reg = ES }, { .src = None, }}, { "OR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, { "OR", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, { "OR", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, { "OR", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, { "OR", { .src = Reg, .reg = AL }, { .src = Imm, .type = TYPE_BYTE }}, { "OR", { .src = Reg, .reg = RAX }, { .src = Imm, .type = TYPE_z }}, { "PUSH", { .src = Reg, .reg = CS }, { .src = None }}, { "INVALID", { .src = None }, { .src = None }}, // Escape to secondary op code map { "ADC", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, { "ADC", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, { "ADC", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, { "ADC", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, { "ADC", { .src = Reg, .reg = AL }, { .src = Imm, .type = TYPE_BYTE }}, { "ADC", { .src = Reg, .reg = RAX }, { .src = Imm, .type = TYPE_z }}, { "PUSH", { .src = Reg, .reg = SS }, { .src = None }}, { "POP", { .src = Reg, .reg = SS }, { .src = None }}, { "SBB", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, { "SBB", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, { "SBB", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, { "SBB", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, { "SBB", { .src = Reg, .reg = AL }, { .src = Imm, .type = TYPE_BYTE }}, { "SBB", { .src = Reg, .reg = RAX }, { .src = Imm, .type = TYPE_z }}, { "PUSH", { .src = Reg, .reg = DS }, { .src = None }}, { "POP", { .src = Reg, .reg = DS }, { .src = None }}, { "AND", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, { "AND", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, { "AND", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, { "AND", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, { "AND", { .src = Reg, .reg = AL }, { .src = Imm, .type = TYPE_BYTE }}, { "AND", { .src = Reg, .reg = RAX }, { .src = Imm, .type = TYPE_z }}, { "seg", { .src = Reg, .reg = ES }, { .src = None }}, { "DAA", { .src = None }, { .src = None }}, { "SUB", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, { "SUB", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, { "SUB", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, { "SUB", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, { "SUB", { .src = Reg, .reg = AL }, { .src = Imm, .type = TYPE_BYTE }}, { "SUB", { .src = Reg, .reg = RAX }, { .src = Imm, .type = TYPE_z }}, { "seg", { .src = Reg, .reg = CS }, { .src = None }}, { "DAS", { .src = None }, { .src = None }}, { "XOR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, { "XOR", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, { "XOR", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, { "XOR", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, { "XOR", { .src = Reg, .reg = AL }, { .src = Imm, .type = TYPE_BYTE }}, { "XOR", { .src = Reg, .reg = RAX }, { .src = Imm, .type = TYPE_z }}, { "seg", { .src = Reg, .reg = SS }, { .src = None }}, { "AAA", { .src = None }, { .src = None }}, { "CMP", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, { "CMP", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, { "CMP", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, { "CMP", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, { "CMP", { .src = Reg, .reg = AL }, { .src = Imm, .type = TYPE_BYTE }}, { "CMP", { .src = Reg, .reg = RAX }, { .src = Imm, .type = TYPE_z }}, { "seg", { .src = Reg, .reg = DS }, { .src = None }}, { "AAS", { .src = None }, { .src = None }}, { "INC", { .src = Reg, .reg = EAX }, { .src = None }}, { "INC", { .src = Reg, .reg = ECX}, { .src = None }}, { "INC", { .src = Reg, .reg = EDX }, { .src = None }}, { "INC", { .src = Reg, .reg = EBX }, { .src = None }}, { "INC", { .src = Reg, .reg = ESP }, { .src = None }}, { "INC", { .src = Reg, .reg = EBP }, { .src = None }}, { "INC", { .src = Reg, .reg = ESI }, { .src = None }}, { "INC", { .src = Reg, .reg = EDI }, { .src = None }}, { "DEC", { .src = Reg, .reg = EAX }, { .src = None }}, { "DEC", { .src = Reg, .reg = ECX }, { .src = None }}, { "DEC", { .src = Reg, .reg = EDX }, { .src = None }}, { "DEC", { .src = Reg, .reg = EBX }, { .src = None }}, { "DEC", { .src = Reg, .reg = ESP }, { .src = None }}, { "DEC", { .src = Reg, .reg = EBP }, { .src = None }}, { "DEC", { .src = Reg, .reg = ESI }, { .src = None }}, { "DEC", { .src = Reg, .reg = EDI }, { .src = None }}, { "PUSH", { .src = Reg, .reg = RAX }, { .src = None, .type = TYPE_NONE }}, // TODO: RAX/r8 { "PUSH", { .src = Reg, .reg = RCX }, { .src = None, .type = TYPE_NONE }}, // TODO: RCX/r9 { "PUSH", { .src = Reg, .reg = RDX }, { .src = None, .type = TYPE_NONE }}, // TODO: RDX/r10k { "PUSH", { .src = Reg, .reg = RBX }, { .src = None, .type = TYPE_NONE }}, // TODO: RBX/r11 { "PUSH", { .src = Reg, .reg = RSP }, { .src = None, .type = TYPE_NONE }}, // TODO: RSP/r12 { "PUSH", { .src = Reg, .reg = RBP }, { .src = None, .type = TYPE_NONE }}, // TODO: RBP/r13 { "PUSH", { .src = Reg, .reg = RSI }, { .src = None, .type = TYPE_NONE }}, // TODO: RSI/r14 { "PUSH", { .src = Reg, .reg = RDI }, { .src = None, .type = TYPE_NONE }}, // TODO: RDI/r15 { "POP", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, // TODO { "POP", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, // TODO { "POP", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO { "POP", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "POP", { .src = ModRM_Reg, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, // TODO { "POP", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "POP", { .src = ModRM_Reg, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, // TODO { "POP", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "PUSHA/D", { .src = None }, { .src = None}}, { "POPA/D", { .src = None }, { .src = None }}, { "BOUND", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO { "ARPL", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "seg", { .src = ModRM_Reg, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, // TODO { "seg", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "INVALID", { .src = None }, { .src = None }}, // Operand size overide prefix { "INVALID", { .src = None }, { .src = None }}, // Address size override prefix { "PUSH", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, // TODO { "IMUL", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, // TODO { "PUSH", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO { "IMUL", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "INSB", { .src = ModRM_Reg, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, // TODO { "INSW/D", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "OUTS/B", { .src = ModRM_Reg, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, // TODO { "OUTS/W/D", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "JO", { .src = TYPE_J, .type = TYPE_BYTE }, { .src = None }}, { "JNO", { .src = TYPE_J, .type = TYPE_BYTE}, { .src = None }}, { "JB", { .src = TYPE_J, .type = TYPE_BYTE }, { .src = None }}, { "JNB", { .src = TYPE_J, .type = TYPE_BYTE }, { .src = None }}, { "JZ", { .src = TYPE_J, .type = TYPE_BYTE }, { .src = None }}, { "JNZ", { .src = TYPE_J, .type = TYPE_BYTE }, { .src = None }}, { "JBE", { .src = TYPE_J, .type = TYPE_BYTE }, { .src = None }}, { "JNBE", { .src = TYPE_J, .type = TYPE_BYTE }, { .src = None }}, { "JS", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, // TODO { "JNS", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, // TODO { "JP", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO { "JNP", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "JL", { .src = ModRM_Reg, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, // TODO { "JNL", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "JLE", { .src = ModRM_Reg, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, // TODO { "JNLE", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "INVALID", { .src = None }, { .src = None }}, // Group 1 Opcode { "INVALID", { .src = None }, { .src = None }}, // Group 1 Opcode { "INVALID", { .src = None }, { .src = None }}, // Group 1 Opcode { "INVALID", { .src = None }, { .src = None }}, // Group 1 Opcode { "TEST", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, { "TEST", { .src = TYPE_E, .type = TYPE_v }, { .src = ModRM_Reg, .type = TYPE_v }}, { "XCHG", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, { "XCHG", { .src = TYPE_E, .type = TYPE_v }, { .src = ModRM_Reg, .type = TYPE_v }}, { "MOV", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, { "MOV", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, { "MOV", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, // TODO { "LEA", { .src = ModRM_Reg, .type = TYPE_v }, { .src = ModRM_MEM }}, { "MOV", { .src = ModRM_Seg_Reg, .type = TYPE_WORD }, { .src = TYPE_E, .type = TYPE_WORD }}, { "INVALID", { .src = None }, { .src = None }}, // Group 1 Opcode { "NOP", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XCHG", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XCHG", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XCHG", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XCHG", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XCHG", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XCHG", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XCHG", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "CBW/WDE/DQE", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "CWD/DQ/QO", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "CALL", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "F/WAIT", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "PUSHF/D/Q", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "POPF/D/Q", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "SAHF", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "LAHF", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOVSB", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOBSW/D/Q", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "CMPSB", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "CMPSW/D/Q", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "TEST", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "TEST", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "STOSB", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "STOW/D/Q", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "LODSB", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "LODSW/D/Q", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "SCASB", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "SCASW/D/Q", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "MOV", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "INVALID", { .src = None }, { .src = None }}, // Group 2 opcode { "INVALID", { .src = None }, { .src = None }}, // Group 2 opcode { "RET", { .src = ModRM_Reg, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO { "RET", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "LES", { .src = ModRM_Reg, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, // TODO { "LDS", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "INVALID", { .src = None }, { .src = None }}, // Group 11 opcode { "INVALID", { .src = None }, { .src = None }}, // Group 11 opcode { "ENTER", { .src = ModRM_Reg, .type = TYPE_WORD }, { .src = Imm, .type = TYPE_BYTE }}, { "LEAVE", { .src = None }, { .src = None }}, { "RET far", { .src = Imm, .type = TYPE_WORD }, { .src = None }}, { "RET far", { .src = None }, { .src = None }}, { "INT 3", { .src = None }, { .src = None }}, { "INT", { .src = Imm, .type = TYPE_BYTE }, { .src = None }}, { "INTO", { .src = None }, { .src = None }}, // Invalid in 64bit mode { "IRET/D/Q", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "INVALID", { .src = None }, { .src = None }}, // Group 2 Opcode { "INVALID", { .src = None }, { .src = None }}, // Group 2 Opcode { "INVALID", { .src = None }, { .src = None }}, // Group 2 Opcode { "INVALID", { .src = None }, { .src = None }}, // Group 2 Opcode { "AAM", { .src = Imm, .type = TYPE_BYTE }, { .src = None }}, { "AAD", { .src = Imm, .type = TYPE_BYTE}, { .src = None }}, { "INVALID", { .src = None }, { .src = None }}, // invalid { "XLAT", { .src = None }, { .src = None }}, // TODO { "INVALID", { .src = None }, { .src = None }}, // TODO x87 instructions { "INVALID", { .src = None }, { .src = None }}, // TODO x87 instructions { "INVALID", { .src = None }, { .src = None }}, // TODO x87 instructions { "INVALID", { .src = None }, { .src = None }}, // TODO x87 instructions { "INVALID", { .src = None }, { .src = None }}, // TODO x87 instructions { "INVALID", { .src = None }, { .src = None }}, // TODO x87 instructions { "INVALID", { .src = None }, { .src = None }}, // TODO x87 instructions { "INVALID", { .src = None }, { .src = None }}, // TODO x87 instructions { "LOOPNE/NZ", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "LOOPE/Z", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "LOOP", { .src = TYPE_J, .type = TYPE_BYTE }, { .src = None }}, { "JrCXZ", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "IN", { .src = Reg, .reg = AL }, { .src = Imm, .type = TYPE_BYTE }}, { "IN", { .src = Reg, .reg = EAX }, { .src = Imm, .type = TYPE_BYTE }}, { "OUT", { .src = Imm, .type = TYPE_BYTE }, { .src = Reg, .reg = AL }}, { "OUT", { .src = Imm, .type = TYPE_BYTE }, { .src = Reg, .reg = EAX }}, { "CALL", { .src = TYPE_J, .type = TYPE_z }, { .src = None }}, { "JMP", { .src = TYPE_J, .type = TYPE_z}, { .src = None }}, { "JMP", { .src = Inst_FAR, .type = TYPE_p }, { .src = None }}, { "JMP", { .src = TYPE_J, .type = TYPE_BYTE }, { .src = None }}, { "IN", { .src = Reg, .reg = AL }, { .src = Reg, .reg = DX }}, { "IN", { .src = Reg, .reg = EAX }, { .src = Reg, .reg = DX }}, { "OUT", { .src = Reg, .reg = DX }, { .src = Reg, .reg = AL }}, { "OUT", { .src = Reg, .reg = DX }, { .src = Reg, .reg = EAX }}, { "LOCK", { .src = ModRM_Reg, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "INT1", { .src = None }, { .src = None }}, { "REPNE", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = ModRM_Reg, .type = TYPE_BYTE }}, // TODO { "REP/E", { .src = TYPE_E, .type = TYPE_v}, { .src = ModRM_Reg, .type = TYPE_v }}, // TODO { "HLT", { .src = None }, { .src = None }}, { "CMC", { .src = None }, { .src = None }}, { "INVALID", { .src = None }, { .src = None }}, // Group 3 opcode { "INVALID", { .src = None }, { .src = None }}, // Group 3 opcode { "CLC", { .src = None }, { .src = None }}, { "STC", { .src = None }, { .src = None }}, { "CLI", { .src = None }, { .src = None }}, { "STI", { .src = None }, { .src = None }}, { "CLD", { .src = None }, { .src = None }}, { "STD", { .src = None }, { .src = None }}, { "INVALID", { .src = None }, { .src = None }}, // Group 4 opcode { "INVALID", { .src = None }, { .src = None }}, // Group 5 opcode }; // TODO: Think about a smart way to retrieve groups opcodes const struct opcode group_1[32U] = { // Opcode 0x80 { "ADD", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "OR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "ADC", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "SBB", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "AND", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "SUB", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "XOR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "CMP", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, // Opcode 0x81 { "ADD", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_z }}, { "OR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_z }}, { "ADC", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_z }}, { "SBB", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_z }}, { "AND", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_z }}, { "SUB", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_z }}, { "XOR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_z }}, { "CMD", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_z }}, // Opcode = 0x82 { "ADD", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "OR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "ADC", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "SBB", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "AND", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "SUB", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "XOR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "CMP", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, // Opcode 0x83 { "ADD", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "OR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "ADC", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "SBB", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "AND", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "SUB", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "XOR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "CMP", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, }; const struct opcode group_1a[8U] = { { "POP", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, { "XOP", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XOP", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XOP", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XOP", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XOP", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XOP", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO { "XOP", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_v }}, // TODO }; const struct opcode group_2[48U] = { // Opcode 0xC0 { "ROL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "ROR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "RCL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "RCR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "SHL/SAL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "SHR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "SHL/SAL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "SAR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, // Opcode 0xC1 { "ROL", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "ROR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "RCL", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "RCR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "SHL/SAL", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "SHR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "SHL/SAL", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, { "SAR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_BYTE }}, // Opcode 0xD0 { "ROL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_1 }}, { "ROR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_1 }}, { "RCL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_1 }}, { "RCR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_1 }}, { "SHL/SAL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_1 }}, { "SHR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_1 }}, { "SHL/SAL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_1 }}, { "SAR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_1 }}, // Opcode 0xD1 { "ROL", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_1 }}, { "ROR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_1 }}, { "RCL", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_1 }}, { "RCR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_1 }}, { "SHL/SAL", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_1 }}, { "SHR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_1 }}, { "SHL/SAL", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_1 }}, { "SAR", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_1 }}, // Opcode 0xD2 { "ROL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "ROR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "RCL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "RCR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "SHL/SAL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "SHR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "SHL/SAL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "SAR", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL // Opcode 0xD3 { "ROL", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "ROR", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "RCL", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "RCR", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "SHL/SAL", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "SHR", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "SHL/SAL", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL { "SAR", { .src = TYPE_E, .type = TYPE_v }, { .src = TYPE_E, .type = TYPE_BYTE }}, // TODO handle CL }; const struct opcode group_3[16U] = { { "TEST", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "TEST", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = None }}, { "NOT", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = None }}, { "NEG", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = None }}, { "MUL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = None }}, { "IMUL", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = None }}, { "DIV", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = None }}, { "IDIV", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = None }}, { "TEST", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_z }}, { "TEST", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, { "NOT", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, { "NEG", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, { "MUL", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, { "IMUL", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, { "DIV", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, { "IDIV", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, }; const struct opcode group_4[2U] = { { "INC", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = None }}, { "DEC", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = None }}, }; const struct opcode group_5[7U] = { { "INC", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, { "DEC", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, { "CALL", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, { "CALL", { .src = ModRM_MEM, .type = TYPE_p }, { .src = None }}, { "JMP", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, { "JMP", { .src = ModRM_MEM, .type = TYPE_p }, { .src = None }}, { "PUSH", { .src = TYPE_E, .type = TYPE_v }, { .src = None }}, }; const struct opcode group_11[2U] = { { "MOV", { .src = TYPE_E, .type = TYPE_BYTE }, { .src = Imm, .type = TYPE_BYTE }}, { "MOV", { .src = TYPE_E, .type = TYPE_v }, { .src = Imm, .type = TYPE_z }}, }; const u8 mod_to_disp_size[4U] = { 4U, 1U, 4U, 0U, }; u8 is_legacy_prefix(u8 byte) { for (u8 index = 0U; index < sizeof(legacy_prefixes); index++) { if (byte == legacy_prefixes[index]) { return TRUE; } } return FALSE; } u8 is_REX_prefix(u8 byte) { for (u8 index = 0U; index < sizeof(REX_prefixes); index++) { if (byte == REX_prefixes[index]) { return TRUE; } } return FALSE; } // TODO: Rework this messy function and params const char* get_ModRM_reg(u8 val, u8 operand_size, u8 use_3DNow, u8 use_SSE) { assert(val < 8U); u8 reg_index = 0U; if (operand_size == 16U) { reg_index = 0U; } else if (operand_size == 32U) { assert(FALSE); } else if (operand_size == 64U) { reg_index = 1U; } if (use_3DNow) { reg_index = 2U; } else if (use_SSE) { reg_index = 3U; } assert(reg_index < 5U); const char* reg = ModRM_registers[val][reg_index]; assert(reg); return reg; } u8 has_ModRM_byte(const enum OPERAND_TYPE op_type) { switch (op_type) { case None: return FALSE; case Inst_FAR: return FALSE; case TYPE_E: return TRUE; case ModRM_Reg: return TRUE; case Imm: return FALSE; case TYPE_J: return FALSE; case ModRM_MEM: return TRUE; case Vector_REG: return FALSE; case ModRM_Seg_Reg: return TRUE; case TYPE_R: return TRUE; case DS_rSI: return FALSE; case ES_rSI: return FALSE; case Reg: return FALSE; } assert(FALSE); return FALSE; } const struct opcode* primary_map_group(u8 opcode, u8 ModRM) { const u8 reg_size = 0x8; const u8 reg_byte_mask = 0b111; u8 reg = (ModRM >> 3U) & reg_byte_mask; if (0x80 <= opcode && opcode <= 0x83) { return &group_1[(opcode - 0x80) * reg_size + reg]; } else if (opcode == 0x8F) { return &group_1a[reg]; } else if (opcode == 0xC0 || opcode == 0xC1) { return &group_2[(opcode == 0xC1) * reg_size + reg]; } else if (0xD0 <= opcode && opcode <= 0xD3) { return &group_2[((opcode - 0xD0) + 2U) * reg_size + reg]; } else if (opcode == 0xF6 || opcode == 0xF7) { return &group_3[(opcode - 0xF6) * reg_size + reg]; } else if (opcode == 0xFE) { return &group_4[reg]; } else if (opcode == 0xFF) { return &group_5[reg]; } else if (opcode == 0xC6 || opcode == 0xC7) { return &group_11[opcode == 0xC7]; } else { return NULL; } } u64 get_immediate(u8** bytes, enum DATA_TYPE type, u8 operand_effective_size) { u64 imm = 0U; if (type == TYPE_BYTE) { imm = **(u8**)bytes; *bytes += 1U; } else if (type == TYPE_WORD) { imm = **(u16**)bytes; *bytes += 2U; } else if (type == TYPE_DWORD) { imm = **(u32**)bytes; *bytes += 4U; } else if (type == TYPE_QWORD) { imm = **(u64**)bytes; *bytes += 8U; } else if (type == TYPE_v) { if (operand_effective_size == 16U) { imm = **(u16**)bytes; *bytes += 2U; } else if (operand_effective_size == 32U) { imm = **(u32**)bytes; *bytes += 4U; } else if (operand_effective_size == 64U) { imm = **(u64**)bytes; *bytes += 8U; } else { print("ERROR: Unhandled operand type\n"); } *bytes += operand_effective_size / 8U; } else if (type == TYPE_z) { if (operand_effective_size == 16U) { imm = **(u16**)bytes; *bytes += 2U; } else { imm = **(u32**)bytes; *bytes += 4U; } } else { print("ERROR: Unhandled operand type\n"); } return imm; } i64 get_rel_offset(u8** bytes, enum DATA_TYPE type, u8 operand_effective_size) { i64 imm = 0U; if (type == TYPE_BYTE) { imm = **(i8**)bytes; *bytes += 1U; } else if (type == TYPE_WORD) { imm = **(i16**)bytes; *bytes += 2U; } else if (type == TYPE_DWORD) { imm = **(i32**)bytes; *bytes += 4U; } else if (type == TYPE_QWORD) { imm = **(i64**)bytes; *bytes += 8U; } else if (type == TYPE_v) { if (operand_effective_size == 16U) { imm = **(i16**)bytes; *bytes += 2U; } else if (operand_effective_size == 32U) { imm = **(i32**)bytes; *bytes += 4U; } else if (operand_effective_size == 64U) { imm = **(i64**)bytes; *bytes += 8U; } else { print("ERROR: Unhandled operand type\n"); } *bytes += operand_effective_size / 8U; } else if (type == TYPE_z) { if (operand_effective_size == 16U) { imm = **(i16**)bytes; *bytes += 2U; } else { imm = **(i32**)bytes; *bytes += 4U; } } else { print("ERROR: Unhandled operand type\n"); } return imm; } u8 get_immediate_size(enum DATA_TYPE type, u8 operand_effective_size) { if (type == TYPE_BYTE) { return 1U; } else if (type == TYPE_WORD) { return 2U; } else if (type == TYPE_DWORD) { return 4U; } else if (type == TYPE_QWORD) { return 8U; } else if (type == TYPE_v) { return operand_effective_size / 8U; } else { print("ERROR: Unhandled operand type\n"); return 0U; } } #define MODRM_MOD(ModRM) (ModRM >> 6U) #define MODRM_REG(ModRM) ((ModRM & 0b00111000) >> 0x3) #define MODRM_RM(ModRM) (ModRM & 0b00000111) #define SIB_SCALE(SIB) (SIB >> 6U) #define SIB_INDEX(SIB) ((SIB & 0b00111000) >> 0x3) #define SIB_BASE(SIB) (SIB & 0b00000111) #define REXW(REX) ((REX & 0b00001000) >> 3U) #define REXR(REX) ((REX & 0b00000100) >> 2U) #define REXX(REX) ((REX & 0b00000010) >> 1U) #define REXB(REX) (REX & 0b00000001) // void unassemble(CONTEXT* ctx, u32 count) { void unassemble(u8* start, u64 size) { // u8* current_byte = (u8*)ctx->Rip; u8* inst_buffer = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if(!read_memory((const void*)start, (void*)inst_buffer, size)) { print("Failed to read instructions\n"); } u8* current_byte = inst_buffer; while ((u64)(current_byte - inst_buffer) < size) { u8* inst_start = current_byte; u8 legacy_prefixes_count = 0U; u8 effective_operand_size = 32U; // 32 by default in 64bit mode u8 effective_address_size = 64U; // 64 by default in 64bit mode UNUSED_VAR(effective_address_size); u8 REX = 0U; while (is_legacy_prefix(*current_byte) && legacy_prefixes_count < MAX_LEGACY_PREFIXES) { if (*current_byte == 0x66) { // Override effective operand size effective_operand_size = 16U; } else if (*current_byte == 0x67) { // Override effect address size effective_address_size = 32U; } legacy_prefixes_count++; current_byte++; } if (is_REX_prefix(*current_byte)) { REX = *current_byte; if (REXW(REX)) { effective_operand_size = 64U; } current_byte++; } // TODO: VEX, XOP prefixes and there escape sequences u8 has_ModRM = FALSE; u8 ModRM = 0U; u8 use_3DNow_reg = FALSE; u8 use_SSE_reg = FALSE; const struct opcode* op = NULL; if (current_byte[0U] == 0x0F && current_byte[1U] == 0x0F) { // 3DNow! assert(FALSE); use_3DNow_reg = TRUE; current_byte += 2U; } else if (current_byte[0U] == 0x0F && (current_byte[1U] == 0x38 || current_byte[1U] == 0x3A)) { // SSE use_SSE_reg = TRUE; assert(FALSE); current_byte += 2U; } else if (current_byte[0U] == 0x0F) { // Secondary op code map needed assert(FALSE); current_byte++; } else { // Primary op code map if ((op = primary_map_group(current_byte[0U], current_byte[1U])) != NULL) { has_ModRM = TRUE; ModRM = current_byte[1U]; current_byte += 2U; } else { op = &primary_opcode_map[current_byte[0U]]; current_byte++; has_ModRM = has_ModRM_byte(op->dst_operand.src) || has_ModRM_byte(op->src_operand.src); if (has_ModRM) { ModRM = *current_byte; current_byte++; } } } i64 rel_offset = 0; if (op->src_operand.src == TYPE_J) { rel_offset = get_rel_offset(¤t_byte, op->src_operand.type, effective_operand_size); } if (op->dst_operand.src == TYPE_J) { rel_offset = get_rel_offset(¤t_byte, op->dst_operand.type, effective_operand_size); } u8 disp_bytes_count = 0U; u8 has_src_SIB = FALSE; u8 has_dst_SIB = FALSE; if (has_ModRM) { u8 Mod = MODRM_MOD(ModRM); u8 RM = MODRM_RM(ModRM); // Table 1-14 // Displacement detection if (Mod == 0b00 && RM == 0b101) { // Displacement in 32 mode, RIP relative addressing in 64 mode disp_bytes_count = mod_to_disp_size[Mod]; // rel_offset = get_rel_offset(¤t_byte, op->dst_operand.type, effective_operand_size); } else if (Mod == 0b01 || Mod == 0b10) { // RBP + offset disp_bytes_count = mod_to_disp_size[Mod]; } // SIB detection if (Mod != 0b11 && RM == 0b100) { // Indexed register indirect addressing mode if (op->src_operand.src == TYPE_E) { has_src_SIB = TRUE; } if (op->dst_operand.src == TYPE_E) { has_dst_SIB = TRUE; } // We shouldn't have both operand needing an SIB byte assert(!(has_src_SIB == has_dst_SIB == TRUE)); } } // SIB u8 SIB = *current_byte; if (has_src_SIB || has_dst_SIB) { u8 Mod = MODRM_MOD(ModRM); if (SIB_BASE(SIB) == 0b101) { disp_bytes_count = mod_to_disp_size[Mod]; } current_byte++; } // Displacement bits i32 disp = 0U; if (disp_bytes_count) { if (disp_bytes_count == 1U) { disp = *(i8*)current_byte; } else if (disp_bytes_count == 4U) { disp = *((i32*)current_byte); } current_byte += disp_bytes_count; } u64 src_imm = 0U; if (op->src_operand.src == Imm) { // Read immediate src_imm = get_immediate(¤t_byte, op->src_operand.type, effective_operand_size); } u64 dst_imm = 0U; if (op->dst_operand.src == Imm) { // Read immediate dst_imm = get_immediate(¤t_byte, op->dst_operand.type, effective_operand_size); } u8 inst_size = (u8)(current_byte - inst_start); for (u8 index = 0U; index < x64_max_inst_size; index++) { if (index < inst_size) { print("%.2x ", inst_start[index]); } else { print(" "); } } print("%s", op->name); // Src operand { switch(op->src_operand.src) { case None: break; case TYPE_E: case ModRM_MEM: { u8 Mod = MODRM_MOD(ModRM); u8 RM = MODRM_RM(ModRM); if (Mod == 0b11) { // Register direct addressing mode print(" %s", get_ModRM_reg(RM, effective_operand_size, use_3DNow_reg, use_SSE_reg)); } else if (RM == 0b101) { print(" RIP + %d", disp); } else if(RM != 0b100) { // Register indirect addressing mode print(" %s", get_ModRM_reg(RM, effective_operand_size, use_3DNow_reg, use_SSE_reg)); } break; } case ModRM_Reg: print(" %s", get_ModRM_reg(MODRM_REG(ModRM), effective_operand_size, use_3DNow_reg, use_SSE_reg)); break; case Imm: print(" 0x%x", src_imm); break; case TYPE_J: print(" RIP + %d", rel_offset); break; case Reg: print(" %s", registers[op->src_operand.reg]); break; default: assert(FALSE); } if (has_src_SIB) { // Read SIB i32 scale_factors[4U] = { 1, 2, 4, 8, }; u8 index = SIB_INDEX(SIB); if (index != 0b100) { print(" scale(0x%x) * [index(%s)]", scale_factors[SIB_SCALE(SIB)], SIB_index_registers[index]); } u8 base = SIB_BASE(SIB); if (base != 0b101 || MODRM_MOD(ModRM) != 0U) { if (index != 0b100) { print(" +"); } print(" [base(%s)]", SIB_base_registers[base]); } if (disp_bytes_count) { print(" + offset(0x%x)", disp); } } } if (op->dst_operand.src != None) { print(","); } // Dst operand { switch(op->dst_operand.src) { case None: break; case TYPE_E: case ModRM_MEM: { u8 Mod = MODRM_MOD(ModRM); u8 RM = MODRM_RM(ModRM); if (Mod == 0b11) { // Register direct addressing mode print(" %s", get_ModRM_reg(RM, effective_operand_size, use_3DNow_reg, use_SSE_reg)); } else if (RM == 0b101) { print(" RIP + %d", disp); } else if(RM != 0b100) { // Register indirect addressing mode print(" %s", get_ModRM_reg(RM, effective_operand_size, use_3DNow_reg, use_SSE_reg)); } break; } case ModRM_Reg: print(" %s", get_ModRM_reg(MODRM_REG(ModRM), effective_operand_size, use_3DNow_reg, use_SSE_reg)); break; case Imm: print(" 0x%x", dst_imm); break; case TYPE_J: print("RIP + %d", rel_offset); break; case Reg: print(" %s", registers[op->src_operand.reg]); break; default: assert(FALSE); } if (has_dst_SIB) { // Read SIB i32 scale_factors[4U] = { 1, 2, 4, 8, }; u8 index = SIB_INDEX(SIB); if (index != 0b100) { print(" scale(0x%x) * [index(%s)]", scale_factors[SIB_SCALE(SIB)], SIB_index_registers[index]); } u8 base = SIB_BASE(SIB); if (base != 0b101 || MODRM_MOD(ModRM) != 0U) { if (index != 0b100) { print(" +"); } print(" [base(%s)]", SIB_base_registers[base]); } if (disp_bytes_count) { print(" + offset(0x%x)", disp); } } } print("\n"); } // u8 low_nibble = current_byte & 0xf; // u8 high_nibble = (current_byte >> 4U) & 0xf; }