diff --git a/include/Zydis/Encoder.h b/include/Zydis/Encoder.h index b945fb42..8748ae86 100644 --- a/include/Zydis/Encoder.h +++ b/include/Zydis/Encoder.h @@ -302,6 +302,13 @@ typedef struct ZydisEncoderRequest_ * encoder deduces address size from `ZydisEncoderOperand` structures that represent * explicit and implicit operands. This hint resolves conflicts when instruction's hidden * operands scale with address size attribute. + * + * This hint is also used for instructions with absolute memory addresses (memory operands + * with displacement and no registers). Since displacement field is a 64-bit signed integer + * it's not possible to determine desired address mode correctly in all situations. Use + * `ZYDIS_ADDRESS_SIZE_HINT_NONE` to prefer address size default for specified machine mode. + * All other `ZYDIS_ADDRESS_SIZE_*` values will force specific address size or cause encoding + * to fail when it isn't possible to encode address provided. */ ZydisAddressSizeHint address_size_hint; /** diff --git a/src/Encoder.c b/src/Encoder.c index 12147ad0..39a78282 100644 --- a/src/Encoder.c +++ b/src/Encoder.c @@ -1578,17 +1578,6 @@ static ZyanBool ZydisIsMemoryOperandCompatible(ZydisEncoderInstructionMatch *mat { return ZYAN_FALSE; } - if (ZydisGetMachineModeWidth(match->request->machine_mode) == 16) - { - if ((ZyanI16)displacement == 0) - { - disp_size = 0; - } - else - { - disp_size = ZydisGetSignedImmSize((ZyanI16)displacement); - } - } match->cd8_scale = ZydisGetCompDispScale(match); if (match->cd8_scale) @@ -1596,7 +1585,8 @@ static ZyanBool ZydisIsMemoryOperandCompatible(ZydisEncoderInstructionMatch *mat const ZyanI64 mask = (1 << match->cd8_scale) - 1; if (!(displacement & mask)) { - disp_size = ZydisGetSignedImmSize(displacement >> match->cd8_scale); + if (ZydisGetSignedImmSize(displacement >> match->cd8_scale) == 8) + disp_size = 8; } else if (disp_size == 8) { @@ -1941,7 +1931,7 @@ static ZyanBool ZydisIsMemoryOperandCompatible(ZydisEncoderInstructionMatch *mat reg_index_class); } } - else + else if (disp_size != 8 || !match->cd8_scale) { ZyanU8 min_disp_size = match->easz ? match->easz : 16; if (((min_disp_size == 16) && !(match->definition->address_sizes & ZYDIS_WIDTH_16)) || @@ -1949,19 +1939,40 @@ static ZyanBool ZydisIsMemoryOperandCompatible(ZydisEncoderInstructionMatch *mat { min_disp_size = 32; } - if (ZydisGetUnsignedImmSize(displacement) == 16) - { - disp_size = 16; - } if (disp_size < min_disp_size) { disp_size = min_disp_size; } - if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) + const ZyanU8 mode_width = ZydisGetMachineModeWidth(match->request->machine_mode); + switch (match->request->address_size_hint) { - candidate_easz = match->easz == 32 ? 32 : 64; + case ZYDIS_ADDRESS_SIZE_HINT_NONE: + if (mode_width >= 32) + { + disp_size = 32; + } + if (mode_width == 64) + { + candidate_easz = 64; + } + break; + case ZYDIS_ADDRESS_SIZE_HINT_16: + if (disp_size != 16) + { + return ZYAN_FALSE; + } + break; + case ZYDIS_ADDRESS_SIZE_HINT_32: + disp_size = 32; + break; + case ZYDIS_ADDRESS_SIZE_HINT_64: + disp_size = 32; + candidate_easz = 64; + break; + default: + ZYAN_UNREACHABLE; } - else + if (candidate_easz == 0) { candidate_easz = disp_size; } diff --git a/tests/re_enc_test_cases.json b/tests/re_enc_test_cases.json index e40efc2f..afd5b295 100644 --- a/tests/re_enc_test_cases.json +++ b/tests/re_enc_test_cases.json @@ -706,5 +706,29 @@ "stack_width": "ZYDIS_STACK_WIDTH_64", "payload": "CACACA", "description": "ret far 0xCACA" + }, + { + "machine_mode": "ZYDIS_MACHINE_MODE_LONG_COMPAT_32", + "stack_width": "ZYDIS_STACK_WIDTH_32", + "payload": "003D00FFFFFF", + "description": "add byte ptr ds:[0xFFFFFF00], bh" + }, + { + "machine_mode": "ZYDIS_MACHINE_MODE_LONG_COMPAT_32", + "stack_width": "ZYDIS_STACK_WIDTH_32", + "payload": "67003E00FF", + "description": "add byte ptr ds:[0xFF00], bh" + }, + { + "machine_mode": "ZYDIS_MACHINE_MODE_LONG_64", + "stack_width": "ZYDIS_STACK_WIDTH_64", + "payload": "678C0CE5E5E5E5E5E5E5E5E5E5E5E5", + "description": "mov word ptr ds:[0xE5E5E5E5], cs" + }, + { + "machine_mode": "ZYDIS_MACHINE_MODE_LONG_COMPAT_32", + "stack_width": "ZYDIS_STACK_WIDTH_16", + "payload": "62E27D4F002D0000020000006291FF", + "description": "vpshufb zmm5 {k7}, zmm0, zmmword ptr ds:[0x00020000]" } ] \ No newline at end of file diff --git a/tools/ZydisFuzzShared.c b/tools/ZydisFuzzShared.c index ab4935a1..3e352d5d 100644 --- a/tools/ZydisFuzzShared.c +++ b/tools/ZydisFuzzShared.c @@ -285,46 +285,40 @@ void ZydisValidateInstructionIdentity(const ZydisDecodedInstruction* insn1, } break; case ZYDIS_OPERAND_TYPE_MEMORY: + { + // Usually this check is done after verifying instruction identity but in this case + // we have to fail early + if (insn1->length < insn2->length) + { + fputs("Suboptimal output size detected\n", ZYAN_STDERR); + abort(); + } + ZyanU64 addr1, addr2; + ZyanStatus status1 = ZydisCalcAbsoluteAddress(insn1, op1, 0, &addr1); + ZyanStatus status2 = ZydisCalcAbsoluteAddress(insn2, op2, + insn1->length - insn2->length, &addr2); + ZyanBool addresses_match = ZYAN_FALSE; + if (ZYAN_SUCCESS(status1) && ZYAN_SUCCESS(status2)) + { + if (addr1 != addr2) + { + fprintf(ZYAN_STDERR, "Mismatch for memory operand %u (absolute address)\n", i); + abort(); + } + addresses_match = ZYAN_TRUE; + } if ((op1->mem.type != op2->mem.type) || (op1->mem.segment != op2->mem.segment) || (op1->mem.base != op2->mem.base) || (op1->mem.index != op2->mem.index) || - (op1->mem.scale != op2->mem.scale && op1->mem.type != ZYDIS_MEMOP_TYPE_MIB) || - (op1->mem.disp.value != op2->mem.disp.value)) + ((op1->mem.scale != op2->mem.scale) && (op1->mem.type != ZYDIS_MEMOP_TYPE_MIB)) || + ((op1->mem.disp.value != op2->mem.disp.value) && !addresses_match)) { - ZyanBool acceptable_mismatch = ZYAN_FALSE; - if (op1->mem.disp.value != op2->mem.disp.value) - { - if ((op1->mem.disp.has_displacement) && - (op2->mem.disp.has_displacement) && - (op1->mem.index == ZYDIS_REGISTER_NONE) && - ((op1->mem.base == ZYDIS_REGISTER_NONE) || - (op1->mem.base == ZYDIS_REGISTER_EIP) || - (op1->mem.base == ZYDIS_REGISTER_RIP))) - { - ZyanU64 addr1, addr2; - ZydisCalcAbsoluteAddress(insn1, op1, 0, &addr1); - ZydisCalcAbsoluteAddress(insn2, op2, 0, &addr2); - acceptable_mismatch = (addr1 == addr2); - } - if ((insn1->machine_mode == ZYDIS_MACHINE_MODE_REAL_16) || - (insn1->machine_mode == ZYDIS_MACHINE_MODE_LEGACY_16) || - (insn1->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_16) || - (insn1->stack_width == 16) || - (insn1->address_width == 16) || - (insn2->address_width == 16)) - { - acceptable_mismatch = ((op1->mem.disp.value & 0xFFFF) == - (op2->mem.disp.value & 0xFFFF)); - } - } - if (!acceptable_mismatch) - { - fprintf(ZYAN_STDERR, "Mismatch for memory operand %u\n", i); - abort(); - } + fprintf(ZYAN_STDERR, "Mismatch for memory operand %u\n", i); + abort(); } break; + } case ZYDIS_OPERAND_TYPE_POINTER: if ((op1->ptr.segment != op2->ptr.segment) || (op1->ptr.offset != op2->ptr.offset))