/*************************************************************************************************** Zyan Disassembler Library (Zydis) Original Author : Florian Bernd * 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. ***************************************************************************************************/ // ReSharper disable CppClangTidyClangDiagnosticImplicitFallthrough // ReSharper disable CppClangTidyClangDiagnosticSwitchEnum // ReSharper disable CppClangTidyClangDiagnosticCoveredSwitchDefault // Temporarily disabled due to a LLVM issue: // ReSharper disable CppClangTidyBugproneNarrowingConversions #include #include #include #include #include /* ============================================================================================== */ /* Internal enums and types */ /* ============================================================================================== */ /* ---------------------------------------------------------------------------------------------- */ /* Decoder context */ /* ---------------------------------------------------------------------------------------------- */ /** * Defines the `ZydisDecoderState` struct. */ typedef struct 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; /* ---------------------------------------------------------------------------------------------- */ /* Register encoding */ /* ---------------------------------------------------------------------------------------------- */ /** * Defines the `ZydisRegisterEncoding` enum. */ typedef enum 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; /* ---------------------------------------------------------------------------------------------- */ /* ============================================================================================== */ /* Internal functions */ /* ============================================================================================== */ /* ---------------------------------------------------------------------------------------------- */ /* Input helper functions */ /* ---------------------------------------------------------------------------------------------- */ /** * 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; } if (state->buffer_len > 0) { *value = state->buffer[0]; return ZYAN_STATUS_SUCCESS; } return ZYDIS_STATUS_NO_MORE_DATA; } /** * 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. */ static void ZydisInputSkip(ZydisDecoderState* state, ZydisDecodedInstruction* instruction) { ZYAN_ASSERT(state); ZYAN_ASSERT(instruction); ZYAN_ASSERT(instruction->length < ZYDIS_MAX_INSTRUCTION_LENGTH); ++instruction->length; ++state->buffer; --state->buffer_len; } /** * 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; } if (state->buffer_len > 0) { *value = state->buffer++[0]; ++instruction->length; --state->buffer_len; return ZYAN_STATUS_SUCCESS; } return ZYDIS_STATUS_NO_MORE_DATA; } /** * 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; ZYAN_MEMCPY(value, state->buffer, number_of_bytes); state->buffer += number_of_bytes; state->buffer_len -= number_of_bytes; return ZYAN_STATUS_SUCCESS; } return ZYDIS_STATUS_NO_MORE_DATA; } /* ---------------------------------------------------------------------------------------------- */ /* Decode functions */ /* ---------------------------------------------------------------------------------------------- */ /** * 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. */ static void ZydisDecodeREX(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU8 data) { ZYAN_ASSERT(instruction); ZYAN_ASSERT((data & 0xF0) == 0x40); instruction->attributes |= ZYDIS_ATTRIB_HAS_REX; instruction->raw.rex.W = (data >> 3) & 0x01; instruction->raw.rex.R = (data >> 2) & 0x01; instruction->raw.rex.X = (data >> 1) & 0x01; instruction->raw.rex.B = (data >> 0) & 0x01; // Update internal fields context->vector_unified.W = instruction->raw.rex.W; context->vector_unified.R = instruction->raw.rex.R; context->vector_unified.X = instruction->raw.rex.X; context->vector_unified.B = instruction->raw.rex.B; } /** * Decodes the `XOP`-prefix. * * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param data The `XOP` bytes. * * @return A zyan status code. */ static ZyanStatus ZydisDecodeXOP(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, const ZyanU8 data[3]) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(data[0] == 0x8F); ZYAN_ASSERT(((data[1] >> 0) & 0x1F) >= 8); ZYAN_ASSERT(instruction->raw.xop.offset == instruction->length - 3); if (instruction->machine_mode == ZYDIS_MACHINE_MODE_REAL_16) { // XOP is invalid in 16-bit real mode return ZYDIS_STATUS_DECODING_ERROR; } instruction->attributes |= ZYDIS_ATTRIB_HAS_XOP; instruction->raw.xop.R = (data[1] >> 7) & 0x01; instruction->raw.xop.X = (data[1] >> 6) & 0x01; instruction->raw.xop.B = (data[1] >> 5) & 0x01; instruction->raw.xop.m_mmmm = (data[1] >> 0) & 0x1F; if ((instruction->raw.xop.m_mmmm < 0x08) || (instruction->raw.xop.m_mmmm > 0x0A)) { // Invalid according to the AMD documentation return ZYDIS_STATUS_INVALID_MAP; } instruction->raw.xop.W = (data[2] >> 7) & 0x01; instruction->raw.xop.vvvv = (data[2] >> 3) & 0x0F; instruction->raw.xop.L = (data[2] >> 2) & 0x01; instruction->raw.xop.pp = (data[2] >> 0) & 0x03; // Update internal fields context->vector_unified.W = instruction->raw.xop.W; context->vector_unified.R = 0x01 & ~instruction->raw.xop.R; context->vector_unified.X = 0x01 & ~instruction->raw.xop.X; context->vector_unified.B = 0x01 & ~instruction->raw.xop.B; context->vector_unified.L = instruction->raw.xop.L; context->vector_unified.LL = instruction->raw.xop.L; context->vector_unified.vvvv = (0x0F & ~instruction->raw.xop.vvvv); return ZYAN_STATUS_SUCCESS; } /** * Decodes the `VEX`-prefix. * * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param data The `VEX` bytes. * * @return A zyan status code. */ static ZyanStatus ZydisDecodeVEX(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, const ZyanU8 data[3]) { ZYAN_ASSERT(instruction); ZYAN_ASSERT((data[0] == 0xC4) || (data[0] == 0xC5)); if (instruction->machine_mode == ZYDIS_MACHINE_MODE_REAL_16) { // VEX is invalid in 16-bit real mode return ZYDIS_STATUS_DECODING_ERROR; } instruction->attributes |= ZYDIS_ATTRIB_HAS_VEX; switch (data[0]) { case 0xC4: ZYAN_ASSERT(instruction->raw.vex.offset == instruction->length - 3); instruction->raw.vex.size = 3; instruction->raw.vex.R = (data[1] >> 7) & 0x01; instruction->raw.vex.X = (data[1] >> 6) & 0x01; instruction->raw.vex.B = (data[1] >> 5) & 0x01; instruction->raw.vex.m_mmmm = (data[1] >> 0) & 0x1F; instruction->raw.vex.W = (data[2] >> 7) & 0x01; instruction->raw.vex.vvvv = (data[2] >> 3) & 0x0F; instruction->raw.vex.L = (data[2] >> 2) & 0x01; instruction->raw.vex.pp = (data[2] >> 0) & 0x03; break; case 0xC5: ZYAN_ASSERT(instruction->raw.vex.offset == instruction->length - 2); instruction->raw.vex.size = 2; instruction->raw.vex.R = (data[1] >> 7) & 0x01; instruction->raw.vex.X = 1; instruction->raw.vex.B = 1; instruction->raw.vex.m_mmmm = 1; instruction->raw.vex.W = 0; instruction->raw.vex.vvvv = (data[1] >> 3) & 0x0F; instruction->raw.vex.L = (data[1] >> 2) & 0x01; instruction->raw.vex.pp = (data[1] >> 0) & 0x03; break; default: ZYAN_UNREACHABLE; } // 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; } // Update internal fields context->vector_unified.W = instruction->raw.vex.W; context->vector_unified.R = 0x01 & ~instruction->raw.vex.R; context->vector_unified.X = 0x01 & ~instruction->raw.vex.X; context->vector_unified.B = 0x01 & ~instruction->raw.vex.B; context->vector_unified.L = instruction->raw.vex.L; context->vector_unified.LL = instruction->raw.vex.L; context->vector_unified.vvvv = (0x0F & ~instruction->raw.vex.vvvv); return ZYAN_STATUS_SUCCESS; } #ifndef ZYDIS_DISABLE_AVX512 /** * Decodes the `EVEX`-prefix. * * @param context A pointer to the `ZydisDecoderContext` struct. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param data The `EVEX` bytes. * * @return A zyan status code. */ static ZyanStatus ZydisDecodeEVEX(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, const ZyanU8 data[4]) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(data[0] == 0x62); ZYAN_ASSERT(instruction->raw.evex.offset == instruction->length - 4); if (instruction->machine_mode == ZYDIS_MACHINE_MODE_REAL_16) { // EVEX is invalid in 16-bit real mode return ZYDIS_STATUS_DECODING_ERROR; } instruction->attributes |= ZYDIS_ATTRIB_HAS_EVEX; instruction->raw.evex.R = (data[1] >> 7) & 0x01; instruction->raw.evex.X = (data[1] >> 6) & 0x01; instruction->raw.evex.B = (data[1] >> 5) & 0x01; instruction->raw.evex.R2 = (data[1] >> 4) & 0x01; if (data[1] & 0x08) { // Invalid according to the intel documentation return ZYDIS_STATUS_MALFORMED_EVEX; } instruction->raw.evex.mmm = (data[1] >> 0) & 0x07; if ((instruction->raw.evex.mmm == 0x00) || (instruction->raw.evex.mmm == 0x04) || (instruction->raw.evex.mmm == 0x07)) { // Invalid according to the intel documentation return ZYDIS_STATUS_INVALID_MAP; } instruction->raw.evex.W = (data[2] >> 7) & 0x01; instruction->raw.evex.vvvv = (data[2] >> 3) & 0x0F; ZYAN_ASSERT(((data[2] >> 2) & 0x01) == 0x01); instruction->raw.evex.pp = (data[2] >> 0) & 0x03; instruction->raw.evex.z = (data[3] >> 7) & 0x01; instruction->raw.evex.L2 = (data[3] >> 6) & 0x01; instruction->raw.evex.L = (data[3] >> 5) & 0x01; instruction->raw.evex.b = (data[3] >> 4) & 0x01; instruction->raw.evex.V2 = (data[3] >> 3) & 0x01; if (!instruction->raw.evex.V2 && (instruction->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)) { return ZYDIS_STATUS_MALFORMED_EVEX; } instruction->raw.evex.aaa = (data[3] >> 0) & 0x07; if (instruction->raw.evex.z && !instruction->raw.evex.aaa) { return ZYDIS_STATUS_INVALID_MASK; // TODO: Dedicated status code } // Update internal fields context->vector_unified.W = instruction->raw.evex.W; context->vector_unified.R = 0x01 & ~instruction->raw.evex.R; context->vector_unified.X = 0x01 & ~instruction->raw.evex.X; context->vector_unified.B = 0x01 & ~instruction->raw.evex.B; context->vector_unified.LL = (data[3] >> 5) & 0x03; context->vector_unified.R2 = 0x01 & ~instruction->raw.evex.R2; context->vector_unified.V2 = 0x01 & ~instruction->raw.evex.V2; context->vector_unified.vvvv = 0x0F & ~instruction->raw.evex.vvvv; context->vector_unified.mask = instruction->raw.evex.aaa; 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; } instruction->attributes |= ZYDIS_ATTRIB_HAS_MVEX; instruction->raw.mvex.R = (data[1] >> 7) & 0x01; instruction->raw.mvex.X = (data[1] >> 6) & 0x01; instruction->raw.mvex.B = (data[1] >> 5) & 0x01; instruction->raw.mvex.R2 = (data[1] >> 4) & 0x01; instruction->raw.mvex.mmmm = (data[1] >> 0) & 0x0F; if (instruction->raw.mvex.mmmm > 0x03) { // Invalid according to the intel documentation return ZYDIS_STATUS_INVALID_MAP; } instruction->raw.mvex.W = (data[2] >> 7) & 0x01; instruction->raw.mvex.vvvv = (data[2] >> 3) & 0x0F; ZYAN_ASSERT(((data[2] >> 2) & 0x01) == 0x00); instruction->raw.mvex.pp = (data[2] >> 0) & 0x03; instruction->raw.mvex.E = (data[3] >> 7) & 0x01; instruction->raw.mvex.SSS = (data[3] >> 4) & 0x07; instruction->raw.mvex.V2 = (data[3] >> 3) & 0x01; instruction->raw.mvex.kkk = (data[3] >> 0) & 0x07; // Update internal fields context->vector_unified.W = instruction->raw.mvex.W; context->vector_unified.R = 0x01 & ~instruction->raw.mvex.R; context->vector_unified.X = 0x01 & ~instruction->raw.mvex.X; context->vector_unified.B = 0x01 & ~instruction->raw.mvex.B; context->vector_unified.R2 = 0x01 & ~instruction->raw.mvex.R2; context->vector_unified.V2 = 0x01 & ~instruction->raw.mvex.V2; context->vector_unified.LL = 2; context->vector_unified.vvvv = 0x0F & ~instruction->raw.mvex.vvvv; context->vector_unified.mask = instruction->raw.mvex.kkk; return ZYAN_STATUS_SUCCESS; } #endif /** * Decodes the `ModRM`-byte. * * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param data The `ModRM` byte. */ static void ZydisDecodeModRM(ZydisDecodedInstruction* instruction, ZyanU8 data) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM)); ZYAN_ASSERT(instruction->raw.modrm.offset == instruction->length - 1); instruction->attributes |= ZYDIS_ATTRIB_HAS_MODRM; instruction->raw.modrm.mod = (data >> 6) & 0x03; instruction->raw.modrm.reg = (data >> 3) & 0x07; instruction->raw.modrm.rm = (data >> 0) & 0x07; } /** * Decodes the `SIB`-byte. * * @param instruction A pointer to the `ZydisDecodedInstruction` struct * @param data The `SIB` byte. */ static void ZydisDecodeSIB(ZydisDecodedInstruction* instruction, ZyanU8 data) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM); ZYAN_ASSERT(instruction->raw.modrm.rm == 4); ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB)); ZYAN_ASSERT(instruction->raw.sib.offset == instruction->length - 1); instruction->attributes |= ZYDIS_ATTRIB_HAS_SIB; instruction->raw.sib.scale = (data >> 6) & 0x03; instruction->raw.sib.index = (data >> 3) & 0x07; instruction->raw.sib.base = (data >> 0) & 0x07; } /* ---------------------------------------------------------------------------------------------- */ /** * 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); instruction->raw.disp.size = size; instruction->raw.disp.offset = instruction->length; switch (size) { case 8: { ZyanU8 value; ZYAN_CHECK(ZydisInputNext(state, instruction, &value)); instruction->raw.disp.value = *(ZyanI8*)&value; break; } case 16: { ZyanU16 value; ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 2)); instruction->raw.disp.value = *(ZyanI16*)&value; break; } case 32: { ZyanU32 value; ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 4)); instruction->raw.disp.value = *(ZyanI32*)&value; break; } case 64: { ZyanU64 value; ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 8)); instruction->raw.disp.value = *(ZyanI64*)&value; break; } default: ZYAN_UNREACHABLE; } // TODO: Fix endianess on big-endian systems return ZYAN_STATUS_SUCCESS; } /** * 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); instruction->raw.imm[id].size = size; instruction->raw.imm[id].offset = instruction->length; instruction->raw.imm[id].is_signed = is_signed; instruction->raw.imm[id].is_relative = is_relative; switch (size) { case 8: { ZyanU8 value; ZYAN_CHECK(ZydisInputNext(state, instruction, &value)); if (is_signed) { instruction->raw.imm[id].value.s = (ZyanI8)value; } else { instruction->raw.imm[id].value.u = value; } break; } case 16: { ZyanU16 value; ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 2)); if (is_signed) { instruction->raw.imm[id].value.s = (ZyanI16)value; } else { instruction->raw.imm[id].value.u = value; } break; } case 32: { ZyanU32 value; ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 4)); if (is_signed) { instruction->raw.imm[id].value.s = (ZyanI32)value; } else { instruction->raw.imm[id].value.u = value; } break; } case 64: { ZyanU64 value; ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 8)); if (is_signed) { instruction->raw.imm[id].value.s = (ZyanI64)value; } else { instruction->raw.imm[id].value.u = value; } break; } default: ZYAN_UNREACHABLE; } // TODO: Fix endianess on big-endian systems return ZYAN_STATUS_SUCCESS; } /* ---------------------------------------------------------------------------------------------- */ /* Semantic instruction decoding */ /* ---------------------------------------------------------------------------------------------- */ #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. */ static void 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; } } // Element count if (operand->element_size && operand->size && (operand->element_type != ZYDIS_ELEMENT_TYPE_CC)) { operand->element_count = operand->size / operand->element_size; } else { operand->element_count = 1; } } #endif #ifndef ZYDIS_MINIMAL_MODE /** * Decodes an register-operand. * * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * @param operand A pointer to the `ZydisDecodedOperand` struct. * @param register_class The register class. * @param register_id The register id. * * @return A zyan status code. */ static ZyanStatus ZydisDecodeOperandRegister(const ZydisDecodedInstruction* instruction, ZydisDecodedOperand* operand, ZydisRegisterClass register_class, ZyanU8 register_id) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(operand); operand->type = ZYDIS_OPERAND_TYPE_REGISTER; if (register_class == ZYDIS_REGCLASS_GPR8) { if ((instruction->attributes & ZYDIS_ATTRIB_HAS_REX) && (register_id >= 4)) { operand->reg.value = ZYDIS_REGISTER_SPL + (register_id - 4); } else { operand->reg.value = ZYDIS_REGISTER_AL + register_id; } } else { operand->reg.value = ZydisRegisterEncode(register_class, register_id); ZYAN_ASSERT(operand->reg.value); /*if (!operand->reg.value) { return ZYAN_STATUS_BAD_REGISTER; }*/ } return ZYAN_STATUS_SUCCESS; } #endif #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)))); operand->type = ZYDIS_OPERAND_TYPE_MEMORY; operand->mem.type = ZYDIS_MEMOP_TYPE_MEM; const ZyanU8 modrm_rm = instruction->raw.modrm.rm; ZyanU8 displacement_size = 0; switch (instruction->address_width) { case 16: { static const ZydisRegister bases[] = { ZYDIS_REGISTER_BX, ZYDIS_REGISTER_BX, ZYDIS_REGISTER_BP, ZYDIS_REGISTER_BP, ZYDIS_REGISTER_SI, ZYDIS_REGISTER_DI, ZYDIS_REGISTER_BP, ZYDIS_REGISTER_BX }; static const ZydisRegister indices[] = { ZYDIS_REGISTER_SI, ZYDIS_REGISTER_DI, ZYDIS_REGISTER_SI, ZYDIS_REGISTER_DI, ZYDIS_REGISTER_NONE, ZYDIS_REGISTER_NONE, ZYDIS_REGISTER_NONE, ZYDIS_REGISTER_NONE }; operand->mem.base = bases[modrm_rm]; operand->mem.index = indices[modrm_rm]; operand->mem.scale = (operand->mem.index == ZYDIS_REGISTER_NONE) ? 0 : 1; switch (instruction->raw.modrm.mod) { case 0: if (modrm_rm == 6) { displacement_size = 16; operand->mem.base = ZYDIS_REGISTER_NONE; } break; case 1: displacement_size = 8; break; case 2: displacement_size = 16; break; default: ZYAN_UNREACHABLE; } break; } case 32: { operand->mem.base = ZYDIS_REGISTER_EAX + ZydisCalcRegisterId(context, instruction, ZYDIS_REG_ENCODING_BASE, ZYDIS_REGCLASS_GPR32); switch (instruction->raw.modrm.mod) { case 0: if (modrm_rm == 5) { if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) { operand->mem.base = ZYDIS_REGISTER_EIP; } else { operand->mem.base = ZYDIS_REGISTER_NONE; } displacement_size = 32; } break; case 1: displacement_size = 8; break; case 2: displacement_size = 32; break; default: ZYAN_UNREACHABLE; } if (modrm_rm == 4) { ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB); operand->mem.index = ZydisRegisterEncode(vidx_register_class ? vidx_register_class : ZYDIS_REGCLASS_GPR32, ZydisCalcRegisterId(context, instruction, vidx_register_class ? ZYDIS_REG_ENCODING_VIDX : ZYDIS_REG_ENCODING_INDEX, vidx_register_class ? vidx_register_class : ZYDIS_REGCLASS_GPR32)); operand->mem.scale = (1 << instruction->raw.sib.scale); if (operand->mem.index == ZYDIS_REGISTER_ESP) { operand->mem.index = ZYDIS_REGISTER_NONE; operand->mem.scale = 0; } if (operand->mem.base == ZYDIS_REGISTER_EBP) { if (instruction->raw.modrm.mod == 0) { operand->mem.base = ZYDIS_REGISTER_NONE; } displacement_size = (instruction->raw.modrm.mod == 1) ? 8 : 32; } } else { operand->mem.index = ZYDIS_REGISTER_NONE; operand->mem.scale = 0; } break; } case 64: { operand->mem.base = ZYDIS_REGISTER_RAX + ZydisCalcRegisterId(context, instruction, ZYDIS_REG_ENCODING_BASE, ZYDIS_REGCLASS_GPR64); switch (instruction->raw.modrm.mod) { case 0: if (modrm_rm == 5) { if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) { operand->mem.base = ZYDIS_REGISTER_RIP; } else { operand->mem.base = ZYDIS_REGISTER_NONE; } displacement_size = 32; } break; case 1: displacement_size = 8; break; case 2: displacement_size = 32; break; default: ZYAN_UNREACHABLE; } if ((modrm_rm & 0x07) == 4) { ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB); operand->mem.index = ZydisRegisterEncode(vidx_register_class ? vidx_register_class : ZYDIS_REGCLASS_GPR64, ZydisCalcRegisterId(context, instruction, vidx_register_class ? ZYDIS_REG_ENCODING_VIDX : ZYDIS_REG_ENCODING_INDEX, vidx_register_class ? vidx_register_class : ZYDIS_REGCLASS_GPR64)); operand->mem.scale = (1 << instruction->raw.sib.scale); if (operand->mem.index == ZYDIS_REGISTER_RSP) { operand->mem.index = ZYDIS_REGISTER_NONE; operand->mem.scale = 0; } if ((operand->mem.base == ZYDIS_REGISTER_RBP) || (operand->mem.base == ZYDIS_REGISTER_R13)) { if (instruction->raw.modrm.mod == 0) { operand->mem.base = ZYDIS_REGISTER_NONE; } displacement_size = (instruction->raw.modrm.mod == 1) ? 8 : 32; } } else { operand->mem.index = ZYDIS_REGISTER_NONE; operand->mem.scale = 0; } break; } default: ZYAN_UNREACHABLE; } if (displacement_size) { ZYAN_ASSERT(instruction->raw.disp.size == displacement_size); operand->mem.disp.has_displacement = ZYAN_TRUE; operand->mem.disp.value = instruction->raw.disp.value; } return ZYAN_STATUS_SUCCESS; } #endif #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. */ static void 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); operand->type = ZYDIS_OPERAND_TYPE_REGISTER; switch (definition->op.reg.type) { case ZYDIS_IMPLREG_TYPE_STATIC: operand->reg.value = definition->op.reg.reg.reg; break; case ZYDIS_IMPLREG_TYPE_GPR_OSZ: { static const ZydisRegisterClass lookup[3] = { ZYDIS_REGCLASS_GPR16, ZYDIS_REGCLASS_GPR32, ZYDIS_REGCLASS_GPR64 }; operand->reg.value = ZydisRegisterEncode(lookup[context->eosz_index], definition->op.reg.reg.id); break; } case ZYDIS_IMPLREG_TYPE_GPR_ASZ: operand->reg.value = ZydisRegisterEncode( (instruction->address_width == 16) ? ZYDIS_REGCLASS_GPR16 : (instruction->address_width == 32) ? ZYDIS_REGCLASS_GPR32 : ZYDIS_REGCLASS_GPR64, definition->op.reg.reg.id); break; case ZYDIS_IMPLREG_TYPE_IP_ASZ: operand->reg.value = (instruction->address_width == 16) ? ZYDIS_REGISTER_IP : (instruction->address_width == 32) ? ZYDIS_REGISTER_EIP : ZYDIS_REGISTER_RIP; break; case ZYDIS_IMPLREG_TYPE_GPR_SSZ: operand->reg.value = ZydisRegisterEncode( (decoder->stack_width == ZYDIS_STACK_WIDTH_16) ? ZYDIS_REGCLASS_GPR16 : (decoder->stack_width == ZYDIS_STACK_WIDTH_32) ? ZYDIS_REGCLASS_GPR32 : ZYDIS_REGCLASS_GPR64, definition->op.reg.reg.id); break; case ZYDIS_IMPLREG_TYPE_IP_SSZ: operand->reg.value = (decoder->stack_width == ZYDIS_STACK_WIDTH_16) ? ZYDIS_REGISTER_EIP : (decoder->stack_width == ZYDIS_STACK_WIDTH_32) ? ZYDIS_REGISTER_EIP : ZYDIS_REGISTER_RIP; break; case ZYDIS_IMPLREG_TYPE_FLAGS_SSZ: operand->reg.value = (decoder->stack_width == ZYDIS_STACK_WIDTH_16) ? ZYDIS_REGISTER_FLAGS : (decoder->stack_width == ZYDIS_STACK_WIDTH_32) ? ZYDIS_REGISTER_EFLAGS : ZYDIS_REGISTER_RFLAGS; break; default: ZYAN_UNREACHABLE; } } #endif #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. */ static void ZydisDecodeOperandImplicitMemory(const ZydisDecoder* decoder, const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction, ZydisDecodedOperand* operand, const ZydisOperandDefinition* definition) { ZYAN_ASSERT(context); ZYAN_ASSERT(operand); ZYAN_ASSERT(definition); static const ZydisRegisterClass lookup[3] = { ZYDIS_REGCLASS_GPR16, ZYDIS_REGCLASS_GPR32, ZYDIS_REGCLASS_GPR64 }; operand->type = ZYDIS_OPERAND_TYPE_MEMORY; operand->mem.type = ZYDIS_MEMOP_TYPE_MEM; switch (definition->op.mem.base) { case ZYDIS_IMPLMEM_BASE_AGPR_REG: operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], ZydisCalcRegisterId(context, instruction, ZYDIS_REG_ENCODING_REG, lookup[context->easz_index])); break; case ZYDIS_IMPLMEM_BASE_AGPR_RM: operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], ZydisCalcRegisterId(context, instruction, ZYDIS_REG_ENCODING_RM, lookup[context->easz_index])); break; case ZYDIS_IMPLMEM_BASE_AAX: operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 0); break; case ZYDIS_IMPLMEM_BASE_ADX: operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 2); break; case ZYDIS_IMPLMEM_BASE_ABX: operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 3); break; case ZYDIS_IMPLMEM_BASE_ASI: operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 6); break; case ZYDIS_IMPLMEM_BASE_ADI: operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 7); break; case ZYDIS_IMPLMEM_BASE_SSP: operand->mem.base = ZydisRegisterEncode(lookup[decoder->stack_width], 4); break; case ZYDIS_IMPLMEM_BASE_SBP: operand->mem.base = ZydisRegisterEncode(lookup[decoder->stack_width], 5); break; default: ZYAN_UNREACHABLE; } if (definition->op.mem.seg) { operand->mem.segment = ZydisRegisterEncode(ZYDIS_REGCLASS_SEGMENT, definition->op.mem.seg - 1); ZYAN_ASSERT(operand->mem.segment); } } #endif #ifndef ZYDIS_MINIMAL_MODE ZyanStatus ZydisDecodeOperands(const ZydisDecoder* decoder, const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction, ZydisDecodedOperand* operands, ZyanU8 operand_count) { ZYAN_ASSERT(decoder); ZYAN_ASSERT(context); ZYAN_ASSERT(context->definition); ZYAN_ASSERT(instruction); ZYAN_ASSERT(operands); ZYAN_ASSERT(operand_count); ZYAN_ASSERT(operand_count <= instruction->operand_count); const ZydisInstructionDefinition* definition = context->definition; const ZydisOperandDefinition* operand = ZydisGetOperandDefinitions(definition); ZYAN_MEMSET(operands, 0, sizeof(ZydisDecodedOperand) * operand_count); ZyanU8 imm_id = 0; for (ZyanU8 i = 0; i < operand_count; ++i) { ZydisRegisterClass register_class = ZYDIS_REGCLASS_INVALID; operands[i].id = i; operands[i].visibility = operand->visibility; operands[i].actions = operand->actions; ZYAN_ASSERT(!(operand->actions & ZYDIS_OPERAND_ACTION_READ & ZYDIS_OPERAND_ACTION_CONDREAD) || (operand->actions & ZYDIS_OPERAND_ACTION_READ) ^ (operand->actions & ZYDIS_OPERAND_ACTION_CONDREAD)); ZYAN_ASSERT(!(operand->actions & ZYDIS_OPERAND_ACTION_WRITE & ZYDIS_OPERAND_ACTION_CONDWRITE) || (operand->actions & ZYDIS_OPERAND_ACTION_WRITE) ^ (operand->actions & ZYDIS_OPERAND_ACTION_CONDWRITE)); // Implicit operands switch (operand->type) { case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_REG: ZydisDecodeOperandImplicitRegister(decoder, context, instruction, &operands[i], operand); break; case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_MEM: ZydisDecodeOperandImplicitMemory(decoder, context, instruction, &operands[i], operand); break; case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_IMM1: operands[i].type = ZYDIS_OPERAND_TYPE_IMMEDIATE; operands[i].size = 8; operands[i].imm.value.u = 1; operands[i].imm.is_signed = ZYAN_FALSE; operands[i].imm.is_relative = ZYAN_FALSE; break; default: break; } if (operands[i].type) { goto FinalizeOperand; } operands[i].encoding = operand->op.encoding; // Register operands switch (operand->type) { case ZYDIS_SEMANTIC_OPTYPE_GPR8: register_class = ZYDIS_REGCLASS_GPR8; break; case ZYDIS_SEMANTIC_OPTYPE_GPR16: register_class = ZYDIS_REGCLASS_GPR16; break; case ZYDIS_SEMANTIC_OPTYPE_GPR32: register_class = ZYDIS_REGCLASS_GPR32; break; case ZYDIS_SEMANTIC_OPTYPE_GPR64: register_class = ZYDIS_REGCLASS_GPR64; break; case ZYDIS_SEMANTIC_OPTYPE_GPR16_32_64: ZYAN_ASSERT((instruction->operand_width == 16) || (instruction->operand_width == 32) || (instruction->operand_width == 64)); register_class = (instruction->operand_width == 16) ? ZYDIS_REGCLASS_GPR16 : ( (instruction->operand_width == 32) ? ZYDIS_REGCLASS_GPR32 : ZYDIS_REGCLASS_GPR64); break; case ZYDIS_SEMANTIC_OPTYPE_GPR32_32_64: ZYAN_ASSERT((instruction->operand_width == 16) || (instruction->operand_width == 32) || (instruction->operand_width == 64)); register_class = (instruction->operand_width == 16) ? ZYDIS_REGCLASS_GPR32 : ( (instruction->operand_width == 32) ? ZYDIS_REGCLASS_GPR32 : ZYDIS_REGCLASS_GPR64); break; case ZYDIS_SEMANTIC_OPTYPE_GPR16_32_32: ZYAN_ASSERT((instruction->operand_width == 16) || (instruction->operand_width == 32) || (instruction->operand_width == 64)); register_class = (instruction->operand_width == 16) ? ZYDIS_REGCLASS_GPR16 : ZYDIS_REGCLASS_GPR32; break; case ZYDIS_SEMANTIC_OPTYPE_GPR_ASZ: ZYAN_ASSERT((instruction->address_width == 16) || (instruction->address_width == 32) || (instruction->address_width == 64)); register_class = (instruction->address_width == 16) ? ZYDIS_REGCLASS_GPR16 : ( (instruction->address_width == 32) ? ZYDIS_REGCLASS_GPR32 : ZYDIS_REGCLASS_GPR64); break; case ZYDIS_SEMANTIC_OPTYPE_FPR: register_class = ZYDIS_REGCLASS_X87; break; case ZYDIS_SEMANTIC_OPTYPE_MMX: register_class = ZYDIS_REGCLASS_MMX; break; case ZYDIS_SEMANTIC_OPTYPE_XMM: register_class = ZYDIS_REGCLASS_XMM; break; case ZYDIS_SEMANTIC_OPTYPE_YMM: register_class = ZYDIS_REGCLASS_YMM; break; case ZYDIS_SEMANTIC_OPTYPE_ZMM: register_class = ZYDIS_REGCLASS_ZMM; break; case ZYDIS_SEMANTIC_OPTYPE_TMM: register_class = ZYDIS_REGCLASS_TMM; break; case ZYDIS_SEMANTIC_OPTYPE_BND: register_class = ZYDIS_REGCLASS_BOUND; break; case ZYDIS_SEMANTIC_OPTYPE_SREG: register_class = ZYDIS_REGCLASS_SEGMENT; break; case ZYDIS_SEMANTIC_OPTYPE_CR: register_class = ZYDIS_REGCLASS_CONTROL; break; case ZYDIS_SEMANTIC_OPTYPE_DR: register_class = ZYDIS_REGCLASS_DEBUG; break; case ZYDIS_SEMANTIC_OPTYPE_MASK: register_class = ZYDIS_REGCLASS_MASK; break; default: break; } if (register_class) { switch (operand->op.encoding) { case ZYDIS_OPERAND_ENCODING_MODRM_REG: ZYAN_CHECK( ZydisDecodeOperandRegister( instruction, &operands[i], register_class, ZydisCalcRegisterId( context, instruction, ZYDIS_REG_ENCODING_REG, register_class))); break; case ZYDIS_OPERAND_ENCODING_MODRM_RM: ZYAN_CHECK( ZydisDecodeOperandRegister( instruction, &operands[i], register_class, ZydisCalcRegisterId( context, instruction, ZYDIS_REG_ENCODING_RM, register_class))); break; case ZYDIS_OPERAND_ENCODING_OPCODE: ZYAN_CHECK( ZydisDecodeOperandRegister( instruction, &operands[i], register_class, ZydisCalcRegisterId( context, instruction, ZYDIS_REG_ENCODING_OPCODE, register_class))); break; case ZYDIS_OPERAND_ENCODING_NDSNDD: ZYAN_CHECK( ZydisDecodeOperandRegister( instruction, &operands[i], register_class, ZydisCalcRegisterId( context, instruction, ZYDIS_REG_ENCODING_NDSNDD, register_class))); break; case ZYDIS_OPERAND_ENCODING_MASK: ZYAN_CHECK( ZydisDecodeOperandRegister( instruction, &operands[i], register_class, ZydisCalcRegisterId( context, instruction, ZYDIS_REG_ENCODING_MASK, register_class))); break; case ZYDIS_OPERAND_ENCODING_IS4: ZYAN_CHECK( ZydisDecodeOperandRegister( instruction, &operands[i], register_class, ZydisCalcRegisterId( context, instruction, ZYDIS_REG_ENCODING_IS4, register_class))); break; default: ZYAN_UNREACHABLE; } if (operand->is_multisource4) { operands[i].attributes |= ZYDIS_OATTRIB_IS_MULTISOURCE4; } goto FinalizeOperand; } // Memory operands switch (operand->type) { case ZYDIS_SEMANTIC_OPTYPE_MEM: ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &operands[i], ZYDIS_REGCLASS_INVALID)); break; case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBX: ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &operands[i], ZYDIS_REGCLASS_XMM)); operands[i].mem.type = ZYDIS_MEMOP_TYPE_VSIB; break; case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBY: ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &operands[i], ZYDIS_REGCLASS_YMM)); operands[i].mem.type = ZYDIS_MEMOP_TYPE_VSIB; break; case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBZ: ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &operands[i], ZYDIS_REGCLASS_ZMM)); operands[i].mem.type = ZYDIS_MEMOP_TYPE_VSIB; break; case ZYDIS_SEMANTIC_OPTYPE_PTR: ZYAN_ASSERT((instruction->raw.imm[0].size == 16) || (instruction->raw.imm[0].size == 32)); ZYAN_ASSERT(instruction->raw.imm[1].size == 16); operands[i].type = ZYDIS_OPERAND_TYPE_POINTER; operands[i].ptr.offset = (ZyanU32)instruction->raw.imm[0].value.u; operands[i].ptr.segment = (ZyanU16)instruction->raw.imm[1].value.u; break; case ZYDIS_SEMANTIC_OPTYPE_AGEN: operands[i].actions = 0; // TODO: Remove after generator update ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &operands[i], ZYDIS_REGCLASS_INVALID)); operands[i].mem.type = ZYDIS_MEMOP_TYPE_AGEN; break; case ZYDIS_SEMANTIC_OPTYPE_MOFFS: ZYAN_ASSERT(instruction->raw.disp.size); operands[i].type = ZYDIS_OPERAND_TYPE_MEMORY; operands[i].mem.type = ZYDIS_MEMOP_TYPE_MEM; operands[i].mem.disp.has_displacement = ZYAN_TRUE; operands[i].mem.disp.value = instruction->raw.disp.value; break; case ZYDIS_SEMANTIC_OPTYPE_MIB: operands[i].actions = 0; // TODO: Remove after generator update ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &operands[i], ZYDIS_REGCLASS_INVALID)); operands[i].mem.type = ZYDIS_MEMOP_TYPE_MIB; break; default: break; } if (operands[i].type) { #if !defined(ZYDIS_DISABLE_AVX512) || !defined(ZYDIS_DISABLE_KNC) // Handle compressed 8-bit displacement if (((instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) || (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX)) && (instruction->raw.disp.size == 8)) { operands[i].mem.disp.value *= context->cd8_scale; } #endif goto FinalizeOperand; } // Immediate operands switch (operand->type) { case ZYDIS_SEMANTIC_OPTYPE_REL: ZYAN_ASSERT(instruction->raw.imm[imm_id].is_relative); ZYAN_FALLTHROUGH; case ZYDIS_SEMANTIC_OPTYPE_IMM: ZYAN_ASSERT((imm_id == 0) || (imm_id == 1)); operands[i].type = ZYDIS_OPERAND_TYPE_IMMEDIATE; operands[i].size = operand->size[context->eosz_index] * 8; if (operand->op.encoding == ZYDIS_OPERAND_ENCODING_IS4) { // The upper half of the 8-bit immediate is used to encode a register specifier ZYAN_ASSERT(instruction->raw.imm[imm_id].size == 8); operands[i].imm.value.u = (ZyanU8)instruction->raw.imm[imm_id].value.u & 0x0F; } else { operands[i].imm.value.u = instruction->raw.imm[imm_id].value.u; } operands[i].imm.is_signed = instruction->raw.imm[imm_id].is_signed; operands[i].imm.is_relative = instruction->raw.imm[imm_id].is_relative; ++imm_id; break; default: break; } ZYAN_ASSERT(operands[i].type == ZYDIS_OPERAND_TYPE_IMMEDIATE); FinalizeOperand: // Set segment-register for memory operands if (operands[i].type == ZYDIS_OPERAND_TYPE_MEMORY) { if (!operand->ignore_seg_override && instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_CS) { operands[i].mem.segment = ZYDIS_REGISTER_CS; } else if (!operand->ignore_seg_override && instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_SS) { operands[i].mem.segment = ZYDIS_REGISTER_SS; } else if (!operand->ignore_seg_override && instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_DS) { operands[i].mem.segment = ZYDIS_REGISTER_DS; } else if (!operand->ignore_seg_override && instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_ES) { operands[i].mem.segment = ZYDIS_REGISTER_ES; } else if (!operand->ignore_seg_override && instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_FS) { operands[i].mem.segment = ZYDIS_REGISTER_FS; } else if (!operand->ignore_seg_override && instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_GS) { operands[i].mem.segment = ZYDIS_REGISTER_GS; } else { if (operands[i].mem.segment == ZYDIS_REGISTER_NONE) { if ((operands[i].mem.base == ZYDIS_REGISTER_RSP) || (operands[i].mem.base == ZYDIS_REGISTER_RBP) || (operands[i].mem.base == ZYDIS_REGISTER_ESP) || (operands[i].mem.base == ZYDIS_REGISTER_EBP) || (operands[i].mem.base == ZYDIS_REGISTER_SP) || (operands[i].mem.base == ZYDIS_REGISTER_BP)) { operands[i].mem.segment = ZYDIS_REGISTER_SS; } else { operands[i].mem.segment = ZYDIS_REGISTER_DS; } } } } ZydisSetOperandSizeAndElementInfo(context, instruction, &operands[i], operand); ++operand; } #if !defined(ZYDIS_DISABLE_AVX512) || !defined(ZYDIS_DISABLE_KNC) // Fix operand-action for EVEX/MVEX instructions with merge-mask if (instruction->avx.mask.mode == ZYDIS_MASK_MODE_MERGING) { ZYAN_ASSERT(operand_count >= 1); switch (operands[0].actions) { case ZYDIS_OPERAND_ACTION_WRITE: if (operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY) { operands[0].actions = ZYDIS_OPERAND_ACTION_CONDWRITE; } else { operands[0].actions = ZYDIS_OPERAND_ACTION_READ_CONDWRITE; } break; case ZYDIS_OPERAND_ACTION_READWRITE: operands[0].actions = ZYDIS_OPERAND_ACTION_READ_CONDWRITE; break; default: break; } } #endif return ZYAN_STATUS_SUCCESS; } #endif /* ---------------------------------------------------------------------------------------------- */ #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. */ static void ZydisSetAttributes(ZydisDecoderState* state, ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition) { ZYAN_ASSERT(state); ZYAN_ASSERT(instruction); ZYAN_ASSERT(definition); if (definition->cpu_state != ZYDIS_RW_ACTION_NONE) { static const ZydisInstructionAttributes mapping[ZYDIS_RW_ACTION_MAX_VALUE + 1] = { /* NONE */ 0, /* READ */ ZYDIS_ATTRIB_CPU_STATE_CR, /* WRITE */ ZYDIS_ATTRIB_CPU_STATE_CW, /* READWRITE */ ZYDIS_ATTRIB_CPU_STATE_CR | ZYDIS_ATTRIB_CPU_STATE_CW }; ZYAN_ASSERT(definition->cpu_state < ZYAN_ARRAY_LENGTH(mapping)); instruction->attributes |= mapping[definition->cpu_state]; } if (definition->fpu_state != ZYDIS_RW_ACTION_NONE) { static const ZydisInstructionAttributes mapping[ZYDIS_RW_ACTION_MAX_VALUE + 1] = { /* NONE */ 0, /* READ */ ZYDIS_ATTRIB_FPU_STATE_CR, /* WRITE */ ZYDIS_ATTRIB_FPU_STATE_CW, /* READWRITE */ ZYDIS_ATTRIB_FPU_STATE_CR | ZYDIS_ATTRIB_FPU_STATE_CW }; ZYAN_ASSERT(definition->fpu_state < ZYAN_ARRAY_LENGTH(mapping)); instruction->attributes |= mapping[definition->fpu_state]; } if (definition->xmm_state != ZYDIS_RW_ACTION_NONE) { static const ZydisInstructionAttributes mapping[ZYDIS_RW_ACTION_MAX_VALUE + 1] = { /* NONE */ 0, /* READ */ ZYDIS_ATTRIB_XMM_STATE_CR, /* WRITE */ ZYDIS_ATTRIB_XMM_STATE_CW, /* READWRITE */ ZYDIS_ATTRIB_XMM_STATE_CR | ZYDIS_ATTRIB_XMM_STATE_CW }; ZYAN_ASSERT(definition->xmm_state < ZYAN_ARRAY_LENGTH(mapping)); instruction->attributes |= mapping[definition->xmm_state]; } switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_LEGACY: { const ZydisInstructionDefinitionLEGACY* def = (const ZydisInstructionDefinitionLEGACY*)definition; if (def->is_privileged) { instruction->attributes |= ZYDIS_ATTRIB_IS_PRIVILEGED; } if (def->accepts_LOCK) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_LOCK; if (state->prefixes.has_lock) { instruction->attributes |= ZYDIS_ATTRIB_HAS_LOCK; instruction->raw.prefixes[state->prefixes.offset_lock].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; } } if (def->accepts_REP) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_REP; } if (def->accepts_REPEREPZ) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_REPE; } if (def->accepts_REPNEREPNZ) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_REPNE; } if (def->accepts_BOUND) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_BND; } if (def->accepts_XACQUIRE) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_XACQUIRE; } if (def->accepts_XRELEASE) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_XRELEASE; } if (def->accepts_hle_without_lock) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_HLE_WITHOUT_LOCK; } switch (state->prefixes.group1) { case 0xF2: if (instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_REPNE) { instruction->attributes |= ZYDIS_ATTRIB_HAS_REPNE; break; } if (instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_XACQUIRE) { if ((instruction->attributes & ZYDIS_ATTRIB_HAS_LOCK) || (def->accepts_hle_without_lock)) { instruction->attributes |= ZYDIS_ATTRIB_HAS_XACQUIRE; break; } } if (state->decoder->decoder_mode[ZYDIS_DECODER_MODE_MPX] && instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_BND) { instruction->attributes |= ZYDIS_ATTRIB_HAS_BND; break; } break; case 0xF3: if (instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_REP) { instruction->attributes |= ZYDIS_ATTRIB_HAS_REP; break; } if (instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_REPE) { instruction->attributes |= ZYDIS_ATTRIB_HAS_REPE; break; } if (instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_XRELEASE) { if ((instruction->attributes & ZYDIS_ATTRIB_HAS_LOCK) || (def->accepts_hle_without_lock)) { instruction->attributes |= ZYDIS_ATTRIB_HAS_XRELEASE; break; } } break; default: break; } if ((instruction->raw.prefixes[state->prefixes.offset_group1].type == ZYDIS_PREFIX_TYPE_IGNORED) && (instruction->attributes & ( ZYDIS_ATTRIB_HAS_REP | ZYDIS_ATTRIB_HAS_REPE | ZYDIS_ATTRIB_HAS_REPNE | ZYDIS_ATTRIB_HAS_BND | ZYDIS_ATTRIB_HAS_XACQUIRE | ZYDIS_ATTRIB_HAS_XRELEASE))) { instruction->raw.prefixes[state->prefixes.offset_group1].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; } if (def->accepts_branch_hints) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_BRANCH_HINTS; switch (state->prefixes.group2) { case 0x2E: instruction->attributes |= ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN; instruction->raw.prefixes[state->prefixes.offset_group2].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; break; case 0x3E: instruction->attributes |= ZYDIS_ATTRIB_HAS_BRANCH_TAKEN; instruction->raw.prefixes[state->prefixes.offset_group2].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; break; default: break; } } if (def->accepts_NOTRACK) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_NOTRACK; if (state->decoder->decoder_mode[ZYDIS_DECODER_MODE_CET] && (state->prefixes.offset_notrack >= 0)) { instruction->attributes |= ZYDIS_ATTRIB_HAS_NOTRACK; instruction->raw.prefixes[state->prefixes.offset_notrack].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; } } 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 */ static void ZydisSetAVXInformation(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(definition); switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_XOP: { // Vector length static const ZyanU16 lookup[2] = { 128, 256 }; ZYAN_ASSERT(context->vector_unified.LL < ZYAN_ARRAY_LENGTH(lookup)); instruction->avx.vector_length = lookup[context->vector_unified.LL]; break; } case ZYDIS_INSTRUCTION_ENCODING_VEX: { // Vector length static const ZyanU16 lookup[2] = { 128, 256 }; ZYAN_ASSERT(context->vector_unified.LL < ZYAN_ARRAY_LENGTH(lookup)); instruction->avx.vector_length = lookup[context->vector_unified.LL]; // Static broadcast-factor const ZydisInstructionDefinitionVEX* def = (const ZydisInstructionDefinitionVEX*)definition; if (def->broadcast) { instruction->avx.broadcast.is_static = ZYAN_TRUE; static ZydisBroadcastMode broadcasts[ZYDIS_VEX_STATIC_BROADCAST_MAX_VALUE + 1] = { ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_1_TO_2, ZYDIS_BROADCAST_MODE_1_TO_4, ZYDIS_BROADCAST_MODE_1_TO_8, ZYDIS_BROADCAST_MODE_1_TO_16, ZYDIS_BROADCAST_MODE_1_TO_32, ZYDIS_BROADCAST_MODE_2_TO_4 }; instruction->avx.broadcast.mode = broadcasts[def->broadcast]; } break; } case ZYDIS_INSTRUCTION_ENCODING_EVEX: { #ifndef ZYDIS_DISABLE_AVX512 const ZydisInstructionDefinitionEVEX* def = (const ZydisInstructionDefinitionEVEX*)definition; // Vector length ZyanU8 vector_length = context->vector_unified.LL; if (def->vector_length) { vector_length = def->vector_length - 1; } static const ZyanU16 lookup[3] = { 128, 256, 512 }; ZYAN_ASSERT(vector_length < ZYAN_ARRAY_LENGTH(lookup)); instruction->avx.vector_length = lookup[vector_length]; context->evex.tuple_type = def->tuple_type; if (def->tuple_type) { ZYAN_ASSERT(instruction->raw.modrm.mod != 3); ZYAN_ASSERT(def->element_size); // Element size static const ZyanU8 element_sizes[ZYDIS_IELEMENT_SIZE_MAX_VALUE + 1] = { 0, 8, 16, 32, 64, 128 }; ZYAN_ASSERT(def->element_size < ZYAN_ARRAY_LENGTH(element_sizes)); context->evex.element_size = element_sizes[def->element_size]; // Compressed disp8 scale and broadcast-factor switch (def->tuple_type) { case ZYDIS_TUPLETYPE_FV: { const ZyanU8 evex_b = instruction->raw.evex.b; ZYAN_ASSERT(evex_b < 2); ZYAN_ASSERT(!evex_b || ((!context->vector_unified.W && (context->evex.element_size == 16 || context->evex.element_size == 32)) || ( context->vector_unified.W && context->evex.element_size == 64))); ZYAN_ASSERT(!evex_b || def->functionality == ZYDIS_EVEX_FUNC_BC); static const ZyanU8 scales[2][3][3] = { /*B0*/ { /*16*/ { 16, 32, 64 }, /*32*/ { 16, 32, 64 }, /*64*/ { 16, 32, 64 } }, /*B1*/ { /*16*/ { 2, 2, 2 }, /*32*/ { 4, 4, 4 }, /*64*/ { 8, 8, 8 } } }; static const ZydisBroadcastMode broadcasts[2][3][3] = { /*B0*/ { /*16*/ { ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID }, /*32*/ { ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID }, /*64*/ { ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID } }, /*B1*/ { /*16*/ { ZYDIS_BROADCAST_MODE_1_TO_8, ZYDIS_BROADCAST_MODE_1_TO_16, ZYDIS_BROADCAST_MODE_1_TO_32 }, /*32*/ { ZYDIS_BROADCAST_MODE_1_TO_4, ZYDIS_BROADCAST_MODE_1_TO_8, ZYDIS_BROADCAST_MODE_1_TO_16 }, /*64*/ { ZYDIS_BROADCAST_MODE_1_TO_2, ZYDIS_BROADCAST_MODE_1_TO_4, ZYDIS_BROADCAST_MODE_1_TO_8 } } }; const ZyanU8 size_index = context->evex.element_size >> 5; ZYAN_ASSERT(size_index < 3); context->cd8_scale = scales[evex_b][size_index][vector_length]; instruction->avx.broadcast.mode = broadcasts[evex_b][size_index][vector_length]; break; } case ZYDIS_TUPLETYPE_HV: { const ZyanU8 evex_b = instruction->raw.evex.b; ZYAN_ASSERT(evex_b < 2); ZYAN_ASSERT(!context->vector_unified.W); ZYAN_ASSERT((context->evex.element_size == 16) || (context->evex.element_size == 32)); ZYAN_ASSERT(!evex_b || def->functionality == ZYDIS_EVEX_FUNC_BC); static const ZyanU8 scales[2][2][3] = { /*B0*/ { /*16*/ { 8, 16, 32 }, /*32*/ { 8, 16, 32 } }, /*B1*/ { /*16*/ { 2, 2, 2 }, /*32*/ { 4, 4, 4 } } }; static const ZydisBroadcastMode broadcasts[2][2][3] = { /*B0*/ { /*16*/ { ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID }, /*32*/ { ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID } }, /*B1*/ { /*16*/ { ZYDIS_BROADCAST_MODE_1_TO_4, ZYDIS_BROADCAST_MODE_1_TO_8, ZYDIS_BROADCAST_MODE_1_TO_16 }, /*32*/ { ZYDIS_BROADCAST_MODE_1_TO_2, ZYDIS_BROADCAST_MODE_1_TO_4, ZYDIS_BROADCAST_MODE_1_TO_8 } } }; const ZyanU8 size_index = context->evex.element_size >> 5; ZYAN_ASSERT(size_index < 3); context->cd8_scale = scales[evex_b][size_index][vector_length]; instruction->avx.broadcast.mode = broadcasts[evex_b][size_index][vector_length]; break; } case ZYDIS_TUPLETYPE_FVM: { static const ZyanU8 scales[3] = { 16, 32, 64 }; context->cd8_scale = scales[vector_length]; break; } case ZYDIS_TUPLETYPE_GSCAT: switch (context->vector_unified.W) { case 0: ZYAN_ASSERT(context->evex.element_size == 32); break; case 1: ZYAN_ASSERT(context->evex.element_size == 64); break; default: ZYAN_UNREACHABLE; } ZYAN_FALLTHROUGH; case ZYDIS_TUPLETYPE_T1S: { static const ZyanU8 scales[6] = { /* */ 0, /* 8*/ 1, /* 16*/ 2, /* 32*/ 4, /* 64*/ 8, /*128*/ 16, }; ZYAN_ASSERT(def->element_size < ZYAN_ARRAY_LENGTH(scales)); context->cd8_scale = scales[def->element_size]; break; }; case ZYDIS_TUPLETYPE_T1F: { static const ZyanU8 scales[3] = { /* 16*/ 2, /* 32*/ 4, /* 64*/ 8 }; const ZyanU8 size_index = context->evex.element_size >> 5; ZYAN_ASSERT(size_index < 3); context->cd8_scale = scales[size_index]; break; } case ZYDIS_TUPLETYPE_T1_4X: ZYAN_ASSERT(context->evex.element_size == 32); ZYAN_ASSERT(context->vector_unified.W == 0); context->cd8_scale = 16; break; case ZYDIS_TUPLETYPE_T2: switch (context->vector_unified.W) { case 0: ZYAN_ASSERT(context->evex.element_size == 32); context->cd8_scale = 8; break; case 1: ZYAN_ASSERT(context->evex.element_size == 64); ZYAN_ASSERT((instruction->avx.vector_length == 256) || (instruction->avx.vector_length == 512)); context->cd8_scale = 16; break; default: ZYAN_UNREACHABLE; } break; case ZYDIS_TUPLETYPE_T4: switch (context->vector_unified.W) { case 0: ZYAN_ASSERT(context->evex.element_size == 32); ZYAN_ASSERT((instruction->avx.vector_length == 256) || (instruction->avx.vector_length == 512)); context->cd8_scale = 16; break; case 1: ZYAN_ASSERT(context->evex.element_size == 64); ZYAN_ASSERT(instruction->avx.vector_length == 512); context->cd8_scale = 32; break; default: ZYAN_UNREACHABLE; } break; case ZYDIS_TUPLETYPE_T8: ZYAN_ASSERT(!context->vector_unified.W); ZYAN_ASSERT(instruction->avx.vector_length == 512); ZYAN_ASSERT(context->evex.element_size == 32); context->cd8_scale = 32; break; case ZYDIS_TUPLETYPE_HVM: { static const ZyanU8 scales[3] = { 8, 16, 32 }; context->cd8_scale = scales[vector_length]; break; } case ZYDIS_TUPLETYPE_QVM: { static const ZyanU8 scales[3] = { 4, 8, 16 }; context->cd8_scale = scales[vector_length]; break; } case ZYDIS_TUPLETYPE_OVM: { static const ZyanU8 scales[3] = { 2, 4, 8 }; context->cd8_scale = scales[vector_length]; break; } case ZYDIS_TUPLETYPE_M128: context->cd8_scale = 16; break; case ZYDIS_TUPLETYPE_DUP: { static const ZyanU8 scales[3] = { 8, 32, 64 }; context->cd8_scale = scales[vector_length]; break; } case ZYDIS_TUPLETYPE_QUARTER: { const ZyanU8 evex_b = instruction->raw.evex.b; ZYAN_ASSERT(evex_b < 2); ZYAN_ASSERT(!context->vector_unified.W); ZYAN_ASSERT(context->evex.element_size == 16); ZYAN_ASSERT(!evex_b || def->functionality == ZYDIS_EVEX_FUNC_BC); static const ZyanU8 scales[2][3] = { /*B0*/ { 4, 8, 16 }, /*B1*/ { 2, 2, 2 } }; static const ZydisBroadcastMode broadcasts[2][3] = { /*B0*/ { ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID }, /*B1*/ { ZYDIS_BROADCAST_MODE_1_TO_2, ZYDIS_BROADCAST_MODE_1_TO_4, ZYDIS_BROADCAST_MODE_1_TO_8 } }; context->cd8_scale = scales[evex_b][vector_length]; instruction->avx.broadcast.mode = broadcasts[evex_b][vector_length]; break; } default: ZYAN_UNREACHABLE; } } else { ZYAN_ASSERT(instruction->raw.modrm.mod == 3); } // Static broadcast-factor if (def->broadcast) { ZYAN_ASSERT(!instruction->avx.broadcast.mode); instruction->avx.broadcast.is_static = ZYAN_TRUE; static const ZydisBroadcastMode broadcasts[ZYDIS_EVEX_STATIC_BROADCAST_MAX_VALUE + 1] = { ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_1_TO_2, ZYDIS_BROADCAST_MODE_1_TO_4, ZYDIS_BROADCAST_MODE_1_TO_8, ZYDIS_BROADCAST_MODE_1_TO_16, ZYDIS_BROADCAST_MODE_1_TO_32, ZYDIS_BROADCAST_MODE_1_TO_64, ZYDIS_BROADCAST_MODE_2_TO_4, ZYDIS_BROADCAST_MODE_2_TO_8, ZYDIS_BROADCAST_MODE_2_TO_16, ZYDIS_BROADCAST_MODE_4_TO_8, ZYDIS_BROADCAST_MODE_4_TO_16, ZYDIS_BROADCAST_MODE_8_TO_16 }; ZYAN_ASSERT(def->broadcast < ZYAN_ARRAY_LENGTH(broadcasts)); instruction->avx.broadcast.mode = broadcasts[def->broadcast]; } // 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; } } // Mask instruction->avx.mask.reg = ZYDIS_REGISTER_K0 + instruction->raw.evex.aaa; switch (def->mask_override) { case ZYDIS_MASK_OVERRIDE_DEFAULT: instruction->avx.mask.mode = ZYDIS_MASK_MODE_MERGING + instruction->raw.evex.z; break; case ZYDIS_MASK_OVERRIDE_ZEROING: instruction->avx.mask.mode = ZYDIS_MASK_MODE_ZEROING; break; case ZYDIS_MASK_OVERRIDE_CONTROL: instruction->avx.mask.mode = ZYDIS_MASK_MODE_CONTROL + instruction->raw.evex.z; break; default: ZYAN_UNREACHABLE; } if (!instruction->raw.evex.aaa) { instruction->avx.mask.mode = ZYDIS_MASK_MODE_DISABLED; } #else ZYAN_UNREACHABLE; #endif break; } case ZYDIS_INSTRUCTION_ENCODING_MVEX: { #ifndef ZYDIS_DISABLE_KNC // Vector length instruction->avx.vector_length = 512; const ZydisInstructionDefinitionMVEX* def = (const ZydisInstructionDefinitionMVEX*)definition; // 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: { static const 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: { static const 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: { static const 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: { static const 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: { static const 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 /* ---------------------------------------------------------------------------------------------- */ /* Physical instruction decoding */ /* ---------------------------------------------------------------------------------------------- */ /** * 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); if (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE) { instruction->raw.prefixes[state->prefixes.offset_osz_override].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; } if (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) { instruction->raw.prefixes[state->prefixes.offset_asz_override].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; } if (rex) { instruction->raw.prefixes[instruction->raw.rex.offset].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; ZydisDecodeREX(state->context, instruction, rex); } if ((state->decoder->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) && (state->prefixes.group2 == 0x3E)) { state->prefixes.offset_notrack = state->prefixes.offset_group2; } return ZYAN_STATUS_SUCCESS; } /** * 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)); } } context->reg_info.is_mod_reg = (instruction->raw.modrm.mod == 3) || (info->flags & ZYDIS_INSTR_ENC_FLAG_FORCE_REG_FORM); } if (info->flags & ZYDIS_INSTR_ENC_FLAG_HAS_DISP) { ZYAN_CHECK(ZydisReadDisplacement( state, instruction, info->disp.size[context->easz_index])); } if (info->flags & ZYDIS_INSTR_ENC_FLAG_HAS_IMM0) { if (info->imm[0].is_relative) { instruction->attributes |= ZYDIS_ATTRIB_IS_RELATIVE; } ZYAN_CHECK(ZydisReadImmediate(state, instruction, 0, info->imm[0].size[context->eosz_index], info->imm[0].is_signed, info->imm[0].is_relative)); } if (info->flags & ZYDIS_INSTR_ENC_FLAG_HAS_IMM1) { ZYAN_ASSERT(!(info->flags & ZYDIS_INSTR_ENC_FLAG_HAS_DISP)); ZYAN_CHECK(ZydisReadImmediate(state, instruction, 1, info->imm[1].size[context->eosz_index], info->imm[1].is_signed, info->imm[1].is_relative)); } return ZYAN_STATUS_SUCCESS; } /* ---------------------------------------------------------------------------------------------- */ /** * 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. */ static void ZydisSetEffectiveOperandWidth(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(definition); static const ZyanU8 operand_size_map[8][8] = { // Default for most instructions { 16, // 16 __ W0 32, // 16 66 W0 32, // 32 __ W0 16, // 32 66 W0 32, // 64 __ W0 16, // 64 66 W0 64, // 64 __ W1 64 // 64 66 W1 }, // Operand size is forced to 8-bit (this is done later to preserve the `eosz_index`) { 16, // 16 __ W0 32, // 16 66 W0 32, // 32 __ W0 16, // 32 66 W0 32, // 64 __ W0 16, // 64 66 W0 64, // 64 __ W1 64 // 64 66 W1 }, // Operand size override 0x66 is ignored { 16, // 16 __ W0 16, // 16 66 W0 32, // 32 __ W0 32, // 32 66 W0 32, // 64 __ W0 32, // 64 66 W0 64, // 64 __ W1 64 // 64 66 W1 }, // REX.W promotes to 32-bit instead of 64-bit { 16, // 16 __ W0 32, // 16 66 W0 32, // 32 __ W0 16, // 32 66 W0 32, // 64 __ W0 16, // 64 66 W0 32, // 64 __ W1 32 // 64 66 W1 }, // Operand size defaults to 64-bit in 64-bit mode { 16, // 16 __ W0 32, // 16 66 W0 32, // 32 __ W0 16, // 32 66 W0 64, // 64 __ W0 16, // 64 66 W0 64, // 64 __ W1 64 // 64 66 W1 }, // Operand size is forced to 64-bit in 64-bit mode { 16, // 16 __ W0 32, // 16 66 W0 32, // 32 __ W0 16, // 32 66 W0 64, // 64 __ W0 64, // 64 66 W0 64, // 64 __ W1 64 // 64 66 W1 }, // Operand size is forced to 32-bit, if no REX.W is present. { 32, // 16 __ W0 32, // 16 66 W0 32, // 32 __ W0 32, // 32 66 W0 32, // 64 __ W0 32, // 64 66 W0 64, // 64 __ W1 64 // 64 66 W1 }, // Operand size is forced to 64-bit in 64-bit mode and forced to 32-bit in all other modes. // This is used for e.g. `mov CR, GPR` and `mov GPR, CR`. { 32, // 16 __ W0 32, // 16 66 W0 32, // 32 __ W0 32, // 32 66 W0 64, // 64 __ W0 64, // 64 66 W0 64, // 64 __ W1 64 // 64 66 W1 } }; ZyanU8 index = (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE) ? 1 : 0; if ((instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_32) || (instruction->machine_mode == ZYDIS_MACHINE_MODE_LEGACY_32)) { index += 2; } else if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) { index += 4; index += (context->vector_unified.W & 0x01) << 1; } ZYAN_ASSERT(definition->operand_size_map < ZYAN_ARRAY_LENGTH(operand_size_map)); ZYAN_ASSERT(index < ZYAN_ARRAY_LENGTH(operand_size_map[definition->operand_size_map])); instruction->operand_width = operand_size_map[definition->operand_size_map][index]; context->eosz_index = instruction->operand_width >> 5; // 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. */ static void ZydisSetEffectiveAddressWidth(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); static const 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; } else if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) { index += 4; } ZYAN_ASSERT(definition->address_size_map < ZYAN_ARRAY_LENGTH(address_size_map)); ZYAN_ASSERT(index < ZYAN_ARRAY_LENGTH(address_size_map[definition->address_size_map])); instruction->address_width = address_size_map[definition->address_size_map][index]; context->easz_index = instruction->address_width >> 5; } /* ---------------------------------------------------------------------------------------------- */ static ZyanStatus ZydisNodeHandlerXOP(const ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_LEGACY: *index = 0; break; case ZYDIS_INSTRUCTION_ENCODING_XOP: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_XOP); *index = (instruction->raw.xop.m_mmmm - 0x08) + (instruction->raw.xop.pp * 3) + 1; break; default: ZYAN_UNREACHABLE; } return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerVEX(const ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_LEGACY: *index = 0; break; case ZYDIS_INSTRUCTION_ENCODING_VEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX); *index = instruction->raw.vex.m_mmmm + (instruction->raw.vex.pp << 2) + 1; break; default: ZYAN_UNREACHABLE; } return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerEMVEX(const ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_LEGACY: *index = 0; break; case ZYDIS_INSTRUCTION_ENCODING_EVEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX); *index = instruction->raw.evex.mmm + (instruction->raw.evex.pp << 3) + 1; break; case ZYDIS_INSTRUCTION_ENCODING_MVEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX); *index = instruction->raw.mvex.mmmm + (instruction->raw.mvex.pp << 2) + 33; break; default: ZYAN_UNREACHABLE; } return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerOpcode(ZydisDecoderState* state, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(state); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); // Handle possible encoding-prefix and opcode-map changes switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_LEGACY: ZYAN_CHECK(ZydisInputNext(state, instruction, &instruction->opcode)); switch (instruction->opcode_map) { case ZYDIS_OPCODE_MAP_DEFAULT: switch (instruction->opcode) { case 0x0F: instruction->opcode_map = ZYDIS_OPCODE_MAP_0F; break; case 0xC4: case 0xC5: case 0x62: { ZyanU8 next_input; ZYAN_CHECK(ZydisInputPeek(state, instruction, &next_input)); if (((next_input & 0xF0) >= 0xC0) || (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)) { if (instruction->attributes & ZYDIS_ATTRIB_HAS_REX) { return ZYDIS_STATUS_ILLEGAL_REX; } if (state->prefixes.has_lock) { return ZYDIS_STATUS_ILLEGAL_LOCK; } if (state->prefixes.mandatory_candidate) { return ZYDIS_STATUS_ILLEGAL_LEGACY_PFX; } ZyanU8 prefix_bytes[4] = { 0, 0, 0, 0 }; prefix_bytes[0] = instruction->opcode; switch (instruction->opcode) { case 0xC4: instruction->raw.vex.offset = instruction->length - 1; // Read additional 3-byte VEX-prefix data ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX)); ZYAN_CHECK(ZydisInputNextBytes(state, instruction, &prefix_bytes[1], 2)); break; case 0xC5: instruction->raw.vex.offset = instruction->length - 1; // Read additional 2-byte VEX-prefix data ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX)); ZYAN_CHECK(ZydisInputNext(state, instruction, &prefix_bytes[1])); break; case 0x62: #if !defined(ZYDIS_DISABLE_AVX512) || !defined(ZYDIS_DISABLE_KNC) // Read additional EVEX/MVEX-prefix data ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX)); ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX)); ZYAN_CHECK(ZydisInputNextBytes(state, instruction, &prefix_bytes[1], 3)); break; #else return ZYDIS_STATUS_DECODING_ERROR; #endif default: ZYAN_UNREACHABLE; } switch (instruction->opcode) { case 0xC4: case 0xC5: // Decode VEX-prefix instruction->encoding = ZYDIS_INSTRUCTION_ENCODING_VEX; ZYAN_CHECK(ZydisDecodeVEX(state->context, instruction, prefix_bytes)); instruction->opcode_map = ZYDIS_OPCODE_MAP_DEFAULT + instruction->raw.vex.m_mmmm; break; case 0x62: #if defined(ZYDIS_DISABLE_AVX512) && defined(ZYDIS_DISABLE_KNC) return ZYDIS_STATUS_DECODING_ERROR; #else switch ((prefix_bytes[2] >> 2) & 0x01) { case 0: #ifndef ZYDIS_DISABLE_KNC instruction->raw.mvex.offset = instruction->length - 4; // `KNC` instructions are only valid in 64-bit mode. // This condition catches the `MVEX` encoded ones to save a bunch of // `mode` filters in the data-tables. // `KNC` instructions with `VEX` encoding still require a `mode` filter. if (state->decoder->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) { return ZYDIS_STATUS_DECODING_ERROR; } // Decode MVEX-prefix instruction->encoding = ZYDIS_INSTRUCTION_ENCODING_MVEX; ZYAN_CHECK(ZydisDecodeMVEX(state->context, instruction, prefix_bytes)); instruction->opcode_map = ZYDIS_OPCODE_MAP_DEFAULT + instruction->raw.mvex.mmmm; break; #else return ZYDIS_STATUS_DECODING_ERROR; #endif case 1: #ifndef ZYDIS_DISABLE_AVX512 instruction->raw.evex.offset = instruction->length - 4; // Decode EVEX-prefix instruction->encoding = ZYDIS_INSTRUCTION_ENCODING_EVEX; ZYAN_CHECK(ZydisDecodeEVEX(state->context, instruction, prefix_bytes)); instruction->opcode_map = ZYDIS_OPCODE_MAP_DEFAULT + instruction->raw.evex.mmm; break; #else return ZYDIS_STATUS_DECODING_ERROR; #endif default: ZYAN_UNREACHABLE; } break; #endif default: ZYAN_UNREACHABLE; } } break; } case 0x8F: { ZyanU8 next_input; ZYAN_CHECK(ZydisInputPeek(state, instruction, &next_input)); if ((next_input & 0x1F) >= 8) { if (instruction->attributes & ZYDIS_ATTRIB_HAS_REX) { return ZYDIS_STATUS_ILLEGAL_REX; } if (state->prefixes.has_lock) { return ZYDIS_STATUS_ILLEGAL_LOCK; } if (state->prefixes.mandatory_candidate) { return ZYDIS_STATUS_ILLEGAL_LEGACY_PFX; } instruction->raw.xop.offset = instruction->length - 1; ZyanU8 prefixBytes[3] = { 0x8F, 0x00, 0x00 }; // Read additional xop-prefix data ZYAN_CHECK(ZydisInputNextBytes(state, instruction, &prefixBytes[1], 2)); // Decode xop-prefix instruction->encoding = ZYDIS_INSTRUCTION_ENCODING_XOP; ZYAN_CHECK(ZydisDecodeXOP(state->context, instruction, prefixBytes)); instruction->opcode_map = ZYDIS_OPCODE_MAP_XOP8 + instruction->raw.xop.m_mmmm - 0x08; } break; } default: break; } break; case ZYDIS_OPCODE_MAP_0F: switch (instruction->opcode) { case 0x0F: if (state->prefixes.has_lock) { return ZYDIS_STATUS_ILLEGAL_LOCK; } instruction->encoding = ZYDIS_INSTRUCTION_ENCODING_3DNOW; instruction->opcode_map = ZYDIS_OPCODE_MAP_0F0F; break; case 0x38: instruction->opcode_map = ZYDIS_OPCODE_MAP_0F38; break; case 0x3A: instruction->opcode_map = ZYDIS_OPCODE_MAP_0F3A; break; default: break; } break; case ZYDIS_OPCODE_MAP_0F38: case ZYDIS_OPCODE_MAP_0F3A: case ZYDIS_OPCODE_MAP_XOP8: case ZYDIS_OPCODE_MAP_XOP9: case ZYDIS_OPCODE_MAP_XOPA: // Nothing to do here break; default: ZYAN_UNREACHABLE; } break; case ZYDIS_INSTRUCTION_ENCODING_3DNOW: // All 3DNOW (0x0F 0x0F) instructions are using the same operand encoding. We just // decode a random (pi2fw) instruction and extract the actual opcode later. *index = 0x0C; return ZYAN_STATUS_SUCCESS; default: ZYAN_CHECK(ZydisInputNext(state, instruction, &instruction->opcode)); break; } *index = instruction->opcode; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerMode(const ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); switch (instruction->machine_mode) { case ZYDIS_MACHINE_MODE_LONG_COMPAT_16: case ZYDIS_MACHINE_MODE_LEGACY_16: case ZYDIS_MACHINE_MODE_REAL_16: *index = 0; break; case ZYDIS_MACHINE_MODE_LONG_COMPAT_32: case ZYDIS_MACHINE_MODE_LEGACY_32: *index = 1; break; case ZYDIS_MACHINE_MODE_LONG_64: *index = 2; break; default: ZYAN_UNREACHABLE; } return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerModeCompact(const ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); *index = (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) ? 0 : 1; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerModrmMod(ZydisDecoderState* state, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(state); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); 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); } *index = instruction->raw.modrm.mod; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerModrmModCompact(ZydisDecoderState* state, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_CHECK(ZydisNodeHandlerModrmMod(state, instruction, index)); *index = (*index == 0x3) ? 0 : 1; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerModrmReg(ZydisDecoderState* state, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(state); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); 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); } *index = instruction->raw.modrm.reg; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerModrmRm(ZydisDecoderState* state, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(state); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); 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); } *index = instruction->raw.modrm.rm; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerMandatoryPrefix(const ZydisDecoderState* state, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(state); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); switch (state->prefixes.mandatory_candidate) { case 0x66: instruction->raw.prefixes[state->prefixes.offset_mandatory].type = ZYDIS_PREFIX_TYPE_MANDATORY; instruction->attributes &= ~ZYDIS_ATTRIB_HAS_OPERANDSIZE; *index = 2; break; case 0xF3: instruction->raw.prefixes[state->prefixes.offset_mandatory].type = ZYDIS_PREFIX_TYPE_MANDATORY; *index = 3; break; case 0xF2: instruction->raw.prefixes[state->prefixes.offset_mandatory].type = ZYDIS_PREFIX_TYPE_MANDATORY; *index = 4; break; default: *index = 1; break; } // TODO: Consume prefix and make sure it's available again, if we need to fallback return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerOperandSize(const ZydisDecoderState* state, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(state); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); if ((instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) && (state->context->vector_unified.W)) { *index = 2; } else { if (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE) { instruction->raw.prefixes[state->prefixes.offset_osz_override].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; } switch (instruction->machine_mode) { case ZYDIS_MACHINE_MODE_LONG_COMPAT_16: case ZYDIS_MACHINE_MODE_LEGACY_16: case ZYDIS_MACHINE_MODE_REAL_16: *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE) ? 1 : 0; break; case ZYDIS_MACHINE_MODE_LONG_COMPAT_32: case ZYDIS_MACHINE_MODE_LEGACY_32: case ZYDIS_MACHINE_MODE_LONG_64: *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE) ? 0 : 1; break; default: ZYAN_UNREACHABLE; } } return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerAddressSize(ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); /*if (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) { instruction->raw.prefixes[context->prefixes.offset_asz_override].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; }*/ switch (instruction->machine_mode) { case ZYDIS_MACHINE_MODE_LONG_COMPAT_16: case ZYDIS_MACHINE_MODE_LEGACY_16: case ZYDIS_MACHINE_MODE_REAL_16: *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) ? 1 : 0; break; case ZYDIS_MACHINE_MODE_LONG_COMPAT_32: case ZYDIS_MACHINE_MODE_LEGACY_32: *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) ? 0 : 1; break; case ZYDIS_MACHINE_MODE_LONG_64: *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) ? 1 : 2; break; default: ZYAN_UNREACHABLE; } return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerVectorLength(const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_XOP: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_XOP); break; case ZYDIS_INSTRUCTION_ENCODING_VEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX); break; case ZYDIS_INSTRUCTION_ENCODING_EVEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX); break; case ZYDIS_INSTRUCTION_ENCODING_MVEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX); break; default: ZYAN_UNREACHABLE; } *index = context->vector_unified.LL; if (*index == 3) { return ZYDIS_STATUS_DECODING_ERROR; } return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerRexW(const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_LEGACY: // nothing to do here break; case ZYDIS_INSTRUCTION_ENCODING_XOP: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_XOP); break; case ZYDIS_INSTRUCTION_ENCODING_VEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX); break; case ZYDIS_INSTRUCTION_ENCODING_EVEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX); break; case ZYDIS_INSTRUCTION_ENCODING_MVEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX); break; default: ZYAN_UNREACHABLE; } *index = context->vector_unified.W; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerRexB(const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_LEGACY: // nothing to do here break; case ZYDIS_INSTRUCTION_ENCODING_XOP: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_XOP); break; case ZYDIS_INSTRUCTION_ENCODING_VEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX); break; case ZYDIS_INSTRUCTION_ENCODING_EVEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX); break; case ZYDIS_INSTRUCTION_ENCODING_MVEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX); break; default: ZYAN_UNREACHABLE; } *index = context->vector_unified.B; return ZYAN_STATUS_SUCCESS; } #ifndef ZYDIS_DISABLE_AVX512 static ZyanStatus ZydisNodeHandlerEvexB(const ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); ZYAN_ASSERT(instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX); ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX); *index = instruction->raw.evex.b; return ZYAN_STATUS_SUCCESS; } #endif #ifndef ZYDIS_DISABLE_KNC static ZyanStatus ZydisNodeHandlerMvexE(const ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); ZYAN_ASSERT(instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX); ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX); *index = instruction->raw.mvex.E; return ZYAN_STATUS_SUCCESS; } #endif /* ---------------------------------------------------------------------------------------------- */ /** * 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); const ZyanBool is_64_bit = (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64); const ZyanBool is_reg = context->reg_info.is_mod_reg; const ZyanBool has_sib = !is_reg && (instruction->raw.modrm.rm == 4); const ZyanBool has_vsib = has_sib && (def_rm == ZYDIS_MEMOP_TYPE_VSIB); ZyanU8 id_reg = instruction->raw.modrm.reg; ZyanU8 id_rm = instruction->raw.modrm.rm; ZyanU8 id_ndsndd = is_64_bit ? context->vector_unified.vvvv : context->vector_unified.vvvv & 0x07; ZyanU8 id_base = has_sib ? instruction->raw.sib.base : instruction->raw.modrm.rm; ZyanU8 id_index = instruction->raw.sib.index; if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) { const ZyanBool is_emvex = (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) || (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX); // 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; id_reg |= (context->vector_unified.R2 << 4) | (context->vector_unified.R << 3); id_rm |= (evex_x << 4) | (context->vector_unified.B << 3); id_ndsndd |= (context->vector_unified.V2 << 4) ; id_base |= (context->vector_unified.B << 3); id_index |= (vsib_v2 << 4) | (context->vector_unified.X << 3); // The masking emulates the actual CPU behavior and does not verify if the resulting ids // are actually valid for the given register kind. static const ZyanU8 mask_reg[ZYDIS_REGKIND_MAX_VALUE + 1] = { /* INVALID */ 0, /* GPR */ (1 << 5) - 1, /* X87 */ (1 << 3) - 1, // ignore `.R`, ignore `.R'` /* MMX */ (1 << 3) - 1, // ignore `.R`, ignore `.R'` /* VR */ (1 << 5) - 1, /* TMM */ (1 << 5) - 1, /* SEGMENT */ (1 << 3) - 1, // ignore `.R`, ignore `.R'` /* TEST */ (1 << 3) - 1, // ignore `.R`, ignore `.R'` /* CONTROL */ (1 << 4) - 1, // ignore `.R'` /* DEBUG */ (1 << 4) - 1, // ignore `.R'` /* MASK */ (1 << 5) - 1, /* BOUND */ (1 << 4) - 1 // ignore `.R'` }; id_reg &= mask_reg[def_reg]; static const ZyanU8 mask_rm[ZYDIS_REGKIND_MAX_VALUE + 1] = { /* INVALID */ 0, /* GPR */ (1 << 4) - 1, // ignore `.X` /* X87 */ (1 << 3) - 1, // ignore `.B`, ignore `.X` /* MMX */ (1 << 3) - 1, // ignore `.B`, ignore `.X` /* VR */ (1 << 5) - 1, /* TMM */ (1 << 4) - 1, // ignore `.X` /* SEGMENT */ (1 << 3) - 1, // ignore `.B`, ignore `.X` /* TEST */ (1 << 3) - 1, // ignore `.B`, ignore `.X` /* CONTROL */ (1 << 4) - 1, // ignore `.X` /* DEBUG */ (1 << 4) - 1, // ignore `.X` /* MASK */ (1 << 3) - 1, // ignore `.B`, ignore `.X` /* BOUND */ (1 << 4) - 1 // ignore `.X` }; id_rm &= (is_reg ? mask_rm[def_rm] : 0xFF); // 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; } static const 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 } }; if ((id_reg >= available_regs[is_64_bit][def_reg]) || (id_ndsndd >= available_regs[is_64_bit][def_ndsndd]) || (is_reg && (id_rm >= available_regs[is_64_bit][def_rm]))) { return ZYDIS_STATUS_BAD_REGISTER; } 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 static const 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); ZyanU8 def_reg = definition->op_reg; ZyanU8 def_rm = definition->op_rm; ZyanU8 def_ndsndd = ZYDIS_REGKIND_INVALID; ZyanBool is_gather = ZYAN_FALSE; ZyanBool no_source_dest_match = ZYAN_FALSE; ZyanBool no_source_source_match = ZYAN_FALSE; #if !defined(ZYDIS_DISABLE_AVX512) || !defined(ZYDIS_DISABLE_KNC) ZydisMaskPolicy mask_policy = ZYDIS_MASK_POLICY_INVALID; #endif switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_LEGACY: { const ZydisInstructionDefinitionLEGACY* def = (const ZydisInstructionDefinitionLEGACY*)definition; if (def->requires_protected_mode && (instruction->machine_mode == ZYDIS_MACHINE_MODE_REAL_16)) { return ZYDIS_STATUS_DECODING_ERROR; } if (def->no_compat_mode && ((instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_16) || (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_32))) { return ZYDIS_STATUS_DECODING_ERROR; } if (state->prefixes.has_lock && !def->accepts_LOCK) { return ZYDIS_STATUS_ILLEGAL_LOCK; } break; } case ZYDIS_INSTRUCTION_ENCODING_3DNOW: { break; } case ZYDIS_INSTRUCTION_ENCODING_XOP: { const ZydisInstructionDefinitionXOP* def = (const ZydisInstructionDefinitionXOP*)definition; def_ndsndd = def->op_ndsndd; break; } case ZYDIS_INSTRUCTION_ENCODING_VEX: { const ZydisInstructionDefinitionVEX* def = (const ZydisInstructionDefinitionVEX*)definition; def_ndsndd = def->op_ndsndd; is_gather = def->is_gather; no_source_source_match = def->no_source_source_match; break; } case ZYDIS_INSTRUCTION_ENCODING_EVEX: { #ifndef ZYDIS_DISABLE_AVX512 const ZydisInstructionDefinitionEVEX* def = (const ZydisInstructionDefinitionEVEX*)definition; def_ndsndd = def->op_ndsndd; is_gather = def->is_gather; no_source_dest_match = def->no_source_dest_match; mask_policy = def->mask_policy; // Check for invalid zero-mask if ((instruction->raw.evex.z) && (!def->accepts_zero_mask)) { return ZYDIS_STATUS_INVALID_MASK; // TODO: Dedicated status code } #else ZYAN_UNREACHABLE; #endif break; } case ZYDIS_INSTRUCTION_ENCODING_MVEX: { #ifndef ZYDIS_DISABLE_KNC const ZydisInstructionDefinitionMVEX* def = (const ZydisInstructionDefinitionMVEX*)definition; def_ndsndd = def->op_ndsndd; is_gather = def->is_gather; mask_policy = def->mask_policy; // Check for invalid MVEX.SSS values static const ZyanU8 lookup[26][8] = { // ZYDIS_MVEX_FUNC_IGNORED { 1, 1, 1, 1, 1, 1, 1, 1 }, // ZYDIS_MVEX_FUNC_INVALID { 1, 0, 0, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_RC { 1, 1, 1, 1, 1, 1, 1, 1 }, // ZYDIS_MVEX_FUNC_SAE { 1, 1, 1, 1, 1, 1, 1, 1 }, // ZYDIS_MVEX_FUNC_F_32 { 1, 0, 0, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_I_32 { 1, 0, 0, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_F_64 { 1, 0, 0, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_I_64 { 1, 0, 0, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_SWIZZLE_32 { 1, 1, 1, 1, 1, 1, 1, 1 }, // ZYDIS_MVEX_FUNC_SWIZZLE_64 { 1, 1, 1, 1, 1, 1, 1, 1 }, // ZYDIS_MVEX_FUNC_SF_32 { 1, 1, 1, 1, 1, 0, 1, 1 }, // ZYDIS_MVEX_FUNC_SF_32_BCST { 1, 1, 1, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16 { 1, 0, 1, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_SF_64 { 1, 1, 1, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_SI_32 { 1, 1, 1, 0, 1, 1, 1, 1 }, // ZYDIS_MVEX_FUNC_SI_32_BCST { 1, 1, 1, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16 { 1, 0, 1, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_SI_64 { 1, 1, 1, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_UF_32 { 1, 0, 0, 1, 1, 1, 1, 1 }, // ZYDIS_MVEX_FUNC_UF_64 { 1, 0, 0, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_UI_32 { 1, 0, 0, 0, 1, 1, 1, 1 }, // ZYDIS_MVEX_FUNC_UI_64 { 1, 0, 0, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_DF_32 { 1, 0, 0, 1, 1, 1, 1, 1 }, // ZYDIS_MVEX_FUNC_DF_64 { 1, 0, 0, 0, 0, 0, 0, 0 }, // ZYDIS_MVEX_FUNC_DI_32 { 1, 0, 0, 0, 1, 1, 1, 1 }, // ZYDIS_MVEX_FUNC_DI_64 { 1, 0, 0, 0, 0, 0, 0, 0 } }; ZYAN_ASSERT(def->functionality < ZYAN_ARRAY_LENGTH(lookup)); ZYAN_ASSERT(instruction->raw.mvex.SSS < 8); if (!lookup[def->functionality][instruction->raw.mvex.SSS]) { return ZYDIS_STATUS_DECODING_ERROR; } #else ZYAN_UNREACHABLE; #endif break; } default: ZYAN_UNREACHABLE; } ZydisDecoderContext* context = state->context; const ZyanBool is_reg = context->reg_info.is_mod_reg; ZyanU8 no_rip_rel = ZYAN_FALSE; ZyanU8 is_sr_dest_reg = ZYAN_FALSE; ZyanU8 is_sr_dest_rm = ZYAN_FALSE; if (def_reg) { is_sr_dest_reg = ZYDIS_OPDEF_GET_REG_HIGH_BIT(def_reg); def_reg = ZYDIS_OPDEF_GET_REG(def_reg); } if (def_rm) { if (is_reg) { is_sr_dest_rm = ZYDIS_OPDEF_GET_REG_HIGH_BIT(def_rm); def_rm = ZYDIS_OPDEF_GET_REG(def_rm); } else { no_rip_rel = ZYDIS_OPDEF_GET_MEM_HIGH_BIT(def_rm); def_rm = ZYDIS_OPDEF_GET_MEM(def_rm); } } // Check RIP-relative memory addressing if (no_rip_rel) { const ZyanBool is_rip_rel = (state->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) && (instruction->raw.modrm.mod == 0) && (instruction->raw.modrm.rm == 5); if (is_rip_rel) { return ZYDIS_STATUS_BAD_REGISTER; } } // Populate- and validate register constraints ZYAN_CHECK(ZydisPopulateRegisterIds(context, instruction, def_reg, def_rm, def_ndsndd)); // `ZYDIS_REGISTER_CS` is not allowed as `MOV` target if (is_sr_dest_reg && (context->reg_info.id_reg == 1)) { return ZYDIS_STATUS_BAD_REGISTER; } if (is_sr_dest_rm && (context->reg_info.id_rm == 1)) { return ZYDIS_STATUS_BAD_REGISTER; } // Check gather registers if (is_gather) { // ZYAN_ASSERT(has_VSIB); ZYAN_ASSERT(instruction->raw.modrm.mod != 3); ZYAN_ASSERT(instruction->raw.modrm.rm == 4); const ZyanU8 index = context->reg_info.id_index; ZyanU8 dest = context->reg_info.id_reg; ZyanU8 mask = 0xF0; if (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_VEX) { ZYAN_ASSERT((def_reg == ZYDIS_REGKIND_VR) && (def_rm == ZYDIS_MEMOP_TYPE_VSIB) && (def_ndsndd == ZYDIS_REGKIND_VR)); mask = context->reg_info.id_ndsndd; } if ((instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) || (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX)) { ZYAN_ASSERT(((def_reg == ZYDIS_REGKIND_INVALID) || (def_reg == ZYDIS_REGKIND_VR)) && (def_rm == ZYDIS_MEMOP_TYPE_VSIB) && (def_ndsndd == ZYDIS_REGKIND_INVALID)); // 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)); const ZyanU8 dest = context->reg_info.id_reg; const ZyanU8 source1 = context->reg_info.id_ndsndd; const ZyanU8 source2 = context->reg_info.id_rm; if ((dest == source1) || (is_reg && (dest == source2))) { return ZYDIS_STATUS_BAD_REGISTER; } } // 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); const ZyanU8 dest = context->reg_info.id_reg; const ZyanU8 source1 = context->reg_info.id_ndsndd; const ZyanU8 source2 = context->reg_info.id_rm; if ((dest == source1) || (dest == source2) || (source1 == source2)) { return ZYDIS_STATUS_BAD_REGISTER; } } #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 return ZYAN_STATUS_SUCCESS; } /* ---------------------------------------------------------------------------------------------- */ /** * 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[ZYDIS_DECODER_MODE_AMD_BRANCHES] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_KNC: index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_KNC] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_MPX: index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_MPX] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_CET: index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_CET] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_LZCNT: index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_LZCNT] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_TZCNT: index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_TZCNT] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_WBNOINVD: index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_WBNOINVD] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_CLDEMOTE: index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_CLDEMOTE] ? 1 : 0; 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); const ZydisInstructionEncodingInfo* info; ZydisGetInstructionEncodingInfo(node, &info); ZYAN_CHECK(ZydisDecodeOptionalInstructionParts(state, instruction, info)); ZYAN_CHECK(ZydisCheckErrorConditions(state, instruction, definition)); if (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_3DNOW) { // Get actual 3DNOW opcode and definition ZYAN_CHECK(ZydisInputNext(state, instruction, &instruction->opcode)); node = ZydisDecoderTreeGetRootNode(); node = ZydisDecoderTreeGetChildNode(node, 0x0F); node = ZydisDecoderTreeGetChildNode(node, 0x0F); node = ZydisDecoderTreeGetChildNode(node, instruction->opcode); if (node->type == ZYDIS_NODETYPE_INVALID) { return ZYDIS_STATUS_DECODING_ERROR; } ZYAN_ASSERT(node->type == ZYDIS_NODETYPE_FILTER_MODRM_MOD_COMPACT); node = ZydisDecoderTreeGetChildNode( node, (instruction->raw.modrm.mod == 0x3) ? 0 : 1); ZYAN_ASSERT(node->type & ZYDIS_NODETYPE_DEFINITION_MASK); ZydisGetInstructionDefinition(instruction->encoding, node->value, &definition); } instruction->mnemonic = definition->mnemonic; #ifndef ZYDIS_MINIMAL_MODE instruction->operand_count = definition->operand_count; instruction->operand_count_visible = definition->operand_count_visible; state->context->definition = definition; instruction->meta.category = definition->category; instruction->meta.isa_set = definition->isa_set; instruction->meta.isa_ext = definition->isa_ext; instruction->meta.branch_type = definition->branch_type; ZYAN_ASSERT((instruction->meta.branch_type == ZYDIS_BRANCH_TYPE_NONE) || ((instruction->meta.category == ZYDIS_CATEGORY_CALL) || (instruction->meta.category == ZYDIS_CATEGORY_COND_BR) || (instruction->meta.category == ZYDIS_CATEGORY_UNCOND_BR) || (instruction->meta.category == ZYDIS_CATEGORY_RET))); instruction->meta.exception_class = definition->exception_class; if (!state->decoder->decoder_mode[ZYDIS_DECODER_MODE_MINIMAL]) { ZydisSetAttributes(state, instruction, definition); switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_XOP: case ZYDIS_INSTRUCTION_ENCODING_VEX: case ZYDIS_INSTRUCTION_ENCODING_EVEX: case ZYDIS_INSTRUCTION_ENCODING_MVEX: ZydisSetAVXInformation(state->context, instruction, definition); break; default: break; } const ZydisDefinitionAccessedFlags* flags; if (ZydisGetAccessedFlags(definition, &flags)) { instruction->attributes |= ZYDIS_ATTRIB_CPUFLAG_ACCESS; } instruction->cpu_flags = &flags->cpu_flags; instruction->fpu_flags = &flags->fpu_flags; } #endif return ZYAN_STATUS_SUCCESS; } ZYAN_UNREACHABLE; } ZYAN_CHECK(status); node = ZydisDecoderTreeGetChildNode(node, index); } while ((node_type != ZYDIS_NODETYPE_INVALID) && !(node_type & ZYDIS_NODETYPE_DEFINITION_MASK)); return ZYAN_STATUS_SUCCESS; } /* ---------------------------------------------------------------------------------------------- */ /* ============================================================================================== */ /* Exported functions */ /* ============================================================================================== */ ZyanStatus ZydisDecoderInit(ZydisDecoder* decoder, ZydisMachineMode machine_mode, ZydisStackWidth stack_width) { static const ZyanBool decoder_modes[ZYDIS_DECODER_MODE_MAX_VALUE + 1] = { #ifdef ZYDIS_MINIMAL_MODE ZYAN_TRUE , // ZYDIS_DECODER_MODE_MINIMAL #else ZYAN_FALSE, // ZYDIS_DECODER_MODE_MINIMAL #endif ZYAN_FALSE, // ZYDIS_DECODER_MODE_AMD_BRANCHES ZYAN_FALSE, // ZYDIS_DECODER_MODE_KNC ZYAN_TRUE , // ZYDIS_DECODER_MODE_MPX ZYAN_TRUE , // ZYDIS_DECODER_MODE_CET ZYAN_TRUE , // ZYDIS_DECODER_MODE_LZCNT ZYAN_TRUE , // ZYDIS_DECODER_MODE_TZCNT ZYAN_FALSE, // ZYDIS_DECODER_MODE_WBNOINVD ZYAN_TRUE // ZYDIS_DECODER_MODE_CLDEMOTE }; if (!decoder) { return ZYAN_STATUS_INVALID_ARGUMENT; } switch (machine_mode) { case ZYDIS_MACHINE_MODE_LONG_64: if (stack_width != ZYDIS_STACK_WIDTH_64) { return ZYAN_STATUS_INVALID_ARGUMENT; } break; case ZYDIS_MACHINE_MODE_LONG_COMPAT_32: case ZYDIS_MACHINE_MODE_LONG_COMPAT_16: case ZYDIS_MACHINE_MODE_LEGACY_32: case ZYDIS_MACHINE_MODE_LEGACY_16: case ZYDIS_MACHINE_MODE_REAL_16: if ((stack_width != ZYDIS_STACK_WIDTH_16) && (stack_width != ZYDIS_STACK_WIDTH_32)) { return ZYAN_STATUS_INVALID_ARGUMENT; } break; default: return ZYAN_STATUS_INVALID_ARGUMENT; } decoder->machine_mode = machine_mode; decoder->stack_width = stack_width; ZYAN_MEMCPY(&decoder->decoder_mode, &decoder_modes, sizeof(decoder_modes)); return ZYAN_STATUS_SUCCESS; } ZyanStatus ZydisDecoderEnableMode(ZydisDecoder* decoder, ZydisDecoderMode mode, ZyanBool enabled) { if (!decoder || ((ZyanUSize)mode > ZYDIS_DECODER_MODE_MAX_VALUE)) { return ZYAN_STATUS_INVALID_ARGUMENT; } #ifdef ZYDIS_MINIMAL_MODE if ((mode == ZYDIS_DECODER_MODE_MINIMAL) && !enabled) { return ZYAN_STATUS_INVALID_OPERATION; } #endif decoder->decoder_mode[mode] = enabled; return ZYAN_STATUS_SUCCESS; } ZyanStatus ZydisDecoderDecodeFull(const ZydisDecoder* decoder, const void* buffer, ZyanUSize length, ZydisDecodedInstruction* instruction, ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]) { if (!decoder || !instruction || !buffer || !operands) { return ZYAN_STATUS_INVALID_ARGUMENT; } if (!length) { return ZYDIS_STATUS_NO_MORE_DATA; } if (decoder->decoder_mode[ZYDIS_DECODER_MODE_MINIMAL]) { return ZYAN_STATUS_MISSING_DEPENDENCY; // TODO: Introduce better status code } ZydisDecoderContext context; ZYAN_CHECK(ZydisDecoderDecodeInstruction(decoder, &context, buffer, length, instruction)); ZYAN_CHECK(ZydisDecoderDecodeOperands(decoder, &context, instruction, operands, instruction->operand_count)); ZYAN_MEMSET(&operands[instruction->operand_count], 0, (ZYDIS_MAX_OPERAND_COUNT - instruction->operand_count) * sizeof(operands[0])); return ZYAN_STATUS_SUCCESS; } ZyanStatus ZydisDecoderDecodeInstruction(const ZydisDecoder* decoder, ZydisDecoderContext* context, const void* buffer, ZyanUSize length, ZydisDecodedInstruction* instruction) { if (!decoder || !instruction || !buffer) { return ZYAN_STATUS_INVALID_ARGUMENT; } if (!length) { return ZYDIS_STATUS_NO_MORE_DATA; } ZydisDecoderState state; ZYAN_MEMSET(&state, 0, sizeof(state)); state.decoder = decoder; state.buffer = (const ZyanU8*)buffer; state.buffer_len = length; state.prefixes.offset_notrack = -1; 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; ZYAN_MEMSET(instruction, 0, sizeof(*instruction)); instruction->machine_mode = decoder->machine_mode; instruction->stack_width = 16 << decoder->stack_width; ZYAN_CHECK(ZydisCollectOptionalPrefixes(&state, instruction)); ZYAN_CHECK(ZydisDecodeInstruction(&state, instruction)); instruction->raw.encoding2 = instruction->encoding; return ZYAN_STATUS_SUCCESS; } ZyanStatus ZydisDecoderDecodeOperands(const ZydisDecoder* decoder, const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction, ZydisDecodedOperand* operands, ZyanU8 operand_count) { #ifdef ZYDIS_MINIMAL_MODE ZYAN_UNUSED(decoder); ZYAN_UNUSED(context); ZYAN_UNUSED(instruction); ZYAN_UNUSED(operands); ZYAN_UNUSED(operand_count); return ZYAN_STATUS_MISSING_DEPENDENCY; // TODO: Introduce better status code #else if (!decoder || !context || !context->definition || !instruction || (operand_count && !operands) || (operand_count > ZYDIS_MAX_OPERAND_COUNT)) { return ZYAN_STATUS_INVALID_ARGUMENT; } if (decoder->decoder_mode[ZYDIS_DECODER_MODE_MINIMAL]) { return ZYAN_STATUS_MISSING_DEPENDENCY; // TODO: Introduce better status code } operand_count = ZYAN_MIN(operand_count, instruction->operand_count); if (!operand_count) { return ZYAN_STATUS_SUCCESS; } return ZydisDecodeOperands(decoder, context, instruction, operands, operand_count); #endif } /* ============================================================================================== */