diff --git a/include/Zydis/Encoder.h b/include/Zydis/Encoder.h index f7f5b19..88a4fd3 100644 --- a/include/Zydis/Encoder.h +++ b/include/Zydis/Encoder.h @@ -36,6 +36,31 @@ extern "C" { #endif +/* ============================================================================================== */ +/* Constants */ +/* ============================================================================================== */ + +/** + * @brief Defines a mask of attributes users may excplicitly ask for. + */ +#define ZYDIS_USER_ENCODABLE_ATTRIB_MASK ( \ + ZYDIS_ATTRIB_HAS_LOCK | \ + ZYDIS_ATTRIB_HAS_REP | \ + ZYDIS_ATTRIB_HAS_REPE | \ + ZYDIS_ATTRIB_HAS_REPNE | \ + ZYDIS_ATTRIB_HAS_BOUND | \ + ZYDIS_ATTRIB_HAS_XACQUIRE | \ + ZYDIS_ATTRIB_HAS_XRELEASE | \ + ZYDIS_ATTRIB_HAS_BRANCH_TAKEN | \ + ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN | \ + ZYDIS_ATTRIB_HAS_SEGMENT_CS | \ + ZYDIS_ATTRIB_HAS_SEGMENT_DS | \ + ZYDIS_ATTRIB_HAS_SEGMENT_SS | \ + ZYDIS_ATTRIB_HAS_SEGMENT_ES | \ + ZYDIS_ATTRIB_HAS_SEGMENT_FS | \ + ZYDIS_ATTRIB_HAS_SEGMENT_GS \ +) + /* ============================================================================================== */ /* Exported functions */ /* ============================================================================================== */ @@ -49,7 +74,7 @@ extern "C" { * * @return A zydis status code. */ -ZYDIS_EXPORT ZydisStatus ZydisEncoderEncodeInstruction(const void* buffer, size_t bufferLen, +ZYDIS_EXPORT ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t bufferLen, ZydisInstructionInfo* info); /* ============================================================================================== */ diff --git a/include/Zydis/Status.h b/include/Zydis/Status.h index 705098f..815b6c9 100644 --- a/include/Zydis/Status.h +++ b/include/Zydis/Status.h @@ -111,6 +111,13 @@ enum ZydisStatusCode ZYDIS_STATUS_INVALID_MASK, ZYDIS_STATUS_INVALID_VSIB, + /* ------------------------------------------------------------------------------------------ */ + /* Encoder */ + /* ------------------------------------------------------------------------------------------ */ + + // TODO: + ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION, + /* ------------------------------------------------------------------------------------------ */ /* Formatter */ /* ------------------------------------------------------------------------------------------ */ diff --git a/include/Zydis/Zydis.h b/include/Zydis/Zydis.h index ef9d7dd..a434f40 100644 --- a/include/Zydis/Zydis.h +++ b/include/Zydis/Zydis.h @@ -35,6 +35,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { diff --git a/src/Encoder.c b/src/Encoder.c index c1603dd..8c1a099 100644 --- a/src/Encoder.c +++ b/src/Encoder.c @@ -26,10 +26,51 @@ #include +#include +#include + /* ============================================================================================== */ /* Internal context */ /* ============================================================================================== */ +typedef struct ZydisEncoderTableOperand_ +{ + ZydisOperandEncoding encoding; +} ZydisEncoderTableOperand; + +typedef uint8_t ZydisModRMMod; + +enum ZydisModRMMods +{ + ZYDIS_MODRM_MOD_NONE, + ZYDIS_MODRM_MOD_REGISTER, + ZYDIS_MODRM_MOD_MEMORY, +}; + +typedef uint8_t ZydisModeConstraint; + +enum ZydisModeConstraints +{ + ZYDIS_MODE_CONSTR_NONE, + ZYDIS_MODE_CONSTR_EXCLUDE64, + ZYDIS_MODE_CONSTR_REQUIRE64, +}; + +typedef struct ZydisEncoderTableEntry_ +{ + uint16_t mnemonic; + uint8_t opcode; + ZydisInstructionEncoding encoding; + uint8_t operandCount; + ZydisEncoderTableOperand operands[5]; + ZydisOpcodeMap map; + ZydisInstructionAttributes mandatoryAttribs; + ZydisModRMMod modRmMod; + ZydisModeConstraint modeConstraint; +} ZydisEncoderTableEntry; + +#include + /** * @brief The encoder context struct. */ @@ -39,6 +80,7 @@ typedef struct ZydisInstructionEncoder_ size_t bufferLen; size_t writeOffs; ZydisInstructionInfo* info; + const ZydisEncoderTableEntry* matchingEntry; } ZydisInstructionEncoder; /* ============================================================================================== */ @@ -56,10 +98,31 @@ static ZydisStatus ZydisEmitByte(ZydisInstructionEncoder* encoder, uint8_t byte) return ZYDIS_STATUS_SUCCESS; } +static ZydisStatus ZydisEmitImm(ZydisInstructionEncoder* encoder, uint64_t imm, int bits) +{ + ZYDIS_ASSERT(bits == 8 || bits == 16 || bits == 32 || bits == 64); + if (encoder->writeOffs + bits / 8 >= encoder->bufferLen) + { + return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE; + } + + // 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; + default: ZYDIS_UNREACHABLE; + } + + return ZYDIS_STATUS_SUCCESS; +} + static ZydisStatus ZydisEncodeLegacyPrefixes(ZydisInstructionEncoder* encoder) { ZYDIS_ASSERT(encoder); - uint64_t attribs = encoder->info->attributes; + ZydisInstructionAttributes attribs = encoder->info->attributes; if (attribs & ZYDIS_ATTRIB_HAS_LOCK) { @@ -173,11 +236,11 @@ static ZydisStatus ZydisEncodeEVEX(ZydisInstructionEncoder *encoder) ZYDIS_CHECK(ZydisEmitByte(encoder, 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 + (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 )); ZYDIS_CHECK(ZydisEmitByte( encoder, @@ -198,7 +261,7 @@ static ZydisStatus ZydisEncodeEVEX(ZydisInstructionEncoder *encoder) return ZYDIS_STATUS_SUCCESS; } -static ZydisStatus ZydisEncodeXOP(ZydisInstructionEncoder *encoder) +static ZydisStatus ZydisEncodeXOP(ZydisInstructionEncoder* encoder) { ZYDIS_ASSERT(encoder); @@ -221,6 +284,204 @@ static ZydisStatus ZydisEncodeXOP(ZydisInstructionEncoder *encoder) 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 + )); + return ZYDIS_STATUS_SUCCESS; +} + +static ZydisStatus ZydisEncodeSIB(ZydisInstructionEncoder* encoder) +{ + ZYDIS_ASSERT(encoder); + 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 + )); + return ZYDIS_STATUS_SUCCESS; +} + +static ZydisStatus ZydisEncodeOpcode(ZydisInstructionEncoder* encoder) +{ + ZYDIS_ASSERT(encoder); + ZYDIS_ASSERT(encoder->matchingEntry); + + // Put opcode extension prefix(es), if required. + switch (encoder->matchingEntry->map) + { + case ZYDIS_OPCODE_MAP_0F: + ZYDIS_CHECK(ZydisEmitByte(encoder, 0x0F)); + break; + case ZYDIS_OPCODE_MAP_0F38: + ZYDIS_CHECK(ZydisEmitByte(encoder, 0x0F)); + ZYDIS_CHECK(ZydisEmitByte(encoder, 0x38)); + break; + case ZYDIS_OPCODE_MAP_0F3A: + ZYDIS_CHECK(ZydisEmitByte(encoder, 0x0F)); + ZYDIS_CHECK(ZydisEmitByte(encoder, 0x3A)); + break; + case ZYDIS_OPCODE_MAP_XOP8: + case ZYDIS_OPCODE_MAP_XOP9: + case ZYDIS_OPCODE_MAP_XOPA: + return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO + break; + case ZYDIS_OPCODE_MAP_DEFAULT: + break; // Nothing to do. + default: + ZYDIS_UNREACHABLE; + } + + // Emit actual opcode. + ZYDIS_CHECK(ZydisEmitByte(encoder, encoder->matchingEntry->opcode)); + + return ZYDIS_STATUS_SUCCESS; +} + +static ZydisStatus ZydisPrepareOperand(ZydisInstructionEncoder* encoder, + ZydisOperandInfo* operand) +{ + switch (operand->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; + } + } break; + case ZYDIS_OPERAND_ENCODING_RM: + case ZYDIS_OPERAND_ENCODING_RM_CD2: + case ZYDIS_OPERAND_ENCODING_RM_CD4: + case ZYDIS_OPERAND_ENCODING_RM_CD8: + case ZYDIS_OPERAND_ENCODING_RM_CD16: + case ZYDIS_OPERAND_ENCODING_RM_CD32: + case ZYDIS_OPERAND_ENCODING_RM_CD64: + { + // Memory operand? + if (operand->reg == ZYDIS_REGISTER_NONE) + { + int32_t divisor = 1; + switch (operand->encoding) + { + 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; + } + + // 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) + { + encoder->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) + { + encoder->info->details.modrm.mod = 0x01 /* 8 bit disp */; + } + } + // Nope, register. + else + { + 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; + } + } + case ZYDIS_OPERAND_ENCODING_OPCODE: + break; // TODO + case ZYDIS_OPERAND_ENCODING_VVVV: + break; // TODO + case ZYDIS_OPERAND_ENCODING_AAA: + break; // TODO + case ZYDIS_OPERAND_ENCODING_IMM8_LO: + case ZYDIS_OPERAND_ENCODING_IMM8_HI: + 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; + default: + ZYDIS_UNREACHABLE; + } + + return ZYDIS_STATUS_SUCCESS; +} + +static ZydisStatus ZydisFindMatchingDef(const ZydisInstructionInfo* info, + const ZydisEncoderTableEntry** matchingEntry) +{ + ZYDIS_ASSERT(info); + ZYDIS_ASSERT(matchingEntry); + + // 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) + { + const ZydisEncoderTableEntry* curEntry = &kEncoderTable[i]; + if (curEntry->mnemonic != info->mnemonic || + curEntry->operandCount != info->operandCount || + curEntry->encoding != info->encoding || + (info->mode == ZYDIS_DISASSEMBLER_MODE_64BIT && + curEntry->modeConstraint == ZYDIS_MODE_CONSTR_EXCLUDE64) || + (info->mode != ZYDIS_DISASSEMBLER_MODE_64BIT && + curEntry->modeConstraint == ZYDIS_MODE_CONSTR_REQUIRE64)) + { + continue; + } + + // Check operands. + for (size_t k = 0; k < curEntry->operandCount; ++k) + { + const ZydisEncoderTableOperand* curEncoderOp = &curEntry->operands[k]; + const ZydisOperandInfo* curReqOp = &info->operands[k]; + if (curEncoderOp->encoding != curReqOp->encoding) goto continueTopLevel; + } + + // Still here? We found our entry! + *matchingEntry = curEntry; + return ZYDIS_STATUS_SUCCESS; + + continueTopLevel: + ; + } + + return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; +} + /* ============================================================================================== */ /* Implementation of public functions */ /* ============================================================================================== */ @@ -232,12 +493,37 @@ ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t bufferLen, if (!buffer || !bufferLen) return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE; ZydisInstructionEncoder encoder; + memset(&encoder, 0, sizeof(encoder)); + memset(&info->details, 0, sizeof(info->details)); encoder.buffer = (uint8_t*)buffer; encoder.bufferLen = bufferLen; encoder.writeOffs = 0; encoder.info = info; + // 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; + + // Analyze and prepare operands. + for (size_t i = 0; i < encoder.matchingEntry->operandCount; ++i) + { + ZYDIS_CHECK(ZydisPrepareOperand(&encoder, &info->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)); return ZYDIS_STATUS_SUCCESS; } diff --git a/tools/ZydisDisasm.c b/tools/ZydisDisasm.c index c5d40bc..3c22cde 100644 --- a/tools/ZydisDisasm.c +++ b/tools/ZydisDisasm.c @@ -1,4 +1,4 @@ -/*************************************************************************************************** +/*************************************************************************************************** Zyan Disassembler Engine (Zydis) @@ -88,10 +88,24 @@ int main(int argc, char** argv) continue; } - char printBuffer[256]; - ZydisFormatterFormatInstruction(&formatter, &info, printBuffer, sizeof(printBuffer)); - puts(printBuffer); + //char printBuffer[256]; + //ZydisFormatterFormatInstruction( + // &formatter, &info, printBuffer, sizeof(printBuffer) + //); + //puts(printBuffer); readOffs += info.length; + + // 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; + //} + // DEBUG CODE END } if (readOffs < sizeof(readBuf))