Skip to content

Commit

Permalink
Enforce predicted instruction size (Fixes #478)
Browse files Browse the repository at this point in the history
  • Loading branch information
mappzor authored and athre0z committed Feb 4, 2024
1 parent df34a98 commit 4a151f8
Show file tree
Hide file tree
Showing 2 changed files with 215 additions and 32 deletions.
16 changes: 16 additions & 0 deletions src/Encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
231 changes: 199 additions & 32 deletions tools/ZydisTestEncoderAbsolute.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,20 @@
/* Enums and Types */
/* ============================================================================================== */

#define TEST_RUNTIME_ADDRESS 0x00004000
#define TEST_RUNTIME_ADDRESS 0x00004000ULL

typedef struct Iterator_
{
ZyanU32 value;
ZyanU32 limit;
} Iterator;

typedef struct DecodedInstruction_
{
ZydisDecodedInstruction insn;
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
} DecodedInstruction;

/* ============================================================================================== */
/* Helper functions */
/* ============================================================================================== */
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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[] =
Expand Down Expand Up @@ -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;
Expand All @@ -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] =
Expand Down Expand Up @@ -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] =
Expand Down Expand Up @@ -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;
Expand All @@ -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));
Expand All @@ -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));
Expand All @@ -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));
Expand All @@ -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;
}

Expand All @@ -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)
{
Expand Down

0 comments on commit 4a151f8

Please sign in to comment.