diff --git a/CMakeLists.txt b/CMakeLists.txt index 082b459..5e6204b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,21 +6,21 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) option(ZYDIS_BUILD_SHARED_LIBS "Build shared libraries rather than static ones" - FALSE) + OFF) option(ZYDIS_FORCE_SHARED_CRT - "Forces shared linkage against the CRT even when building a static library" - FALSE) + "Forces shared linkage against the CRT even when building a static library. MSVC only." + OFF) option(ZYDIS_FEATURE_IMPLICITLY_USED_REGISTERS "Include information about implicitly used registers" - TRUE) + OFF) option(ZYDIS_FEATURE_AFFECTED_FLAGS "Include information about affected flags" - TRUE) + OFF) option(ZYDIS_FEATURE_CPUID "Include information about CPUID feature-flags" - FALSE) -option(ZYDIS_BUILD_EXAMPLES "Build examples" TRUE) -option(ZYDIS_BUILD_TOOLS "Build tools" TRUE) + OFF) +option(ZYDIS_BUILD_EXAMPLES "Build examples" ON) +option(ZYDIS_BUILD_TOOLS "Build tools" ON) if (NOT CONFIGURED_ONCE) if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR @@ -49,7 +49,7 @@ endif () # Library set(headers "include/Zydis/Decoder.h" - "include/Zydis/Encoder.h" + #"include/Zydis/Encoder.h" "include/Zydis/Defines.h" "include/Zydis/Formatter.h" "include/Zydis/InstructionInfo.h" @@ -62,7 +62,7 @@ set(headers "include/Zydis/Internal/InstructionTable.h") set(sources "src/Decoder.c" - "src/Encoder.c" + #"src/Encoder.c" "src/Formatter.c" "src/InstructionTable.c" "src/Mnemonic.c" diff --git a/assets/InstructionEditor/Forms/formMain.pas b/assets/InstructionEditor/Forms/formMain.pas index afbd937..7b729f1 100644 --- a/assets/InstructionEditor/Forms/formMain.pas +++ b/assets/InstructionEditor/Forms/formMain.pas @@ -931,7 +931,7 @@ begin begin O := NodeData^.Definition.Operands.Operands[I]; S := S + IntToStr(Integer(O.OperandType)) + ',' + IntToStr(Integer(O.Encoding)) + ',' + - IntToStr(Integer(O.AccessMode)) + ','; + IntToStr(Integer(O.Action)) + ','; end; Clipboard.AsText := S; end; @@ -959,7 +959,7 @@ begin O := NodeData^.Definition.Operands.Operands[J]; O.OperandType := TOperandType(StrToInt(A[I])); O.Encoding := TOperandEncoding(StrToInt(A[I + 1])); - O.AccessMode := TOperandAccessMode(StrToInt(A[I + 2])); + O.Action := TOperandAction(StrToInt(A[I + 2])); Inc(I, 3); Inc(J); end; diff --git a/include/Zydis/Zydis.h b/include/Zydis/Zydis.h index a434f40..5afc7a4 100644 --- a/include/Zydis/Zydis.h +++ b/include/Zydis/Zydis.h @@ -36,6 +36,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { diff --git a/src/Decoder.c b/src/Decoder.c index 40fecf7..726085f 100644 --- a/src/Decoder.c +++ b/src/Decoder.c @@ -524,7 +524,7 @@ static ZydisStatus ZydisDecodeOperandImmediate(ZydisInstructionDecoder* decoder, case 32: { uint32_t data[4] = { 0, 0, 0, 0 }; - for (int i = sizeof(data) / sizeof(data[0]); i > 0; --i) + for (int i = ZYDIS_ARRAY_SIZE(data); i > 0; --i) { ZYDIS_CHECK(ZydisInputNext(decoder, info, (uint8_t*)&data[i - 1])); } @@ -1255,6 +1255,7 @@ static ZydisStatus ZydisDecodeOperand(ZydisInstructionDecoder* decoder, ZydisIns case ZYDIS_SEM_OPERAND_TYPE_REL8: info->attributes |= ZYDIS_ATTRIB_IS_RELATIVE; operand->imm.isRelative = ZYDIS_TRUE; + // Intentional fallthrough. case ZYDIS_SEM_OPERAND_TYPE_IMM8: operand->size = 8; operand->imm.isSigned = ZYDIS_TRUE; @@ -1266,6 +1267,7 @@ static ZydisStatus ZydisDecodeOperand(ZydisInstructionDecoder* decoder, ZydisIns case ZYDIS_SEM_OPERAND_TYPE_REL16: info->attributes |= ZYDIS_ATTRIB_IS_RELATIVE; operand->imm.isRelative = ZYDIS_TRUE; + // Intentional fallthrough. case ZYDIS_SEM_OPERAND_TYPE_IMM16: operand->size = 16; operand->imm.isSigned = ZYDIS_TRUE; @@ -1280,6 +1282,7 @@ static ZydisStatus ZydisDecodeOperand(ZydisInstructionDecoder* decoder, ZydisIns case ZYDIS_SEM_OPERAND_TYPE_REL64: info->attributes |= ZYDIS_ATTRIB_IS_RELATIVE; operand->imm.isRelative = ZYDIS_TRUE; + // Intentional fallthrough. case ZYDIS_SEM_OPERAND_TYPE_IMM64: operand->size = 64; operand->imm.isSigned = ZYDIS_TRUE; @@ -2383,6 +2386,16 @@ ZydisStatus ZydisDecoderDecodeInstructionEx(ZydisInstructionDecoder* decoder, } } + // For relative operands, apply instruction length offset. + for (size_t i = 0; i < info->operandCount; ++i) + { + if (info->operands[i].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && + info->operands[i].imm.isRelative) + { + info->operands[i].imm.value.sqword += info->length; + } + } + // Replace XCHG rAX, rAX with NOP alias if (info->mnemonic == ZYDIS_MNEMONIC_XCHG) { diff --git a/src/Encoder.c b/src/Encoder.c index 36f1dec..782bcc5 100644 --- a/src/Encoder.c +++ b/src/Encoder.c @@ -78,10 +78,11 @@ typedef struct ZydisEncoderTableEntry_ ZydisEncoderTableOperand operands[5]; ZydisOpcodeMap map; ZydisInstructionAttributes attribs; - ZydisModRMMod modRmMod; ZydisModeConstraint modeConstraint; ZydisPrefixBit prefixBits; uint8_t mandatoryPrefix; // 0x00 = None + uint8_t modrmReg; // 0xFF = None + const char* cmt; } ZydisEncoderTableEntry; struct ZydisPrefixAcceptMapping @@ -250,8 +251,8 @@ static ZydisStatus ZydisEmitVEX(ZydisEncoderContext* ctx) ZYDIS_ASSERT(ctx); // Can we use short 2-byte VEX encoding? - if (ctx->info->details.vex.X == 1 && - ctx->info->details.vex.B == 1 && + if (ctx->info->details.vex.X == 0 && + ctx->info->details.vex.B == 0 && ctx->info->details.vex.W == 0 && ctx->info->details.vex.m_mmmm == 1) { @@ -419,6 +420,8 @@ static ZydisStatus ZydisPrepareOpcode(ZydisEncoderContext* ctx) static ZydisStatus ZydisSimplifyOperandType(ZydisSemanticOperandType semType, ZydisOperandType* simpleType) { + ZYDIS_ASSERT(simpleType); + // TODO: Better mapping, this is just for testing. switch (semType) { @@ -454,6 +457,8 @@ static ZydisStatus ZydisSimplifyOperandType(ZydisSemanticOperandType semType, static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx, ZydisRegister reg, char topBitLoc) { + ZYDIS_ASSERT(ctx); + int16_t regID = ZydisRegisterGetId(reg); if (regID == -1) return ZYDIS_STATUS_INVALID_PARAMETER; @@ -467,9 +472,11 @@ static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx, case 'X': ctx->info->details.sib.index = lowerBits; break; default: ZYDIS_UNREACHABLE; } + + // No top bit? Quick exit. + if (!topBit) return ZYDIS_STATUS_SUCCESS; uint8_t* topBitDst = NULL; - switch (ctx->info->encoding) { case ZYDIS_INSTRUCTION_ENCODING_DEFAULT: @@ -527,21 +534,73 @@ static ZydisBool ZydisIsBPReg(ZydisRegister reg) reg == ZYDIS_REGISTER_RBP; } -static ZydisBool ZydisIsStackReg(ZydisRegister reg) +static ZydisBool ZydisIsSPReg(ZydisRegister reg) { return reg == ZYDIS_REGISTER_SPL || reg == ZYDIS_REGISTER_SP || reg == ZYDIS_REGISTER_ESP || - reg == ZYDIS_REGISTER_RSP || - ZydisIsBPReg(reg); + reg == ZYDIS_REGISTER_RSP; +} + +static ZydisBool ZydisIsIPReg(ZydisRegister reg) +{ + return reg == ZYDIS_REGISTER_IP || + reg == ZYDIS_REGISTER_EIP || + reg == ZYDIS_REGISTER_RIP; +} + +static ZydisBool ZydisIsStackReg(ZydisRegister reg) +{ + return ZydisIsSPReg(reg) || ZydisIsBPReg(reg); +} + +static ZydisStatus ZydisPrepareSegmentPrefix(ZydisEncoderContext* ctx, + ZydisRegister segment, ZydisRegister base) +{ + // Segment prefix required? + switch (segment) + { + case ZYDIS_REGISTER_ES: + ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_ES; + break; + case ZYDIS_REGISTER_SS: + if (!ZydisIsStackReg(base)) + { + ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_SS; + } + break; + case ZYDIS_REGISTER_CS: + ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_CS; + break; + case ZYDIS_REGISTER_DS: + if (ZydisIsStackReg(base)) + { + ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_DS; + } + break; + case ZYDIS_REGISTER_FS: + ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_FS; + break; + case ZYDIS_REGISTER_GS: + ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_GS; + break; + default: + return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO: Better status. + } + + return ZYDIS_STATUS_SUCCESS; } static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx, ZydisOperandInfo* operand, const ZydisEncoderTableOperand* tableEntry) { - // TODO: RIP relative addressing + ZYDIS_ASSERT(ctx); + ZYDIS_ASSERT(operand); + ZYDIS_ASSERT(tableEntry); - // Absolute memory access? + ZYDIS_CHECK(ZydisPrepareSegmentPrefix(ctx, operand->mem.segment, operand->mem.base)); + + // Absolute memory access? Special case. if (operand->mem.base == ZYDIS_REGISTER_NONE) { ctx->disp = operand->mem.disp.value.sdword; @@ -567,6 +626,34 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx, return ZYDIS_STATUS_SUCCESS; } + // rIP relative addressing? Special case. + if (ZydisIsIPReg(operand->mem.base)) + { + // rIP addressing is only available since AMD64. + if (ctx->info->mode != ZYDIS_DISASSEMBLER_MODE_64BIT) + { + return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO + } + + // Only available with either EIP or RIP, not with IP. + if (operand->mem.base == ZYDIS_REGISTER_IP) + { + return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO + } + + ctx->disp = operand->mem.disp.value.sdword; + ctx->dispBitSize = 32; + ctx->info->details.modrm.mod = 0x00; + ctx->info->details.modrm.rm = 0x05 /* RIP relative mem */; + + if (operand->mem.base == ZYDIS_REGISTER_EIP) + { + ctx->info->attributes |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE; + } + + return ZYDIS_STATUS_SUCCESS; + } + // Process base register. ZYDIS_CHECK(ZydisPrepareRegOperand(ctx, operand->mem.base, 'B')); @@ -612,49 +699,13 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx, return ZYDIS_STATUS_INVALID_PARAMETER; // TODO } - // Segment prefix required? - switch (operand->mem.segment) + // SIB byte required? rSP can only be encoded with SIB. + if (operand->mem.index || operand->mem.scale || ZydisIsSPReg(operand->mem.base)) { - case ZYDIS_REGISTER_ES: - ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_ES; - break; - case ZYDIS_REGISTER_SS: - ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_SS; - break; - case ZYDIS_REGISTER_CS: - if (!ZydisIsStackReg(operand->mem.base)) - { - ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_CS; - } - break; - case ZYDIS_REGISTER_DS: - if (ZydisIsStackReg(operand->mem.base)) - { - ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_DS; - } - break; - case ZYDIS_REGISTER_FS: - ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_FS; - break; - case ZYDIS_REGISTER_GS: - ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_GS; - break; - default: - return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO: Better status. - } - - // SIB byte required? - if (operand->mem.index || operand->mem.scale) - { - // Base and index register must be of same register class, verify. - if (ZydisRegisterGetClass(operand->mem.index) != baseRegClass) - { - return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO - } - // Translate scale to SIB format. switch (operand->mem.scale) { + case 0: // We take 0 (uninitialized, 0 from memset) as * 1. case 1: ctx->info->details.sib.scale = 0x00; break; case 2: ctx->info->details.sib.scale = 0x01; break; case 4: ctx->info->details.sib.scale = 0x02; break; @@ -662,11 +713,25 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx, default: return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO } - // Base & index register. + // Move base register info to SIB. ctx->info->details.sib.base = ctx->info->details.modrm.rm; - ZYDIS_CHECK(ZydisPrepareRegOperand(ctx, operand->mem.index, 'X')); - ctx->info->details.modrm.rm = 0x04 /* SIB */; + + // Process index register. + if (operand->mem.index != ZYDIS_REGISTER_NONE) + { + // Base and index register must be of same register class, verify. + if (ZydisRegisterGetClass(operand->mem.index) != baseRegClass) + { + return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO + } + ZYDIS_CHECK(ZydisPrepareRegOperand(ctx, operand->mem.index, 'X')); + } + else + { + ctx->info->details.sib.index = 0x04 /* no index */; + } + ctx->info->attributes |= ZYDIS_ATTRIB_HAS_SIB; } @@ -727,12 +792,33 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx, static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, ZydisOperandInfo* operand, const ZydisEncoderTableOperand* tableEntry) { + ZYDIS_ASSERT(ctx); + ZYDIS_ASSERT(operand); + ZYDIS_ASSERT(tableEntry); + switch (tableEntry->encoding) { case ZYDIS_OPERAND_ENCODING_NONE: - break; // Nothing to do. + // For some encodings, we have to switch on the sem op type. + switch (tableEntry->type) + { + case ZYDIS_SEM_OPERAND_TYPE_MOFFS16: + case ZYDIS_SEM_OPERAND_TYPE_MOFFS32: + case ZYDIS_SEM_OPERAND_TYPE_MOFFS64: + ZYDIS_CHECK(ZydisPrepareSegmentPrefix( + ctx, operand->mem.segment, ZYDIS_REGISTER_NONE + )); + ctx->imms[0] = operand->mem.disp.value.sqword; + ctx->immBitSizes[0] = operand->mem.disp.dataSize; + break; + default: + // Hidden operand, nothing to encode. + break; + } + break; case ZYDIS_OPERAND_ENCODING_REG: { + ZYDIS_ASSERT(!ctx->info->details.modrm.reg); ZYDIS_CHECK(ZydisPrepareRegOperand(ctx, operand->reg, 'R')); } break; case ZYDIS_OPERAND_ENCODING_RM: @@ -743,6 +829,12 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, case ZYDIS_OPERAND_ENCODING_RM_CD32: case ZYDIS_OPERAND_ENCODING_RM_CD64: { + ZYDIS_ASSERT(!ctx->info->details.modrm.mod); + ZYDIS_ASSERT(!ctx->info->details.modrm.rm); + ZYDIS_ASSERT(!ctx->info->details.sib.base); + ZYDIS_ASSERT(!ctx->info->details.sib.index); + ZYDIS_ASSERT(!ctx->info->details.sib.scale); + // Memory operand? if (operand->type == ZYDIS_OPERAND_TYPE_MEMORY) { @@ -762,8 +854,8 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, { 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; + ctx->info->opcode += reg & 0x07; + ctx->info->details.rex.B = (reg & 0x08) >> 3; if (ctx->info->details.rex.B) ctx->info->attributes |= ZYDIS_ATTRIB_HAS_REX; break; } @@ -811,6 +903,8 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, static ZydisStatus ZydisPrepareMandatoryPrefixes(ZydisEncoderContext* ctx) { + ZYDIS_ASSERT(ctx); + // Is a prefix mandatory? 0x00 is a sentinel value for `None` in the table. if (ctx->matchingEntry->mandatoryPrefix != 0x00) { @@ -847,6 +941,8 @@ static ZydisStatus ZydisPrepareMandatoryPrefixes(ZydisEncoderContext* ctx) static ZydisStatus ZydisDeriveEncodingForOp(ZydisOperandDefinition* operand) { + ZYDIS_ASSERT(operand); + switch (operand->type) { default: @@ -885,9 +981,10 @@ static ZydisStatus ZydisFindMatchingDef(const ZydisInstructionInfo* info, const ZydisEncoderTableOperand* curEncoderOp = &curEntry->operands[k]; const ZydisOperandInfo* curReqOp = &info->operands[k]; if (curReqOp->encoding != curEncoderOp->encoding) goto continueTopLevel; - ZydisOperandType simpleType; - ZYDIS_CHECK(ZydisSimplifyOperandType(curEncoderOp->type, &simpleType)); - if (curReqOp->type != simpleType) goto continueTopLevel; + //ZydisOperandType simpleType; + //ZYDIS_CHECK(ZydisSimplifyOperandType(curEncoderOp->type, &simpleType)); + //if (curReqOp->type != simpleType) goto continueTopLevel; + if (curReqOp->temp != curEncoderOp->type) goto continueTopLevel; } // Still here? We found our entry! @@ -963,12 +1060,22 @@ ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t* bufferLen, info->details.evex.B = (pb & ZYDIS_PREFBIT_EVEX_B ) ? 1 : 0; info->details.evex.L2 = (pb & ZYDIS_PREFBIT_EVEX_L2) ? 1 : 0; info->details.vex.L = (pb & ZYDIS_PREFBIT_VEX_L ) ? 1 : 0; - info->details.rex.W = (pb & ZYDIS_PREFBIT_REX_W ) ? 1 : 0; + if (pb & ZYDIS_PREFBIT_REX_W) + { + info->details.rex.W = 1; + info->attributes |= ZYDIS_ATTRIB_HAS_REX; + } ZYDIS_CHECK(ZydisPrepareMandatoryPrefixes(&ctx)); // Prepare opcode. ZYDIS_CHECK(ZydisPrepareOpcode(&ctx)); + // Some instructions have additional opcode bits encoded in ModRM.reg. + if (ctx.matchingEntry->modrmReg != 0xFF) + { + ctx.info->details.modrm.reg = ctx.matchingEntry->modrmReg; + } + // Analyze and prepare operands. if (info->operandCount > ZYDIS_ARRAY_SIZE(info->operands)) { diff --git a/tools/ZydisDisasm.c b/tools/ZydisDisasm.c index 8cca3bf..fc583b1 100644 --- a/tools/ZydisDisasm.c +++ b/tools/ZydisDisasm.c @@ -95,6 +95,7 @@ int main(int argc, char** argv) // TODO: Remove // DEBUG CODE START +#if 0 for (size_t i = 0; i < info.length; ++i) { printf("%02X ", *(readBuf + readOffs + i)); @@ -115,6 +116,7 @@ int main(int argc, char** argv) putchar('\n'); ZYDIS_ASSERT(encBufferSize == info.length); ZYDIS_ASSERT(!memcmp(encBuffer, readBuf + readOffs, encBufferSize)); +#endif // DEBUG CODE END readOffs += info.length;