diff --git a/include/Zydis/Decoder.h b/include/Zydis/Decoder.h index af60de4..400d76c 100644 --- a/include/Zydis/Decoder.h +++ b/include/Zydis/Decoder.h @@ -36,18 +36,6 @@ extern "C" { #endif -/* ============================================================================================== */ -/* Macros */ -/* ============================================================================================== */ - -/* ---------------------------------------------------------------------------------------------- */ -/* Constants */ -/* ---------------------------------------------------------------------------------------------- */ - -#define ZYDIS_MAX_INSTRUCTION_LENGTH 15 - -/* ---------------------------------------------------------------------------------------------- */ - /* ============================================================================================== */ /* Enums and types */ /* ============================================================================================== */ diff --git a/include/Zydis/Defines.h b/include/Zydis/Defines.h index c5cbf19..5394549 100644 --- a/include/Zydis/Defines.h +++ b/include/Zydis/Defines.h @@ -120,6 +120,12 @@ #define ZYDIS_ASSERT(condition) assert(condition) #define ZYDIS_UNREACHABLE assert(0) +/* ============================================================================================== */ +/* Utils */ +/* ============================================================================================== */ + +#define ZYDIS_ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + /* ============================================================================================== */ #endif /* ZYDIS_DEFINES_H */ diff --git a/include/Zydis/Encoder.h b/include/Zydis/Encoder.h index 88a4fd3..5a3616f 100644 --- a/include/Zydis/Encoder.h +++ b/include/Zydis/Encoder.h @@ -74,7 +74,7 @@ extern "C" { * * @return A zydis status code. */ -ZYDIS_EXPORT ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t bufferLen, +ZYDIS_EXPORT ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t* bufferLen, ZydisInstructionInfo* info); /* ============================================================================================== */ diff --git a/include/Zydis/InstructionInfo.h b/include/Zydis/InstructionInfo.h index 04aa77e..d538536 100644 --- a/include/Zydis/InstructionInfo.h +++ b/include/Zydis/InstructionInfo.h @@ -40,6 +40,18 @@ extern "C" { #endif +/* ============================================================================================== */ +/* Macros */ +/* ============================================================================================== */ + +/* ---------------------------------------------------------------------------------------------- */ +/* Constants */ +/* ---------------------------------------------------------------------------------------------- */ + +#define ZYDIS_MAX_INSTRUCTION_LENGTH 15 + +/* ---------------------------------------------------------------------------------------------- */ + /* ============================================================================================== */ /* Operand info */ /* ============================================================================================== */ @@ -315,12 +327,12 @@ typedef struct ZydisOperandInfo_ int64_t sqword; } value; /** - * @brief The physical displacement size. + * @brief The physical displacement size, in bits. */ uint8_t dataSize; /** * @brief The offset of the displacement data, relative to the beginning of the - * instruction. + * instruction, in bytes. */ uint8_t dataOffset; } disp; @@ -361,12 +373,12 @@ typedef struct ZydisOperandInfo_ uint64_t uqword; } value; /** - * @brief The physical immediate size. + * @brief The physical immediate size, in bits. */ uint8_t dataSize; /** * @brief The offset of the immediate data, relative to the beginning of the - * instruction. + * instruction, in bytes. */ uint8_t dataOffset; } imm; diff --git a/src/Encoder.c b/src/Encoder.c index 8c1a099..21659be 100644 --- a/src/Encoder.c +++ b/src/Encoder.c @@ -25,6 +25,7 @@ ***************************************************************************************************/ #include +#include #include #include @@ -36,6 +37,7 @@ typedef struct ZydisEncoderTableOperand_ { ZydisOperandEncoding encoding; + ZydisSemanticOperandType type; } ZydisEncoderTableOperand; typedef uint8_t ZydisModRMMod; @@ -74,34 +76,40 @@ typedef struct ZydisEncoderTableEntry_ /** * @brief The encoder context struct. */ -typedef struct ZydisInstructionEncoder_ +typedef struct ZydisEncoderContext_ { uint8_t* buffer; size_t bufferLen; size_t writeOffs; ZydisInstructionInfo* info; const ZydisEncoderTableEntry* matchingEntry; -} ZydisInstructionEncoder; + uint8_t dispBitSize; + uint64_t disp; + uint8_t immBitSizes[2]; + uint64_t imms[2]; +} ZydisEncoderContext; /* ============================================================================================== */ /* Internal helpers */ /* ============================================================================================== */ -static ZydisStatus ZydisEmitByte(ZydisInstructionEncoder* encoder, uint8_t byte) +static ZydisStatus ZydisEmitByte(ZydisEncoderContext* ctx, uint8_t byte) { - if (encoder->writeOffs + 1 >= encoder->bufferLen) + if (ctx->writeOffs + 1 >= ctx->bufferLen) { return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE; } - encoder->buffer[encoder->writeOffs++] = byte; + ctx->buffer[ctx->writeOffs++] = byte; return ZYDIS_STATUS_SUCCESS; } -static ZydisStatus ZydisEmitImm(ZydisInstructionEncoder* encoder, uint64_t imm, int bits) +static ZydisStatus ZydisEmitImm(ZydisEncoderContext* ctx, uint64_t imm, int bits) { ZYDIS_ASSERT(bits == 8 || bits == 16 || bits == 32 || bits == 64); - if (encoder->writeOffs + bits / 8 >= encoder->bufferLen) + size_t newWriteOffs = ctx->writeOffs + bits / 8; + if (newWriteOffs >= ctx->bufferLen || + newWriteOffs > ZYDIS_MAX_INSTRUCTION_LENGTH) { return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE; } @@ -109,117 +117,118 @@ static ZydisStatus ZydisEmitImm(ZydisInstructionEncoder* encoder, uint64_t imm, // TODO: bswap on big-endian switch (bits) { - case 8: *(uint8_t* )&encoder->buffer[encoder->writeOffs++] = (uint8_t )imm; break; - case 16: *(uint16_t*)&encoder->buffer[encoder->writeOffs++] = (uint16_t)imm; break; - case 32: *(uint32_t*)&encoder->buffer[encoder->writeOffs++] = (uint32_t)imm; break; - case 64: *(uint64_t*)&encoder->buffer[encoder->writeOffs++] = (uint64_t)imm; break; + case 8: *(uint8_t* )&ctx->buffer[ctx->writeOffs] = (uint8_t )imm; break; + case 16: *(uint16_t*)&ctx->buffer[ctx->writeOffs] = (uint16_t)imm; break; + case 32: *(uint32_t*)&ctx->buffer[ctx->writeOffs] = (uint32_t)imm; break; + case 64: *(uint64_t*)&ctx->buffer[ctx->writeOffs] = (uint64_t)imm; break; default: ZYDIS_UNREACHABLE; } + ctx->writeOffs = newWriteOffs; return ZYDIS_STATUS_SUCCESS; } -static ZydisStatus ZydisEncodeLegacyPrefixes(ZydisInstructionEncoder* encoder) +static ZydisStatus ZydisEmitLegacyPrefixes(ZydisEncoderContext* ctx) { - ZYDIS_ASSERT(encoder); - ZydisInstructionAttributes attribs = encoder->info->attributes; + ZYDIS_ASSERT(ctx); + ZydisInstructionAttributes attribs = ctx->info->attributes; if (attribs & ZYDIS_ATTRIB_HAS_LOCK) { - ZYDIS_CHECK(ZydisEmitByte(encoder, 0xF0)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0xF0)); } if (attribs & (ZYDIS_ATTRIB_HAS_REP | ZYDIS_ATTRIB_HAS_REPE | ZYDIS_ATTRIB_HAS_XRELEASE)) { - ZYDIS_CHECK(ZydisEmitByte(encoder, 0xF3)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0xF3)); } if (attribs & (ZYDIS_ATTRIB_HAS_REPNE | ZYDIS_ATTRIB_HAS_BOUND | ZYDIS_ATTRIB_HAS_XACQUIRE)) { - ZYDIS_CHECK(ZydisEmitByte(encoder, 0xF2)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0xF2)); } if (attribs & (ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN | ZYDIS_ATTRIB_HAS_SEGMENT_CS)) { - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x2E)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x2E)); } if (attribs & (ZYDIS_ATTRIB_HAS_BRANCH_TAKEN | ZYDIS_ATTRIB_HAS_SEGMENT_DS)) { - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x3E)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x3E)); } if (attribs & ZYDIS_ATTRIB_HAS_SEGMENT_SS) { - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x36)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x36)); } if (attribs & ZYDIS_ATTRIB_HAS_SEGMENT_ES) { - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x26)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x26)); } if (attribs & ZYDIS_ATTRIB_HAS_SEGMENT_FS) { - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x64)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x64)); } if (attribs & ZYDIS_ATTRIB_HAS_SEGMENT_GS) { - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x65)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x65)); } if (attribs & ZYDIS_ATTRIB_HAS_OPERANDSIZE) { - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x66)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x66)); } if (attribs & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) { - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x67)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x67)); } return ZYDIS_STATUS_SUCCESS; } -static ZydisStatus ZydisEncodeREX(ZydisInstructionEncoder *encoder) +static ZydisStatus ZydisEmitREX(ZydisEncoderContext* ctx) { - ZYDIS_ASSERT(encoder); + ZYDIS_ASSERT(ctx); ZYDIS_CHECK(ZydisEmitByte( - encoder, + ctx, 0x40 | - (encoder->info->details.rex.W & 0x01) << 3 | - (encoder->info->details.rex.R & 0x01) << 2 | - (encoder->info->details.rex.X & 0x01) << 1 | - (encoder->info->details.rex.B & 0x01) << 0 + (ctx->info->details.rex.W & 0x01) << 3 | + (ctx->info->details.rex.R & 0x01) << 2 | + (ctx->info->details.rex.X & 0x01) << 1 | + (ctx->info->details.rex.B & 0x01) << 0 )); return ZYDIS_STATUS_SUCCESS; } -static ZydisStatus ZydisEncodeVEX(ZydisInstructionEncoder *encoder) +static ZydisStatus ZydisEmitVEX(ZydisEncoderContext* ctx) { - ZYDIS_ASSERT(encoder); + ZYDIS_ASSERT(ctx); // Write opcode. - uint8_t opcode = encoder->info->details.vex.data[0]; - ZYDIS_CHECK(ZydisEmitByte(encoder, opcode)); + uint8_t opcode = ctx->info->details.vex.data[0]; + ZYDIS_CHECK(ZydisEmitByte(ctx, opcode)); // Write prefix' "operands". switch (opcode) { case 0xC4: ZYDIS_CHECK(ZydisEmitByte( - encoder, - (encoder->info->details.vex.R & 0x01) << 7 | - (encoder->info->details.vex.X & 0x01) << 6 | - (encoder->info->details.vex.B & 0x01) << 5 | - (encoder->info->details.vex.m_mmmm & 0x1F) << 0 + ctx, + (ctx->info->details.vex.R & 0x01) << 7 | + (ctx->info->details.vex.X & 0x01) << 6 | + (ctx->info->details.vex.B & 0x01) << 5 | + (ctx->info->details.vex.m_mmmm & 0x1F) << 0 )); ZYDIS_CHECK(ZydisEmitByte( - encoder, - (encoder->info->details.vex.W & 0x01) << 7 | - (encoder->info->details.vex.vvvv & 0x0F) << 3 | - (encoder->info->details.vex.L & 0x01) << 2 | - (encoder->info->details.vex.pp & 0x03) << 0 + ctx, + (ctx->info->details.vex.W & 0x01) << 7 | + (ctx->info->details.vex.vvvv & 0x0F) << 3 | + (ctx->info->details.vex.L & 0x01) << 2 | + (ctx->info->details.vex.pp & 0x03) << 0 )); break; case 0xC5: ZYDIS_CHECK(ZydisEmitByte( - encoder, - (encoder->info->details.vex.R & 0x01) << 7 | - (encoder->info->details.vex.vvvv & 0x0F) << 3 | - (encoder->info->details.vex.L & 0x01) << 2 | - (encoder->info->details.vex.pp & 0x03) << 0 + ctx, + (ctx->info->details.vex.R & 0x01) << 7 | + (ctx->info->details.vex.vvvv & 0x0F) << 3 | + (ctx->info->details.vex.L & 0x01) << 2 | + (ctx->info->details.vex.pp & 0x03) << 0 )); break; default: @@ -229,103 +238,99 @@ static ZydisStatus ZydisEncodeVEX(ZydisInstructionEncoder *encoder) return ZYDIS_STATUS_SUCCESS; } -static ZydisStatus ZydisEncodeEVEX(ZydisInstructionEncoder *encoder) +static ZydisStatus ZydisEmitEVEX(ZydisEncoderContext* ctx) { - ZYDIS_ASSERT(encoder); - - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x62)); + ZYDIS_ASSERT(ctx); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x62)); ZYDIS_CHECK(ZydisEmitByte( - encoder, - (encoder->info->details.evex.R & 0x01) << 7 | - (encoder->info->details.evex.X & 0x01) << 6 | - (encoder->info->details.evex.B & 0x01) << 5 | - (encoder->info->details.evex.R2 & 0x01) << 4 | - (encoder->info->details.evex.mm & 0x03) << 0 + ctx, + (ctx->info->details.evex.R & 0x01) << 7 | + (ctx->info->details.evex.X & 0x01) << 6 | + (ctx->info->details.evex.B & 0x01) << 5 | + (ctx->info->details.evex.R2 & 0x01) << 4 | + (ctx->info->details.evex.mm & 0x03) << 0 )); ZYDIS_CHECK(ZydisEmitByte( - encoder, - (encoder->info->details.evex.W & 0x01) << 7 | - (encoder->info->details.evex.vvvv & 0x0F) << 3 | - (encoder->info->details.evex.pp & 0x03) << 0 + ctx, + (ctx->info->details.evex.W & 0x01) << 7 | + (ctx->info->details.evex.vvvv & 0x0F) << 3 | + (ctx->info->details.evex.pp & 0x03) << 0 )); ZYDIS_CHECK(ZydisEmitByte( - encoder, - (encoder->info->details.evex.z & 0x01) << 7 | - (encoder->info->details.evex.L2 & 0x01) << 6 | - (encoder->info->details.evex.L & 0x01) << 5 | - (encoder->info->details.evex.b & 0x01) << 4 | - (encoder->info->details.evex.V2 & 0x01) << 3 | - (encoder->info->details.evex.aaa & 0x07) << 0 - )); - - return ZYDIS_STATUS_SUCCESS; -} - -static ZydisStatus ZydisEncodeXOP(ZydisInstructionEncoder* encoder) -{ - ZYDIS_ASSERT(encoder); - - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x8F)); - ZYDIS_CHECK(ZydisEmitByte( - encoder, - (encoder->info->details.xop.R & 0x01) << 7 | - (encoder->info->details.xop.X & 0x01) << 6 | - (encoder->info->details.xop.B & 0x01) << 5 | - (encoder->info->details.xop.m_mmmm & 0x1F) << 0 - )); - ZYDIS_CHECK(ZydisEmitByte( - encoder, - (encoder->info->details.xop.W & 0x01) << 7 | - (encoder->info->details.xop.vvvv & 0x0F) << 3 | - (encoder->info->details.xop.L & 0x01) << 2 | - (encoder->info->details.xop.pp & 0x03) << 0 - )); - - return ZYDIS_STATUS_SUCCESS; -} - -static ZydisStatus ZydisEncodeModRM(ZydisInstructionEncoder* encoder) -{ - ZYDIS_ASSERT(encoder); - ZYDIS_CHECK(ZydisEmitByte( - encoder, - (encoder->info->details.modrm.mod & 0x03) << 6 | - (encoder->info->details.modrm.reg & 0x07) << 3 | - (encoder->info->details.modrm.rm & 0x07) << 0 + ctx, + (ctx->info->details.evex.z & 0x01) << 7 | + (ctx->info->details.evex.L2 & 0x01) << 6 | + (ctx->info->details.evex.L & 0x01) << 5 | + (ctx->info->details.evex.b & 0x01) << 4 | + (ctx->info->details.evex.V2 & 0x01) << 3 | + (ctx->info->details.evex.aaa & 0x07) << 0 )); return ZYDIS_STATUS_SUCCESS; } -static ZydisStatus ZydisEncodeSIB(ZydisInstructionEncoder* encoder) +static ZydisStatus ZydisEmitXOP(ZydisEncoderContext* ctx) { - ZYDIS_ASSERT(encoder); + ZYDIS_ASSERT(ctx); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x8F)); ZYDIS_CHECK(ZydisEmitByte( - encoder, - (encoder->info->details.sib.scale & 0x03) << 6 | - (encoder->info->details.sib.index & 0x07) << 3 | - (encoder->info->details.sib.base & 0x07) << 0 + ctx, + (ctx->info->details.xop.R & 0x01) << 7 | + (ctx->info->details.xop.X & 0x01) << 6 | + (ctx->info->details.xop.B & 0x01) << 5 | + (ctx->info->details.xop.m_mmmm & 0x1F) << 0 + )); + ZYDIS_CHECK(ZydisEmitByte( + ctx, + (ctx->info->details.xop.W & 0x01) << 7 | + (ctx->info->details.xop.vvvv & 0x0F) << 3 | + (ctx->info->details.xop.L & 0x01) << 2 | + (ctx->info->details.xop.pp & 0x03) << 0 )); return ZYDIS_STATUS_SUCCESS; } -static ZydisStatus ZydisEncodeOpcode(ZydisInstructionEncoder* encoder) +static ZydisStatus ZydisEmitModRM(ZydisEncoderContext* ctx) { - ZYDIS_ASSERT(encoder); - ZYDIS_ASSERT(encoder->matchingEntry); + ZYDIS_ASSERT(ctx); + ZYDIS_CHECK(ZydisEmitByte( + ctx, + (ctx->info->details.modrm.mod & 0x03) << 6 | + (ctx->info->details.modrm.reg & 0x07) << 3 | + (ctx->info->details.modrm.rm & 0x07) << 0 + )); + return ZYDIS_STATUS_SUCCESS; +} + +static ZydisStatus ZydisEmitSIB(ZydisEncoderContext* ctx) +{ + ZYDIS_ASSERT(ctx); + ZYDIS_CHECK(ZydisEmitByte( + ctx, + (ctx->info->details.sib.scale & 0x03) << 6 | + (ctx->info->details.sib.index & 0x07) << 3 | + (ctx->info->details.sib.base & 0x07) << 0 + )); + return ZYDIS_STATUS_SUCCESS; +} + +static ZydisStatus ZydisEmitOpcode(ZydisEncoderContext* ctx) +{ + ZYDIS_ASSERT(ctx); + ZYDIS_ASSERT(ctx->matchingEntry); // Put opcode extension prefix(es), if required. - switch (encoder->matchingEntry->map) + switch (ctx->matchingEntry->map) { case ZYDIS_OPCODE_MAP_0F: - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x0F)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x0F)); break; case ZYDIS_OPCODE_MAP_0F38: - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x0F)); - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x38)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x0F)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x38)); break; case ZYDIS_OPCODE_MAP_0F3A: - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x0F)); - ZYDIS_CHECK(ZydisEmitByte(encoder, 0x3A)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x0F)); + ZYDIS_CHECK(ZydisEmitByte(ctx, 0x3A)); break; case ZYDIS_OPCODE_MAP_XOP8: case ZYDIS_OPCODE_MAP_XOP9: @@ -339,29 +344,75 @@ static ZydisStatus ZydisEncodeOpcode(ZydisInstructionEncoder* encoder) } // Emit actual opcode. - ZYDIS_CHECK(ZydisEmitByte(encoder, encoder->matchingEntry->opcode)); + ZYDIS_CHECK(ZydisEmitByte(ctx, ctx->info->opcode)); return ZYDIS_STATUS_SUCCESS; } -static ZydisStatus ZydisPrepareOperand(ZydisInstructionEncoder* encoder, - ZydisOperandInfo* operand) +static ZydisStatus ZydisSimplifyOperandType(ZydisSemanticOperandType semType, + ZydisOperandType* simpleType) { - switch (operand->encoding) + // TODO: Better mapping, this is just for testing. + switch (semType) + { + case ZYDIS_SEM_OPERAND_TYPE_GPR8: + case ZYDIS_SEM_OPERAND_TYPE_GPR16: + case ZYDIS_SEM_OPERAND_TYPE_GPR32: + case ZYDIS_SEM_OPERAND_TYPE_GPR64: + *simpleType = ZYDIS_OPERAND_TYPE_REGISTER; + break; + case ZYDIS_SEM_OPERAND_TYPE_IMM8: + case ZYDIS_SEM_OPERAND_TYPE_IMM16: + case ZYDIS_SEM_OPERAND_TYPE_IMM32: + case ZYDIS_SEM_OPERAND_TYPE_IMM64: + case ZYDIS_SEM_OPERAND_TYPE_REL8: + case ZYDIS_SEM_OPERAND_TYPE_REL16: + case ZYDIS_SEM_OPERAND_TYPE_REL32: + case ZYDIS_SEM_OPERAND_TYPE_REL64: + *simpleType = ZYDIS_OPERAND_TYPE_IMMEDIATE; + break; + case ZYDIS_SEM_OPERAND_TYPE_MEM8: + case ZYDIS_SEM_OPERAND_TYPE_MEM16: + case ZYDIS_SEM_OPERAND_TYPE_MEM32: + case ZYDIS_SEM_OPERAND_TYPE_MEM64: + *simpleType = ZYDIS_OPERAND_TYPE_MEMORY; + break; + default: + return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; + } + + return ZYDIS_STATUS_SUCCESS; +} + +static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx, + ZydisRegister reg, uint8_t useRM) +{ + uint8_t* regRM = useRM ? &ctx->info->details.modrm.rm + : &ctx->info->details.modrm.reg; + uint8_t* rexRB = useRM ? &ctx->info->details.rex.B + : &ctx->info->details.rex.R; + + int16_t regID = ZydisRegisterGetId(reg); + if (regID == -1) return ZYDIS_STATUS_INVALID_PARAMETER; + + *regRM = regID & 0x07; + *rexRB = (regID & 0x08) >> 3; + ctx->info->attributes |= ZYDIS_ATTRIB_HAS_MODRM; + if (*rexRB) ctx->info->attributes |= ZYDIS_ATTRIB_HAS_REX; + + return ZYDIS_STATUS_SUCCESS; +} + +static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, + ZydisOperandInfo* operand, const ZydisEncoderTableOperand* tableEntry) +{ + switch (tableEntry->encoding) { case ZYDIS_OPERAND_ENCODING_NONE: break; // Nothing to do. case ZYDIS_OPERAND_ENCODING_REG: { - int16_t reg = ZydisRegisterGetId(operand->reg); - if (reg == -1) return ZYDIS_STATUS_INVALID_PARAMETER; - encoder->info->details.modrm.reg = reg & 0x07; - encoder->info->details.rex.R = (reg & 0x08) >> 3; - encoder->info->attributes |= ZYDIS_ATTRIB_HAS_MODRM; - if (encoder->info->details.rex.R) - { - encoder->info->attributes |= ZYDIS_ATTRIB_HAS_REX; - } + ZYDIS_CHECK(ZydisPrepareRegOperand(ctx, operand->reg, ZYDIS_FALSE)); } break; case ZYDIS_OPERAND_ENCODING_RM: case ZYDIS_OPERAND_ENCODING_RM_CD2: @@ -372,68 +423,129 @@ static ZydisStatus ZydisPrepareOperand(ZydisInstructionEncoder* encoder, case ZYDIS_OPERAND_ENCODING_RM_CD64: { // Memory operand? - if (operand->reg == ZYDIS_REGISTER_NONE) + if (operand->type == ZYDIS_OPERAND_TYPE_MEMORY) { - int32_t divisor = 1; - switch (operand->encoding) + // Has base register? + if (operand->mem.base != ZYDIS_REGISTER_NONE) { - case ZYDIS_OPERAND_ENCODING_RM: divisor = 1; break; - case ZYDIS_OPERAND_ENCODING_RM_CD2: divisor = 2; break; - case ZYDIS_OPERAND_ENCODING_RM_CD4: divisor = 4; break; - case ZYDIS_OPERAND_ENCODING_RM_CD8: divisor = 8; break; - case ZYDIS_OPERAND_ENCODING_RM_CD16: divisor = 16; break; - case ZYDIS_OPERAND_ENCODING_RM_CD32: divisor = 32; break; - case ZYDIS_OPERAND_ENCODING_RM_CD64: divisor = 64; break; - default: ZYDIS_UNREACHABLE; + ZYDIS_CHECK(ZydisPrepareRegOperand(ctx, operand->reg, ZYDIS_TRUE)); + } + else + { + // TODO: Does rm=0x05 work with sbyte disps? + ctx->info->details.modrm.rm = 0x05 /* memory */; } - // Has compressed disp encoding and is compression possible? - int32_t* sdword = &operand->mem.disp.value.sdword; - encoder->info->details.modrm.mod = 0x02 /* 32 bit disp */; - if (divisor != 1 && - *sdword % divisor == 0 && - *sdword / divisor <= INT8_MAX && - *sdword / divisor >= INT8_MIN) + // SIB byte required? + if (operand->mem.index || operand->mem.scale) { - encoder->info->details.modrm.mod = 0x01 /* 8 bit disp */; - *sdword /= divisor; + switch (operand->mem.scale) + { + case 1: ctx->info->details.sib.index = 0x01; break; + case 2: ctx->info->details.sib.index = 0x02; break; + case 4: ctx->info->details.sib.index = 0x03; break; + case 8: ctx->info->details.sib.index = 0x04; break; + default: return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO + } + + // TODO: base, index + + ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SIB; } - // Nope, regular encoding. Does it fit a byte anyway? - else if (*sdword <= INT8_MAX && *sdword >= INT8_MIN) + + // Has displacement? + if (operand->mem.disp.value.sdword) { - encoder->info->details.modrm.mod = 0x01 /* 8 bit disp */; + int32_t divisor = 1; + switch (tableEntry->encoding) + { + case ZYDIS_OPERAND_ENCODING_RM: break; + case ZYDIS_OPERAND_ENCODING_RM_CD2: divisor = 2; break; + case ZYDIS_OPERAND_ENCODING_RM_CD4: divisor = 4; break; + case ZYDIS_OPERAND_ENCODING_RM_CD8: divisor = 8; break; + case ZYDIS_OPERAND_ENCODING_RM_CD16: divisor = 16; break; + case ZYDIS_OPERAND_ENCODING_RM_CD32: divisor = 32; break; + case ZYDIS_OPERAND_ENCODING_RM_CD64: divisor = 64; break; + default: ZYDIS_UNREACHABLE; + } + + // Has compressed disp encoding and is compression possible? + int32_t* sdword = &operand->mem.disp.value.sdword; + if (divisor != 1 && + *sdword % divisor == 0 && + *sdword / divisor <= INT8_MAX && + *sdword / divisor >= INT8_MIN) + { + ctx->dispBitSize = 8; + ctx->info->details.modrm.mod = 0x01 /* 8 bit disp */; + *sdword /= divisor; + } + // Nope, regular encoding. Does it fit a byte anyway? + else if (*sdword <= INT8_MAX && *sdword >= INT8_MIN) + { + ctx->dispBitSize = 8; + ctx->info->details.modrm.mod = 0x01 /* 8 bit disp */; + } + // No compression possible, emit as 32 bit. + else + { + ctx->dispBitSize = 32; + ctx->info->details.modrm.mod = 0x02 /* 32 bit disp */; + } + + ctx->disp = *sdword; + } + // No displacement. + else + { + ctx->info->details.modrm.mod = 0x00 /* no disp */; } } // Nope, register. - else + else if (operand->type == ZYDIS_OPERAND_TYPE_REGISTER) { - int16_t reg = ZydisRegisterGetId(operand->reg); - if (reg == -1) return ZYDIS_STATUS_INVALID_PARAMETER; - encoder->info->details.modrm.reg = reg & 0x07; - encoder->info->details.rex.B = (reg & 0x08) >> 3; - encoder->info->attributes |= ZYDIS_ATTRIB_HAS_MODRM; - if (encoder->info->details.rex.B) - { - encoder->info->attributes |= ZYDIS_ATTRIB_HAS_REX; - } - encoder->info->details.modrm.mod = 0x03; + ZYDIS_CHECK(ZydisPrepareRegOperand(ctx, operand->reg, ZYDIS_TRUE)); + ctx->info->details.modrm.mod = 0x03 /* reg */; } + + ctx->info->attributes |= ZYDIS_ATTRIB_HAS_MODRM; + break; } case ZYDIS_OPERAND_ENCODING_OPCODE: - break; // TODO + { + int16_t reg = ZydisRegisterGetId(operand->reg); + if (reg == -1) return ZYDIS_STATUS_INVALID_PARAMETER; + ctx->info->opcode += reg & 0x0F; + ctx->info->details.rex.R = (reg & 0x08) >> 3; + if (ctx->info->details.rex.B) ctx->info->attributes |= ZYDIS_ATTRIB_HAS_REX; + break; + } case ZYDIS_OPERAND_ENCODING_VVVV: break; // TODO case ZYDIS_OPERAND_ENCODING_AAA: break; // TODO case ZYDIS_OPERAND_ENCODING_IMM8_LO: + { + ctx->immBitSizes[0] = 8; + ctx->imms[0] |= operand->imm.value.ubyte & 0x0F; + break; + } case ZYDIS_OPERAND_ENCODING_IMM8_HI: + { + ctx->immBitSizes[0] = 8; + ctx->imms[0] |= (operand->imm.value.ubyte & 0x0F) << 4; + break; + } case ZYDIS_OPERAND_ENCODING_IMM8: case ZYDIS_OPERAND_ENCODING_IMM16: case ZYDIS_OPERAND_ENCODING_IMM32: case ZYDIS_OPERAND_ENCODING_IMM64: - // Nothing to do here, we put those in a later stage. - // TODO: moffs - break; + { + uint8_t immIdx = ctx->immBitSizes[0] ? 1 : 0; + ctx->immBitSizes[immIdx] = operand->imm.dataSize; + ctx->imms[immIdx] = operand->imm.value.uqword; + break; + } default: ZYDIS_UNREACHABLE; } @@ -441,6 +553,17 @@ static ZydisStatus ZydisPrepareOperand(ZydisInstructionEncoder* encoder, return ZYDIS_STATUS_SUCCESS; } +static ZydisStatus ZydisDeriveEncodingForOp(ZydisOperandDefinition* operand) +{ + switch (operand->type) + { + default: + return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO + } + + return ZYDIS_STATUS_SUCCESS; +} + static ZydisStatus ZydisFindMatchingDef(const ZydisInstructionInfo* info, const ZydisEncoderTableEntry** matchingEntry) { @@ -449,7 +572,7 @@ static ZydisStatus ZydisFindMatchingDef(const ZydisInstructionInfo* info, // Locate entries with matching mnemonic. // TODO: do binary search / hash based lookup instead - for (size_t i = 0; i < sizeof(kEncoderTable) / sizeof(kEncoderTable[0]); ++i) + for (size_t i = 0; i < ZYDIS_ARRAY_SIZE(kEncoderTable); ++i) { const ZydisEncoderTableEntry* curEntry = &kEncoderTable[i]; if (curEntry->mnemonic != info->mnemonic || @@ -468,7 +591,10 @@ static ZydisStatus ZydisFindMatchingDef(const ZydisInstructionInfo* info, { const ZydisEncoderTableOperand* curEncoderOp = &curEntry->operands[k]; const ZydisOperandInfo* curReqOp = &info->operands[k]; - if (curEncoderOp->encoding != curReqOp->encoding) goto continueTopLevel; + if (curReqOp->encoding != curEncoderOp->encoding) goto continueTopLevel; + ZydisOperandType simpleType; + ZYDIS_CHECK(ZydisSimplifyOperandType(curEncoderOp->type, &simpleType)); + if (curReqOp->type != simpleType) goto continueTopLevel; } // Still here? We found our entry! @@ -486,45 +612,60 @@ static ZydisStatus ZydisFindMatchingDef(const ZydisInstructionInfo* info, /* Implementation of public functions */ /* ============================================================================================== */ -ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t bufferLen, +ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t* bufferLen, ZydisInstructionInfo* info) { - if (!info) return ZYDIS_STATUS_INVALID_PARAMETER; - if (!buffer || !bufferLen) return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE; + if (!info || !bufferLen) return ZYDIS_STATUS_INVALID_PARAMETER; + if (!buffer || !*bufferLen) return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE; - ZydisInstructionEncoder encoder; - memset(&encoder, 0, sizeof(encoder)); + ZydisEncoderContext ctx; + memset(&ctx, 0, sizeof(ctx)); memset(&info->details, 0, sizeof(info->details)); - encoder.buffer = (uint8_t*)buffer; - encoder.bufferLen = bufferLen; - encoder.writeOffs = 0; - encoder.info = info; + ctx.buffer = (uint8_t*)buffer; + ctx.bufferLen = *bufferLen; + ctx.writeOffs = 0; + ctx.info = info; + *bufferLen = 0; // Mask out attributes that can't be set explicitly by user. info->attributes &= ZYDIS_USER_ENCODABLE_ATTRIB_MASK; // Search matching instruction, collect information about what needs to be - // encoded, what prefixes are required etc.. - ZYDIS_CHECK(ZydisFindMatchingDef(info, &encoder.matchingEntry)); - info->opcode = encoder.matchingEntry->opcode; - info->attributes |= encoder.matchingEntry->mandatoryAttribs; + // encoded, what prefixes are required, etc. + ZYDIS_CHECK(ZydisFindMatchingDef(info, &ctx.matchingEntry)); + info->opcode = ctx.matchingEntry->opcode; + info->attributes |= ctx.matchingEntry->mandatoryAttribs; // Analyze and prepare operands. - for (size_t i = 0; i < encoder.matchingEntry->operandCount; ++i) + if (info->operandCount > ZYDIS_ARRAY_SIZE(info->operands)) { - ZYDIS_CHECK(ZydisPrepareOperand(&encoder, &info->operands[i])); + // TODO: Better status? + return ZYDIS_STATUS_INVALID_PARAMETER; + } + + for (size_t i = 0; i < ctx.matchingEntry->operandCount; ++i) + { + ZYDIS_CHECK(ZydisPrepareOperand( + &ctx, &info->operands[i], + &ctx.matchingEntry->operands[i] + )); } // Do actual encoding work. - ZYDIS_CHECK(ZydisEncodeLegacyPrefixes(&encoder)); - if (info->attributes & ZYDIS_ATTRIB_HAS_REX ) ZYDIS_CHECK(ZydisEncodeREX (&encoder)); - if (info->attributes & ZYDIS_ATTRIB_HAS_VEX ) ZYDIS_CHECK(ZydisEncodeVEX (&encoder)); - if (info->attributes & ZYDIS_ATTRIB_HAS_EVEX ) ZYDIS_CHECK(ZydisEncodeEVEX (&encoder)); - if (info->attributes & ZYDIS_ATTRIB_HAS_XOP ) ZYDIS_CHECK(ZydisEncodeXOP (&encoder)); - ZYDIS_CHECK(ZydisEncodeOpcode(&encoder)); - if (info->attributes & ZYDIS_ATTRIB_HAS_MODRM) ZYDIS_CHECK(ZydisEncodeModRM(&encoder)); - if (info->attributes & ZYDIS_ATTRIB_HAS_SIB ) ZYDIS_CHECK(ZydisEncodeSIB (&encoder)); + ZYDIS_CHECK(ZydisEmitLegacyPrefixes(&ctx)); + if (info->attributes & ZYDIS_ATTRIB_HAS_REX ) ZYDIS_CHECK(ZydisEmitREX (&ctx)); + if (info->attributes & ZYDIS_ATTRIB_HAS_VEX ) ZYDIS_CHECK(ZydisEmitVEX (&ctx)); + if (info->attributes & ZYDIS_ATTRIB_HAS_EVEX ) ZYDIS_CHECK(ZydisEmitEVEX (&ctx)); + if (info->attributes & ZYDIS_ATTRIB_HAS_XOP ) ZYDIS_CHECK(ZydisEmitXOP (&ctx)); + ZYDIS_CHECK(ZydisEmitOpcode(&ctx)); + if (info->attributes & ZYDIS_ATTRIB_HAS_MODRM) ZYDIS_CHECK(ZydisEmitModRM(&ctx)); + if (info->attributes & ZYDIS_ATTRIB_HAS_SIB ) ZYDIS_CHECK(ZydisEmitSIB (&ctx)); + if (ctx.dispBitSize) ZYDIS_CHECK(ZydisEmitImm(&ctx, ctx.disp, ctx.dispBitSize)); + if (ctx.immBitSizes[0]) ZYDIS_CHECK(ZydisEmitImm(&ctx, ctx.imms[0], ctx.immBitSizes[0])); + if (ctx.immBitSizes[1]) ZYDIS_CHECK(ZydisEmitImm(&ctx, ctx.imms[1], ctx.immBitSizes[1])); + + *bufferLen = ctx.writeOffs; return ZYDIS_STATUS_SUCCESS; } diff --git a/tools/ZydisDisasm.c b/tools/ZydisDisasm.c index 3c22cde..1b22b3b 100644 --- a/tools/ZydisDisasm.c +++ b/tools/ZydisDisasm.c @@ -88,24 +88,30 @@ int main(int argc, char** argv) continue; } - //char printBuffer[256]; - //ZydisFormatterFormatInstruction( - // &formatter, &info, printBuffer, sizeof(printBuffer) - //); - //puts(printBuffer); - readOffs += info.length; + char printBuffer[256]; + ZydisFormatterFormatInstruction( + &formatter, &info, printBuffer, sizeof(printBuffer) + ); + puts(printBuffer); // TODO: Remove // DEBUG CODE START - //uint8_t encBuffer[15]; - //ZydisStatus encStatus = ZydisEncoderEncodeInstruction( - // encBuffer, sizeof(encBuffer), &info - //); - //if (!ZYDIS_SUCCESS(encStatus)) { - // //__asm int 3; - // *(volatile int*)0 = 0; - //} + uint8_t encBuffer[15]; + size_t encBufferSize = sizeof(encBuffer); + ZydisStatus encStatus = ZydisEncoderEncodeInstruction( + encBuffer, &encBufferSize, &info + ); + ZYDIS_ASSERT(ZYDIS_SUCCESS(encStatus)); + for (size_t i = 0; i < encBufferSize; ++i) + { + printf("%02X ", encBuffer[i]); + } + putchar('\n'); + ZYDIS_ASSERT(encBufferSize == info.length); + ZYDIS_ASSERT(!memcmp(encBuffer, readBuf + readOffs, encBufferSize)); // DEBUG CODE END + + readOffs += info.length; } if (readOffs < sizeof(readBuf))