* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE.
/** * Defines the `ZydisDecoderState` struct.
*/ typedefstruct ZydisDecoderState_
{ /** * A pointer to the `ZydisDecoder` instance.
*/ const ZydisDecoder* decoder; /** * A pointer to the `ZydisDecoderContext` struct.
*/
ZydisDecoderContext* context; /** * The input buffer.
*/ const ZyanU8* buffer; /** * The input buffer length.
*/
ZyanUSize buffer_len; /** * Prefix information.
*/ struct
{ /** * Signals, if the instruction has a `LOCK` prefix (`F0`). * * This prefix originally belongs to group 1, but separating it from the other ones makes * parsing easier for us later.
*/
ZyanBool has_lock; /** * The effective prefix of group 1 (either `F2` or `F3`).
*/
ZyanU8 group1; /** * The effective prefix of group 2 (`2E`, `36`, `3E`, `26`, `64` or `65`).
*/
ZyanU8 group2; /** * The effective segment prefix.
*/
ZyanU8 effective_segment; /** * The prefix that should be treated as the mandatory-prefix, if the * current instruction needs one. * * The last `F3`/`F2` prefix has precedence over previous ones and * `F3`/`F2` in general have precedence over `66`.
*/
ZyanU8 mandatory_candidate; /** * The offset of the effective `LOCK` prefix.
*/
ZyanU8 offset_lock; /** * The offset of the effective prefix in group 1.
*/
ZyanU8 offset_group1; /** * The offset of the effective prefix in group 2.
*/
ZyanU8 offset_group2; /** * The offset of the operand-size override prefix (`66`). * * This is the only prefix in group 3.
*/
ZyanU8 offset_osz_override; /** * The offset of the address-size override prefix (`67`). * * This is the only prefix in group 4.
*/
ZyanU8 offset_asz_override; /** * The offset of the effective segment prefix.
*/
ZyanU8 offset_segment; /** * The offset of the mandatory-candidate prefix.
*/
ZyanU8 offset_mandatory; /** * The offset of a possible `CET` `no-lock` prefix.
*/
ZyanI8 offset_notrack;
} prefixes;
} ZydisDecoderState;
/** * Defines the `ZydisRegisterEncoding` enum.
*/ typedefenum ZydisRegisterEncoding_
{
ZYDIS_REG_ENCODING_INVALID, /** * The register-id is encoded as part of the opcode (bits [3..0]). * * Possible extension by: * - `REX.B`
*/
ZYDIS_REG_ENCODING_OPCODE, /** * The register-id is encoded in `modrm.reg`. * * Possible extension by: * - `.R` * - `.R'` (vector only, EVEX/MVEX)
*/
ZYDIS_REG_ENCODING_REG, /** * The register-id is encoded in `.vvvv`. * * Possible extension by: * - `.v'` (vector only, EVEX/MVEX).
*/
ZYDIS_REG_ENCODING_NDSNDD, /** * The register-id is encoded in `modrm.rm`. * * Possible extension by: * - `.B` * - `.X` (vector only, EVEX/MVEX)`
*/
ZYDIS_REG_ENCODING_RM, /** * The register-id is encoded in `modrm.rm` or `sib.base` (if `SIB` is present). * * Possible extension by: * - `.B`
*/
ZYDIS_REG_ENCODING_BASE, /** * The register-id is encoded in `sib.index`. * * Possible extension by: * - `.X`
*/
ZYDIS_REG_ENCODING_INDEX, /** * The register-id is encoded in `sib.index`. * * Possible extension by: * - `.X` * - `.V'` (vector only, EVEX/MVEX)
*/
ZYDIS_REG_ENCODING_VIDX, /** * The register-id is encoded in an additional 8-bit immediate value. * * Bits [7:4] in 64-bit mode with possible extension by bit [3] (vector only), bits [7:5] for * all other modes.
*/
ZYDIS_REG_ENCODING_IS4, /** * The register-id is encoded in `EVEX.aaa/MVEX.kkk`.
*/
ZYDIS_REG_ENCODING_MASK,
/** * Maximum value of this enum.
*/
ZYDIS_REG_ENCODING_MAX_VALUE = ZYDIS_REG_ENCODING_MASK, /** * The minimum number of bits required to represent all values of this enum.
*/
ZYDIS_REG_ENCODING_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_REG_ENCODING_MAX_VALUE)
} ZydisRegisterEncoding;
/** * Reads one byte from the current read-position of the input data-source. * * @param state A pointer to the `ZydisDecoderState` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param value A pointer to the memory that receives the byte from the input data-source. * * @return A zyan status code. * * This function may fail, if the `ZYDIS_MAX_INSTRUCTION_LENGTH` limit got exceeded, or no more * data is available.
*/ static ZyanStatus ZydisInputPeek(ZydisDecoderState* state,
ZydisDecodedInstruction* instruction, ZyanU8* value)
{
ZYAN_ASSERT(state);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(value);
if (instruction->length >= ZYDIS_MAX_INSTRUCTION_LENGTH)
{ return ZYDIS_STATUS_INSTRUCTION_TOO_LONG;
}
/** * Increases the read-position of the input data-source by one byte. * * @param state A pointer to the `ZydisDecoderState` instance * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * * This function is supposed to get called ONLY after a successful call of `ZydisInputPeek`. * * This function increases the `length` field of the `ZydisDecodedInstruction` struct by one.
*/ staticvoid ZydisInputSkip(ZydisDecoderState* state, ZydisDecodedInstruction* instruction)
{
ZYAN_ASSERT(state);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(instruction->length < ZYDIS_MAX_INSTRUCTION_LENGTH);
/** * Reads one byte from the current read-position of the input data-source and increases * the read-position by one byte afterwards. * * @param state A pointer to the `ZydisDecoderState` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param value A pointer to the memory that receives the byte from the input data-source. * * @return A zyan status code. * * This function acts like a subsequent call of `ZydisInputPeek` and `ZydisInputSkip`.
*/ static ZyanStatus ZydisInputNext(ZydisDecoderState* state,
ZydisDecodedInstruction* instruction, ZyanU8* value)
{
ZYAN_ASSERT(state);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(value);
if (instruction->length >= ZYDIS_MAX_INSTRUCTION_LENGTH)
{ return ZYDIS_STATUS_INSTRUCTION_TOO_LONG;
}
/** * Reads a variable amount of bytes from the current read-position of the input * data-source and increases the read-position by specified amount of bytes afterwards. * * @param state A pointer to the `ZydisDecoderState` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param value A pointer to the memory that receives the byte from the input * data-source. * @param number_of_bytes The number of bytes to read from the input data-source. * * @return A zyan status code. * * This function acts like a subsequent call of `ZydisInputPeek` and `ZydisInputSkip`.
*/ static ZyanStatus ZydisInputNextBytes(ZydisDecoderState* state,
ZydisDecodedInstruction* instruction, ZyanU8* value, ZyanU8 number_of_bytes)
{
ZYAN_ASSERT(state);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(value);
if (instruction->length + number_of_bytes > ZYDIS_MAX_INSTRUCTION_LENGTH)
{ return ZYDIS_STATUS_INSTRUCTION_TOO_LONG;
}
if (state->buffer_len >= number_of_bytes)
{
instruction->length += number_of_bytes;
/** * Decodes the `REX`-prefix. * * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param data The `REX` byte.
*/ staticvoid ZydisDecodeREX(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction,
ZyanU8 data)
{
ZYAN_ASSERT(instruction);
ZYAN_ASSERT((data & 0xF0) == 0x40);
if ((instruction->raw.xop.m_mmmm < 0x08) || (instruction->raw.xop.m_mmmm > 0x0A))
{ // Invalid according to the AMD documentation return ZYDIS_STATUS_INVALID_MAP;
}
// Map 0 is only valid for some KNC instructions #ifdef ZYDIS_DISABLE_KNC if ((instruction->raw.vex.m_mmmm == 0) || (instruction->raw.vex.m_mmmm > 0x03)) #else if (instruction->raw.vex.m_mmmm > 0x03) #endif
{ // Invalid according to the intel documentation return ZYDIS_STATUS_INVALID_MAP;
}
if (!instruction->raw.evex.V2 && (instruction->machine_mode != ZYDIS_MACHINE_MODE_LONG_64))
{ return ZYDIS_STATUS_MALFORMED_EVEX;
} if (!instruction->raw.evex.b && (context->vector_unified.LL == 3))
{ // LL = 3 is only valid for instructions with embedded rounding control return ZYDIS_STATUS_MALFORMED_EVEX;
}
return ZYAN_STATUS_SUCCESS;
} #endif
#ifndef ZYDIS_DISABLE_KNC /** * Decodes the `MVEX`-prefix. * * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param data The `MVEX` bytes. * * @return A zyan status code.
*/ static ZyanStatus ZydisDecodeMVEX(ZydisDecoderContext* context,
ZydisDecodedInstruction* instruction, const ZyanU8 data[4])
{
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(data[0] == 0x62);
ZYAN_ASSERT(instruction->raw.mvex.offset == instruction->length - 4);
if (instruction->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)
{ // MVEX is only valid in 64-bit mode return ZYDIS_STATUS_DECODING_ERROR;
}
/** * Reads a displacement value. * * @param state A pointer to the `ZydisDecoderState` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param size The physical size of the displacement value. * * @return A zyan status code.
*/ static ZyanStatus ZydisReadDisplacement(ZydisDecoderState* state,
ZydisDecodedInstruction* instruction, ZyanU8 size)
{
ZYAN_ASSERT(state);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(instruction->raw.disp.size == 0);
/** * Reads an immediate value. * * @param state A pointer to the `ZydisDecoderState` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param id The immediate id (either `0` or `1`). * @param size The physical size of the immediate value. * @param is_signed Signals, if the immediate value is signed. * @param is_relative Signals, if the immediate value is a relative offset. * * @return A zyan status code.
*/ static ZyanStatus ZydisReadImmediate(ZydisDecoderState* state,
ZydisDecodedInstruction* instruction, ZyanU8 id, ZyanU8 size, ZyanBool is_signed,
ZyanBool is_relative)
{
ZYAN_ASSERT(state);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT((id == 0) || (id == 1));
ZYAN_ASSERT(is_signed || !is_relative);
ZYAN_ASSERT(instruction->raw.imm[id].size == 0);
#ifndef ZYDIS_MINIMAL_MODE /** * Calculates the register-id for a specific register-encoding and register-class. * * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the ` ZydisDecodedInstruction` struct. * @param encoding The register-encoding. * @param register_class The register-class. * * @return A zyan status code. * * This function calculates the register-id by combining different fields and flags of previously * decoded structs.
*/ static ZyanU8 ZydisCalcRegisterId(const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction, ZydisRegisterEncoding encoding,
ZydisRegisterClass register_class)
{
ZYAN_ASSERT(context);
ZYAN_ASSERT(instruction);
// TODO: Combine OPCODE and IS4 in `ZydisPopulateRegisterIds` and get rid of this // TODO: function entirely
switch (encoding)
{ case ZYDIS_REG_ENCODING_REG: return context->reg_info.id_reg; case ZYDIS_REG_ENCODING_NDSNDD: return context->reg_info.id_ndsndd; case ZYDIS_REG_ENCODING_RM: return context->reg_info.id_rm; case ZYDIS_REG_ENCODING_BASE: return context->reg_info.id_base; case ZYDIS_REG_ENCODING_INDEX: case ZYDIS_REG_ENCODING_VIDX: return context->reg_info.id_index; case ZYDIS_REG_ENCODING_OPCODE:
{
ZYAN_ASSERT((register_class == ZYDIS_REGCLASS_GPR8) ||
(register_class == ZYDIS_REGCLASS_GPR16) ||
(register_class == ZYDIS_REGCLASS_GPR32) ||
(register_class == ZYDIS_REGCLASS_GPR64));
ZyanU8 value = (instruction->opcode & 0x0F); if (value > 7)
{
value = value - 8;
} if (instruction->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)
{ return value;
} return value | (context->vector_unified.B << 3);
} case ZYDIS_REG_ENCODING_IS4:
{ if (instruction->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)
{ return (instruction->raw.imm[0].value.u >> 4) & 0x07;
}
ZyanU8 value = (instruction->raw.imm[0].value.u >> 4) & 0x0F; // We have to check the instruction-encoding, because the extension by bit [3] is only // valid for EVEX and MVEX instructions if ((instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) ||
(instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX))
{ switch (register_class)
{ case ZYDIS_REGCLASS_XMM: case ZYDIS_REGCLASS_YMM: case ZYDIS_REGCLASS_ZMM:
value |= ((instruction->raw.imm[0].value.u & 0x08) << 1); default: break;
}
} return value;
} case ZYDIS_REG_ENCODING_MASK: return context->vector_unified.mask; default:
ZYAN_UNREACHABLE;
}
} #endif
#ifndef ZYDIS_MINIMAL_MODE /** * Sets the operand-size and element-specific information for the given operand. * * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param operand A pointer to the `ZydisDecodedOperand` struct. * @param definition A pointer to the `ZydisOperandDefinition` struct.
*/ staticvoid ZydisSetOperandSizeAndElementInfo(const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction, ZydisDecodedOperand* operand, const ZydisOperandDefinition* definition)
{
ZYAN_ASSERT(context);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(operand);
ZYAN_ASSERT(definition);
// Operand size switch (operand->type)
{ case ZYDIS_OPERAND_TYPE_REGISTER:
{ if (definition->size[context->eosz_index])
{
operand->size = definition->size[context->eosz_index] * 8;
} else
{
operand->size = ZydisRegisterGetWidth(instruction->machine_mode,
operand->reg.value);
}
operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
operand->element_size = operand->size; break;
} case ZYDIS_OPERAND_TYPE_MEMORY: switch (instruction->encoding)
{ case ZYDIS_INSTRUCTION_ENCODING_LEGACY: case ZYDIS_INSTRUCTION_ENCODING_3DNOW: case ZYDIS_INSTRUCTION_ENCODING_XOP: case ZYDIS_INSTRUCTION_ENCODING_VEX: if (operand->mem.type == ZYDIS_MEMOP_TYPE_AGEN)
{
ZYAN_ASSERT(definition->size[context->eosz_index] == 0);
operand->size = instruction->address_width;
operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
} else
{
ZYAN_ASSERT(definition->size[context->eosz_index] ||
(instruction->meta.category == ZYDIS_CATEGORY_AMX_TILE));
operand->size = definition->size[context->eosz_index] * 8;
} break; case ZYDIS_INSTRUCTION_ENCODING_EVEX: #ifndef ZYDIS_DISABLE_AVX512 if (definition->size[context->eosz_index])
{ // Operand size is hardcoded
operand->size = definition->size[context->eosz_index] * 8;
} else
{ // Operand size depends on the tuple-type, the element-size and the number of // elements
ZYAN_ASSERT(instruction->avx.vector_length);
ZYAN_ASSERT(context->evex.element_size); switch (context->evex.tuple_type)
{ case ZYDIS_TUPLETYPE_FV: if (instruction->avx.broadcast.mode)
{
operand->size = context->evex.element_size;
} else
{
operand->size = instruction->avx.vector_length;
} break; case ZYDIS_TUPLETYPE_HV: if (instruction->avx.broadcast.mode)
{
operand->size = context->evex.element_size;
} else
{
operand->size = (ZyanU16)instruction->avx.vector_length / 2;
} break; case ZYDIS_TUPLETYPE_QUARTER: if (instruction->avx.broadcast.mode)
{
operand->size = context->evex.element_size;
} else
{
operand->size = (ZyanU16)instruction->avx.vector_length / 4;
} break; default:
ZYAN_UNREACHABLE;
}
}
ZYAN_ASSERT(operand->size); #else
ZYAN_UNREACHABLE; #endif break; case ZYDIS_INSTRUCTION_ENCODING_MVEX: #ifndef ZYDIS_DISABLE_KNC if (definition->size[context->eosz_index])
{ // Operand size is hardcoded
operand->size = definition->size[context->eosz_index] * 8;
} else
{
ZYAN_ASSERT(definition->element_type == ZYDIS_IELEMENT_TYPE_VARIABLE);
ZYAN_ASSERT(instruction->avx.vector_length == 512);
switch (instruction->avx.conversion.mode)
{ case ZYDIS_CONVERSION_MODE_INVALID:
operand->size = 512; switch (context->mvex.functionality)
{ case ZYDIS_MVEX_FUNC_SF_32: case ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16: case ZYDIS_MVEX_FUNC_UF_32: case ZYDIS_MVEX_FUNC_DF_32:
operand->element_type = ZYDIS_ELEMENT_TYPE_FLOAT32;
operand->element_size = 32; break; case ZYDIS_MVEX_FUNC_SF_32_BCST:
operand->size = 256;
operand->element_type = ZYDIS_ELEMENT_TYPE_FLOAT32;
operand->element_size = 32; break; case ZYDIS_MVEX_FUNC_SI_32: case ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16: case ZYDIS_MVEX_FUNC_UI_32: case ZYDIS_MVEX_FUNC_DI_32:
operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
operand->element_size = 32; break; case ZYDIS_MVEX_FUNC_SI_32_BCST:
operand->size = 256;
operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
operand->element_size = 32; break; case ZYDIS_MVEX_FUNC_SF_64: case ZYDIS_MVEX_FUNC_UF_64: case ZYDIS_MVEX_FUNC_DF_64:
operand->element_type = ZYDIS_ELEMENT_TYPE_FLOAT64;
operand->element_size = 64; break; case ZYDIS_MVEX_FUNC_SI_64: case ZYDIS_MVEX_FUNC_UI_64: case ZYDIS_MVEX_FUNC_DI_64:
operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
operand->element_size = 64; break; default:
ZYAN_UNREACHABLE;
} break; case ZYDIS_CONVERSION_MODE_FLOAT16:
operand->size = 256;
operand->element_type = ZYDIS_ELEMENT_TYPE_FLOAT16;
operand->element_size = 16; break; case ZYDIS_CONVERSION_MODE_SINT16:
operand->size = 256;
operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
operand->element_size = 16; break; case ZYDIS_CONVERSION_MODE_UINT16:
operand->size = 256;
operand->element_type = ZYDIS_ELEMENT_TYPE_UINT;
operand->element_size = 16; break; case ZYDIS_CONVERSION_MODE_SINT8:
operand->size = 128;
operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
operand->element_size = 8; break; case ZYDIS_CONVERSION_MODE_UINT8:
operand->size = 128;
operand->element_type = ZYDIS_ELEMENT_TYPE_UINT;
operand->element_size = 8; break; default:
ZYAN_UNREACHABLE;
}
switch (instruction->avx.broadcast.mode)
{ case ZYDIS_BROADCAST_MODE_INVALID: // Nothing to do here break; case ZYDIS_BROADCAST_MODE_1_TO_8: case ZYDIS_BROADCAST_MODE_1_TO_16:
operand->size = operand->element_size; break; case ZYDIS_BROADCAST_MODE_4_TO_8: case ZYDIS_BROADCAST_MODE_4_TO_16:
operand->size = operand->element_size * 4; break; default:
ZYAN_UNREACHABLE;
}
} #else
ZYAN_UNREACHABLE; #endif break; default:
ZYAN_UNREACHABLE;
} break; case ZYDIS_OPERAND_TYPE_POINTER:
ZYAN_ASSERT((instruction->raw.imm[0].size == 16) ||
(instruction->raw.imm[0].size == 32));
ZYAN_ASSERT( instruction->raw.imm[1].size == 16);
operand->size = instruction->raw.imm[0].size + instruction->raw.imm[1].size; break; case ZYDIS_OPERAND_TYPE_IMMEDIATE:
operand->size = definition->size[context->eosz_index] * 8; break; default:
ZYAN_UNREACHABLE;
}
// Element-type and -size if (definition->element_type && (definition->element_type != ZYDIS_IELEMENT_TYPE_VARIABLE))
{
ZydisGetElementInfo(definition->element_type, &operand->element_type,
&operand->element_size); if (!operand->element_size)
{ // The element size is the same as the operand size. This is used for single element // scaling operands
operand->element_size = operand->size;
}
}
#ifndef ZYDIS_MINIMAL_MODE /** * Decodes a memory operand. * * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param operand A pointer to the `ZydisDecodedOperand` struct. * @param vidx_register_class The register-class to use as the index register-class for * instructions with `VSIB` addressing. * * @return A zyan status code.
*/ static ZyanStatus ZydisDecodeOperandMemory(const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction, ZydisDecodedOperand* operand,
ZydisRegisterClass vidx_register_class)
{
ZYAN_ASSERT(context);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(operand);
ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM);
ZYAN_ASSERT(instruction->raw.modrm.mod != 3);
ZYAN_ASSERT(!vidx_register_class || ((instruction->raw.modrm.rm == 4) &&
((instruction->address_width == 32) || (instruction->address_width == 64))));
#ifndef ZYDIS_MINIMAL_MODE /** * Decodes an implicit register operand. * * @param decoder A pointer to the `ZydisDecoder` instance. * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param operand A pointer to the `ZydisDecodedOperand` struct. * @param definition A pointer to the `ZydisOperandDefinition` struct.
*/ staticvoid ZydisDecodeOperandImplicitRegister(const ZydisDecoder* decoder, const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction,
ZydisDecodedOperand* operand, const ZydisOperandDefinition* definition)
{
ZYAN_ASSERT(context);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(operand);
ZYAN_ASSERT(definition);
#ifndef ZYDIS_MINIMAL_MODE /** * Decodes an implicit memory operand. * * @param decoder A pointer to the `ZydisDecoder` instance. * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param operand A pointer to the `ZydisDecodedOperand` struct. * @param definition A pointer to the `ZydisOperandDefinition` struct.
*/ staticvoid ZydisDecodeOperandImplicitMemory(const ZydisDecoder* decoder, const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction,
ZydisDecodedOperand* operand, const ZydisOperandDefinition* definition)
{
ZYAN_ASSERT(context);
ZYAN_ASSERT(operand);
ZYAN_ASSERT(definition);
#ifndef ZYDIS_MINIMAL_MODE /** * Sets attributes for the given instruction. * * @param state A pointer to the `ZydisDecoderState` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param definition A pointer to the `ZydisInstructionDefinition` struct.
*/ staticvoid ZydisSetAttributes(ZydisDecoderState* state, ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition)
{
ZYAN_ASSERT(state);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(definition);
if (def->accepts_segment && !def->accepts_branch_hints)
{
instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_SEGMENT; if (state->prefixes.effective_segment &&
!(instruction->attributes & ZYDIS_ATTRIB_HAS_NOTRACK))
{ switch (state->prefixes.effective_segment)
{ case 0x2E:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_CS; break; case 0x36:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_SS; break; case 0x3E:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_DS; break; case 0x26:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_ES; break; case 0x64:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_FS; break; case 0x65:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_GS; break; default:
ZYAN_UNREACHABLE;
}
} if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT)
{
instruction->raw.prefixes[state->prefixes.offset_segment].type =
ZYDIS_PREFIX_TYPE_EFFECTIVE;
}
}
break;
} case ZYDIS_INSTRUCTION_ENCODING_3DNOW: case ZYDIS_INSTRUCTION_ENCODING_XOP: case ZYDIS_INSTRUCTION_ENCODING_VEX: case ZYDIS_INSTRUCTION_ENCODING_EVEX: case ZYDIS_INSTRUCTION_ENCODING_MVEX: if (definition->accepts_segment)
{
instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_SEGMENT; if (state->prefixes.effective_segment)
{ switch (state->prefixes.effective_segment)
{ case 0x2E:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_CS; break; case 0x36:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_SS; break; case 0x3E:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_DS; break; case 0x26:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_ES; break; case 0x64:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_FS; break; case 0x65:
instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_GS; break; default:
ZYAN_UNREACHABLE;
}
} if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT)
{
instruction->raw.prefixes[state->prefixes.offset_segment].type =
ZYDIS_PREFIX_TYPE_EFFECTIVE;
}
} break; default:
ZYAN_UNREACHABLE;
}
} #endif
#ifndef ZYDIS_MINIMAL_MODE /** * Sets AVX-specific information for the given instruction. * * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param definition A pointer to the `ZydisInstructionDefinition` struct. * * Information set for `XOP`: * - Vector Length * * Information set for `VEX`: * - Vector length * - Static broadcast-factor * * Information set for `EVEX`: * - Vector length * - Broadcast-factor (static and dynamic) * - Rounding-mode and SAE * - Mask mode * - Compressed 8-bit displacement scale-factor * * Information set for `MVEX`: * - Vector length * - Broadcast-factor (static and dynamic) * - Rounding-mode and SAE * - Swizzle- and conversion-mode * - Mask mode * - Eviction hint * - Compressed 8-bit displacement scale-factor
*/ staticvoid ZydisSetAVXInformation(ZydisDecoderContext* context,
ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition)
{
ZYAN_ASSERT(context);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(definition);
// Rounding mode and SAE if (instruction->raw.evex.b)
{ switch (def->functionality)
{ case ZYDIS_EVEX_FUNC_INVALID: case ZYDIS_EVEX_FUNC_BC: // Noting to do here break; case ZYDIS_EVEX_FUNC_RC:
instruction->avx.rounding.mode = ZYDIS_ROUNDING_MODE_RN + context->vector_unified.LL;
ZYAN_FALLTHROUGH; case ZYDIS_EVEX_FUNC_SAE:
instruction->avx.has_sae = ZYAN_TRUE; break; default:
ZYAN_UNREACHABLE;
}
}
// Static broadcast-factor
ZyanU8 index = def->has_element_granularity;
ZYAN_ASSERT(!index || !def->broadcast); if (!index && def->broadcast)
{
instruction->avx.broadcast.is_static = ZYAN_TRUE; switch (def->broadcast)
{ case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_8:
instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_1_TO_8;
index = 1; break; case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_16:
instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_1_TO_16;
index = 1; break; case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_8:
instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_4_TO_8;
index = 2; break; case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_16:
instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_4_TO_16;
index = 2; break; default:
ZYAN_UNREACHABLE;
}
}
// Compressed disp8 scale and broadcast-factor switch (def->functionality)
{ case ZYDIS_MVEX_FUNC_IGNORED: case ZYDIS_MVEX_FUNC_INVALID: case ZYDIS_MVEX_FUNC_RC: case ZYDIS_MVEX_FUNC_SAE: case ZYDIS_MVEX_FUNC_SWIZZLE_32: case ZYDIS_MVEX_FUNC_SWIZZLE_64: // Nothing to do here break; case ZYDIS_MVEX_FUNC_F_32: case ZYDIS_MVEX_FUNC_I_32: case ZYDIS_MVEX_FUNC_F_64: case ZYDIS_MVEX_FUNC_I_64:
context->cd8_scale = 64; break; case ZYDIS_MVEX_FUNC_SF_32: case ZYDIS_MVEX_FUNC_SF_32_BCST: case ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16: case ZYDIS_MVEX_FUNC_UF_32:
{ staticconst ZyanU8 lookup[3][8] =
{
{ 64, 4, 16, 32, 16, 16, 32, 32 },
{ 4, 0, 0, 2, 1, 1, 2, 2 },
{ 16, 0, 0, 8, 4, 4, 8, 8 }
};
ZYAN_ASSERT(instruction->raw.mvex.SSS < ZYAN_ARRAY_LENGTH(lookup[index]));
context->cd8_scale = lookup[index][instruction->raw.mvex.SSS]; break;
} case ZYDIS_MVEX_FUNC_SI_32: case ZYDIS_MVEX_FUNC_UI_32: case ZYDIS_MVEX_FUNC_SI_32_BCST: case ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16:
{ staticconst ZyanU8 lookup[3][8] =
{
{ 64, 4, 16, 0, 16, 16, 32, 32 },
{ 4, 0, 0, 0, 1, 1, 2, 2 },
{ 16, 0, 0, 0, 4, 4, 8, 8 }
};
ZYAN_ASSERT(instruction->raw.mvex.SSS < ZYAN_ARRAY_LENGTH(lookup[index]));
context->cd8_scale = lookup[index][instruction->raw.mvex.SSS]; break;
} case ZYDIS_MVEX_FUNC_SF_64: case ZYDIS_MVEX_FUNC_UF_64: case ZYDIS_MVEX_FUNC_SI_64: case ZYDIS_MVEX_FUNC_UI_64:
{ staticconst ZyanU8 lookup[3][3] =
{
{ 64, 8, 32 },
{ 8, 0, 0 },
{ 32, 0, 0 }
};
ZYAN_ASSERT(instruction->raw.mvex.SSS < ZYAN_ARRAY_LENGTH(lookup[index]));
context->cd8_scale = lookup[index][instruction->raw.mvex.SSS]; break;
} case ZYDIS_MVEX_FUNC_DF_32: case ZYDIS_MVEX_FUNC_DI_32:
{ staticconst ZyanU8 lookup[2][8] =
{
{ 64, 0, 0, 32, 16, 16, 32, 32 },
{ 4, 0, 0, 2, 1, 1, 2, 2 }
};
ZYAN_ASSERT(index < 2);
ZYAN_ASSERT(instruction->raw.mvex.SSS < ZYAN_ARRAY_LENGTH(lookup[index]));
context->cd8_scale = lookup[index][instruction->raw.mvex.SSS]; break;
} case ZYDIS_MVEX_FUNC_DF_64: case ZYDIS_MVEX_FUNC_DI_64:
{ staticconst ZyanU8 lookup[2][1] =
{
{ 64 },
{ 8 }
};
ZYAN_ASSERT(index < 2);
ZYAN_ASSERT(instruction->raw.mvex.SSS < ZYAN_ARRAY_LENGTH(lookup[index]));
context->cd8_scale = lookup[index][instruction->raw.mvex.SSS]; break;
} default:
ZYAN_UNREACHABLE;
}
// Rounding mode, sae, swizzle, convert
context->mvex.functionality = def->functionality; switch (def->functionality)
{ case ZYDIS_MVEX_FUNC_IGNORED: case ZYDIS_MVEX_FUNC_INVALID: case ZYDIS_MVEX_FUNC_F_32: case ZYDIS_MVEX_FUNC_I_32: case ZYDIS_MVEX_FUNC_F_64: case ZYDIS_MVEX_FUNC_I_64: // Nothing to do here break; case ZYDIS_MVEX_FUNC_RC:
instruction->avx.rounding.mode = ZYDIS_ROUNDING_MODE_RN + (instruction->raw.mvex.SSS & 3);
ZYAN_FALLTHROUGH; case ZYDIS_MVEX_FUNC_SAE: if (instruction->raw.mvex.SSS >= 4)
{
instruction->avx.has_sae = ZYAN_TRUE;
} break; case ZYDIS_MVEX_FUNC_SWIZZLE_32: case ZYDIS_MVEX_FUNC_SWIZZLE_64:
instruction->avx.swizzle.mode = ZYDIS_SWIZZLE_MODE_DCBA + instruction->raw.mvex.SSS; break; case ZYDIS_MVEX_FUNC_SF_32: case ZYDIS_MVEX_FUNC_SF_32_BCST: case ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16: switch (instruction->raw.mvex.SSS)
{ case 0: break; case 1:
instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_1_TO_16; break; case 2:
instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_4_TO_16; break; case 3:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_FLOAT16; break; case 4:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT8; break; case 5:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT8; break; case 6:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT16; break; case 7:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT16; break; default:
ZYAN_UNREACHABLE;
} break; case ZYDIS_MVEX_FUNC_SI_32: case ZYDIS_MVEX_FUNC_SI_32_BCST: case ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16: switch (instruction->raw.mvex.SSS)
{ case 0: break; case 1:
instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_1_TO_16; break; case 2:
instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_4_TO_16; break; case 4:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT8; break; case 5:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT8; break; case 6:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT16; break; case 7:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT16; break; default:
ZYAN_UNREACHABLE;
} break; case ZYDIS_MVEX_FUNC_SF_64: case ZYDIS_MVEX_FUNC_SI_64: switch (instruction->raw.mvex.SSS)
{ case 0: break; case 1:
instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_1_TO_8; break; case 2:
instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_4_TO_8; break; default:
ZYAN_UNREACHABLE;
} break; case ZYDIS_MVEX_FUNC_UF_32: case ZYDIS_MVEX_FUNC_DF_32: switch (instruction->raw.mvex.SSS)
{ case 0: break; case 3:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_FLOAT16; break; case 4:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT8; break; case 5:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT8; break; case 6:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT16; break; case 7:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT16; break; default:
ZYAN_UNREACHABLE;
} break; case ZYDIS_MVEX_FUNC_UF_64: case ZYDIS_MVEX_FUNC_DF_64: break; case ZYDIS_MVEX_FUNC_UI_32: case ZYDIS_MVEX_FUNC_DI_32: switch (instruction->raw.mvex.SSS)
{ case 0: break; case 4:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT8; break; case 5:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT8; break; case 6:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT16; break; case 7:
instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT16; break; default:
ZYAN_UNREACHABLE;
} break; case ZYDIS_MVEX_FUNC_UI_64: case ZYDIS_MVEX_FUNC_DI_64: break; default:
ZYAN_UNREACHABLE;
}
// Eviction hint if ((instruction->raw.modrm.mod != 3) && instruction->raw.mvex.E)
{
instruction->avx.has_eviction_hint = ZYAN_TRUE;
}
// Mask
instruction->avx.mask.mode = ZYDIS_MASK_MODE_MERGING;
instruction->avx.mask.reg = ZYDIS_REGISTER_K0 + instruction->raw.mvex.kkk; #else
ZYAN_UNREACHABLE; #endif break;
} default: // Nothing to do here break;
}
} #endif
/** * Collects optional instruction prefixes. * * @param state A pointer to the `ZydisDecoderState` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * * @return A zyan status code. * * This function sets the corresponding flag for each prefix and automatically decodes the last * `REX`-prefix (if exists).
*/ static ZyanStatus ZydisCollectOptionalPrefixes(ZydisDecoderState* state,
ZydisDecodedInstruction* instruction)
{
ZYAN_ASSERT(state);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(instruction->raw.prefix_count == 0);
ZyanU8 rex = 0x00;
ZyanU8 offset = 0;
ZyanBool done = ZYAN_FALSE; do
{
ZyanU8 prefix_byte;
ZYAN_CHECK(ZydisInputPeek(state, instruction, &prefix_byte)); switch (prefix_byte)
{ case 0xF0:
state->prefixes.has_lock = ZYAN_TRUE;
state->prefixes.offset_lock = offset; break; case 0xF2:
ZYAN_FALLTHROUGH; case 0xF3:
state->prefixes.group1 = prefix_byte;
state->prefixes.mandatory_candidate = prefix_byte;
state->prefixes.offset_group1 = offset;
state->prefixes.offset_mandatory = offset; break; case 0x2E:
ZYAN_FALLTHROUGH; case 0x36:
ZYAN_FALLTHROUGH; case 0x3E:
ZYAN_FALLTHROUGH; case 0x26: if (state->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{ if ((prefix_byte == 0x3E) &&
(state->prefixes.effective_segment != 0x64) &&
(state->prefixes.effective_segment != 0x65))
{
state->prefixes.offset_notrack = offset;
}
state->prefixes.group2 = prefix_byte;
state->prefixes.offset_group2 = offset; break;
}
ZYAN_FALLTHROUGH; case 0x64:
ZYAN_FALLTHROUGH; case 0x65:
state->prefixes.group2 = prefix_byte;
state->prefixes.offset_group2 = offset;
state->prefixes.effective_segment = prefix_byte;
state->prefixes.offset_segment = offset;
state->prefixes.offset_notrack = -1; break; case 0x66: // context->prefixes.has_osz_override = ZYAN_TRUE;
state->prefixes.offset_osz_override = offset; if (!state->prefixes.mandatory_candidate)
{
state->prefixes.mandatory_candidate = 0x66;
state->prefixes.offset_mandatory = offset;
}
instruction->attributes |= ZYDIS_ATTRIB_HAS_OPERANDSIZE; break; case 0x67: // context->prefixes.has_asz_override = ZYAN_TRUE;
state->prefixes.offset_asz_override = offset;
instruction->attributes |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE; break; default: if ((state->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
(prefix_byte & 0xF0) == 0x40)
{
rex = prefix_byte;
instruction->raw.rex.offset = offset;
} else
{
done = ZYAN_TRUE;
} break;
} if (!done)
{ // Invalidate `REX`, if it's not the last legacy prefix if (rex && (rex != prefix_byte))
{
rex = 0x00;
instruction->raw.rex.offset = 0;
}
instruction->raw.prefixes[instruction->raw.prefix_count++].value = prefix_byte;
ZydisInputSkip(state, instruction);
++offset;
}
} while (!done);
/** * Decodes optional instruction parts like the ModRM byte, the SIB byte and * additional displacements and/or immediate values. * * @param state A pointer to the `ZydisDecoderState` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param info A pointer to the `ZydisInstructionEncodingInfo` struct. * * @return A zyan status code.
*/ static ZyanStatus ZydisDecodeOptionalInstructionParts(ZydisDecoderState* state,
ZydisDecodedInstruction* instruction, const ZydisInstructionEncodingInfo* info)
{
ZYAN_ASSERT(state);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(info);
ZydisDecoderContext* context = state->context;
if (info->flags & ZYDIS_INSTR_ENC_FLAG_HAS_MODRM)
{ if (!instruction->raw.modrm.offset)
{
instruction->raw.modrm.offset = instruction->length;
ZyanU8 modrm_byte;
ZYAN_CHECK(ZydisInputNext(state, instruction, &modrm_byte));
ZydisDecodeModRM(instruction, modrm_byte);
}
if (!(info->flags & ZYDIS_INSTR_ENC_FLAG_FORCE_REG_FORM))
{
ZyanU8 has_sib = 0;
ZyanU8 displacement_size = 0; switch (instruction->address_width)
{ case 16: switch (instruction->raw.modrm.mod)
{ case 0: if (instruction->raw.modrm.rm == 6)
{
displacement_size = 16;
} break; case 1:
displacement_size = 8; break; case 2:
displacement_size = 16; break; case 3: break; default:
ZYAN_UNREACHABLE;
} break; case 32: case 64:
has_sib =
(instruction->raw.modrm.mod != 3) && (instruction->raw.modrm.rm == 4); switch (instruction->raw.modrm.mod)
{ case 0: if (instruction->raw.modrm.rm == 5)
{ if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
instruction->attributes |= ZYDIS_ATTRIB_IS_RELATIVE;
}
displacement_size = 32;
} break; case 1:
displacement_size = 8; break; case 2:
displacement_size = 32; break; case 3: break; default:
ZYAN_UNREACHABLE;
} break; default:
ZYAN_UNREACHABLE;
} if (has_sib)
{
instruction->raw.sib.offset = instruction->length;
ZyanU8 sib_byte;
ZYAN_CHECK(ZydisInputNext(state, instruction, &sib_byte));
ZydisDecodeSIB(instruction, sib_byte); if (instruction->raw.sib.base == 5)
{
displacement_size = (instruction->raw.modrm.mod == 1) ? 8 : 32;
}
} if (displacement_size)
{
ZYAN_CHECK(ZydisReadDisplacement(state, instruction, displacement_size));
}
}
/** * Sets the effective operand size for the given instruction. * * @param context A pointer to the `ZydisDecoderContext` struct * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param definition A pointer to the `ZydisInstructionDefinition` struct.
*/ staticvoid ZydisSetEffectiveOperandWidth(ZydisDecoderContext* context,
ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition)
{
ZYAN_ASSERT(context);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(definition);
// TODO: Cleanup code and remove hardcoded condition if (definition->operand_size_map == 1)
{
instruction->operand_width = 8;
}
}
/** * Sets the effective address width for the given instruction. * * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param definition A pointer to the `ZydisInstructionDefinition` struct.
*/ staticvoid ZydisSetEffectiveAddressWidth(ZydisDecoderContext* context,
ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition)
{
ZYAN_ASSERT(context);
ZYAN_ASSERT(instruction);
staticconst ZyanU8 address_size_map[3][8] =
{ // Default for most instructions
{
16, // 16 __
32, // 16 67
32, // 32 __
16, // 32 67
64, // 64 __
32 // 64 67
}, // The address-size override is ignored
{
16, // 16 __
16, // 16 67
32, // 32 __
32, // 32 67
64, // 64 __
64 // 64 67
}, // The address-size is forced to 64-bit in 64-bit mode and 32-bit in non 64-bit mode. This // is used by e.g. `ENCLS`, `ENCLV`, `ENCLU`.
{
32, // 16 __
32, // 16 67
32, // 32 __
32, // 32 67
64, // 64 __
64 // 64 67
}
};
ZyanU8 index = (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) ? 1 : 0; if ((instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_32) ||
(instruction->machine_mode == ZYDIS_MACHINE_MODE_LEGACY_32))
{
index += 2;
} elseif (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
index += 4;
}
/** * Populates the internal register id fields for `REG`, `RM`, `NDSNDD`, `BASE` and `INDEX`/`VIDX` * encoded operands and performs sanity checks. * * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param def_reg The type definition for the `.reg` encoded operand. * @param def_rm The type definition for the `.rm` encoded operand. * @param def_ndsndd The type definition for the `.vvvv` encoded operand. * * @return A zyan status code. * * This function sets all unused register ids to `-1`. This rule does currently not apply to * `base` and `index`. * * Definition encoding: * - `def_reg` -> `ZydisRegisterKind` * - `def_ndsndd` -> `ZydisRegisterKind` * - `def_rm` -> `ZydisRegisterKind` (`.mod == 3`) or ZydisMemoryOperandType (`.mod != 3`)
*/ static ZyanStatus ZydisPopulateRegisterIds(ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction, ZyanU8 def_reg, ZyanU8 def_rm, ZyanU8 def_ndsndd)
{
ZYAN_ASSERT(context);
ZYAN_ASSERT(instruction);
// The `index` extension by `.v'` is only valid for VSIB operands const ZyanU8 vsib_v2 = has_vsib ? context->vector_unified.V2 : 0; // The `rm` extension by `.X` is only valid for EVEX/MVEX instructions const ZyanU8 evex_x = is_emvex ? context->vector_unified.X : 0;
// Commented out for future reference. Not required at the moment as it's always either // a "take all" or "take nothing" situation.
//static const ZyanU8 mask_ndsndd[ZYDIS_REGKIND_MAX_VALUE + 1] = //{ // /* INVALID */ 0, // /* GPR */ (1 << 5) - 1, // /* X87 */ 0, // never encoded in `.vvvv` // /* MMX */ 0, // never encoded in `.vvvv` // /* VR */ (1 << 5) - 1, // /* TMM */ (1 << 5) - 1, // /* SEGMENT */ 0, // never encoded in `.vvvv` // /* TEST */ 0, // never encoded in `.vvvv` // /* CONTROL */ 0, // never encoded in `.vvvv` // /* DEBUG */ 0, // never encoded in `.vvvv` // /* MASK */ (1 << 5) - 1, // /* BOUND */ 0 // never encoded in `.vvvv` //};
}
// Validate
// `.vvvv` is not allowed, if the instruction does not encode a NDS/NDD operand if (!def_ndsndd && context->vector_unified.vvvv)
{ return ZYDIS_STATUS_BAD_REGISTER;
} // `.v'` is not allowed, if the instruction does not encode a NDS/NDD or VSIB operand if (!def_ndsndd && !has_vsib && context->vector_unified.V2)
{ return ZYDIS_STATUS_BAD_REGISTER;
}
staticconst ZyanU8 available_regs[2][ZYDIS_REGKIND_MAX_VALUE + 1] =
{ // 16/32 bit mode
{ /* INVALID */ 255, /* GPR */ 8, /* X87 */ 8, /* MMX */ 8, /* VR */ 8, /* TMM */ 8, /* SEGMENT */ 6, /* TEST */ 8, /* CONTROL */ 8, /* DEBUG */ 8, /* MASK */ 8, /* BOUND */ 4
}, // 64 bit mode
{ /* INVALID */ 255, /* GPR */ 16, /* X87 */ 8, /* MMX */ 8, /* VR */ 32, /* TMM */ 8, /* SEGMENT */ 6, /* TEST */ 8, /* CONTROL */ 16, // Attempts to reference DR8..DR15 result in undefined opcode (#UD) exceptions. DR4 and // DR5 are only valid, if the debug extension (DE) flag in CR4 is set. As we can't // check this at runtime we just allow them. /* DEBUG */ 8, /* MASK */ 8, /* BOUND */ 4
}
};
ZyanI8 id_cr = -1; if (def_reg == ZYDIS_REGKIND_CONTROL)
{
id_cr = id_reg;
} if (is_reg && (def_rm == ZYDIS_REGKIND_CONTROL))
{
id_cr = id_rm;
} if (id_cr >= 0)
{ // Attempts to reference CR1, CR5, CR6, CR7, and CR9..CR15 result in undefined opcode (#UD) // exceptions staticconst ZyanU8 lookup[16] =
{
1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0
};
ZYAN_ASSERT((ZyanUSize)id_cr < ZYAN_ARRAY_LENGTH(lookup)); if (!lookup[id_cr])
{ return ZYDIS_STATUS_BAD_REGISTER;
}
}
// Assign to context
context->reg_info.id_reg = def_reg ? id_reg : -1;
context->reg_info.id_rm = def_rm && is_reg ? id_rm : -1;
context->reg_info.id_ndsndd = def_ndsndd ? id_ndsndd : -1;
context->reg_info.id_base = id_base; // TODO: Set unused register to -1 as well
context->reg_info.id_index = id_index; // TODO: Set unused register to -1 as well
return ZYAN_STATUS_SUCCESS;
}
/** * Checks for certain post-decode error-conditions. * * @param state A pointer to the `ZydisDecoderState` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param definition A pointer to the `ZydisInstructionDefinition` struct. * * @return A zyan status code. * * This function is called immediately after a valid instruction-definition was found.
*/ static ZyanStatus ZydisCheckErrorConditions(ZydisDecoderState* state, const ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition)
{
ZYAN_ASSERT(state);
ZYAN_ASSERT(instruction);
ZYAN_ASSERT(definition);
// Some gather instructions (like `VGATHERPF0{D|Q}{PS|PD}`) do not have a destination // operand if (!def_reg)
{
dest = 0xF1;
}
}
// If any pair of the index, mask, or destination registers are the same, the instruction // results a UD fault if ((dest == index) || (dest == mask) || (index == mask))
{ return ZYDIS_STATUS_BAD_REGISTER;
}
}
// Check if any source register matches the destination register if (no_source_dest_match)
{
ZYAN_ASSERT((instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) ||
(instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_VEX));
// If any pair of the source or destination registers are the same, the instruction results a // UD fault if (no_source_source_match) // TODO: Find better name
{
ZYAN_ASSERT(instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_VEX);
ZYAN_ASSERT(is_reg);
#if !defined(ZYDIS_DISABLE_AVX512) || !defined(ZYDIS_DISABLE_KNC) // Check for invalid MASK registers switch (mask_policy)
{ case ZYDIS_MASK_POLICY_INVALID: case ZYDIS_MASK_POLICY_ALLOWED: // Nothing to do here break; case ZYDIS_MASK_POLICY_REQUIRED: if (!context->vector_unified.mask)
{ return ZYDIS_STATUS_INVALID_MASK;
} break; case ZYDIS_MASK_POLICY_FORBIDDEN: if (context->vector_unified.mask)
{ return ZYDIS_STATUS_INVALID_MASK;
} break; default:
ZYAN_UNREACHABLE;
} #endif
/** * Uses the decoder-tree to decode the current instruction. * * @param state A pointer to the `ZydisDecoderState` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * * @return A zyan status code.
*/ static ZyanStatus ZydisDecodeInstruction(ZydisDecoderState* state,
ZydisDecodedInstruction* instruction)
{
ZYAN_ASSERT(state);
ZYAN_ASSERT(instruction);
// Iterate through the decoder tree const ZydisDecoderTreeNode* node = ZydisDecoderTreeGetRootNode(); const ZydisDecoderTreeNode* temp = ZYAN_NULL;
ZydisDecoderTreeNodeType node_type; do
{
node_type = node->type;
ZyanU16 index = 0;
ZyanStatus status = 0; switch (node_type)
{ case ZYDIS_NODETYPE_INVALID: if (temp)
{
node = temp;
temp = ZYAN_NULL;
node_type = ZYDIS_NODETYPE_FILTER_MANDATORY_PREFIX; if (state->prefixes.mandatory_candidate != 0x00)
{
instruction->raw.prefixes[state->prefixes.offset_mandatory].type =
ZYDIS_PREFIX_TYPE_IGNORED;
} if (state->prefixes.mandatory_candidate == 0x66)
{ if (state->prefixes.offset_osz_override ==
state->prefixes.offset_mandatory)
{
instruction->raw.prefixes[state->prefixes.offset_mandatory].type =
ZYDIS_PREFIX_TYPE_EFFECTIVE;
}
instruction->attributes |= ZYDIS_ATTRIB_HAS_OPERANDSIZE;
} continue;
} return ZYDIS_STATUS_DECODING_ERROR; case ZYDIS_NODETYPE_FILTER_XOP:
status = ZydisNodeHandlerXOP(instruction, &index); break; case ZYDIS_NODETYPE_FILTER_VEX:
status = ZydisNodeHandlerVEX(instruction, &index); break; case ZYDIS_NODETYPE_FILTER_EMVEX:
status = ZydisNodeHandlerEMVEX(instruction, &index); break; case ZYDIS_NODETYPE_FILTER_OPCODE:
status = ZydisNodeHandlerOpcode(state, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_MODE:
status = ZydisNodeHandlerMode(instruction, &index); break; case ZYDIS_NODETYPE_FILTER_MODE_COMPACT:
status = ZydisNodeHandlerModeCompact(instruction, &index); break; case ZYDIS_NODETYPE_FILTER_MODRM_MOD:
status = ZydisNodeHandlerModrmMod(state, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_MODRM_MOD_COMPACT:
status = ZydisNodeHandlerModrmModCompact(state, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_MODRM_REG:
status = ZydisNodeHandlerModrmReg(state, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_MODRM_RM:
status = ZydisNodeHandlerModrmRm(state, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_PREFIX_GROUP1:
index = state->prefixes.group1 ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MANDATORY_PREFIX:
status = ZydisNodeHandlerMandatoryPrefix(state, instruction, &index);
temp = ZydisDecoderTreeGetChildNode(node, 0); // TODO: Return to this point, if index == 0 contains a value and the previous path // TODO: was not successful // TODO: Restore consumed prefix break; case ZYDIS_NODETYPE_FILTER_OPERAND_SIZE:
status = ZydisNodeHandlerOperandSize(state, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_ADDRESS_SIZE:
status = ZydisNodeHandlerAddressSize(instruction, &index); break; case ZYDIS_NODETYPE_FILTER_VECTOR_LENGTH:
status = ZydisNodeHandlerVectorLength(state->context, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_REX_W:
status = ZydisNodeHandlerRexW(state->context, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_REX_B:
status = ZydisNodeHandlerRexB(state->context, instruction, &index); break; #ifndef ZYDIS_DISABLE_AVX512 case ZYDIS_NODETYPE_FILTER_EVEX_B:
status = ZydisNodeHandlerEvexB(instruction, &index); break; #endif #ifndef ZYDIS_DISABLE_KNC case ZYDIS_NODETYPE_FILTER_MVEX_E:
status = ZydisNodeHandlerMvexE(instruction, &index); break; #endif case ZYDIS_NODETYPE_FILTER_MODE_AMD:
index = !!(state->decoder->decoder_mode & (1 << ZYDIS_DECODER_MODE_AMD_BRANCHES)); break; case ZYDIS_NODETYPE_FILTER_MODE_KNC:
index = !!(state->decoder->decoder_mode & (1 << ZYDIS_DECODER_MODE_KNC)); break; case ZYDIS_NODETYPE_FILTER_MODE_MPX:
index = !!(state->decoder->decoder_mode & (1 << ZYDIS_DECODER_MODE_MPX)); break; case ZYDIS_NODETYPE_FILTER_MODE_CET:
index = !!(state->decoder->decoder_mode & (1 << ZYDIS_DECODER_MODE_CET)); break; case ZYDIS_NODETYPE_FILTER_MODE_LZCNT:
index = !!(state->decoder->decoder_mode & (1 << ZYDIS_DECODER_MODE_LZCNT)); break; case ZYDIS_NODETYPE_FILTER_MODE_TZCNT:
index = !!(state->decoder->decoder_mode & (1 << ZYDIS_DECODER_MODE_TZCNT)); break; case ZYDIS_NODETYPE_FILTER_MODE_WBNOINVD:
index = !!(state->decoder->decoder_mode & (1 << ZYDIS_DECODER_MODE_WBNOINVD)); break; case ZYDIS_NODETYPE_FILTER_MODE_CLDEMOTE:
index = !!(state->decoder->decoder_mode & (1 << ZYDIS_DECODER_MODE_CLDEMOTE)); break; case ZYDIS_NODETYPE_FILTER_MODE_IPREFETCH:
index = !!(state->decoder->decoder_mode & (1 << ZYDIS_DECODER_MODE_IPREFETCH)); break; case ZYDIS_NODETYPE_FILTER_MODE_UD0_COMPAT:
index = !!(state->decoder->decoder_mode & (1 << ZYDIS_DECODER_MODE_UD0_COMPAT)); break; default: if (node_type & ZYDIS_NODETYPE_DEFINITION_MASK)
{ const ZydisInstructionDefinition* definition;
ZydisGetInstructionDefinition(instruction->encoding, node->value, &definition);
ZydisSetEffectiveOperandWidth(state->context, instruction, definition);
ZydisSetEffectiveAddressWidth(state->context, instruction, definition);
ZydisDecoderContext default_context; if (!context)
{ // Use a fallback context if no custom one has been provided
context = &default_context;
}
ZYAN_MEMSET(context, 0, sizeof(*context));
state.context = context;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.