Пользователь
- Статус
- Оффлайн
- Регистрация
- 3 Янв 2020
- Сообщения
- 162
- Реакции
- 172
Hey all, I’m sharing a simple x64 decoder / hooking method that I made for personal learning purposes, figured i share this. This is bare bones,it won't be undetected or anything fancy It’s just a learning tool to help understand how x64 instruction decoding and function hooking work. was tested with various functions and works fine
The code supports a number of basic instructions and provides a framework for handling memory relocations and function trampolines especially for cs2 internal hooking experiment with it on your own.
This is open for improvements and feedback. Feel free contribute
It's a source no virus total needed
.h
The code supports a number of basic instructions and provides a framework for handling memory relocations and function trampolines especially for cs2 internal hooking experiment with it on your own.
This is open for improvements and feedback. Feel free contribute
It's a source no virus total needed
Key features:
- X64 Instruction Decoder – Decodes common x64 instructions.
- Function Hooking – Creates trampolines and hooks functions.
- Learning-Oriented – Comments throughout to help explain what each part does. yeah there is some ai comments
cpp:
namespace decoder {
// ---[ 2. Opcode Tables ]---
// Sourced and expanded to cover the vast majority of instructions
// encountered in typical x64 code, mirroring MinHook's coverage needs.
static const OpcodeInfo main_opcode_table[256] = {
// 0x00 - 0x0F
{ "ADD", OF_MODRM }, { "ADD", OF_MODRM }, { "ADD", OF_MODRM }, { "ADD", OF_MODRM },
{ "ADD", OF_IMM8 }, { "ADD", OF_IMM32 }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
{ "OR", OF_MODRM }, { "OR", OF_MODRM }, { "OR", OF_MODRM }, { "OR", OF_MODRM },
{ "OR", OF_IMM8 }, { "OR", OF_IMM32 }, { nullptr, OF_NONE }, { "TWO_BYTE", OF_TWOBYTE },
// 0x10 - 0x1F
{ "ADC", OF_MODRM }, { "ADC", OF_MODRM }, { "ADC", OF_MODRM }, { "ADC", OF_MODRM },
{ "ADC", OF_IMM8 }, { "ADC", OF_IMM32 }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
{ "SBB", OF_MODRM }, { "SBB", OF_MODRM }, { "SBB", OF_MODRM }, { "SBB", OF_MODRM },
{ "SBB", OF_IMM8 }, { "SBB", OF_IMM32 }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
// 0x20 - 0x2F
{ "AND", OF_MODRM }, { "AND", OF_MODRM }, { "AND", OF_MODRM }, { "AND", OF_MODRM },
{ "AND", OF_IMM8 }, { "AND", OF_IMM32 }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
{ "SUB", OF_MODRM }, { "SUB", OF_MODRM }, { "SUB", OF_MODRM }, { "SUB", OF_MODRM },
{ "SUB", OF_IMM8 }, { "SUB", OF_IMM32 }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
// 0x30 - 0x3F
{ "XOR", OF_MODRM }, { "XOR", OF_MODRM }, { "XOR", OF_MODRM }, { "XOR", OF_MODRM },
{ "XOR", OF_IMM8 }, { "XOR", OF_IMM32 }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
{ "CMP", OF_MODRM }, { "CMP", OF_MODRM }, { "CMP", OF_MODRM }, { "CMP", OF_MODRM },
{ "CMP", OF_IMM8 }, { "CMP", OF_IMM32 }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
// 0x40 - 0x4F (REX prefixes are handled in the decoder logic)
{ "REX", OF_NONE }, { "REX", OF_NONE }, { "REX", OF_NONE }, { "REX", OF_NONE },
{ "REX", OF_NONE }, { "REX", OF_NONE }, { "REX", OF_NONE }, { "REX", OF_NONE },
{ "REX", OF_NONE }, { "REX", OF_NONE }, { "REX", OF_NONE }, { "REX", OF_NONE },
{ "REX", OF_NONE }, { "REX", OF_NONE }, { "REX", OF_NONE }, { "REX", OF_NONE },
// 0x50 - 0x5F
{ "PUSH", OF_NONE }, { "PUSH", OF_NONE }, { "PUSH", OF_NONE }, { "PUSH", OF_NONE },
{ "PUSH", OF_NONE }, { "PUSH", OF_NONE }, { "PUSH", OF_NONE }, { "PUSH", OF_NONE },
{ "POP", OF_NONE }, { "POP", OF_NONE }, { "POP", OF_NONE }, { "POP", OF_NONE },
{ "POP", OF_NONE }, { "POP", OF_NONE }, { "POP", OF_NONE }, { "POP", OF_NONE },
// 0x60 - 0x6F
{ nullptr, OF_NONE }, { nullptr, OF_NONE }, { "MOVS", OF_MODRM }, { "MOVS", OF_MODRM },
{ nullptr, OF_NONE }, { nullptr, OF_NONE }, { "OP_PREFIX", OF_NONE }, { "ADDR_PREFIX", OF_NONE },
{ "PUSH", OF_IMM32 }, { "IMUL", OF_MODRM | OF_IMM32 }, { "PUSH", OF_IMM8 }, { "IMUL", OF_MODRM | OF_IMM8 },
{ "INSB", OF_NONE }, { "INSW", OF_NONE }, { "OUTSB", OF_NONE }, { "OUTSW", OF_NONE },
// 0x70 - 0x7F
{ "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 },
{ "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 },
{ "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 },
{ "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 }, { "Jcc", OF_RELATIVE | OF_IMM8 },
// 0x80 - 0x8F
{ "GRP1", OF_MODRM | OF_IMM8 }, { "GRP1", OF_MODRM | OF_IMM32 }, { nullptr, OF_NONE }, { "GRP1", OF_MODRM | OF_IMM8 },
{ "TEST", OF_MODRM }, { "TEST", OF_MODRM }, { "XCHG", OF_MODRM }, { "XCHG", OF_MODRM },
{ "MOV", OF_MODRM }, { "MOV", OF_MODRM }, { "MOV", OF_MODRM }, { "MOV", OF_MODRM },
{ "LEA", OF_MODRM }, { "MOV", OF_MODRM }, { "POP", OF_MODRM }, { "NOP", OF_NONE },
// 0x90 - 0x9F
{ "NOP", OF_NONE }, { "XCHG", OF_NONE }, { "XCHG", OF_NONE }, { "XCHG", OF_NONE },
{ "XCHG", OF_NONE }, { "XCHG", OF_NONE }, { "XCHG", OF_NONE }, { "XCHG", OF_NONE },
{ "CBW", OF_NONE }, { "CWD", OF_NONE }, { "CALLF", OF_NONE }, { "WAIT", OF_NONE },
{ "PUSHF", OF_NONE }, { "POPF", OF_NONE }, { "SAHF", OF_NONE }, { "LAHF", OF_NONE },
// 0xA0 - 0xAF
{ "MOV", OF_IMM64 }, { "MOV", OF_IMM64 }, { "MOV", OF_IMM64 }, { "MOV", OF_IMM64 },
{ "MOVSB", OF_NONE }, { "MOVSW", OF_NONE }, { "CMPSB", OF_NONE }, { "CMPSW", OF_NONE },
{ "TEST", OF_IMM8 }, { "TEST", OF_IMM32 }, { "STOSB", OF_NONE }, { "STOSW", OF_NONE },
{ "LODSB", OF_NONE }, { "LODSW", OF_NONE }, { "SCASB", OF_NONE }, { "SCASW", OF_NONE },
// 0xB0 - 0xBF
{ "MOV", OF_IMM8 }, { "MOV", OF_IMM8 }, { "MOV", OF_IMM8 }, { "MOV", OF_IMM8 },
{ "MOV", OF_IMM8 }, { "MOV", OF_IMM8 }, { "MOV", OF_IMM8 }, { "MOV", OF_IMM8 },
{ "MOV", OF_IMM64 }, { "MOV", OF_IMM64 }, { "MOV", OF_IMM64 }, { "MOV", OF_IMM64 },
{ "MOV", OF_IMM64 }, { "MOV", OF_IMM64 }, { "MOV", OF_IMM64 }, { "MOV", OF_IMM64 },
// 0xC0 - 0xCF
{ "GRP2", OF_MODRM | OF_IMM8 }, { "GRP2", OF_MODRM | OF_IMM8 }, { "RET", OF_IMM16 }, { "RET", OF_NONE },
{ "LES", OF_MODRM }, { "LDS", OF_MODRM }, { "MOV", OF_MODRM | OF_IMM8 }, { "MOV", OF_MODRM | OF_IMM32 },
{ "ENTER", OF_NONE }, { "LEAVE", OF_NONE }, { "RETF", OF_IMM16 }, { "RETF", OF_NONE },
{ "INT3", OF_NONE }, { "INT", OF_IMM8 }, { "INTO", OF_NONE }, { "IRET", OF_NONE },
// 0xD0 - 0xDF
{ "GRP2", OF_MODRM }, { "GRP2", OF_MODRM }, { "GRP2", OF_MODRM }, { "GRP2", OF_MODRM },
{ nullptr, OF_NONE }, { nullptr, OF_NONE }, { nullptr, OF_NONE }, { "XLAT", OF_NONE },
{ "FPU", OF_NONE }, { "FPU", OF_NONE }, { "FPU", OF_NONE }, { "FPU", OF_NONE },
{ "FPU", OF_NONE }, { "FPU", OF_NONE }, { "FPU", OF_NONE }, { "FPU", OF_NONE },
// 0xE0 - 0xEF
{ "LOOPNE", OF_RELATIVE | OF_IMM8 }, { "LOOPE", OF_RELATIVE | OF_IMM8 }, { "LOOP", OF_RELATIVE | OF_IMM8 }, { "JRCXZ", OF_RELATIVE | OF_IMM8 },
{ "IN", OF_IMM8 }, { "IN", OF_NONE }, { "OUT", OF_IMM8 }, { "OUT", OF_NONE },
{ "CALL", OF_RELATIVE | OF_IMM32 }, { "JMP", OF_RELATIVE | OF_IMM32 }, { "JMPF", OF_NONE }, { "JMP", OF_RELATIVE | OF_IMM8 },
{ "IN", OF_IMM8 }, { "IN", OF_NONE }, { "OUT", OF_IMM8 }, { "OUT", OF_NONE },
// 0xF0 - 0xFF
{ "LOCK", OF_NONE }, { nullptr, OF_NONE }, { "REPNE", OF_NONE }, { "REP", OF_NONE },
{ "HLT", OF_NONE }, { "CMC", OF_NONE }, { "GRP3", OF_MODRM }, { "GRP3", OF_MODRM },
{ "CLC", OF_NONE }, { "STC", OF_NONE }, { "CLI", OF_NONE }, { "STI", OF_NONE },
{ "CLD", OF_NONE }, { "STD", OF_NONE }, { "GRP4", OF_MODRM }, { "GRP5", OF_MODRM }
};
static const OpcodeInfo two_byte_opcode_table[256] = {
// 0x00 - 0x0F
{ "GRP6", OF_MODRM }, { "GRP7", OF_MODRM }, { "LAR", OF_MODRM }, { "LSL", OF_MODRM },
{ nullptr, OF_NONE }, { "SYSCALL", OF_NONE }, { "CLTS", OF_NONE }, { "SYSRET", OF_NONE },
{ "INVD", OF_NONE }, { "WBINVD", OF_NONE }, { nullptr, OF_NONE }, { "UD2", OF_NONE },
{ nullptr, OF_NONE }, { "PREFETCH", OF_MODRM }, { "FEMMS", OF_NONE }, { nullptr, OF_NONE },
// 0x10 - 0x1F (SSE)
{ "MOVUPS", OF_MODRM }, { "MOVUPS", OF_MODRM }, { "MOVSS", OF_MODRM }, { "MOVSS", OF_MODRM },
{ "MOVUPD", OF_MODRM }, { "MOVUPD", OF_MODRM }, { "MOVSD", OF_MODRM }, { "MOVSD", OF_MODRM },
{ "MOVLPS", OF_MODRM }, { "MOVLPS", OF_MODRM }, { "UNPCKLPS", OF_MODRM }, { "UNPCKHPS", OF_MODRM },
{ "MOVLPD", OF_MODRM }, { "MOVLPD", OF_MODRM }, { "UNPCKLPD", OF_MODRM }, { "UNPCKHPD", OF_MODRM },
// 0x20 - 0x2F
{ "MOV", OF_MODRM }, { "MOV", OF_MODRM }, { "MOV", OF_MODRM }, { "MOV", OF_MODRM },
{ nullptr, OF_NONE }, { nullptr, OF_NONE }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
{ "CVTPI2PS", OF_MODRM }, { "CVTTPS2PI", OF_MODRM }, { "CVTPS2PI", OF_MODRM }, { "CVTSI2SS", OF_MODRM },
{ "CVTPI2PD", OF_MODRM }, { "CVTTPD2PI", OF_MODRM }, { "CVTPD2PI", OF_MODRM }, { "CVTSI2SD", OF_MODRM },
// 0x30 - 0x3F
{ "WRMSR", OF_NONE }, { "RDTSC", OF_NONE }, { "RDMSR", OF_NONE }, { "RDPMC", OF_NONE },
{ "SYSENTER", OF_NONE }, { "SYSEXIT", OF_NONE }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
{ "THREE_BYTE_38", OF_THREEBYTE_38 }, { nullptr, OF_NONE }, { "THREE_BYTE_3A", OF_THREEBYTE_3A }, { nullptr, OF_NONE },
{ nullptr, OF_NONE }, { nullptr, OF_NONE }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
// 0x40 - 0x4F (CMOVcc)
{ "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM },
{ "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM },
{ "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM },
{ "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM }, { "CMOVcc", OF_MODRM },
// 0x50 - 0x5F (SSE)
{ "MOVMSKPS", OF_MODRM }, { "SQRTPS", OF_MODRM }, { "RSQRTPS", OF_MODRM }, { "RCPPS", OF_MODRM },
{ "ANDPS", OF_MODRM }, { "ANDNPS", OF_MODRM }, { "ORPS", OF_MODRM }, { "XORPS", OF_MODRM },
{ "ADDPS", OF_MODRM }, { "MULPS", OF_MODRM }, { "CVTPS2PD", OF_MODRM }, { "CVTDQ2PS", OF_MODRM },
{ "SUBPS", OF_MODRM }, { "MINPS", OF_MODRM }, { "DIVPS", OF_MODRM }, { "MAXPS", OF_MODRM },
// 0x60 - 0x6F (MMX/SSE)
{ "PUNPCKLBW", OF_MODRM }, { "PUNPCKLWD", OF_MODRM }, { "PUNPCKLDQ", OF_MODRM }, { "PACKSSWB", OF_MODRM },
{ "PCMPGTB", OF_MODRM }, { "PCMPGTW", OF_MODRM }, { "PCMPGTD", OF_MODRM }, { "PACKUSWB", OF_MODRM },
{ "PUNPCKHBW", OF_MODRM }, { "PUNPCKHWD", OF_MODRM }, { "PUNPCKHDQ", OF_MODRM }, { "PACKSSDW", OF_MODRM },
{ "PUNPCKLQDQ", OF_MODRM }, { "PUNPCKHQDQ", OF_MODRM }, { "MOVD", OF_MODRM }, { "MOVQ", OF_MODRM },
// 0x70 - 0x7F (SSE)
{ "PSHUFW", OF_MODRM | OF_IMM8 }, { "GRP12", OF_MODRM }, { "GRP13", OF_MODRM }, { "GRP14", OF_MODRM },
{ "PCMPEQB", OF_MODRM }, { "PCMPEQW", OF_MODRM }, { "PCMPEQD", OF_MODRM }, { "EMMS", OF_NONE },
{ "VMREAD", OF_MODRM }, { "VMWRITE", OF_MODRM }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
{ "HADDPD", OF_MODRM }, { "HSUBPD", OF_MODRM }, { "MOVD", OF_MODRM }, { "MOVQ", OF_MODRM },
// 0x80 - 0x8F (Jcc near) - THIS IS THE CRITICAL FIX
{ "Jcc", OF_RELATIVE | OF_IMM32 }, { "Jcc", OF_RELATIVE | OF_IMM32 },
{ "Jcc", OF_RELATIVE | OF_IMM32 }, { "Jcc", OF_RELATIVE | OF_IMM32 },
{ "Jcc", OF_RELATIVE | OF_IMM32 }, { "Jcc", OF_RELATIVE | OF_IMM32 },
{ "Jcc", OF_RELATIVE | OF_IMM32 }, { "Jcc", OF_RELATIVE | OF_IMM32 },
{ "Jcc", OF_RELATIVE | OF_IMM32 }, { "Jcc", OF_RELATIVE | OF_IMM32 },
{ "Jcc", OF_RELATIVE | OF_IMM32 }, { "Jcc", OF_RELATIVE | OF_IMM32 },
{ "Jcc", OF_RELATIVE | OF_IMM32 }, { "Jcc", OF_RELATIVE | OF_IMM32 },
{ "Jcc", OF_RELATIVE | OF_IMM32 }, { "Jcc", OF_RELATIVE | OF_IMM32 },
// 0x90 - 0x9F (SETcc)
{ "SETcc", OF_MODRM }, { "SETcc", OF_MODRM }, { "SETcc", OF_MODRM }, { "SETcc", OF_MODRM },
{ "SETcc", OF_MODRM }, { "SETcc", OF_MODRM }, { "SETcc", OF_MODRM }, { "SETcc", OF_MODRM },
{ "SETcc", OF_MODRM }, { "SETcc", OF_MODRM }, { "SETcc", OF_MODRM }, { "SETcc", OF_MODRM },
{ "SETcc", OF_MODRM }, { "SETcc", OF_MODRM }, { "SETcc", OF_MODRM }, { "SETcc", OF_MODRM },
// 0xA0 - 0xAF
{ "PUSH", OF_NONE }, { "POP", OF_NONE }, { "CPUID", OF_NONE }, { "BT", OF_MODRM },
{ "SHLD", OF_MODRM | OF_IMM8 }, { "SHLD", OF_MODRM }, { nullptr, OF_NONE }, { nullptr, OF_NONE },
{ "PUSH", OF_NONE }, { "POP", OF_NONE }, { "RSM", OF_NONE }, { "BTS", OF_MODRM },
{ "SHRD", OF_MODRM | OF_IMM8 }, { "SHRD", OF_MODRM }, { "GRP15", OF_MODRM }, { "IMUL", OF_MODRM },
// 0xB0 - 0xBF
{ "CMPXCHG", OF_MODRM }, { "CMPXCHG", OF_MODRM }, { "LSS", OF_MODRM }, { "BTR", OF_MODRM },
{ "LFS", OF_MODRM }, { "LGS", OF_MODRM }, { "MOVZX", OF_MODRM }, { "MOVZX", OF_MODRM },
{ "POPCNT", OF_MODRM }, { "GRP10", OF_MODRM }, { "GRP8", OF_MODRM | OF_IMM8 }, { "BTC", OF_MODRM },
{ "BSF", OF_MODRM }, { "BSR", OF_MODRM }, { "MOVSX", OF_MODRM }, { "MOVSX", OF_MODRM },
// 0xC0 - 0xCF
{ "XADD", OF_MODRM }, { "XADD", OF_MODRM }, { "CMPPS", OF_MODRM | OF_IMM8 }, { "MOVNTI", OF_MODRM },
{ "PINSRW", OF_MODRM | OF_IMM8 }, { "PEXTRW", OF_MODRM | OF_IMM8 }, { "SHUFPS", OF_MODRM | OF_IMM8 }, { "GRP9", OF_MODRM },
{ "BSWAP", OF_NONE }, { "BSWAP", OF_NONE }, { "BSWAP", OF_NONE }, { "BSWAP", OF_NONE },
{ "BSWAP", OF_NONE }, { "BSWAP", OF_NONE }, { "BSWAP", OF_NONE }, { "BSWAP", OF_NONE },
// 0xD0 - 0xDF (MMX/SSE)
{ "ADDSUBPD", OF_MODRM }, { "PSRLW", OF_MODRM }, { "PSRLD", OF_MODRM }, { "PSRLQ", OF_MODRM },
{ "PADDQ", OF_MODRM }, { "PMULLW", OF_MODRM }, { "MOVQ", OF_MODRM }, { "PMOVMSKB", OF_MODRM },
{ "PSUBUSB", OF_MODRM }, { "PSUBUSW", OF_MODRM }, { "PMINUB", OF_MODRM }, { "PAND", OF_MODRM },
{ "PADDUSB", OF_MODRM }, { "PADDUSW", OF_MODRM }, { "PMAXUB", OF_MODRM }, { "PANDN", OF_MODRM },
// 0xE0 - 0xEF (MMX/SSE)
{ "PAVGB", OF_MODRM }, { "PSRAW", OF_MODRM }, { "PSRAD", OF_MODRM }, { "PAVGW", OF_MODRM },
{ "PMULHUW", OF_MODRM }, { "PMULHW", OF_MODRM }, { "CVTTPD2DQ", OF_MODRM }, { "MOVNTQ", OF_MODRM },
{ "PSUBSB", OF_MODRM }, { "PSUBSW", OF_MODRM }, { "PMINSW", OF_MODRM }, { "POR", OF_MODRM },
{ "PADDSB", OF_MODRM }, { "PADDSW", OF_MODRM }, { "PMAXSW", OF_MODRM }, { "PXOR", OF_MODRM },
// 0xF0 - 0xFF (MMX/SSE)
{ "LDDQU", OF_MODRM }, { "PSLLW", OF_MODRM }, { "PSLLD", OF_MODRM }, { "PSLLQ", OF_MODRM },
{ "PMULUDQ", OF_MODRM }, { "PMADDWD", OF_MODRM }, { "PSADBW", OF_MODRM }, { "MASKMOVQ", OF_MODRM },
{ "PSUBB", OF_MODRM }, { "PSUBW", OF_MODRM }, { "PSUBD", OF_MODRM }, { "PSUBQ", OF_MODRM },
{ "PADDB", OF_MODRM }, { "PADDW", OF_MODRM }, { "PADDD", OF_MODRM }, { "UD0", OF_NONE }
};
// ---[ 3. Decoding and Relocation Logic ]---
static ModRM decode_modrm(uint8_t b) {
return { (uint8_t)((b >> 6) & 0x3), (uint8_t)((b >> 3) & 0x7), (uint8_t)(b & 0x7) };
}
// Return the total length of decoded instructions covering at least min_total_len bytes
size_t length_disasm_until_min_bytes(uint8_t* code, size_t max_len, size_t min_total_len) {
size_t total_len = 0;
size_t cur_offset = 0;
while (cur_offset < max_len && total_len < min_total_len) {
Instruction inst;
if (!decode_instruction(code + cur_offset, max_len - cur_offset, inst))
break;
total_len += inst.length;
cur_offset += inst.length;
}
return total_len;
}
bool decode_instruction(const uint8_t* code, size_t max_len, Instruction& out_inst) {
if (!code || max_len == 0) return false;
out_inst = {};
out_inst.address = code;
size_t idx = 0;
// Prefix Parsing
bool possible_rex = true;
while (idx < max_len) {
uint8_t current_byte = code[idx];
// VEX prefixes (C4, C5)
if (current_byte == 0xC5 && idx + 1 < max_len) {
out_inst.has_vex = true;
out_inst.vex_prefix[0] = current_byte;
out_inst.vex_prefix[1] = code[idx + 1];
out_inst.vex_pp = (code[idx + 1] >> 0) & 0x3;
out_inst.vex_L = (code[idx + 1] >> 2) & 0x1;
out_inst.vex_vvvv = (~(code[idx + 1] >> 3)) & 0xF;
idx += 2;
goto opcode_parse;
}
if (current_byte == 0xC4 && idx + 2 < max_len) {
out_inst.has_vex = true;
out_inst.vex_prefix[0] = current_byte;
out_inst.vex_prefix[1] = code[idx + 1];
out_inst.vex_prefix[2] = code[idx + 2];
out_inst.vex_pp = (code[idx + 2] >> 0) & 0x3;
out_inst.vex_L = (code[idx + 2] >> 2) & 0x1;
out_inst.vex_vvvv = (~(code[idx + 2] >> 3)) & 0xF;
idx += 3;
goto opcode_parse;
}
if (possible_rex && (current_byte >= 0x40 && current_byte <= 0x4F)) {
out_inst.rex = current_byte;
idx++;
continue;
}
switch (current_byte) {
case 0xF0: case 0xF2: case 0xF3: case 0x2E: case 0x36:
case 0x3E: case 0x26: case 0x64: case 0x65: case 0x66: case 0x67:
if (out_inst.num_prefixes < 4) {
out_inst.prefixes[out_inst.num_prefixes++] = current_byte;
}
idx++;
possible_rex = false;
continue;
}
break;
}
opcode_parse:
if (idx >= max_len) return false;
uint8_t opcode_byte = code[idx++];
const OpcodeInfo* info = &main_opcode_table[opcode_byte];
if (info->flags & OF_TWOBYTE) {
if (idx >= max_len) return false;
opcode_byte = code[idx++];
info = &two_byte_opcode_table[opcode_byte];
out_inst.is_two_byte = true;
// Further check for three-byte opcodes, though tables are sparse
if ((info->flags & OF_THREEBYTE_38) || (info->flags & OF_THREEBYTE_3A)) {
// In a full implementation, you'd have 0F38/0F3A tables
if (idx >= max_len) return false;
opcode_byte = code[idx++];
// info = &three_byte_table[opcode_byte];
out_inst.is_three_byte = true;
}
}
if (!info || !info->mnemonic) {
out_inst.length = idx;
out_inst.mnemonic = "unknown";
return true;
}
out_inst.mnemonic = info->mnemonic;
out_inst.opcode = opcode_byte;
if (info->flags & OF_MODRM) {
if (idx >= max_len) return false;
out_inst.has_modrm = true;
uint8_t modrm_byte = code[idx++];
out_inst.modrm = modrm_byte;
ModRM modrm = decode_modrm(modrm_byte);
if (modrm.mod != 3 && modrm.rm == 4) {
if (idx >= max_len) return false;
out_inst.has_sib = true;
out_inst.sib = code[idx++];
}
if (modrm.mod == 1) {
if (idx >= max_len) return false;
out_inst.disp_offset = idx;
out_inst.displacement = *reinterpret_cast<const int8_t*>(&code[idx]);
idx++;
}
else if (modrm.mod == 2 || (modrm.mod == 0 && modrm.rm == 5)) {
if (idx + 4 > max_len) return false;
out_inst.disp_offset = idx;
out_inst.displacement = *reinterpret_cast<const int32_t*>(&code[idx]);
if (modrm.mod == 0 && modrm.rm == 5) out_inst.is_rip_relative = true;
idx += 4;
}
}
size_t imm_size = 0;
if (info->flags & OF_IMM8) imm_size = 1;
else if (info->flags & OF_IMM16) imm_size = 2;
else if (info->flags & OF_IMM32) imm_size = 4;
else if ((info->flags & OF_IMM64) || (out_inst.rex & 0x08 && opcode_byte >= 0xB8 && opcode_byte <= 0xBF)) {
imm_size = 8;
}
if (imm_size > 0) {
if (idx + imm_size > max_len) return false;
out_inst.imm_offset = idx;
switch (imm_size) {
case 1: out_inst.immediate = *reinterpret_cast<const int8_t*>(&code[idx]); break;
case 2: out_inst.immediate = *reinterpret_cast<const int16_t*>(&code[idx]); break;
case 4: out_inst.immediate = *reinterpret_cast<const int32_t*>(&code[idx]); break;
case 8: out_inst.immediate = *reinterpret_cast<const int64_t*>(&code[idx]); break;
}
idx += imm_size;
}
out_inst.length = idx;
return true;
}
void write_absolute_jump(uint8_t* buffer, void* destination) {
buffer[0] = 0xFF;
buffer[1] = 0x25;
*reinterpret_cast<int32_t*>(&buffer[2]) = 0;
*reinterpret_cast<uint64_t*>(&buffer[6]) = reinterpret_cast<uint64_t>(destination);
}
size_t get_relocated_length(const Instruction& inst) {
// Handle decomposition of LOOP instructions. They become larger.
// LOOP/LOOPZ/LOOPNZ are 2 bytes originally.
switch (inst.opcode) {
case 0xE0: // LOOPNE/LOOPNZ: dec ecx; jne/jnz target
case 0xE1: // LOOPE/LOOPZ: dec ecx; je/jz target
case 0xE2: // LOOP: dec ecx; jne/jnz target
// Decomposes to: DEC ECX (1 byte) + Jcc NEAR (6 bytes) = 7 bytes.
return 7;
case 0xE3: // JRCXZ: test rcx,rcx; jz target
// Decomposes to: TEST RCX,RCX (3 bytes) + JZ NEAR (6 bytes) = 9 bytes.
return 9;
}
if (inst.mnemonic == "Jcc") {
// Expanded to J(!cc) NEAR skip + JMP ABS target
return 20;
}
if (inst.mnemonic == "JMP" || inst.mnemonic == "CALL") {
const OpcodeInfo* info = inst.is_two_byte ? &two_byte_opcode_table[inst.opcode] : &main_opcode_table[inst.opcode];
if (info && (info->flags & OF_RELATIVE)) {
// Expanded to JMP ABS target
return 14;
}
}
// All other instructions do not change in length.
return inst.length;
}
// Follows a chain of JMP instructions to find the real function address.
uint8_t* resolve_jumps(uint8_t* func_ptr) {
// We'll follow a maximum of 10 jumps to prevent infinite loops.
for (int i = 0; i < 10; ++i) {
Instruction inst;
if (!decode_instruction(func_ptr, 16, inst)) {
// Cannot decode, assume it's the real function.
return func_ptr;
}
// We are only interested in unconditional jumps.
if (inst.mnemonic != "JMP") {
return func_ptr;
}
// This is the absolute address the JMP instruction points to.
uint64_t target_address = 0;
// Check if it's a relative JMP (e.g., JMP 0x...).
const OpcodeInfo* info = inst.is_two_byte
? &two_byte_opcode_table[inst.opcode]
: &main_opcode_table[inst.opcode];
if (info && (info->flags & OF_RELATIVE)) {
uint64_t next_ip = reinterpret_cast<uint64_t>(func_ptr) + inst.length;
target_address = next_ip + inst.immediate;
}
// Check if it's a JMP via a memory pointer (e.g., JMP [rip+0x...]).
else if (inst.is_rip_relative) {
uint64_t next_ip = reinterpret_cast<uint64_t>(func_ptr) + inst.length;
uint64_t pointer_address = next_ip + inst.displacement;
// Read the 64-bit address from that memory location.
target_address = *reinterpret_cast<uint64_t*>(pointer_address);
}
else {
// It's another type of JMP (e.g., JMP RAX) that we can't resolve.
// Assume we're at the destination.
return func_ptr;
}
func_ptr = reinterpret_cast<uint8_t*>(target_address);
}
// If we've jumped 10 times, we're probably in a loop, return what we have.
return func_ptr;
}
bool relocate_instruction(const Instruction& inst, uint8_t* trampoline_ptr, size_t& trampoline_len) {
uint64_t original_addr = reinterpret_cast<uint64_t>(inst.address);
uint64_t trampoline_addr = reinterpret_cast<uint64_t>(trampoline_ptr);
uint64_t original_next_ip = original_addr + inst.length;
// The absolute address of the original jump/call/loop target
uint64_t original_target = original_next_ip + inst.immediate;
// --- Special Handling for LOOP/JRCXZ Instructions ---
switch (inst.opcode) {
case 0xE0: // LOOPNE/LOOPNZ target -> DEC ECX; JNE target
case 0xE1: // LOOPE/LOOPZ target -> DEC ECX; JE target
case 0xE2: // LOOP target -> DEC ECX; JNZ target
{
// Get the corresponding Jcc near opcode (0x80 series)
uint8_t jcc_opcode;
if (inst.opcode == 0xE0) jcc_opcode = 0x85; // JNE
else if (inst.opcode == 0xE1) jcc_opcode = 0x84; // JE
else jcc_opcode = 0x85; // JNZ for LOOP
// 1. Write 'DEC ECX' (opcode FF C9)
// Note: LOOP instructions use ECX even in 64-bit mode unless a REX.W prefix is used, which is rare.
trampoline_ptr[0] = 0xFF;
trampoline_ptr[1] = 0xC9;
// 2. Write the 6-byte near conditional jump to the absolute target
trampoline_ptr[2] = 0x0F;
trampoline_ptr[3] = jcc_opcode;
int32_t relative_offset = static_cast<int32_t>(original_target - (trampoline_addr + 8)); // Target - End of this instruction
*reinterpret_cast<int32_t*>(&trampoline_ptr[4]) = relative_offset;
trampoline_len = 8; // DEC ECX (2 bytes) + Jcc NEAR (6 bytes)
return true;
}
case 0xE3: // JRCXZ target -> TEST RCX, RCX; JZ target
{
// 1. Write 'TEST RCX, RCX' (opcode 48 85 C9)
trampoline_ptr[0] = 0x48;
trampoline_ptr[1] = 0x85;
trampoline_ptr[2] = 0xC9;
// 2. Write 'JZ NEAR' (opcode 0F 84) to the absolute target
trampoline_ptr[3] = 0x0F;
trampoline_ptr[4] = 0x84; // JZ
int32_t relative_offset = static_cast<int32_t>(original_target - (trampoline_addr + 9)); // Target - End of this instruction
*reinterpret_cast<int32_t*>(&trampoline_ptr[5]) = relative_offset;
trampoline_len = 9; // TEST (3 bytes) + JZ NEAR (6 bytes)
return true;
}
}
// For all other instructions, start by copying the original bytes
memcpy(trampoline_ptr, inst.address, inst.length);
trampoline_len = inst.length;
if (inst.is_rip_relative) {
uint64_t rip_rel_target = original_next_ip + inst.displacement;
int32_t new_disp = static_cast<int32_t>(rip_rel_target - (trampoline_addr + inst.length));
*reinterpret_cast<int32_t*>(trampoline_ptr + inst.disp_offset) = new_disp;
return true;
}
const OpcodeInfo* info = inst.is_two_byte ? &two_byte_opcode_table[inst.opcode] : &main_opcode_table[inst.opcode];
if (info && (info->flags & OF_RELATIVE)) {
if (inst.mnemonic == "JMP" || inst.mnemonic == "CALL") {
write_absolute_jump(trampoline_ptr, reinterpret_cast<void*>(original_target));
trampoline_len = 14;
}
else if (inst.mnemonic == "Jcc") {
uint8_t condition = inst.opcode & 0x0F;
uint8_t opposite_condition = condition ^ 1;
trampoline_ptr[0] = 0x0F;
trampoline_ptr[1] = 0x80 + opposite_condition;
*reinterpret_cast<int32_t*>(&trampoline_ptr[2]) = 14;
write_absolute_jump(trampoline_ptr + 6, reinterpret_cast<void*>(original_target));
trampoline_len = 20;
}
}
return true;
}
uint8_t* resolve_jump_thunk(uint8_t* fn, int depth) {
if (depth > 10)
return fn; // Prevent infinite recursion
Instruction inst;
// The instruction decoder is used here to find JMPs
if (!decode_instruction(fn, 15, inst) || inst.mnemonic != "JMP") {
return fn;
}
uint64_t target = 0;
uint64_t next_ip = reinterpret_cast<uint64_t>(fn) + inst.length;
// JMP rel32 (E9) or JMP rel8 (EB)
if (inst.opcode == 0xE9 || inst.opcode == 0xEB) {
target = next_ip + inst.immediate;
}
// JMP r/m64 (FF /4) with RIP-relative addressing
else if (inst.opcode == 0xFF && inst.is_rip_relative) {
uint8_t* effective_address = reinterpret_cast<uint8_t*>(next_ip + inst.displacement);
if (!IsBadReadPtr(effective_address, sizeof(uint64_t))) {
target = *reinterpret_cast<uint64_t*>(effective_address);
}
else {
return fn; // Can't read memory
}
}
else {
return fn; // Not a simple JMP we can resolve
}
return resolve_jump_thunk(reinterpret_cast<uint8_t*>(target), depth + 1);
}
} // namespace decoder
.h
c++:
namespace decoder {
// Global array of 64-bit register names for easy lookup.
static const char* reg64[16] = {
"RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI",
"R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15"
};
// Defines the type of an operand (Register, Memory, or Immediate value).
enum class OperandType {
REG,
MEM,
IMM
};
// Represents a single operand for an instruction.
struct Operand {
OperandType type;
uint8_t size;
int64_t value = 0;
std::string text; // Formatted text for the operand (e.g., "RAX", "[RIP+0x1234]").
};
// Represents the SIB byte, used for complex memory addressing.
struct SIB {
uint8_t scale, index, base;
};
struct Instruction {
const uint8_t* address;
uint8_t length;
std::string mnemonic;
uint8_t prefixes[4];
uint8_t num_prefixes;
uint8_t rex;
uint8_t opcode;
bool is_two_byte;
bool is_three_byte; // For 0F 38/3A opcodes
bool has_modrm;
uint8_t modrm;
bool has_sib;
uint8_t sib;
uint8_t disp_offset;
int32_t displacement;
bool is_rip_relative;
uint8_t imm_offset;
int64_t immediate;
// VEX Prefix Info
bool has_vex;
uint8_t vex_prefix[3];
uint8_t vex_L;
uint8_t vex_pp;
uint8_t vex_vvvv;
};
// Represents the ModR/M byte fields.
struct ModRM {
uint8_t mod;
uint8_t reg;
uint8_t rm;
};
// Describes the properties of an opcode.
enum OpcodeFlags : uint32_t {
OF_NONE = 0,
OF_MODRM = 1 << 0, // Instruction has a ModR/M byte
OF_IMM8 = 1 << 1, // Instruction has an 8-bit immediate
OF_IMM16 = 1 << 2, // Instruction has a 16-bit immediate
OF_IMM32 = 1 << 3, // Instruction has a 32-bit immediate
OF_IMM64 = 1 << 4, // Instruction has a 64-bit immediate
OF_RELATIVE = 1 << 5, // Immediate is a relative offset (for JMP, CALL, Jcc)
OF_TWOBYTE = 1 << 6, // Indicates a two-byte opcode map (0F xx)
OF_THREEBYTE_38 = 1 << 7, // Three-byte opcode map (0F 38 xx)
OF_THREEBYTE_3A = 1 << 8, // Three-byte opcode map (0F 3A xx)
};
// Maps an opcode to its mnemonic and properties.
struct OpcodeInfo {
const char* mnemonic;
uint32_t flags;
};
bool decode_instruction(const uint8_t* code, size_t max_len, Instruction& out_inst);;
bool relocate_instruction(const Instruction& inst, uint8_t* trampoline_ptr, size_t& trampoline_len);
uint8_t* resolve_jump_thunk(uint8_t* fn, int depth);
void write_absolute_jump(uint8_t* buffer, void* destination);
size_t length_disasm_until_min_bytes(uint8_t* code, size_t max_len, size_t min_total_len);
size_t get_relocated_length(const Instruction& inst);
uint8_t* resolve_jumps(uint8_t* func_ptr);
} // namespace decoder
Вложения
Последнее редактирование: