Skip to content

Commit

Permalink
Reworked absolute address handling (Fixes #471)
Browse files Browse the repository at this point in the history
  • Loading branch information
mappzor authored and athre0z committed Feb 4, 2024
1 parent 58f8c65 commit 88f88c6
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 53 deletions.
7 changes: 7 additions & 0 deletions include/Zydis/Encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
/**
Expand Down
51 changes: 31 additions & 20 deletions src/Encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -1578,25 +1578,15 @@ 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)
{
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)
{
Expand Down Expand Up @@ -1941,27 +1931,48 @@ 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)) ||
(min_disp_size == 64))
{
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;
}
Expand Down
24 changes: 24 additions & 0 deletions tests/re_enc_test_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -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]"
}
]
60 changes: 27 additions & 33 deletions tools/ZydisFuzzShared.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down

0 comments on commit 88f88c6

Please sign in to comment.