/*************************************************************************************************** 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. ***************************************************************************************************/ #include #include #include #include #include /* ============================================================================================== */ /* Internal enums and types */ /* ============================================================================================== */ /* ---------------------------------------------------------------------------------------------- */ /* Decoder context */ /* ---------------------------------------------------------------------------------------------- */ /** * Defines the `ZydisDecoderContext` struct. */ typedef struct ZydisDecoderContext_ { /** * A pointer to the `ZydisDecoder` instance. */ const ZydisDecoder* decoder; /** * The input buffer. */ const ZyanU8* buffer; /** * The input buffer length. */ ZyanUSize buffer_len; 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 3 (`2E`,`36`, `3E`, `26`, `64` or `65`). */ ZyanU8 group2; /** * Signals, if the instruction has an operand-size override prefix (`66`). * * This is the only prefix in group 3. */ // ZyanBool has_osz_override; /** * Signals, if the instruction has an address-size override prefix (`67`). * * This is the only prefix in group 4. */ // ZyanBool has_asz_override; /** * 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; } prefixes; /** * Contains the effective operand-size index. * * 0 = 16 bit, 1 = 32 bit, 2 = 64 bit */ ZyanU8 eosz_index; /** * Contains the effective address-size index. * * 0 = 16 bit, 1 = 32 bit, 2 = 64 bit */ ZyanU8 easz_index; /** * Contains some cached REX/XOP/VEX/EVEX/MVEX values to provide uniform access. */ struct { ZyanU8 W; ZyanU8 R; ZyanU8 X; ZyanU8 B; ZyanU8 L; ZyanU8 LL; ZyanU8 R2; ZyanU8 V2; ZyanU8 v_vvvv; ZyanU8 mask; } cache; #ifndef ZYDIS_DISABLE_AVX512 /** * Internal EVEX-specific information. */ struct { /** * The EVEX tuple-type. */ ZydisEVEXTupleType tuple_type; /** * The EVEX element-size. */ ZyanU8 element_size; } evex; #endif #ifndef ZYDIS_DISABLE_KNC /** * Internal MVEX-specific information. */ struct { /** * The MVEX functionality. */ ZydisMVEXFunctionality functionality; } mvex; #endif #if !defined(ZYDIS_DISABLE_AVX512) || !defined(ZYDIS_DISABLE_KNC) /** * The scale factor for EVEX/MVEX compressed 8-bit displacement values. */ ZyanU8 cd8_scale; #endif } ZydisDecoderContext; /* ---------------------------------------------------------------------------------------------- */ /* Register encoding */ /* ---------------------------------------------------------------------------------------------- */ /** * Defines the `ZydisRegisterEncoding` enum. */ typedef enum ZydisRegisterEncoding_ { ZYDIS_REG_ENCODING_INVALID, /** * The register-id is encoded as part of the opcode. * * Possible extension by `REX/XOP/VEX/EVEX/MVEX.B`. */ ZYDIS_REG_ENCODING_OPCODE, /** * The register-id is encoded in `modrm.reg`. * * Possible extension by `EVEX/MVEX.R'` (vector only) and `REX/XOP/VEX/EVEX/MVEX.R`. */ ZYDIS_REG_ENCODING_REG, /** * The register-id is encoded in `XOP/VEX/EVEX/MVEX.vvvv`. * * Possible extension by `EVEX/MVEX.v'` (vector only). */ ZYDIS_REG_ENCODING_NDSNDD, /** * The register-id is encoded in `modrm.rm`. * * Possible extension by `EVEX/MVEX.X` (vector only) and `REX/XOP/VEX/EVEX/MVEX.B`. */ ZYDIS_REG_ENCODING_RM, /** * The register-id is encoded in `modrm.rm` or `sib.base` (if SIB is present). * * Possible extension by `REX/XOP/VEX/EVEX/MVEX.B`. */ ZYDIS_REG_ENCODING_BASE, /** * The register-id is encoded in `sib.index`. * * Possible extension by `REX/XOP/VEX/EVEX/MVEX.X`. */ ZYDIS_REG_ENCODING_INDEX, /** * The register-id is encoded in `sib.index`. * * Possible extension by `EVEX/MVEX.V'` (vector only) and `REX/XOP/VEX/EVEX/MVEX.X`. */ 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 context A pointer to the `ZydisDecoderContext` instance. * @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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU8* value) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(value); if (instruction->length >= ZYDIS_MAX_INSTRUCTION_LENGTH) { return ZYDIS_STATUS_INSTRUCTION_TOO_LONG; } if (context->buffer_len > 0) { *value = context->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 context A pointer to the `ZydisDecoderContext` 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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(instruction->length < ZYDIS_MAX_INSTRUCTION_LENGTH); ++instruction->length; ++context->buffer; --context->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 context A pointer to the `ZydisDecoderContext` instance. * @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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU8* value) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(value); if (instruction->length >= ZYDIS_MAX_INSTRUCTION_LENGTH) { return ZYDIS_STATUS_INSTRUCTION_TOO_LONG; } if (context->buffer_len > 0) { *value = context->buffer++[0]; ++instruction->length; --context->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 context A pointer to the `ZydisDecoderContext` instance. * @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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU8* value, ZyanU8 number_of_bytes) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(value); if (instruction->length + number_of_bytes > ZYDIS_MAX_INSTRUCTION_LENGTH) { return ZYDIS_STATUS_INSTRUCTION_TOO_LONG; } if (context->buffer_len >= number_of_bytes) { instruction->length += number_of_bytes; ZYAN_MEMCPY(value, context->buffer, number_of_bytes); context->buffer += number_of_bytes; context->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->cache.W = instruction->raw.rex.W; context->cache.R = instruction->raw.rex.R; context->cache.X = instruction->raw.rex.X; context->cache.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); 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->cache.W = instruction->raw.xop.W; context->cache.R = 0x01 & ~instruction->raw.xop.R; context->cache.X = 0x01 & ~instruction->raw.xop.X; context->cache.B = 0x01 & ~instruction->raw.xop.B; context->cache.L = instruction->raw.xop.L; context->cache.LL = instruction->raw.xop.L; context->cache.v_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)); 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->cache.W = instruction->raw.vex.W; context->cache.R = 0x01 & ~instruction->raw.vex.R; context->cache.X = 0x01 & ~instruction->raw.vex.X; context->cache.B = 0x01 & ~instruction->raw.vex.B; context->cache.L = instruction->raw.vex.L; context->cache.LL = instruction->raw.vex.L; context->cache.v_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); 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] >> 2) & 0x03) != 0x00) { // Invalid according to the intel documentation return ZYDIS_STATUS_MALFORMED_EVEX; } instruction->raw.evex.mm = (data[1] >> 0) & 0x03; if (instruction->raw.evex.mm == 0x00) { // 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 && (context->decoder->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->cache.W = instruction->raw.evex.W; context->cache.R = 0x01 & ~instruction->raw.evex.R; context->cache.X = 0x01 & ~instruction->raw.evex.X; context->cache.B = 0x01 & ~instruction->raw.evex.B; context->cache.LL = (data[3] >> 5) & 0x03; context->cache.R2 = 0x01 & ~instruction->raw.evex.R2; context->cache.V2 = 0x01 & ~instruction->raw.evex.V2; context->cache.v_vvvv = ((0x01 & ~instruction->raw.evex.V2) << 4) | (0x0F & ~instruction->raw.evex.vvvv); context->cache.mask = instruction->raw.evex.aaa; if (!instruction->raw.evex.V2 && (context->decoder->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)) { return ZYDIS_STATUS_MALFORMED_EVEX; } if (!instruction->raw.evex.b && (context->cache.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); 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->cache.W = instruction->raw.mvex.W; context->cache.R = 0x01 & ~instruction->raw.mvex.R; context->cache.X = 0x01 & ~instruction->raw.mvex.X; context->cache.B = 0x01 & ~instruction->raw.mvex.B; context->cache.R2 = 0x01 & ~instruction->raw.mvex.R2; context->cache.V2 = 0x01 & ~instruction->raw.mvex.V2; context->cache.LL = 2; context->cache.v_vvvv = ((0x01 & ~instruction->raw.mvex.V2) << 4) | (0x0F & ~instruction->raw.mvex.vvvv); context->cache.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 context A pointer to the `ZydisDecoderContext` 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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU8 size) { ZYAN_ASSERT(context); 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(context, instruction, &value)); instruction->raw.disp.value = *(ZyanI8*)&value; break; } case 16: { ZyanU16 value; ZYAN_CHECK(ZydisInputNextBytes(context, instruction, (ZyanU8*)&value, 2)); instruction->raw.disp.value = *(ZyanI16*)&value; break; } case 32: { ZyanU32 value; ZYAN_CHECK(ZydisInputNextBytes(context, instruction, (ZyanU8*)&value, 4)); instruction->raw.disp.value = *(ZyanI32*)&value; break; } case 64: { ZyanU64 value; ZYAN_CHECK(ZydisInputNextBytes(context, 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 context A pointer to the `ZydisDecoderContext` 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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU8 id, ZyanU8 size, ZyanBool is_signed, ZyanBool is_relative) { ZYAN_ASSERT(context); 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(context, 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(context, 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(context, 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(context, 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; } /* ---------------------------------------------------------------------------------------------- */ /* Semantical 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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZydisRegisterEncoding encoding, ZydisRegisterClass register_class) { switch (context->decoder->machine_mode) { case ZYDIS_MACHINE_MODE_LONG_COMPAT_16: case ZYDIS_MACHINE_MODE_LEGACY_16: case ZYDIS_MACHINE_MODE_REAL_16: case ZYDIS_MACHINE_MODE_LONG_COMPAT_32: case ZYDIS_MACHINE_MODE_LEGACY_32: switch (encoding) { 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; } return value; } case ZYDIS_REG_ENCODING_REG: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM); return instruction->raw.modrm.reg; case ZYDIS_REG_ENCODING_NDSNDD: return context->cache.v_vvvv & 0x07; case ZYDIS_REG_ENCODING_RM: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM); return instruction->raw.modrm.rm; case ZYDIS_REG_ENCODING_BASE: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM); ZYAN_ASSERT(instruction->raw.modrm.mod != 3); if (instruction->raw.modrm.rm == 4) { ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB); return instruction->raw.sib.base; } return instruction->raw.modrm.rm; case ZYDIS_REG_ENCODING_INDEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM); ZYAN_ASSERT(instruction->raw.modrm.mod != 3); ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB); return instruction->raw.sib.index; case ZYDIS_REG_ENCODING_VIDX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM); ZYAN_ASSERT(instruction->raw.modrm.mod != 3); ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB); ZYAN_ASSERT((register_class == ZYDIS_REGCLASS_XMM) || (register_class == ZYDIS_REGCLASS_YMM) || (register_class == ZYDIS_REGCLASS_ZMM)); return instruction->raw.sib.index; case ZYDIS_REG_ENCODING_IS4: return (instruction->raw.imm[0].value.u >> 4) & 0x07; case ZYDIS_REG_ENCODING_MASK: return context->cache.mask; default: ZYAN_UNREACHABLE; } case ZYDIS_MACHINE_MODE_LONG_64: switch (encoding) { 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; } return value | (context->cache.B << 3); } case ZYDIS_REG_ENCODING_REG: { ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM); ZyanU8 value = instruction->raw.modrm.reg; switch (register_class) { case ZYDIS_REGCLASS_GPR8: case ZYDIS_REGCLASS_GPR16: case ZYDIS_REGCLASS_GPR32: case ZYDIS_REGCLASS_GPR64: case ZYDIS_REGCLASS_XMM: case ZYDIS_REGCLASS_YMM: case ZYDIS_REGCLASS_ZMM: case ZYDIS_REGCLASS_CONTROL: case ZYDIS_REGCLASS_DEBUG: value |= (context->cache.R << 3); break; default: break; } // R' only exists for EVEX and MVEX. No encoding check needed switch (register_class) { case ZYDIS_REGCLASS_XMM: case ZYDIS_REGCLASS_YMM: case ZYDIS_REGCLASS_ZMM: value |= (context->cache.R2 << 4); break; default: break; } return value; } case ZYDIS_REG_ENCODING_NDSNDD: // v' only exists for EVEX and MVEX. No encoding check needed switch (register_class) { case ZYDIS_REGCLASS_XMM: case ZYDIS_REGCLASS_YMM: case ZYDIS_REGCLASS_ZMM: return context->cache.v_vvvv; case ZYDIS_REGCLASS_MASK: return context->cache.v_vvvv & 0x07; default: return context->cache.v_vvvv & 0x0F; } case ZYDIS_REG_ENCODING_RM: { ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM); ZyanU8 value = instruction->raw.modrm.rm; switch (register_class) { case ZYDIS_REGCLASS_GPR8: case ZYDIS_REGCLASS_GPR16: case ZYDIS_REGCLASS_GPR32: case ZYDIS_REGCLASS_GPR64: case ZYDIS_REGCLASS_XMM: case ZYDIS_REGCLASS_YMM: case ZYDIS_REGCLASS_ZMM: case ZYDIS_REGCLASS_CONTROL: case ZYDIS_REGCLASS_DEBUG: value |= (context->cache.B << 3); break; default: break; } // We have to check the instruction-encoding, because the extension by X 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 |= (context->cache.X << 4); break; default: break; } } return value; } case ZYDIS_REG_ENCODING_BASE: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM); ZYAN_ASSERT(instruction->raw.modrm.mod != 3); if (instruction->raw.modrm.rm == 4) { ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB); return instruction->raw.sib.base | (context->cache.B << 3); } return instruction->raw.modrm.rm | (context->cache.B << 3); case ZYDIS_REG_ENCODING_INDEX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM); ZYAN_ASSERT(instruction->raw.modrm.mod != 3); ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB); return instruction->raw.sib.index | (context->cache.X << 3); case ZYDIS_REG_ENCODING_VIDX: ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM); ZYAN_ASSERT(instruction->raw.modrm.mod != 3); ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB); ZYAN_ASSERT((register_class == ZYDIS_REGCLASS_XMM) || (register_class == ZYDIS_REGCLASS_YMM) || (register_class == ZYDIS_REGCLASS_ZMM)); // v' only exists for EVEX and MVEX. No encoding check needed return instruction->raw.sib.index | (context->cache.X << 3) | (context->cache.V2 << 4); case ZYDIS_REG_ENCODING_IS4: { 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->cache.mask; default: ZYAN_UNREACHABLE; } 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` instance. * @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(ZydisDecoderContext* context, 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(context->decoder->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]); 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; 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(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` instance. * @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(ZydisDecoderContext* context, 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 (context->decoder->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 (context->decoder->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 context A pointer to the `ZydisDecoderContext` instance. * @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(ZydisDecoderContext* context, 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_GPR_SSZ: operand->reg.value = ZydisRegisterEncode( (context->decoder->address_width == ZYDIS_ADDRESS_WIDTH_16) ? ZYDIS_REGCLASS_GPR16 : (context->decoder->address_width == ZYDIS_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_IP_SSZ: operand->reg.value = (context->decoder->address_width == ZYDIS_ADDRESS_WIDTH_16) ? ZYDIS_REGISTER_EIP : (context->decoder->address_width == ZYDIS_ADDRESS_WIDTH_32) ? ZYDIS_REGISTER_EIP : ZYDIS_REGISTER_RIP; break; case ZYDIS_IMPLREG_TYPE_FLAGS_SSZ: operand->reg.value = (context->decoder->address_width == ZYDIS_ADDRESS_WIDTH_16) ? ZYDIS_REGISTER_FLAGS : (context->decoder->address_width == ZYDIS_ADDRESS_WIDTH_32) ? ZYDIS_REGISTER_EFLAGS : ZYDIS_REGISTER_RFLAGS; break; default: ZYAN_UNREACHABLE; } } #endif #ifndef ZYDIS_MINIMAL_MODE /** * Decodes an implicit memory operand. * * @param context A pointer to the `ZydisDecoderContext` instance. * @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(ZydisDecoderContext* context, 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_ASP: operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 4); break; case ZYDIS_IMPLMEM_BASE_ABP: operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 5); 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; 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 /** * Decodes the instruction operands. * * @param context A pointer to the`ZydisDecoderContext` struct. * @param instruction A pointer to the`ZydisDecodedInstruction` struct. * @param definition A pointer to the`ZydisInstructionDefinition` struct. * * @return A zyan status code. */ static ZyanStatus ZydisDecodeOperands(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(definition); ZyanU8 imm_id = 0; const ZydisOperandDefinition* operand; instruction->operand_count = ZydisGetOperandDefinitions(definition, &operand); ZYAN_ASSERT(instruction->operand_count <= ZYAN_ARRAY_LENGTH(instruction->operands)); for (ZyanU8 i = 0; i < instruction->operand_count; ++i) { ZydisRegisterClass register_class = ZYDIS_REGCLASS_INVALID; instruction->operands[i].id = i; instruction->operands[i].visibility = operand->visibility; instruction->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(context, instruction, &instruction->operands[i], operand); break; case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_MEM: ZydisDecodeOperandImplicitMemory(context, instruction, &instruction->operands[i], operand); break; case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_IMM1: instruction->operands[i].type = ZYDIS_OPERAND_TYPE_IMMEDIATE; instruction->operands[i].size = 8; instruction->operands[i].imm.value.u = 1; instruction->operands[i].imm.is_signed = ZYAN_FALSE; instruction->operands[i].imm.is_relative = ZYAN_FALSE; break; default: break; } if (instruction->operands[i].type) { goto FinalizeOperand; } instruction->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_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, &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, &instruction->operands[i], register_class, ZydisCalcRegisterId( context, instruction, ZYDIS_REG_ENCODING_RM, register_class))); break; case ZYDIS_OPERAND_ENCODING_OPCODE: ZYAN_CHECK( ZydisDecodeOperandRegister( instruction, &instruction->operands[i], register_class, ZydisCalcRegisterId( context, instruction, ZYDIS_REG_ENCODING_OPCODE, register_class))); break; case ZYDIS_OPERAND_ENCODING_NDSNDD: ZYAN_CHECK( ZydisDecodeOperandRegister( instruction, &instruction->operands[i], register_class, ZydisCalcRegisterId( context, instruction, ZYDIS_REG_ENCODING_NDSNDD, register_class))); break; case ZYDIS_OPERAND_ENCODING_MASK: ZYAN_CHECK( ZydisDecodeOperandRegister( instruction, &instruction->operands[i], register_class, ZydisCalcRegisterId( context, instruction, ZYDIS_REG_ENCODING_MASK, register_class))); break; case ZYDIS_OPERAND_ENCODING_IS4: ZYAN_CHECK( ZydisDecodeOperandRegister( instruction, &instruction->operands[i], register_class, ZydisCalcRegisterId( context, instruction, ZYDIS_REG_ENCODING_IS4, register_class))); break; default: ZYAN_UNREACHABLE; } goto FinalizeOperand; } // Memory operands switch (operand->type) { case ZYDIS_SEMANTIC_OPTYPE_MEM: ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &instruction->operands[i], ZYDIS_REGCLASS_INVALID)); break; case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBX: ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &instruction->operands[i], ZYDIS_REGCLASS_XMM)); break; case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBY: ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &instruction->operands[i], ZYDIS_REGCLASS_YMM)); break; case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBZ: ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &instruction->operands[i], ZYDIS_REGCLASS_ZMM)); 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); instruction->operands[i].type = ZYDIS_OPERAND_TYPE_POINTER; instruction->operands[i].ptr.offset = (ZyanU32)instruction->raw.imm[0].value.u; instruction->operands[i].ptr.segment = (ZyanU16)instruction->raw.imm[1].value.u; break; case ZYDIS_SEMANTIC_OPTYPE_AGEN: instruction->operands[i].actions = 0; // TODO: Remove after generator update ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &instruction->operands[i], ZYDIS_REGCLASS_INVALID)); instruction->operands[i].mem.type = ZYDIS_MEMOP_TYPE_AGEN; break; case ZYDIS_SEMANTIC_OPTYPE_MOFFS: ZYAN_ASSERT(instruction->raw.disp.size); instruction->operands[i].type = ZYDIS_OPERAND_TYPE_MEMORY; instruction->operands[i].mem.type = ZYDIS_MEMOP_TYPE_MEM; instruction->operands[i].mem.disp.has_displacement = ZYAN_TRUE; instruction->operands[i].mem.disp.value = instruction->raw.disp.value; break; case ZYDIS_SEMANTIC_OPTYPE_MIB: instruction->operands[i].actions = 0; // TODO: Remove after generator update ZYAN_CHECK( ZydisDecodeOperandMemory( context, instruction, &instruction->operands[i], ZYDIS_REGCLASS_INVALID)); instruction->operands[i].mem.type = ZYDIS_MEMOP_TYPE_MIB; break; default: break; } if (instruction->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)) { instruction->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); case ZYDIS_SEMANTIC_OPTYPE_IMM: ZYAN_ASSERT((imm_id == 0) || (imm_id == 1)); instruction->operands[i].type = ZYDIS_OPERAND_TYPE_IMMEDIATE; instruction->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); instruction->operands[i].imm.value.u = (ZyanU8)instruction->raw.imm[imm_id].value.u & 0x0F; } else { instruction->operands[i].imm.value.u = instruction->raw.imm[imm_id].value.u; } instruction->operands[i].imm.is_signed = instruction->raw.imm[imm_id].is_signed; instruction->operands[i].imm.is_relative = instruction->raw.imm[imm_id].is_relative; ++imm_id; break; default: break; } ZYAN_ASSERT(instruction->operands[i].type == ZYDIS_OPERAND_TYPE_IMMEDIATE); FinalizeOperand: // Set segment-register for memory operands if (instruction->operands[i].type == ZYDIS_OPERAND_TYPE_MEMORY) { if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_CS) { instruction->operands[i].mem.segment = ZYDIS_REGISTER_CS; } else if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_SS) { instruction->operands[i].mem.segment = ZYDIS_REGISTER_SS; } else if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_DS) { instruction->operands[i].mem.segment = ZYDIS_REGISTER_DS; } else if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_ES) { instruction->operands[i].mem.segment = ZYDIS_REGISTER_ES; } else if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_FS) { instruction->operands[i].mem.segment = ZYDIS_REGISTER_FS; } else if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_GS) { instruction->operands[i].mem.segment = ZYDIS_REGISTER_GS; } else { if (instruction->operands[i].mem.segment == ZYDIS_REGISTER_NONE) { if ((instruction->operands[i].mem.base == ZYDIS_REGISTER_RSP) || (instruction->operands[i].mem.base == ZYDIS_REGISTER_RBP) || (instruction->operands[i].mem.base == ZYDIS_REGISTER_ESP) || (instruction->operands[i].mem.base == ZYDIS_REGISTER_EBP) || (instruction->operands[i].mem.base == ZYDIS_REGISTER_SP) || (instruction->operands[i].mem.base == ZYDIS_REGISTER_BP)) { instruction->operands[i].mem.segment = ZYDIS_REGISTER_SS; } else { instruction->operands[i].mem.segment = ZYDIS_REGISTER_DS; } } } } ZydisSetOperandSizeAndElementInfo(context, instruction, &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(instruction->operand_count >= 1); switch (instruction->operands[0].actions) { case ZYDIS_OPERAND_ACTION_WRITE: if (instruction->operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY) { instruction->operands[0].actions = ZYDIS_OPERAND_ACTION_CONDWRITE; } else { instruction->operands[0].actions = ZYDIS_OPERAND_ACTION_READ_CONDWRITE; } break; case ZYDIS_OPERAND_ACTION_READWRITE: instruction->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 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 ZydisSetAttributes(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition) { ZYAN_ASSERT(context); 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 (context->prefixes.has_lock) { instruction->attributes |= ZYDIS_ATTRIB_HAS_LOCK; instruction->raw.prefixes[context->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 (context->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 (context->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[context->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[context->prefixes.offset_group1].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; } if (def->accepts_branch_hints) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_BRANCH_HINTS; switch (context->prefixes.group2) { case 0x2E: instruction->attributes |= ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN; instruction->raw.prefixes[context->prefixes.offset_group2].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; break; case 0x3E: instruction->attributes |= ZYDIS_ATTRIB_HAS_BRANCH_TAKEN; instruction->raw.prefixes[context->prefixes.offset_group2].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; break; default: break; } } else { if (def->accepts_segment) { instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_SEGMENT; } if (context->prefixes.effective_segment && def->accepts_segment) { switch (context->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[context->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 (context->prefixes.effective_segment) { switch (context->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[context->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->cache.LL < ZYAN_ARRAY_LENGTH(lookup)); instruction->avx.vector_length = lookup[context->cache.LL]; break; } case ZYDIS_INSTRUCTION_ENCODING_VEX: { // Vector length static const ZyanU16 lookup[2] = { 128, 256 }; ZYAN_ASSERT(context->cache.LL < ZYAN_ARRAY_LENGTH(lookup)); instruction->avx.vector_length = lookup[context->cache.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->cache.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; const ZyanU8 evex_w = context->cache.W; ZYAN_ASSERT(evex_b < 2); ZYAN_ASSERT(evex_w < 2); ZYAN_ASSERT(!evex_b || ((!evex_w && context->evex.element_size == 32) || ( evex_w && context->evex.element_size == 64))); ZYAN_ASSERT(!evex_b || def->functionality == ZYDIS_EVEX_FUNC_BC); static const ZyanU8 scales[2][2][3] = { /*B0*/ { /*W0*/ { 16, 32, 64 }, /*W1*/ { 16, 32, 64 } }, /*B1*/ { /*W0*/ { 4, 4, 4 }, /*W1*/ { 8, 8, 8 } } }; static const ZydisBroadcastMode broadcasts[2][2][3] = { /*B0*/ { /*W0*/ { ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID }, /*W1*/ { ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID, ZYDIS_BROADCAST_MODE_INVALID } }, /*B1*/ { /*W0*/ { ZYDIS_BROADCAST_MODE_1_TO_4, ZYDIS_BROADCAST_MODE_1_TO_8, ZYDIS_BROADCAST_MODE_1_TO_16 }, /*W1*/ { ZYDIS_BROADCAST_MODE_1_TO_2, ZYDIS_BROADCAST_MODE_1_TO_4, ZYDIS_BROADCAST_MODE_1_TO_8 } } }; context->cd8_scale = scales[evex_b][evex_w][vector_length]; instruction->avx.broadcast.mode = broadcasts[evex_b][evex_w][vector_length]; break; } case ZYDIS_TUPLETYPE_HV: { const ZyanU8 evex_b = instruction->raw.evex.b; ZYAN_ASSERT(evex_b < 2); ZYAN_ASSERT(!context->cache.W); ZYAN_ASSERT(context->evex.element_size == 32); ZYAN_ASSERT(!evex_b || def->functionality == ZYDIS_EVEX_FUNC_BC); static const ZyanU8 scales[2][3] = { /*B0*/ { 8, 16, 32 }, /*B1*/ { 4, 4, 4 } }; 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; } 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->cache.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: switch (context->evex.element_size) { case 32: context->cd8_scale = 4; break; case 64: context->cd8_scale = 8; break; default: ZYAN_UNREACHABLE; } break; case ZYDIS_TUPLETYPE_T1_4X: ZYAN_ASSERT(context->evex.element_size == 32); ZYAN_ASSERT(context->cache.W == 0); context->cd8_scale = 16; break; case ZYDIS_TUPLETYPE_T2: switch (context->cache.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->cache.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->cache.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; } 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->cache.LL; // Intentional 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; break; 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 context A pointer to the `ZydisDecoderContext` 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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction) { ZYAN_ASSERT(context); 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(context, instruction, &prefix_byte)); switch (prefix_byte) { case 0xF0: context->prefixes.has_lock = ZYAN_TRUE; context->prefixes.offset_lock = offset; break; case 0xF2: ZYAN_FALLTHROUGH; case 0xF3: context->prefixes.group1 = prefix_byte; context->prefixes.mandatory_candidate = prefix_byte; context->prefixes.offset_group1 = offset; context->prefixes.offset_mandatory = offset; break; case 0x2E: ZYAN_FALLTHROUGH; case 0x36: ZYAN_FALLTHROUGH; case 0x3E: ZYAN_FALLTHROUGH; case 0x26: context->prefixes.group2 = prefix_byte; context->prefixes.offset_group2 = offset; if ((context->decoder->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) || ((context->prefixes.effective_segment != 0x64) && (context->prefixes.effective_segment != 0x65))) { context->prefixes.effective_segment = prefix_byte; context->prefixes.offset_segment = offset; } break; case 0x64: ZYAN_FALLTHROUGH; case 0x65: context->prefixes.group2 = prefix_byte; context->prefixes.offset_group2 = offset; context->prefixes.effective_segment = prefix_byte; context->prefixes.offset_segment = offset; break; case 0x66: // context->prefixes.has_osz_override = ZYAN_TRUE; context->prefixes.offset_osz_override = offset; if (!context->prefixes.mandatory_candidate) { context->prefixes.mandatory_candidate = 0x66; context->prefixes.offset_mandatory = offset; } instruction->attributes |= ZYDIS_ATTRIB_HAS_OPERANDSIZE; break; case 0x67: // context->prefixes.has_asz_override = ZYAN_TRUE; context->prefixes.offset_asz_override = offset; instruction->attributes |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE; break; default: if ((context->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(context, instruction); ++offset; } } while (!done); if (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE) { instruction->raw.prefixes[context->prefixes.offset_osz_override].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; } if (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) { instruction->raw.prefixes[context->prefixes.offset_asz_override].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; } if (rex) { instruction->raw.prefixes[instruction->raw.rex.offset].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; ZydisDecodeREX(context, instruction, rex); } return ZYAN_STATUS_SUCCESS; } /** * Decodes optional instruction parts like the ModRM byte, the SIB byte and * additional displacements and/or immediate values. * * @param context A pointer to the `ZydisDecoderContext` 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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, const ZydisInstructionEncodingInfo* info) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(info); 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(context, instruction, &modrm_byte)); ZydisDecodeModRM(instruction, modrm_byte); } ZyanU8 has_sib = 0; ZyanU8 displacement_size = 0; if (!(info->flags & ZYDIS_INSTR_ENC_FLAG_FORCE_REG_FORM)) { 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 (context->decoder->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(context, 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(context, instruction, displacement_size)); } } } if (info->flags & ZYDIS_INSTR_ENC_FLAG_HAS_DISP) { ZYAN_CHECK(ZydisReadDisplacement( context, 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(context, 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(context, 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; switch (context->decoder->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 += 2; break; case ZYDIS_MACHINE_MODE_LONG_64: index += 4; index += (context->cache.W & 0x01) << 1; break; default: ZYAN_UNREACHABLE; } 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]; switch (instruction->operand_width) { case 16: context->eosz_index = 0; break; case 32: context->eosz_index = 1; break; case 64: context->eosz_index = 2; break; default: ZYAN_UNREACHABLE; } // 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; switch (context->decoder->address_width) { case ZYDIS_ADDRESS_WIDTH_16: index += 0; break; case ZYDIS_ADDRESS_WIDTH_32: index += 2; break; case ZYDIS_ADDRESS_WIDTH_64: index += 4; break; default: ZYAN_UNREACHABLE; } 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]; switch (instruction->address_width) { case 16: context->easz_index = 0; break; case 32: context->easz_index = 1; break; case 64: context->easz_index = 2; break; default: ZYAN_UNREACHABLE; } } /* ---------------------------------------------------------------------------------------------- */ static ZyanStatus ZydisNodeHandlerXOP(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(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(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.mm + (instruction->raw.evex.pp << 2) + 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) + 17; break; default: ZYAN_UNREACHABLE; } return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerOpcode(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(context); 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(context, 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(context, instruction, &next_input)); if (((next_input & 0xF0) >= 0xC0) || (context->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)) { if (instruction->attributes & ZYDIS_ATTRIB_HAS_REX) { return ZYDIS_STATUS_ILLEGAL_REX; } if (context->prefixes.has_lock) { return ZYDIS_STATUS_ILLEGAL_LOCK; } if (context->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(context, 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(context, 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(context, 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(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 (context->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(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(context, instruction, prefix_bytes)); instruction->opcode_map = ZYDIS_OPCODE_MAP_DEFAULT + instruction->raw.evex.mm; 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(context, instruction, &next_input)); if ((next_input & 0x1F) >= 8) { if (instruction->attributes & ZYDIS_ATTRIB_HAS_REX) { return ZYDIS_STATUS_ILLEGAL_REX; } if (context->prefixes.has_lock) { return ZYDIS_STATUS_ILLEGAL_LOCK; } if (context->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(context, instruction, &prefixBytes[1], 2)); // Decode xop-prefix instruction->encoding = ZYDIS_INSTRUCTION_ENCODING_XOP; ZYAN_CHECK(ZydisDecodeXOP(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 (context->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(context, instruction, &instruction->opcode)); break; } *index = instruction->opcode; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerMode(ZydisDecoderContext* context, ZyanU16* index) { ZYAN_ASSERT(context); ZYAN_ASSERT(index); switch (context->decoder->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(ZydisDecoderContext* context, ZyanU16* index) { ZYAN_ASSERT(context); ZYAN_ASSERT(index); *index = (context->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) ? 0 : 1; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerModrmMod(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); if (!instruction->raw.modrm.offset) { instruction->raw.modrm.offset = instruction->length; ZyanU8 modrm_byte; ZYAN_CHECK(ZydisInputNext(context, instruction, &modrm_byte)); ZydisDecodeModRM(instruction, modrm_byte); } *index = instruction->raw.modrm.mod; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerModrmModCompact(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_CHECK(ZydisNodeHandlerModrmMod(context, instruction, index)); *index = (*index == 0x3) ? 0 : 1; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerModrmReg(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); if (!instruction->raw.modrm.offset) { instruction->raw.modrm.offset = instruction->length; ZyanU8 modrm_byte; ZYAN_CHECK(ZydisInputNext(context, instruction, &modrm_byte)); ZydisDecodeModRM(instruction, modrm_byte); } *index = instruction->raw.modrm.reg; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerModrmRm(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); if (!instruction->raw.modrm.offset) { instruction->raw.modrm.offset = instruction->length; ZyanU8 modrm_byte; ZYAN_CHECK(ZydisInputNext(context, instruction, &modrm_byte)); ZydisDecodeModRM(instruction, modrm_byte); } *index = instruction->raw.modrm.rm; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerMandatoryPrefix(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); switch (context->prefixes.mandatory_candidate) { case 0x66: instruction->raw.prefixes[context->prefixes.offset_mandatory].type = ZYDIS_PREFIX_TYPE_MANDATORY; instruction->attributes &= ~ZYDIS_ATTRIB_HAS_OPERANDSIZE; *index = 2; break; case 0xF3: instruction->raw.prefixes[context->prefixes.offset_mandatory].type = ZYDIS_PREFIX_TYPE_MANDATORY; *index = 3; break; case 0xF2: instruction->raw.prefixes[context->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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(context); ZYAN_ASSERT(instruction); ZYAN_ASSERT(index); if ((context->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) && (context->cache.W)) { *index = 2; } else { if (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE) { instruction->raw.prefixes[context->prefixes.offset_osz_override].type = ZYDIS_PREFIX_TYPE_EFFECTIVE; } switch (context->decoder->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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, ZyanU16* index) { ZYAN_ASSERT(context); 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 (context->decoder->address_width) { case ZYDIS_ADDRESS_WIDTH_16: *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) ? 1 : 0; break; case ZYDIS_ADDRESS_WIDTH_32: *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) ? 0 : 1; break; case ZYDIS_ADDRESS_WIDTH_64: *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) ? 1 : 2; break; default: ZYAN_UNREACHABLE; } return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerVectorLength(ZydisDecoderContext* context, 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->cache.LL; if (*index == 3) { return ZYDIS_STATUS_DECODING_ERROR; } return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerRexW(ZydisDecoderContext* context, 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->cache.W; return ZYAN_STATUS_SUCCESS; } static ZyanStatus ZydisNodeHandlerRexB(ZydisDecoderContext* context, 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->cache.B; return ZYAN_STATUS_SUCCESS; } #ifndef ZYDIS_DISABLE_AVX512 static ZyanStatus ZydisNodeHandlerEvexB(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(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 /* ---------------------------------------------------------------------------------------------- */ /** * Checks for certain post-decode error-conditions. * * @param context A pointer to the `ZydisDecoderContext` instance. * @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(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition) { const ZydisRegisterConstraint constr_REG = definition->constr_REG; const ZydisRegisterConstraint constr_RM = definition->constr_RM; // We set this to `NONE` instead of `UNUSED` to save up some unnecessary runtime checks ZydisRegisterConstraint constr_NDSNDD = ZYDIS_REG_CONSTRAINTS_NONE; ZyanBool has_VSIB = ZYAN_FALSE; ZyanBool is_gather = 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 && (context->decoder->machine_mode == ZYDIS_MACHINE_MODE_REAL_16)) { return ZYDIS_STATUS_DECODING_ERROR; } if (context->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; constr_NDSNDD = def->constr_NDSNDD; break; } case ZYDIS_INSTRUCTION_ENCODING_VEX: { const ZydisInstructionDefinitionVEX* def = (const ZydisInstructionDefinitionVEX*)definition; constr_NDSNDD = def->constr_NDSNDD; is_gather = def->is_gather; break; } case ZYDIS_INSTRUCTION_ENCODING_EVEX: { #ifndef ZYDIS_DISABLE_AVX512 const ZydisInstructionDefinitionEVEX* def = (const ZydisInstructionDefinitionEVEX*)definition; constr_NDSNDD = def->constr_NDSNDD; is_gather = def->is_gather; 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; constr_NDSNDD = def->constr_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, 1, 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; } // Validate register constraints switch (constr_REG) { case ZYDIS_REG_CONSTRAINTS_UNUSED: case ZYDIS_REG_CONSTRAINTS_NONE: break; case ZYDIS_REG_CONSTRAINTS_GPR: if ((context->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) && context->cache.R2) { return ZYDIS_STATUS_BAD_REGISTER; } break; case ZYDIS_REG_CONSTRAINTS_SR_DEST: // `ZYDIS_REGISTER_CR` is not allowed as `MOV` target if (instruction->raw.modrm.reg == 1) { return ZYDIS_STATUS_BAD_REGISTER; } ZYAN_FALLTHROUGH; case ZYDIS_REG_CONSTRAINTS_SR: { if (instruction->raw.modrm.reg > 5) { return ZYDIS_STATUS_BAD_REGISTER; } break; } case ZYDIS_REG_CONSTRAINTS_CR: { // Attempts to reference CR1, CR5, CR6, CR7, and CR9CR15 result in undefined opcode (#UD) // exceptions const ZyanU8 value = instruction->raw.modrm.reg | (context->cache.R << 3); static const ZyanU8 lookup[16] = { 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }; ZYAN_ASSERT(value < ZYAN_ARRAY_LENGTH(lookup)); if (!lookup[value]) { return ZYDIS_STATUS_BAD_REGISTER; } break; } case ZYDIS_REG_CONSTRAINTS_DR: // Attempts to reference DR8DR15 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, // we just allow them. if (context->cache.R) { return ZYDIS_STATUS_BAD_REGISTER; } break; case ZYDIS_REG_CONSTRAINTS_MASK: if ((context->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) && (context->cache.R || context->cache.R2)) { return ZYDIS_STATUS_BAD_REGISTER; } break; case ZYDIS_REG_CONSTRAINTS_BND: ZYAN_ASSERT(!context->cache.R2); if (context->cache.R || instruction->raw.modrm.reg > 3) { return ZYDIS_STATUS_BAD_REGISTER; } break; default: ZYAN_UNREACHABLE; } switch (constr_RM) { case ZYDIS_REG_CONSTRAINTS_UNUSED: case ZYDIS_REG_CONSTRAINTS_NONE: break; case ZYDIS_REG_CONSTRAINTS_SR_DEST: // `ZYDIS_REGISTER_CR` is not allowed as `MOV` target if (instruction->raw.modrm.rm == 1) { return ZYDIS_STATUS_BAD_REGISTER; } ZYAN_FALLTHROUGH; case ZYDIS_REG_CONSTRAINTS_SR: { if (instruction->raw.modrm.rm > 6) { return ZYDIS_STATUS_BAD_REGISTER; } break; } case ZYDIS_REG_CONSTRAINTS_MASK: break; case ZYDIS_REG_CONSTRAINTS_BND: if (context->cache.B || instruction->raw.modrm.rm > 3) { return ZYDIS_STATUS_BAD_REGISTER; } break; case ZYDIS_REG_CONSTRAINTS_VSIB: has_VSIB = ZYAN_TRUE; break; case ZYDIS_REG_CONSTRAINTS_NO_REL: if ((context->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) && (instruction->raw.modrm.mod == 0) && (instruction->raw.modrm.rm == 5)) { return ZYDIS_STATUS_DECODING_ERROR; } break; default: ZYAN_UNREACHABLE; } switch (constr_NDSNDD) { case ZYDIS_REG_CONSTRAINTS_UNUSED: // `.vvvv` is not allowed, if the instruction does not encode a NDS/NDD operand if (context->cache.v_vvvv & 0x0F) { return ZYDIS_STATUS_BAD_REGISTER; } // `.v'` is not allowed, if the instruction does not encode a NDS/NDD or VSIB operand if (!has_VSIB && context->cache.V2) { return ZYDIS_STATUS_BAD_REGISTER; } break; case ZYDIS_REG_CONSTRAINTS_NONE: ZYAN_ASSERT(!has_VSIB || ((instruction->encoding != ZYDIS_INSTRUCTION_ENCODING_EVEX) && (instruction->encoding != ZYDIS_INSTRUCTION_ENCODING_MVEX))); break; case ZYDIS_REG_CONSTRAINTS_GPR: // `.v'` is invalid for GPR-registers if (context->cache.V2) { return ZYDIS_STATUS_BAD_REGISTER; } break; case ZYDIS_REG_CONSTRAINTS_MASK: if ((context->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) && (context->cache.v_vvvv > 7)) { return ZYDIS_STATUS_BAD_REGISTER; } break; default: ZYAN_UNREACHABLE; } // Check gather registers if (is_gather) { ZYAN_ASSERT(has_VSIB); ZYAN_ASSERT(instruction->raw.modrm.mod != 3); ZYAN_ASSERT(instruction->raw.modrm.rm == 4); ZyanU8 dest = instruction->raw.modrm.reg; ZyanU8 index = instruction->raw.sib.index; if (context->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) { dest = dest | (context->cache.R << 3) | (context->cache.R2 << 4); index = index | (context->cache.X << 3) | (context->cache.V2 << 4); } ZyanU8 mask = 0xF0; switch (instruction->encoding) { case ZYDIS_INSTRUCTION_ENCODING_VEX: ZYAN_ASSERT((constr_REG == ZYDIS_REG_CONSTRAINTS_NONE) && (constr_RM == ZYDIS_REG_CONSTRAINTS_VSIB) && (constr_NDSNDD == ZYDIS_REG_CONSTRAINTS_NONE)); if (context->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) { mask = context->cache.v_vvvv; } else { // Ignore the high-register bits in 16- and 32-bit mode mask = context->cache.v_vvvv & 0x07; } break; case ZYDIS_INSTRUCTION_ENCODING_EVEX: case ZYDIS_INSTRUCTION_ENCODING_MVEX: ZYAN_ASSERT(((constr_REG == ZYDIS_REG_CONSTRAINTS_UNUSED) || (constr_REG == ZYDIS_REG_CONSTRAINTS_NONE)) && (constr_RM == ZYDIS_REG_CONSTRAINTS_VSIB) && (constr_NDSNDD == ZYDIS_REG_CONSTRAINTS_UNUSED)); // Some gather instructions (like `VGATHERPF0{D|Q}{PS|PD}`) do not have a destination // operand if (constr_REG == ZYDIS_REG_CONSTRAINTS_UNUSED) { dest = 0xF1; } break; default: ZYAN_UNREACHABLE; } // 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; } } #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->cache.mask) { return ZYDIS_STATUS_INVALID_MASK; } break; case ZYDIS_MASK_POLICY_FORBIDDEN: if (context->cache.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 context A pointer to the `ZydisDecoderContext` instance. * @param instruction A pointer to the `ZydisDecodedInstruction` struct. * * @return A zyan status code. */ static ZyanStatus ZydisDecodeInstruction(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction) { ZYAN_ASSERT(context); 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 (context->prefixes.mandatory_candidate != 0x00) { instruction->raw.prefixes[context->prefixes.offset_mandatory].type = ZYDIS_PREFIX_TYPE_IGNORED; } if (context->prefixes.mandatory_candidate == 0x66) { if (context->prefixes.offset_osz_override == context->prefixes.offset_mandatory) { instruction->raw.prefixes[context->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(context, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_MODE: status = ZydisNodeHandlerMode(context, &index); break; case ZYDIS_NODETYPE_FILTER_MODE_COMPACT: status = ZydisNodeHandlerModeCompact(context, &index); break; case ZYDIS_NODETYPE_FILTER_MODRM_MOD: status = ZydisNodeHandlerModrmMod(context, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_MODRM_MOD_COMPACT: status = ZydisNodeHandlerModrmModCompact(context, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_MODRM_REG: status = ZydisNodeHandlerModrmReg(context, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_MODRM_RM: status = ZydisNodeHandlerModrmRm(context, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_PREFIX_GROUP1: index = context->prefixes.group1 ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MANDATORY_PREFIX: status = ZydisNodeHandlerMandatoryPrefix(context, instruction, &index); temp = ZydisDecoderTreeGetChildNode(node, 0); // TODO: Return to this point, if index == 0 contains a value and the previous path // TODO: was not successfull // TODO: Restore consumed prefix break; case ZYDIS_NODETYPE_FILTER_OPERAND_SIZE: status = ZydisNodeHandlerOperandSize(context, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_ADDRESS_SIZE: status = ZydisNodeHandlerAddressSize(context, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_VECTOR_LENGTH: status = ZydisNodeHandlerVectorLength(context, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_REX_W: status = ZydisNodeHandlerRexW(context, instruction, &index); break; case ZYDIS_NODETYPE_FILTER_REX_B: status = ZydisNodeHandlerRexB(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 = context->decoder->decoder_mode[ZYDIS_DECODER_MODE_AMD_BRANCHES] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_KNC: index = context->decoder->decoder_mode[ZYDIS_DECODER_MODE_KNC] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_MPX: index = context->decoder->decoder_mode[ZYDIS_DECODER_MODE_MPX] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_CET: index = context->decoder->decoder_mode[ZYDIS_DECODER_MODE_CET] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_LZCNT: index = context->decoder->decoder_mode[ZYDIS_DECODER_MODE_LZCNT] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_TZCNT: index = context->decoder->decoder_mode[ZYDIS_DECODER_MODE_TZCNT] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_WBNOINVD: index = context->decoder->decoder_mode[ZYDIS_DECODER_MODE_WBNOINVD] ? 1 : 0; break; case ZYDIS_NODETYPE_FILTER_MODE_CLDEMOTE: index = context->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(context, instruction, definition); ZydisSetEffectiveAddressWidth(context, instruction, definition); const ZydisInstructionEncodingInfo* info; ZydisGetInstructionEncodingInfo(node, &info); ZYAN_CHECK(ZydisDecodeOptionalInstructionParts(context, instruction, info)); ZYAN_CHECK(ZydisCheckErrorConditions(context, instruction, definition)); if (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_3DNOW) { // Get actual 3dnow opcode and definition ZYAN_CHECK(ZydisInputNext(context, 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->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 (!context->decoder->decoder_mode[ZYDIS_DECODER_MODE_MINIMAL]) { ZydisSetAttributes(context, 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(context, instruction, definition); break; default: break; } ZYAN_CHECK(ZydisDecodeOperands(context, instruction, definition)); const ZydisAccessedFlags* flags; if (ZydisGetAccessedFlags(definition, &flags)) { instruction->attributes |= ZYDIS_ATTRIB_CPUFLAG_ACCESS; ZYAN_ASSERT((ZYAN_ARRAY_LENGTH(instruction->accessed_flags) == ZYAN_ARRAY_LENGTH(flags->action )) && (sizeof (instruction->accessed_flags) == sizeof (flags->action ))); ZYAN_MEMCPY(&instruction->accessed_flags, &flags->action, sizeof(flags->action)); } } #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, ZydisAddressWidth address_width) { static const ZyanBool decoderModes[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 (address_width != ZYDIS_ADDRESS_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 ((address_width != ZYDIS_ADDRESS_WIDTH_16) && (address_width != ZYDIS_ADDRESS_WIDTH_32)) { return ZYAN_STATUS_INVALID_ARGUMENT; } break; default: return ZYAN_STATUS_INVALID_ARGUMENT; } decoder->machine_mode = machine_mode; decoder->address_width = address_width; ZYAN_MEMCPY(&decoder->decoder_mode, &decoderModes, sizeof(decoderModes)); return ZYAN_STATUS_SUCCESS; } ZyanStatus ZydisDecoderEnableMode(ZydisDecoder* decoder, ZydisDecoderMode mode, ZyanBool enabled) { if (!decoder || (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 ZydisDecoderDecodeBuffer(const ZydisDecoder* decoder, const void* buffer, ZyanUSize length, ZydisDecodedInstruction* instruction) { if (!decoder || !instruction) { return ZYAN_STATUS_INVALID_ARGUMENT; } if (!buffer || !length) { return ZYDIS_STATUS_NO_MORE_DATA; } ZydisDecoderContext context; ZYAN_MEMSET(&context, 0, sizeof(context)); context.decoder = decoder; context.buffer = (ZyanU8*)buffer; context.buffer_len = length; ZYAN_MEMSET(instruction, 0, sizeof(*instruction)); instruction->machine_mode = decoder->machine_mode; static const ZyanU8 lookup[ZYDIS_ADDRESS_WIDTH_MAX_VALUE + 1] = { 16, 32, 64 }; instruction->stack_width = lookup[decoder->address_width]; ZYAN_CHECK(ZydisCollectOptionalPrefixes(&context, instruction)); ZYAN_CHECK(ZydisDecodeInstruction(&context, instruction)); return ZYAN_STATUS_SUCCESS; } /* ============================================================================================== */