diff --git a/src/Encoder.c b/src/Encoder.c index c64ea1bc..49f7e303 100644 --- a/src/Encoder.c +++ b/src/Encoder.c @@ -4470,6 +4470,22 @@ ZYDIS_EXPORT ZyanStatus ZydisEncoderEncodeInstructionAbsolute(ZydisEncoderReques } op->imm.s = rel; adjusted_rel = ZYAN_TRUE; + if (rel_info->accepts_scaling_hints == ZYDIS_SIZE_HINT_NONE) + { + if (request->branch_width == ZYDIS_BRANCH_WIDTH_NONE) + { + request->branch_width = + (ZydisBranchWidth)(ZYDIS_BRANCH_WIDTH_8 + size_index); + } + } + else + { + if (request->operand_size_hint == ZYDIS_OPERAND_SIZE_HINT_NONE) + { + request->operand_size_hint = + (ZydisOperandSizeHint)(ZYDIS_OPERAND_SIZE_HINT_8 + size_index); + } + } break; } break; diff --git a/tools/ZydisTestEncoderAbsolute.c b/tools/ZydisTestEncoderAbsolute.c index 915b71e2..4fcd80b2 100644 --- a/tools/ZydisTestEncoderAbsolute.c +++ b/tools/ZydisTestEncoderAbsolute.c @@ -39,7 +39,7 @@ /* Enums and Types */ /* ============================================================================================== */ -#define TEST_RUNTIME_ADDRESS 0x00004000 +#define TEST_RUNTIME_ADDRESS 0x00004000ULL typedef struct Iterator_ { @@ -47,6 +47,12 @@ typedef struct Iterator_ ZyanU32 limit; } Iterator; +typedef struct DecodedInstruction_ +{ + ZydisDecodedInstruction insn; + ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; +} DecodedInstruction; + /* ============================================================================================== */ /* Helper functions */ /* ============================================================================================== */ @@ -80,26 +86,27 @@ static void PrintBytes(ZyanU8 *bytes, ZyanUSize count) } } -/* ============================================================================================== */ -/* Tests */ -/* ============================================================================================== */ - -static ZyanBool RunTest(ZydisEncoderRequest *req, const char *test_name, ZyanU8 rel_op_index, - ZyanBool is_rip_rel_test) +static ZyanI8 GetRelativeOperandIndex(const ZydisEncoderRequest *req) { - ZyanU8 instruction1[ZYDIS_MAX_INSTRUCTION_LENGTH]; - ZyanUSize length1 = sizeof(instruction1); - if (ZYAN_FAILED(ZydisEncoderEncodeInstruction(req, instruction1, &length1))) + for (ZyanU8 i = 0; i < req->operand_count; ++i) { - ZYAN_PRINTF("%s: NOT ENCODABLE\n", test_name); - return ZYAN_TRUE; + const ZyanBool is_rip_rel = (req->operands[i].type == ZYDIS_OPERAND_TYPE_MEMORY) && + ((req->operands[i].mem.base == ZYDIS_REGISTER_EIP) || + (req->operands[i].mem.base == ZYDIS_REGISTER_RIP)); + if (is_rip_rel || (req->operands[i].type == ZYDIS_OPERAND_TYPE_IMMEDIATE)) + { + return (ZyanI8)i; + } } + return -1; +} +static ZyanBool Disassemble(DecodedInstruction *decoded, ZyanU8 *bytes, ZyanUSize size, + ZydisMachineMode machine_mode) +{ ZydisDecoder decoder; ZydisStackWidth stack_width; - ZydisDecodedInstruction dec_instruction; - ZydisDecodedOperand dec_operands[ZYDIS_MAX_OPERAND_COUNT]; - switch (req->machine_mode) + switch (machine_mode) { case ZYDIS_MACHINE_MODE_LONG_COMPAT_16: stack_width = ZYDIS_STACK_WIDTH_16; @@ -113,19 +120,45 @@ static ZyanBool RunTest(ZydisEncoderRequest *req, const char *test_name, ZyanU8 default: ZYAN_UNREACHABLE; } - if (ZYAN_FAILED(ZydisDecoderInit(&decoder, req->machine_mode, stack_width))) + if (ZYAN_FAILED(ZydisDecoderInit(&decoder, machine_mode, stack_width))) + { + return ZYAN_FALSE; + } + if (ZYAN_FAILED(ZydisDecoderDecodeFull(&decoder, bytes, size, &decoded->insn, + decoded->operands))) + { + return ZYAN_FALSE; + } + return ZYAN_TRUE; +} + +/* ============================================================================================== */ +/* Tests */ +/* ============================================================================================== */ + +static ZyanBool RunTest(ZydisEncoderRequest *req, const char *test_name, ZyanBool is_rip_rel_test) +{ + ZyanU8 instruction1[ZYDIS_MAX_INSTRUCTION_LENGTH]; + ZyanUSize length1 = sizeof(instruction1); + if (ZYAN_FAILED(ZydisEncoderEncodeInstruction(req, instruction1, &length1))) + { + ZYAN_PRINTF("%s: NOT ENCODABLE\n", test_name); + return ZYAN_TRUE; + } + ZyanI8 rel_op_index = GetRelativeOperandIndex(req); + if (rel_op_index < 0) { - ZYAN_PRINTF("%s: FAILED TO INITIALIZE DECODER\n", test_name); + ZYAN_PRINTF("%s: NO RELATIVE OPERAND FOUND\n", test_name); return ZYAN_FALSE; } - if (ZYAN_FAILED(ZydisDecoderDecodeFull(&decoder, instruction1, sizeof(instruction1), - &dec_instruction, dec_operands))) + DecodedInstruction decoded; + if (!Disassemble(&decoded, instruction1, length1, req->machine_mode)) { - ZYAN_PRINTF("%s: FAILED TO DECODE INSTRUCTION\n", test_name); + ZYAN_PRINTF("%s: FAILED TO DISASSEMBLE\n", test_name); return ZYAN_FALSE; } ZyanU64 absolute_address = 0; - if (ZYAN_FAILED(ZydisCalcAbsoluteAddress(&dec_instruction, &dec_operands[rel_op_index], + if (ZYAN_FAILED(ZydisCalcAbsoluteAddress(&decoded.insn, &decoded.operands[rel_op_index], TEST_RUNTIME_ADDRESS, &absolute_address))) { ZYAN_PRINTF("%s: FAILED TO COMPUTE ABSOLUTE ADDRESS\n", test_name); @@ -163,6 +196,59 @@ static ZyanBool RunTest(ZydisEncoderRequest *req, const char *test_name, ZyanU8 return ZYAN_TRUE; } +static ZyanBool RunTestAbsolute(ZydisEncoderRequest *req, const char *test_name, + ZyanU64 runtime_address) +{ + ZyanI8 rel_op_index = GetRelativeOperandIndex(req); + if (rel_op_index < 0) + { + ZYAN_PRINTF("%s: NO RELATIVE OPERAND FOUND\n", test_name); + return ZYAN_FALSE; + } + ZyanU64 desired_address; + const ZydisEncoderOperand *rel_op = &req->operands[rel_op_index]; + if (rel_op->type == ZYDIS_OPERAND_TYPE_IMMEDIATE) + { + desired_address = rel_op->imm.u; + } + else if (rel_op->type == ZYDIS_OPERAND_TYPE_MEMORY) + { + desired_address = rel_op->mem.displacement; + } + else + { + return ZYAN_FALSE; + } + ZyanU8 instruction[ZYDIS_MAX_INSTRUCTION_LENGTH]; + ZyanUSize length = sizeof(instruction); + if (ZYAN_FAILED(ZydisEncoderEncodeInstructionAbsolute(req, instruction, &length, + runtime_address))) + { + ZYAN_PRINTF("%s: FAILED TO ENCODE INSTRUCTION\n", test_name); + return ZYAN_FALSE; + } + DecodedInstruction decoded; + if (!Disassemble(&decoded, instruction, length, req->machine_mode)) + { + ZYAN_PRINTF("%s: FAILED TO DISASSEMBLE\n", test_name); + return ZYAN_FALSE; + } + ZyanU64 absolute_address = 0; + if (ZYAN_FAILED(ZydisCalcAbsoluteAddress(&decoded.insn, &decoded.operands[rel_op_index], + runtime_address, &absolute_address))) + { + ZYAN_PRINTF("%s: FAILED TO COMPUTE ABSOLUTE ADDRESS\n", test_name); + return ZYAN_FALSE; + } + if (absolute_address != desired_address) + { + ZYAN_PRINTF("%s: %016" PRIX64 " != %016" PRIX64 "\n", test_name, absolute_address, + desired_address); + return ZYAN_FALSE; + } + return ZYAN_TRUE; +} + static ZyanBool RunBranchingTests(void) { static const ZydisMnemonic instructions[] = @@ -303,9 +389,6 @@ static ZyanBool RunBranchingTests(void) req.prefixes = prefix; req.branch_type = branch_type; req.branch_width = branch_width; - req.operand_count = 1; - req.operands[0].type = ZYDIS_OPERAND_TYPE_IMMEDIATE; - req.operands[0].imm.u = rel; if (mnemonic != ZYDIS_MNEMONIC_JKZD) { req.operand_count = 1; @@ -329,8 +412,7 @@ static ZyanBool RunBranchingTests(void) str_branch_types[iter_branches[3].value], str_branch_widths[iter_branches[4].value], str_prefixes[iter_branches[5].value]); - all_passed &= RunTest(&req, test_name, (mnemonic != ZYDIS_MNEMONIC_JKZD) ? 0 : 1, - ZYAN_FALSE); + all_passed &= RunTest(&req, test_name, ZYAN_FALSE); } while (AdvanceIterators(iter_branches, ZYAN_ARRAY_LENGTH(iter_branches))); Iterator iter_asz_branches[4] = @@ -364,7 +446,7 @@ static ZyanBool RunBranchingTests(void) str_branch_types[iter_asz_branches[1].value], str_branch_widths[iter_asz_branches[2].value], str_address_hints[iter_asz_branches[3].value]); - all_passed &= RunTest(&req, test_name, 0, ZYAN_FALSE); + all_passed &= RunTest(&req, test_name, ZYAN_FALSE); } while (AdvanceIterators(iter_asz_branches, ZYAN_ARRAY_LENGTH(iter_asz_branches))); Iterator iter_osz_branches[3] = @@ -393,7 +475,7 @@ static ZyanBool RunBranchingTests(void) str_modes[iter_osz_branches[0].value], rel, str_operand_hints[iter_osz_branches[2].value]); - all_passed &= RunTest(&req, test_name, 0, ZYAN_FALSE); + all_passed &= RunTest(&req, test_name, ZYAN_FALSE); } while (AdvanceIterators(iter_osz_branches, ZYAN_ARRAY_LENGTH(iter_osz_branches))); return all_passed; @@ -415,7 +497,7 @@ static ZyanBool RunRipRelativeTests(void) req.operands[1].mem.base = ZYDIS_REGISTER_RIP; req.operands[1].mem.displacement = 0x66666666; req.operands[1].mem.size = 8; - all_passed &= RunTest(&req, ZydisMnemonicGetString(req.mnemonic), 1, ZYAN_TRUE); + all_passed &= RunTest(&req, ZydisMnemonicGetString(req.mnemonic), ZYAN_TRUE); // Displacement + immediate ZYAN_MEMSET(&req, 0, sizeof(req)); @@ -428,7 +510,7 @@ static ZyanBool RunRipRelativeTests(void) req.operands[0].mem.size = 4; req.operands[1].type = ZYDIS_OPERAND_TYPE_IMMEDIATE; req.operands[1].imm.u = 0x11223344; - all_passed &= RunTest(&req, ZydisMnemonicGetString(req.mnemonic), 0, ZYAN_TRUE); + all_passed &= RunTest(&req, ZydisMnemonicGetString(req.mnemonic), ZYAN_TRUE); // EIP-relative ZYAN_MEMSET(&req, 0, sizeof(req)); @@ -441,7 +523,7 @@ static ZyanBool RunRipRelativeTests(void) req.operands[0].mem.size = 4; req.operands[1].type = ZYDIS_OPERAND_TYPE_REGISTER; req.operands[1].reg.value = ZYDIS_REGISTER_EBX; - all_passed &= RunTest(&req, ZydisMnemonicGetString(req.mnemonic), 0, ZYAN_TRUE); + all_passed &= RunTest(&req, ZydisMnemonicGetString(req.mnemonic), ZYAN_TRUE); // AMD 3DNow! ZYAN_MEMSET(&req, 0, sizeof(req)); @@ -454,8 +536,91 @@ static ZyanBool RunRipRelativeTests(void) req.operands[1].mem.base = ZYDIS_REGISTER_RIP; req.operands[1].mem.displacement = 0x66666666; req.operands[1].mem.size = 8; - all_passed &= RunTest(&req, ZydisMnemonicGetString(req.mnemonic), 1, ZYAN_TRUE); + all_passed &= RunTest(&req, ZydisMnemonicGetString(req.mnemonic), ZYAN_TRUE); + + return all_passed; +} + +static ZyanBool RunRangeTests(void) +{ + static const ZydisMnemonic instructions[] = + { + ZYDIS_MNEMONIC_CALL, + ZYDIS_MNEMONIC_JZ, + ZYDIS_MNEMONIC_JKZD, + ZYDIS_MNEMONIC_JMP, + ZYDIS_MNEMONIC_XBEGIN, + }; + static const ZydisMachineMode modes[] = + { + ZYDIS_MACHINE_MODE_LONG_COMPAT_16, + ZYDIS_MACHINE_MODE_LONG_COMPAT_32, + ZYDIS_MACHINE_MODE_LONG_64, + }; + static const ZyanU8 mode_widths[] = + { + 16, + 32, + 64, + }; + static const ZyanU64 offsets[] = + { + 0x80, + 0x8000, + }; + ZydisEncoderRequest req; + ZyanBool all_passed = ZYAN_TRUE; + Iterator iterators[3] = + { + { 0, ZYAN_ARRAY_LENGTH(instructions) }, + { 0, ZYAN_ARRAY_LENGTH(modes) }, + { 0, ZYAN_ARRAY_LENGTH(offsets) }, + }; + do + { + ZydisMnemonic mnemonic = instructions[iterators[0].value]; + ZydisMachineMode mode = modes[iterators[1].value]; + ZyanU64 offset = offsets[iterators[2].value]; + const ZydisEncoderRelInfo *rel_info = ZydisGetRelInfo(mnemonic); + static const ZyanU8 empty_row[3] = { 0, 0, 0 }; + if (!memcmp(rel_info->size[iterators[1].value], empty_row, sizeof(empty_row))) + { + continue; + } + for (int i = 0; i < 8; ++i) + { + ZyanU64 target_address = TEST_RUNTIME_ADDRESS + offset + i; + ZYAN_MEMSET(&req, 0, sizeof(req)); + req.machine_mode = mode; + req.mnemonic = mnemonic; + if (mnemonic != ZYDIS_MNEMONIC_JKZD) + { + req.operand_count = 1; + req.operands[0].type = ZYDIS_OPERAND_TYPE_IMMEDIATE; + req.operands[0].imm.u = target_address; + } + else + { + req.operand_count = 2; + req.operands[0].type = ZYDIS_OPERAND_TYPE_REGISTER; + req.operands[0].reg.value = ZYDIS_REGISTER_K1; + req.operands[1].type = ZYDIS_OPERAND_TYPE_IMMEDIATE; + req.operands[1].imm.u = target_address; + } + char test_name[256]; + snprintf(test_name, sizeof(test_name), "M%02u:%016" PRIX64 ":%s", + mode_widths[mode], + target_address, + ZydisMnemonicGetString(mnemonic)); + all_passed &= RunTestAbsolute(&req, test_name, TEST_RUNTIME_ADDRESS); + } + } while (AdvanceIterators(iterators, ZYAN_ARRAY_LENGTH(iterators))); + + if (all_passed) + { + ZYAN_PRINTF("All range tests passed\n"); + } return all_passed; } @@ -470,6 +635,8 @@ int main(void) all_passed &= RunBranchingTests(); ZYAN_PRINTF("\nEIP/RIP-relative tests:\n"); all_passed &= RunRipRelativeTests(); + ZYAN_PRINTF("\nRange tests:\n"); + all_passed &= RunRangeTests(); ZYAN_PRINTF("\n"); if (!all_passed) {