diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c7da76..f6bb614 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,7 @@ add_library( bddisasm STATIC bddisasm/crt.c bddisasm/bddisasm.c + bddisasm/bdformat.c # Add the headers so they will show up in IDEs. bddisasm/include/instructions.h bddisasm/include/mnemonics.h diff --git a/bddisasm/bddisasm.c b/bddisasm/bddisasm.c index d550dd3..22bc236 100644 --- a/bddisasm/bddisasm.c +++ b/bddisasm/bddisasm.c @@ -8,133 +8,10 @@ // The table definitions. #include "include/tabledefs.h" -// Handy macros. -#define RET_EQ(x, y, z) if ((x) == (y)) { return (z); } -#define RET_GE(x, y, z) if ((x) >= (y)) { return (z); } -#define RET_GT(x, y, z) if ((x) > (y)) { return (z); } - #ifndef UNREFERENCED_PARAMETER #define UNREFERENCED_PARAMETER(P) ((void)(P)) #endif -#ifndef BDDISASM_NO_FORMAT - -static const char *gReg8Bit[] = -{ - "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", -}; - -static const char *gReg8Bit64[] = -{ - "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", - "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b", -}; - -static const char *gReg16Bit[] = -{ - "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", - "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w", -}; - -static const char *gReg32Bit[] = -{ - "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", - "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d", -}; - -static const char *gReg64Bit[] = -{ - "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", - "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" -}; - -static const char *gRegFpu[] = -{ - "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7", -}; - -static const char *gRegMmx[] = -{ - "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", -}; - -static const char *gRegControl[] = -{ - "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", - "cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15", -}; - -static const char *gRegDebug[] = -{ - "dr0", "dr1", "dr2", "dr3", "dr4", "dr5", "dr6", "dr7", - "dr8", "dr9", "dr10", "dr11", "dr12", "dr13", "dr14", "dr15", -}; - -static const char *gRegTest[] = -{ - "tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7", - "tr8", "tr9", "tr10", "tr11", "tr12", "tr13", "tr14", "tr15", -}; - -static const char *gRegXmm[] = -{ - "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", - "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", - "xmm16", "xmm17", "xmm18", "xmm19", "xmm20", "xmm21", "xmm22", "xmm23", - "xmm24", "xmm25", "xmm26", "xmm27", "xmm28", "xmm29", "xmm30", "xmm31", -}; - -static const char *gRegYmm[] = -{ - "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", - "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15", - "ymm16", "ymm17", "ymm18", "ymm19", "ymm20", "ymm21", "ymm22", "ymm23", - "ymm24", "ymm25", "ymm26", "ymm27", "ymm28", "ymm29", "ymm30", "ymm31" -}; - -static const char *gRegZmm[] = -{ - "zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5", "zmm6", "zmm7", - "zmm8", "zmm9", "zmm10", "zmm11", "zmm12", "zmm13", "zmm14", "zmm15", - "zmm16", "zmm17", "zmm18", "zmm19", "zmm20", "zmm21", "zmm22", "zmm23", - "zmm24", "zmm25", "zmm26", "zmm27", "zmm28", "zmm29", "zmm30", "zmm31", -}; - -static const char *gRegSeg[] = -{ - "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7", -}; - -static const char *gRegBound[] = -{ - "bnd0", "bnd1", "bnd2", "bnd3", -}; - -static const char *gRegMask[] = -{ - "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7", -}; - -static const char *gRegTile[] = -{ - "tmm0", "tmm1", "tmm2", "tmm3", "tmm4", "tmm5", "tmm6", "tmm7", -}; - -static const char *gConditionCodes[] = -{ - "EQ", "LT", "LE", "UNORD", "NEQ", "NLT", "NLE", "ORD", - "EQ_UQ", "NGE", "NGT", "false", "NEQ_OQ", "GE", "GT", "TRUE", - "EQ_OS", "LT_OQ", "LE_OQ", "UNORD_S", "NEQ_US", "NLT_UQ", "NLE_UQ", "ORD_S", - "EQ_US", "NGE_UQ", "NGT_UQ", "FALSE_OS", "NEQ_OS", "GE_OQ", "GT_OQ", "TRUE_US", -}; - -static const char *gEmbeddedRounding[] = -{ - "rn", "rd", "ru", "rz", -}; - -#endif // !BDDISASM_NO_FORMAT - static const uint16_t gOperandMap[] = { @@ -329,52 +206,6 @@ NdGetVersion( } -// -// NdSprintf -// -#ifndef BDDISASM_NO_FORMAT -static NDSTATUS -NdSprintf( - char *Destination, - size_t DestinationSize, - const char *Formatstring, - ... - ) -// -// Wrapper on vsnprintf. -// -{ - int res; - va_list args; - - if (NULL == Destination) - { - return ND_STATUS_INVALID_PARAMETER; - } - - if (NULL == Formatstring) - { - return ND_STATUS_INVALID_PARAMETER; - } - - nd_memzero(Destination, DestinationSize); - - va_start(args, Formatstring); - - // _vsnprintf is used instead of the more secure _vsnprintf_s because the mini-Petru supports just - // the unsecured version, and we depend on it. - res = nd_vsnprintf_s(Destination, DestinationSize, DestinationSize - 1, Formatstring, args); - - va_end(args); - - if ((res < 0) || ((size_t)res >= DestinationSize - 1)) - { - return ND_STATUS_BUFFER_OVERFLOW; - } - - return ND_STATUS_SUCCESS; -} -#endif // !BDDISASM_NO_FORMAT // @@ -4314,884 +4145,6 @@ NdDecode( } -// -// NdToText -// -#ifndef BDDISASM_NO_FORMAT -NDSTATUS -NdToText( - const INSTRUX *Instrux, - uint64_t Rip, - uint32_t BufferSize, - char *Buffer - ) -{ - NDSTATUS status; - char *res, temp[64]; - uint32_t opIndex, opsStored; - const ND_OPERAND *pOp; - bool alignmentStored; - - // pre-init - status = ND_STATUS_SUCCESS; - res = NULL; - opIndex = 0; - opsStored = 0; - pOp = NULL; - alignmentStored = false; - - // Validate args. - if (NULL == Instrux) - { - return ND_STATUS_INVALID_PARAMETER; - } - - if (NULL == Buffer) - { - return ND_STATUS_INVALID_PARAMETER; - } - - if (BufferSize < ND_MIN_BUF_SIZE) - { - return ND_STATUS_INVALID_PARAMETER; - } - - // init the text. - nd_memzero(Buffer, BufferSize); - nd_memzero(temp, sizeof(temp)); - - // First, store the prefixes. - if (Instrux->Rep != 0) - { - // Check for REPZ/REPNZ support, and store prefixes. - if (ND_REPC_SUPPORT(Instrux)) - { - if (Instrux->Rep == ND_PREFIX_G1_REPE_REPZ) - { - res = nd_strcat_s(Buffer, BufferSize, "REPZ "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - else if (Instrux->Rep == ND_PREFIX_G1_REPNE_REPNZ) - { - res = nd_strcat_s(Buffer, BufferSize, "REPNZ "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - } - - // Check for REP support and store prefixes. - if (ND_REP_SUPPORT(Instrux)) - { - if (Instrux->Rep == ND_PREFIX_G1_REPE_REPZ) - { - res = nd_strcat_s(Buffer, BufferSize, "REP "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - else if (Instrux->Rep == ND_PREFIX_G1_REPNE_REPNZ) - { - res = nd_strcat_s(Buffer, BufferSize, "REPNZ "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - } - - if (Instrux->IsXreleaseEnabled) - { - res = nd_strcat_s(Buffer, BufferSize, "XRELEASE "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - else if (Instrux->IsXacquireEnabled) - { - res = nd_strcat_s(Buffer, BufferSize, "XACQUIRE "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - } - - if (Instrux->HasLock) - { - if (ND_LOCK_SUPPORT(Instrux)) - { - res = nd_strcat_s(Buffer, BufferSize, "LOCK "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - } - - if (Instrux->Rep == ND_PREFIX_G1_BND) - { - if (ND_BND_SUPPORT(Instrux)) - { - res = nd_strcat_s(Buffer, BufferSize, "BND "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - } - - if (Instrux->HasSeg && ND_BHINT_SUPPORT(Instrux)) - { - switch (Instrux->Seg) - { - case ND_PREFIX_G2_BR_TAKEN: - res = nd_strcat_s(Buffer, BufferSize, "BHT "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - - case ND_PREFIX_G2_BR_NOT_TAKEN: - res = nd_strcat_s(Buffer, BufferSize, "BHNT "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - - case ND_PREFIX_G2_BR_ALT: - res = nd_strcat_s(Buffer, BufferSize, "BHALT "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - - default: - break; - } - } - - if (Instrux->HasSeg && ND_DNT_SUPPORT(Instrux)) - { - if (!Instrux->IsCetTracked) - { - res = nd_strcat_s(Buffer, BufferSize, "DNT "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - } - - // Store the mnemonic. - res = nd_strcat_s(Buffer, BufferSize, Instrux->Mnemonic); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - - // Store condition code, if any. - if (ND_HAS_SSE_CONDITION(Instrux)) - { - res = nd_strcat_s(Buffer, BufferSize, gConditionCodes[Instrux->SseCondition]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - - // If there are no explicit operands, we can leave. - if (0 == Instrux->ExpOperandsCount) - { - return ND_STATUS_SUCCESS; - } - - // Now the operands. - for (opIndex = 0; opIndex < Instrux->OperandsCount; opIndex++) - { - status = ND_STATUS_SUCCESS; - - pOp = &Instrux->Operands[opIndex]; - - if (pOp->Type == ND_OP_NOT_PRESENT) - { - break; - } - - if (pOp->Flags.IsDefault) - { - continue; - } - - // If this is a mask operand that has been used as masking for a previous operand, than we - // can safely skip it. We check this by seeing where is the operand encoded. If it's encoded - // in the evex.aaa field, than it is a conventional mask. - if ((pOp->Encoding == ND_OPE_A) && (pOp->Type == ND_OP_REG) && - (pOp->Info.Register.Type == ND_REG_MSK) && (opIndex > 0)) - { - continue; - } - - // Store alignment. - if (!alignmentStored) - { - size_t idx = 0; - - while ((idx < BufferSize) && (Buffer[idx])) - { - idx++; - } - - while ((idx < 9) && (idx + 1 < BufferSize)) - { - Buffer[idx++] = 0x20; - } - - if (idx + 1 < BufferSize) - { - Buffer[idx++] = 0x20; - } - - Buffer[idx] = 0; - - alignmentStored = true; - } - - // Store the comma, if this isn't the first operand. - if (opsStored > 0) - { - res = nd_strcat_s(Buffer, BufferSize, ", "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - - opsStored++; - - switch (pOp->Type) - { - case ND_OP_REG: - switch (pOp->Info.Register.Type) - { - case ND_REG_GPR: - { - if (pOp->Info.Register.Reg >= ND_MAX_GPR_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - // General purpose register. - switch (pOp->Info.Register.Size) - { - case ND_SIZE_8BIT: - // 8 bit register. - if ((Instrux->EncMode != ND_ENCM_LEGACY) || Instrux->HasRex) - { - res = nd_strcat_s(Buffer, BufferSize, gReg8Bit64[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - else - { - res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_SIZE_16BIT: - // 16 bit register. - res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - - case ND_SIZE_32BIT: - // 32 bit register. - res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - - case ND_SIZE_64BIT: - // 64 bit register. - res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - - default: - return ND_STATUS_INVALID_INSTRUX; - } - } - break; - - case ND_REG_SEG: - { - if (pOp->Info.Register.Reg >= ND_MAX_SEG_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - res = nd_strcat_s(Buffer, BufferSize, gRegSeg[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_REG_FPU: - { - if (pOp->Info.Register.Reg >= ND_MAX_FPU_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - res = nd_strcat_s(Buffer, BufferSize, gRegFpu[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_REG_MMX: - { - if (pOp->Info.Register.Reg >= ND_MAX_MMX_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - res = nd_strcat_s(Buffer, BufferSize, gRegMmx[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_REG_SSE: - { - if (pOp->Info.Register.Reg >= ND_MAX_SSE_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - switch (pOp->Info.Register.Size) - { - case ND_SIZE_128BIT: - res = nd_strcat_s(Buffer, BufferSize, gRegXmm[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case ND_SIZE_256BIT: - res = nd_strcat_s(Buffer, BufferSize, gRegYmm[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case ND_SIZE_512BIT: - res = nd_strcat_s(Buffer, BufferSize, gRegZmm[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - default: - return ND_STATUS_INVALID_INSTRUX; - } - } - break; - - case ND_REG_CR: - { - if (pOp->Info.Register.Reg >= ND_MAX_CR_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - res = nd_strcat_s(Buffer, BufferSize, gRegControl[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_REG_DR: - { - if (pOp->Info.Register.Reg >= ND_MAX_DR_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - res = nd_strcat_s(Buffer, BufferSize, gRegDebug[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_REG_TR: - { - if (pOp->Info.Register.Reg >= ND_MAX_TR_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - res = nd_strcat_s(Buffer, BufferSize, gRegTest[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_REG_BND: - { - // Sanity check. - if (pOp->Info.Register.Reg >= ND_MAX_BND_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - res = nd_strcat_s(Buffer, BufferSize, gRegBound[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_REG_MSK: - { - // Sanity check. - if (pOp->Info.Register.Reg >= ND_MAX_MSK_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - res = nd_strcat_s(Buffer, BufferSize, gRegMask[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_REG_TILE: - { - // Sanity check. - if (pOp->Info.Register.Reg >= ND_MAX_TILE_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - res = nd_strcat_s(Buffer, BufferSize, gRegTile[pOp->Info.Register.Reg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - default: - break; - } - - if (pOp->Info.Register.Count > 1) - { - status = NdSprintf(temp, sizeof(temp), "+%d", pOp->Info.Register.Count - 1); - if (!ND_SUCCESS(status)) - { - return status; - } - - res = nd_strcat_s(Buffer, BufferSize, temp); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - - break; - - case ND_OP_BANK: - // Nothing to show. - break; - - case ND_OP_CONST: - { - // Implicit constant - status = NdSprintf(temp, sizeof(temp), "%d", pOp->Info.Constant.Const); - if (!ND_SUCCESS(status)) - { - return status; - } - - res = nd_strcat_s(Buffer, BufferSize, temp); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_OP_IMM: - { - switch (pOp->Size) - { - case 1: - status = NdSprintf(temp, sizeof(temp), "0x%02x", (uint8_t)pOp->Info.Immediate.Imm); - break; - case 2: - status = NdSprintf(temp, sizeof(temp), "0x%04x", (uint16_t)pOp->Info.Immediate.Imm); - break; - case 4: - status = NdSprintf(temp, sizeof(temp), "0x%08x", (uint32_t)pOp->Info.Immediate.Imm); - break; - case 8: - status = NdSprintf(temp, sizeof(temp), "0x%016llx", (uint64_t)pOp->Info.Immediate.Imm); - break; - } - if (!ND_SUCCESS(status)) - { - return status; - } - - res = nd_strcat_s(Buffer, BufferSize, temp); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_OP_OFFS: - { - uint64_t dest = Rip + Instrux->Length + pOp->Info.RelativeOffset.Rel; - - // Truncate to the actual word length. - switch (Instrux->WordLength) - { - case 2: - dest &= 0xFFFF; - break; - case 4: - dest &= 0xFFFFFFFF; - break; - default: - break; - } - - status = NdSprintf(temp, sizeof(temp), "0x%llx", dest); - if (!ND_SUCCESS(status)) - { - return status; - } - - res = nd_strcat_s(Buffer, BufferSize, temp); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_OP_ADDR: - { - switch (Instrux->AddrLength) - { - case 4: - status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%04x", - pOp->Info.Address.BaseSeg, (uint16_t)pOp->Info.Address.Offset); - break; - case 6: - status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%08x", - pOp->Info.Address.BaseSeg, (uint32_t)pOp->Info.Address.Offset); - break; - case 10: - status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%016llx", - pOp->Info.Address.BaseSeg, (uint64_t)pOp->Info.Address.Offset); - break; - default: - return ND_STATUS_INVALID_INSTRUX; - } - - if (!ND_SUCCESS(status)) - { - return status; - } - - res = nd_strcat_s(Buffer, BufferSize, temp); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - case ND_OP_MEM: - { - // Prepend the size. For VSIB addressing, store the VSIB element size, not the total accessed size. - ND_OPERAND_SIZE size = pOp->Info.Memory.IsVsib ? pOp->Info.Memory.Vsib.ElemSize : pOp->Size; - - switch (size) - { - case 1: - res = nd_strcat_s(Buffer, BufferSize, "byte ptr "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case 2: - res = nd_strcat_s(Buffer, BufferSize, "word ptr "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case 4: - res = nd_strcat_s(Buffer, BufferSize, "dword ptr "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case 6: - res = nd_strcat_s(Buffer, BufferSize, "fword ptr "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case 8: - res = nd_strcat_s(Buffer, BufferSize, "qword ptr "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case 10: - res = nd_strcat_s(Buffer, BufferSize, "tbyte ptr "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case 16: - res = nd_strcat_s(Buffer, BufferSize, "xmmword ptr "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case 32: - res = nd_strcat_s(Buffer, BufferSize, "ymmword ptr "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case 48: - res = nd_strcat_s(Buffer, BufferSize, "m384 ptr "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case 64: - res = nd_strcat_s(Buffer, BufferSize, "zmmword ptr "); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - default: - break; - } - - // Perpend the segment, only if it is overridden via a prefix. - if (pOp->Info.Memory.HasSeg && Instrux->HasSeg) - { - if (pOp->Info.Memory.Seg >= ND_MAX_SEG_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - if ((ND_CODE_64 != Instrux->DefCode) || (NDR_FS == pOp->Info.Memory.Seg) || - (NDR_GS == pOp->Info.Memory.Seg)) - { - res = nd_strcat_s(Buffer, BufferSize, gRegSeg[pOp->Info.Memory.Seg]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - - res = nd_strcat_s(Buffer, BufferSize, ":"); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - } - - // Prepend the "[" - res = nd_strcat_s(Buffer, BufferSize, "["); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - - // Base, if any. - if (pOp->Info.Memory.HasBase) - { - if (pOp->Info.Memory.Base >= ND_MAX_GPR_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - switch (pOp->Info.Memory.BaseSize) - { - case ND_SIZE_8BIT: - res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Memory.Base]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case ND_SIZE_16BIT: - res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Memory.Base]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case ND_SIZE_32BIT: - res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Memory.Base]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case ND_SIZE_64BIT: - res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Memory.Base]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - default: - return ND_STATUS_INVALID_INSTRUX; - } - } - - // Index, if any. Special treatment for VSIB addressing. Also, perpend a "+" if base is present. - if (pOp->Info.Memory.HasIndex) - { - if (pOp->Info.Memory.Index >= (pOp->Info.Memory.IsVsib ? ND_MAX_SSE_REGS : ND_MAX_GPR_REGS)) - { - return ND_STATUS_INVALID_INSTRUX; - } - - if (pOp->Info.Memory.HasBase) - { - res = nd_strcat_s(Buffer, BufferSize, "+"); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - - switch (pOp->Info.Memory.IndexSize) - { - case ND_SIZE_8BIT: - res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Memory.Index]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case ND_SIZE_16BIT: - res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Memory.Index]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case ND_SIZE_32BIT: - res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Memory.Index]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case ND_SIZE_64BIT: - res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Memory.Index]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case ND_SIZE_128BIT: - res = nd_strcat_s(Buffer, BufferSize, gRegXmm[pOp->Info.Memory.Index]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case ND_SIZE_256BIT: - res = nd_strcat_s(Buffer, BufferSize, gRegYmm[pOp->Info.Memory.Index]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - case ND_SIZE_512BIT: - res = nd_strcat_s(Buffer, BufferSize, gRegZmm[pOp->Info.Memory.Index]); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - break; - default: - return ND_STATUS_INVALID_INSTRUX; - } - - // If index is present, scale is also present. - if (pOp->Info.Memory.Scale != 1 && !pOp->Info.Memory.IsMib) - { - status = NdSprintf(temp, sizeof(temp), "*%d", pOp->Info.Memory.Scale); - if (!ND_SUCCESS(status)) - { - return status; - } - - res = nd_strcat_s(Buffer, BufferSize, temp); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - } - - // Handle displacement. - if (pOp->Info.Memory.HasDisp) - { - uint64_t normDisp, disp; - - disp = pOp->Info.Memory.Disp; - - // If this is direct addressing (O operand) or we don't have base or index, than we don't normalize - // the displacement, since it is used as a direct offset. Note that the second condition also - // includes the RIP-relative case. - if (pOp->Info.Memory.IsDirect || !(pOp->Info.Memory.HasBase || pOp->Info.Memory.HasIndex)) - { - normDisp = disp; - } - else - { - switch (pOp->Info.Memory.DispSize) - { - case 1: - normDisp = ((disp & 0x80) ? ~disp + 1ULL : disp) & 0xFF; - break; - case 2: - normDisp = ((disp & 0x8000) ? ~disp + 1ULL : disp) & 0xFFFF; - break; - case 4: - normDisp = ((disp & 0x80000000) ? ~disp + 1ULL : disp) & 0xFFFFFFFF; - break; - default: - normDisp = disp; - break; - } - - // Handle compressed displacement. It is fine to cast the normDisp to uint32_t, as the - // compressed displacement only works with uint8_t displacements. Also, in this phase, - // the normDisp is converted to a positive quantity, so no sign-extension is needed. - if (pOp->Info.Memory.HasCompDisp) - { - normDisp = (uint64_t)(uint32_t)normDisp * pOp->Info.Memory.CompDispSize; - } - } - - - // Now displacement. - if (pOp->Info.Memory.HasBase || pOp->Info.Memory.HasIndex) - { - res = nd_strcat_s(Buffer, BufferSize, Instrux->SignDisp ? "-" : "+"); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - - if (pOp->Info.Memory.IsRipRel) - { - uint64_t target = disp + Rip + Instrux->Length; - - if (Instrux->AddrMode == ND_ADDR_32) - { - target &= 0xFFFFFFFF; - } - - status = NdSprintf(temp, sizeof(temp), "rel 0x%llx", target); - } - else - { - uint8_t trimSize; - - trimSize = (Instrux->AddrMode == ND_ADDR_16) ? 2 : ((Instrux->AddrMode == ND_ADDR_32) ? 4 : 8); - - // Truncate the displacement size to the size of the address length. - normDisp = ND_TRIM(trimSize, normDisp); - - status = NdSprintf(temp, sizeof(temp), "0x%llx", normDisp); - } - if (!ND_SUCCESS(status)) - { - return status; - } - - res = nd_strcat_s(Buffer, BufferSize, temp); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - - // And the ending "]" - res = nd_strcat_s(Buffer, BufferSize, "]"); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - break; - - default: - return ND_STATUS_INVALID_INSTRUX; - } - - // Handle memory broadcast. - if (pOp->Decorator.HasBroadcast) - { - status = NdSprintf(temp, sizeof(temp), "{1to%d}", pOp->Decorator.Broadcast.Count); - if (!ND_SUCCESS(status)) - { - return status; - } - - res = nd_strcat_s(Buffer, BufferSize, temp); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - - // Handle masking. - if (pOp->Decorator.HasMask) - { - if (pOp->Decorator.Mask.Msk >= ND_MAX_MSK_REGS) - { - return ND_STATUS_INVALID_INSTRUX; - } - - status = NdSprintf(temp, sizeof(temp), "{%s}", gRegMask[pOp->Decorator.Mask.Msk]); - if (!ND_SUCCESS(status)) - { - return status; - } - - res = nd_strcat_s(Buffer, BufferSize, temp); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - - // Handle zeroing. Note that zeroing without masking is ignored. - if (pOp->Decorator.HasZero && pOp->Decorator.HasMask) - { - res = nd_strcat_s(Buffer, BufferSize, "{z}"); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - - // Append Suppress All Exceptions decorator. - if (pOp->Decorator.HasSae && !pOp->Decorator.HasEr) - { - // ER implies SAE, so if we have ER, we will list that. - res = nd_strcat_s(Buffer, BufferSize, ", {sae}"); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - - // Append Embedded Rounding decorator. - if (pOp->Decorator.HasEr) - { - if (Instrux->RoundingMode >= 4) - { - return ND_STATUS_INVALID_INSTRUX; - } - - status = NdSprintf(temp, sizeof(temp), ", {%s-sae}", gEmbeddedRounding[Instrux->RoundingMode]); - if (!ND_SUCCESS(status)) - { - return status; - } - - res = nd_strcat_s(Buffer, BufferSize, temp); - RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); - } - } - - return ND_STATUS_SUCCESS; -} -#else -NDSTATUS -NdToText( - const INSTRUX *Instrux, - uint64_t Rip, - uint32_t BufferSize, - char *Buffer - ) -{ - UNREFERENCED_PARAMETER(Instrux); - UNREFERENCED_PARAMETER(Rip); - - // At least make sure the buffer is NULL-terminated so integrators can use NdToText without checking if the - // BDDISASM_NO_FORMAT macro is defined. This makes switching between versions with formatting and versions without - // formatting easier. - if (Buffer != NULL && BufferSize >= 1) - { - *Buffer = '\0'; - } - - return ND_STATUS_SUCCESS; -} -#endif // !BDDISASM_NO_FORMAT // diff --git a/bddisasm/bddisasm.vcxproj b/bddisasm/bddisasm.vcxproj index e625ea6..ae1cbf3 100644 --- a/bddisasm/bddisasm.vcxproj +++ b/bddisasm/bddisasm.vcxproj @@ -420,6 +420,7 @@ + NotUsing diff --git a/bddisasm/bddisasm.vcxproj.filters b/bddisasm/bddisasm.vcxproj.filters index 5ddd5ae..b4de484 100644 --- a/bddisasm/bddisasm.vcxproj.filters +++ b/bddisasm/bddisasm.vcxproj.filters @@ -27,6 +27,9 @@ Source Files + + Source Files + diff --git a/bddisasm/bdformat.c b/bddisasm/bdformat.c new file mode 100644 index 0000000..fc5842c --- /dev/null +++ b/bddisasm/bdformat.c @@ -0,0 +1,1050 @@ +/* + * Copyright (c) 2020 Bitdefender + * SPDX-License-Identifier: Apache-2.0 + */ +#include "include/nd_crt.h" +#include "../inc/bddisasm.h" + + + +#ifndef BDDISASM_NO_FORMAT + +static const char *gReg8Bit[] = +{ + "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", +}; + +static const char *gReg8Bit64[] = +{ + "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", + "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b", +}; + +static const char *gReg16Bit[] = +{ + "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", + "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w", +}; + +static const char *gReg32Bit[] = +{ + "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", + "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d", +}; + +static const char *gReg64Bit[] = +{ + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" +}; + +static const char *gRegFpu[] = +{ + "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7", +}; + +static const char *gRegMmx[] = +{ + "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", +}; + +static const char *gRegControl[] = +{ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", + "cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15", +}; + +static const char *gRegDebug[] = +{ + "dr0", "dr1", "dr2", "dr3", "dr4", "dr5", "dr6", "dr7", + "dr8", "dr9", "dr10", "dr11", "dr12", "dr13", "dr14", "dr15", +}; + +static const char *gRegTest[] = +{ + "tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7", + "tr8", "tr9", "tr10", "tr11", "tr12", "tr13", "tr14", "tr15", +}; + +static const char *gRegXmm[] = +{ + "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", + "xmm16", "xmm17", "xmm18", "xmm19", "xmm20", "xmm21", "xmm22", "xmm23", + "xmm24", "xmm25", "xmm26", "xmm27", "xmm28", "xmm29", "xmm30", "xmm31", +}; + +static const char *gRegYmm[] = +{ + "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", + "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15", + "ymm16", "ymm17", "ymm18", "ymm19", "ymm20", "ymm21", "ymm22", "ymm23", + "ymm24", "ymm25", "ymm26", "ymm27", "ymm28", "ymm29", "ymm30", "ymm31" +}; + +static const char *gRegZmm[] = +{ + "zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5", "zmm6", "zmm7", + "zmm8", "zmm9", "zmm10", "zmm11", "zmm12", "zmm13", "zmm14", "zmm15", + "zmm16", "zmm17", "zmm18", "zmm19", "zmm20", "zmm21", "zmm22", "zmm23", + "zmm24", "zmm25", "zmm26", "zmm27", "zmm28", "zmm29", "zmm30", "zmm31", +}; + +static const char *gRegSeg[] = +{ + "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7", +}; + +static const char *gRegBound[] = +{ + "bnd0", "bnd1", "bnd2", "bnd3", +}; + +static const char *gRegMask[] = +{ + "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7", +}; + +static const char *gRegTile[] = +{ + "tmm0", "tmm1", "tmm2", "tmm3", "tmm4", "tmm5", "tmm6", "tmm7", +}; + +static const char *gConditionCodes[] = +{ + "EQ", "LT", "LE", "UNORD", "NEQ", "NLT", "NLE", "ORD", + "EQ_UQ", "NGE", "NGT", "false", "NEQ_OQ", "GE", "GT", "TRUE", + "EQ_OS", "LT_OQ", "LE_OQ", "UNORD_S", "NEQ_US", "NLT_UQ", "NLE_UQ", "ORD_S", + "EQ_US", "NGE_UQ", "NGT_UQ", "FALSE_OS", "NEQ_OS", "GE_OQ", "GT_OQ", "TRUE_US", +}; + +static const char *gEmbeddedRounding[] = +{ + "rn", "rd", "ru", "rz", +}; + + + +// +// NdSprintf +// +static NDSTATUS +NdSprintf( + char *Destination, + size_t DestinationSize, + const char *Formatstring, + ... + ) +// +// Wrapper on vsnprintf. +// +{ + int res; + va_list args; + + if (NULL == Destination) + { + return ND_STATUS_INVALID_PARAMETER; + } + + if (NULL == Formatstring) + { + return ND_STATUS_INVALID_PARAMETER; + } + + nd_memzero(Destination, DestinationSize); + + va_start(args, Formatstring); + + // _vsnprintf is used instead of the more secure _vsnprintf_s because the mini-Petru supports just + // the unsecured version, and we depend on it. + res = nd_vsnprintf_s(Destination, DestinationSize, DestinationSize - 1, Formatstring, args); + + va_end(args); + + if ((res < 0) || ((size_t)res >= DestinationSize - 1)) + { + return ND_STATUS_BUFFER_OVERFLOW; + } + + return ND_STATUS_SUCCESS; +} + + +// +// NdToText +// +NDSTATUS +NdToText( + const INSTRUX *Instrux, + uint64_t Rip, + uint32_t BufferSize, + char *Buffer + ) +{ + NDSTATUS status; + char *res, temp[64]; + uint32_t opIndex, opsStored; + const ND_OPERAND *pOp; + bool alignmentStored; + + // pre-init + status = ND_STATUS_SUCCESS; + res = NULL; + opIndex = 0; + opsStored = 0; + pOp = NULL; + alignmentStored = false; + + // Validate args. + if (NULL == Instrux) + { + return ND_STATUS_INVALID_PARAMETER; + } + + if (NULL == Buffer) + { + return ND_STATUS_INVALID_PARAMETER; + } + + if (BufferSize < ND_MIN_BUF_SIZE) + { + return ND_STATUS_INVALID_PARAMETER; + } + + // init the text. + nd_memzero(Buffer, BufferSize); + nd_memzero(temp, sizeof(temp)); + + // First, store the prefixes. + if (Instrux->Rep != 0) + { + // Check for REPZ/REPNZ support, and store prefixes. + if (ND_REPC_SUPPORT(Instrux)) + { + if (Instrux->Rep == ND_PREFIX_G1_REPE_REPZ) + { + res = nd_strcat_s(Buffer, BufferSize, "REPZ "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + else if (Instrux->Rep == ND_PREFIX_G1_REPNE_REPNZ) + { + res = nd_strcat_s(Buffer, BufferSize, "REPNZ "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + } + + // Check for REP support and store prefixes. + if (ND_REP_SUPPORT(Instrux)) + { + if (Instrux->Rep == ND_PREFIX_G1_REPE_REPZ) + { + res = nd_strcat_s(Buffer, BufferSize, "REP "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + else if (Instrux->Rep == ND_PREFIX_G1_REPNE_REPNZ) + { + res = nd_strcat_s(Buffer, BufferSize, "REPNZ "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + } + + if (Instrux->IsXreleaseEnabled) + { + res = nd_strcat_s(Buffer, BufferSize, "XRELEASE "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + else if (Instrux->IsXacquireEnabled) + { + res = nd_strcat_s(Buffer, BufferSize, "XACQUIRE "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + } + + if (Instrux->HasLock) + { + if (ND_LOCK_SUPPORT(Instrux)) + { + res = nd_strcat_s(Buffer, BufferSize, "LOCK "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + } + + if (Instrux->Rep == ND_PREFIX_G1_BND) + { + if (ND_BND_SUPPORT(Instrux)) + { + res = nd_strcat_s(Buffer, BufferSize, "BND "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + } + + if (Instrux->HasSeg && ND_BHINT_SUPPORT(Instrux)) + { + switch (Instrux->Seg) + { + case ND_PREFIX_G2_BR_TAKEN: + res = nd_strcat_s(Buffer, BufferSize, "BHT "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + + case ND_PREFIX_G2_BR_NOT_TAKEN: + res = nd_strcat_s(Buffer, BufferSize, "BHNT "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + + case ND_PREFIX_G2_BR_ALT: + res = nd_strcat_s(Buffer, BufferSize, "BHALT "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + + default: + break; + } + } + + if (Instrux->HasSeg && ND_DNT_SUPPORT(Instrux)) + { + if (!Instrux->IsCetTracked) + { + res = nd_strcat_s(Buffer, BufferSize, "DNT "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + } + + // Store the mnemonic. + res = nd_strcat_s(Buffer, BufferSize, Instrux->Mnemonic); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + + // Store condition code, if any. + if (ND_HAS_SSE_CONDITION(Instrux)) + { + res = nd_strcat_s(Buffer, BufferSize, gConditionCodes[Instrux->SseCondition]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + + // If there are no explicit operands, we can leave. + if (0 == Instrux->ExpOperandsCount) + { + return ND_STATUS_SUCCESS; + } + + // Now the operands. + for (opIndex = 0; opIndex < Instrux->OperandsCount; opIndex++) + { + status = ND_STATUS_SUCCESS; + + pOp = &Instrux->Operands[opIndex]; + + if (pOp->Type == ND_OP_NOT_PRESENT) + { + break; + } + + if (pOp->Flags.IsDefault) + { + continue; + } + + // If this is a mask operand that has been used as masking for a previous operand, than we + // can safely skip it. We check this by seeing where is the operand encoded. If it's encoded + // in the evex.aaa field, than it is a conventional mask. + if ((pOp->Encoding == ND_OPE_A) && (pOp->Type == ND_OP_REG) && + (pOp->Info.Register.Type == ND_REG_MSK) && (opIndex > 0)) + { + continue; + } + + // Store alignment. + if (!alignmentStored) + { + size_t idx = 0; + + while ((idx < BufferSize) && (Buffer[idx])) + { + idx++; + } + + while ((idx < 9) && (idx + 1 < BufferSize)) + { + Buffer[idx++] = 0x20; + } + + if (idx + 1 < BufferSize) + { + Buffer[idx++] = 0x20; + } + + Buffer[idx] = 0; + + alignmentStored = true; + } + + // Store the comma, if this isn't the first operand. + if (opsStored > 0) + { + res = nd_strcat_s(Buffer, BufferSize, ", "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + + opsStored++; + + switch (pOp->Type) + { + case ND_OP_REG: + switch (pOp->Info.Register.Type) + { + case ND_REG_GPR: + { + if (pOp->Info.Register.Reg >= ND_MAX_GPR_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + // General purpose register. + switch (pOp->Info.Register.Size) + { + case ND_SIZE_8BIT: + // 8 bit register. + if ((Instrux->EncMode != ND_ENCM_LEGACY) || Instrux->HasRex) + { + res = nd_strcat_s(Buffer, BufferSize, gReg8Bit64[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + else + { + res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_SIZE_16BIT: + // 16 bit register. + res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + + case ND_SIZE_32BIT: + // 32 bit register. + res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + + case ND_SIZE_64BIT: + // 64 bit register. + res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + + default: + return ND_STATUS_INVALID_INSTRUX; + } + } + break; + + case ND_REG_SEG: + { + if (pOp->Info.Register.Reg >= ND_MAX_SEG_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + res = nd_strcat_s(Buffer, BufferSize, gRegSeg[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_REG_FPU: + { + if (pOp->Info.Register.Reg >= ND_MAX_FPU_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + res = nd_strcat_s(Buffer, BufferSize, gRegFpu[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_REG_MMX: + { + if (pOp->Info.Register.Reg >= ND_MAX_MMX_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + res = nd_strcat_s(Buffer, BufferSize, gRegMmx[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_REG_SSE: + { + if (pOp->Info.Register.Reg >= ND_MAX_SSE_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + switch (pOp->Info.Register.Size) + { + case ND_SIZE_128BIT: + res = nd_strcat_s(Buffer, BufferSize, gRegXmm[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case ND_SIZE_256BIT: + res = nd_strcat_s(Buffer, BufferSize, gRegYmm[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case ND_SIZE_512BIT: + res = nd_strcat_s(Buffer, BufferSize, gRegZmm[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + default: + return ND_STATUS_INVALID_INSTRUX; + } + } + break; + + case ND_REG_CR: + { + if (pOp->Info.Register.Reg >= ND_MAX_CR_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + res = nd_strcat_s(Buffer, BufferSize, gRegControl[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_REG_DR: + { + if (pOp->Info.Register.Reg >= ND_MAX_DR_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + res = nd_strcat_s(Buffer, BufferSize, gRegDebug[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_REG_TR: + { + if (pOp->Info.Register.Reg >= ND_MAX_TR_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + res = nd_strcat_s(Buffer, BufferSize, gRegTest[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_REG_BND: + { + // Sanity check. + if (pOp->Info.Register.Reg >= ND_MAX_BND_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + res = nd_strcat_s(Buffer, BufferSize, gRegBound[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_REG_MSK: + { + // Sanity check. + if (pOp->Info.Register.Reg >= ND_MAX_MSK_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + res = nd_strcat_s(Buffer, BufferSize, gRegMask[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_REG_TILE: + { + // Sanity check. + if (pOp->Info.Register.Reg >= ND_MAX_TILE_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + res = nd_strcat_s(Buffer, BufferSize, gRegTile[pOp->Info.Register.Reg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + default: + break; + } + + if (pOp->Info.Register.Count > 1) + { + status = NdSprintf(temp, sizeof(temp), "+%d", pOp->Info.Register.Count - 1); + if (!ND_SUCCESS(status)) + { + return status; + } + + res = nd_strcat_s(Buffer, BufferSize, temp); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + + break; + + case ND_OP_BANK: + // Nothing to show. + break; + + case ND_OP_CONST: + { + // Implicit constant + status = NdSprintf(temp, sizeof(temp), "%d", pOp->Info.Constant.Const); + if (!ND_SUCCESS(status)) + { + return status; + } + + res = nd_strcat_s(Buffer, BufferSize, temp); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_OP_IMM: + { + switch (pOp->Size) + { + case 1: + status = NdSprintf(temp, sizeof(temp), "0x%02x", (uint8_t)pOp->Info.Immediate.Imm); + break; + case 2: + status = NdSprintf(temp, sizeof(temp), "0x%04x", (uint16_t)pOp->Info.Immediate.Imm); + break; + case 4: + status = NdSprintf(temp, sizeof(temp), "0x%08x", (uint32_t)pOp->Info.Immediate.Imm); + break; + case 8: + status = NdSprintf(temp, sizeof(temp), "0x%016llx", (uint64_t)pOp->Info.Immediate.Imm); + break; + } + if (!ND_SUCCESS(status)) + { + return status; + } + + res = nd_strcat_s(Buffer, BufferSize, temp); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_OP_OFFS: + { + uint64_t dest = Rip + Instrux->Length + pOp->Info.RelativeOffset.Rel; + + // Truncate to the actual word length. + switch (Instrux->WordLength) + { + case 2: + dest &= 0xFFFF; + break; + case 4: + dest &= 0xFFFFFFFF; + break; + default: + break; + } + + status = NdSprintf(temp, sizeof(temp), "0x%llx", dest); + if (!ND_SUCCESS(status)) + { + return status; + } + + res = nd_strcat_s(Buffer, BufferSize, temp); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_OP_ADDR: + { + switch (Instrux->AddrLength) + { + case 4: + status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%04x", + pOp->Info.Address.BaseSeg, (uint16_t)pOp->Info.Address.Offset); + break; + case 6: + status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%08x", + pOp->Info.Address.BaseSeg, (uint32_t)pOp->Info.Address.Offset); + break; + case 10: + status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%016llx", + pOp->Info.Address.BaseSeg, (uint64_t)pOp->Info.Address.Offset); + break; + default: + return ND_STATUS_INVALID_INSTRUX; + } + + if (!ND_SUCCESS(status)) + { + return status; + } + + res = nd_strcat_s(Buffer, BufferSize, temp); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + case ND_OP_MEM: + { + // Prepend the size. For VSIB addressing, store the VSIB element size, not the total accessed size. + ND_OPERAND_SIZE size = pOp->Info.Memory.IsVsib ? pOp->Info.Memory.Vsib.ElemSize : pOp->Size; + + switch (size) + { + case 1: + res = nd_strcat_s(Buffer, BufferSize, "byte ptr "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case 2: + res = nd_strcat_s(Buffer, BufferSize, "word ptr "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case 4: + res = nd_strcat_s(Buffer, BufferSize, "dword ptr "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case 6: + res = nd_strcat_s(Buffer, BufferSize, "fword ptr "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case 8: + res = nd_strcat_s(Buffer, BufferSize, "qword ptr "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case 10: + res = nd_strcat_s(Buffer, BufferSize, "tbyte ptr "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case 16: + res = nd_strcat_s(Buffer, BufferSize, "xmmword ptr "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case 32: + res = nd_strcat_s(Buffer, BufferSize, "ymmword ptr "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case 48: + res = nd_strcat_s(Buffer, BufferSize, "m384 ptr "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case 64: + res = nd_strcat_s(Buffer, BufferSize, "zmmword ptr "); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + default: + break; + } + + // Perpend the segment, only if it is overridden via a prefix. + if (pOp->Info.Memory.HasSeg && Instrux->HasSeg) + { + if (pOp->Info.Memory.Seg >= ND_MAX_SEG_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + if ((ND_CODE_64 != Instrux->DefCode) || (NDR_FS == pOp->Info.Memory.Seg) || + (NDR_GS == pOp->Info.Memory.Seg)) + { + res = nd_strcat_s(Buffer, BufferSize, gRegSeg[pOp->Info.Memory.Seg]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + + res = nd_strcat_s(Buffer, BufferSize, ":"); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + } + + // Prepend the "[" + res = nd_strcat_s(Buffer, BufferSize, "["); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + + // Base, if any. + if (pOp->Info.Memory.HasBase) + { + if (pOp->Info.Memory.Base >= ND_MAX_GPR_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + switch (pOp->Info.Memory.BaseSize) + { + case ND_SIZE_8BIT: + res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Memory.Base]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case ND_SIZE_16BIT: + res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Memory.Base]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case ND_SIZE_32BIT: + res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Memory.Base]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case ND_SIZE_64BIT: + res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Memory.Base]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + default: + return ND_STATUS_INVALID_INSTRUX; + } + } + + // Index, if any. Special treatment for VSIB addressing. Also, perpend a "+" if base is present. + if (pOp->Info.Memory.HasIndex) + { + if (pOp->Info.Memory.Index >= (pOp->Info.Memory.IsVsib ? ND_MAX_SSE_REGS : ND_MAX_GPR_REGS)) + { + return ND_STATUS_INVALID_INSTRUX; + } + + if (pOp->Info.Memory.HasBase) + { + res = nd_strcat_s(Buffer, BufferSize, "+"); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + + switch (pOp->Info.Memory.IndexSize) + { + case ND_SIZE_8BIT: + res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Memory.Index]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case ND_SIZE_16BIT: + res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Memory.Index]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case ND_SIZE_32BIT: + res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Memory.Index]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case ND_SIZE_64BIT: + res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Memory.Index]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case ND_SIZE_128BIT: + res = nd_strcat_s(Buffer, BufferSize, gRegXmm[pOp->Info.Memory.Index]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case ND_SIZE_256BIT: + res = nd_strcat_s(Buffer, BufferSize, gRegYmm[pOp->Info.Memory.Index]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + case ND_SIZE_512BIT: + res = nd_strcat_s(Buffer, BufferSize, gRegZmm[pOp->Info.Memory.Index]); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + break; + default: + return ND_STATUS_INVALID_INSTRUX; + } + + // If index is present, scale is also present. + if (pOp->Info.Memory.Scale != 1 && !pOp->Info.Memory.IsMib) + { + status = NdSprintf(temp, sizeof(temp), "*%d", pOp->Info.Memory.Scale); + if (!ND_SUCCESS(status)) + { + return status; + } + + res = nd_strcat_s(Buffer, BufferSize, temp); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + } + + // Handle displacement. + if (pOp->Info.Memory.HasDisp) + { + uint64_t normDisp, disp; + + disp = pOp->Info.Memory.Disp; + + // If this is direct addressing (O operand) or we don't have base or index, than we don't normalize + // the displacement, since it is used as a direct offset. Note that the second condition also + // includes the RIP-relative case. + if (pOp->Info.Memory.IsDirect || !(pOp->Info.Memory.HasBase || pOp->Info.Memory.HasIndex)) + { + normDisp = disp; + } + else + { + switch (pOp->Info.Memory.DispSize) + { + case 1: + normDisp = ((disp & 0x80) ? ~disp + 1ULL : disp) & 0xFF; + break; + case 2: + normDisp = ((disp & 0x8000) ? ~disp + 1ULL : disp) & 0xFFFF; + break; + case 4: + normDisp = ((disp & 0x80000000) ? ~disp + 1ULL : disp) & 0xFFFFFFFF; + break; + default: + normDisp = disp; + break; + } + + // Handle compressed displacement. It is fine to cast the normDisp to uint32_t, as the + // compressed displacement only works with uint8_t displacements. Also, in this phase, + // the normDisp is converted to a positive quantity, so no sign-extension is needed. + if (pOp->Info.Memory.HasCompDisp) + { + normDisp = (uint64_t)(uint32_t)normDisp * pOp->Info.Memory.CompDispSize; + } + } + + + // Now displacement. + if (pOp->Info.Memory.HasBase || pOp->Info.Memory.HasIndex) + { + res = nd_strcat_s(Buffer, BufferSize, Instrux->SignDisp ? "-" : "+"); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + + if (pOp->Info.Memory.IsRipRel) + { + uint64_t target = disp + Rip + Instrux->Length; + + if (Instrux->AddrMode == ND_ADDR_32) + { + target &= 0xFFFFFFFF; + } + + status = NdSprintf(temp, sizeof(temp), "rel 0x%llx", target); + } + else + { + uint8_t trimSize; + + trimSize = (Instrux->AddrMode == ND_ADDR_16) ? 2 : ((Instrux->AddrMode == ND_ADDR_32) ? 4 : 8); + + // Truncate the displacement size to the size of the address length. + normDisp = ND_TRIM(trimSize, normDisp); + + status = NdSprintf(temp, sizeof(temp), "0x%llx", normDisp); + } + if (!ND_SUCCESS(status)) + { + return status; + } + + res = nd_strcat_s(Buffer, BufferSize, temp); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + + // And the ending "]" + res = nd_strcat_s(Buffer, BufferSize, "]"); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + break; + + default: + return ND_STATUS_INVALID_INSTRUX; + } + + // Handle memory broadcast. + if (pOp->Decorator.HasBroadcast) + { + status = NdSprintf(temp, sizeof(temp), "{1to%d}", pOp->Decorator.Broadcast.Count); + if (!ND_SUCCESS(status)) + { + return status; + } + + res = nd_strcat_s(Buffer, BufferSize, temp); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + + // Handle masking. + if (pOp->Decorator.HasMask) + { + if (pOp->Decorator.Mask.Msk >= ND_MAX_MSK_REGS) + { + return ND_STATUS_INVALID_INSTRUX; + } + + status = NdSprintf(temp, sizeof(temp), "{%s}", gRegMask[pOp->Decorator.Mask.Msk]); + if (!ND_SUCCESS(status)) + { + return status; + } + + res = nd_strcat_s(Buffer, BufferSize, temp); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + + // Handle zeroing. Note that zeroing without masking is ignored. + if (pOp->Decorator.HasZero && pOp->Decorator.HasMask) + { + res = nd_strcat_s(Buffer, BufferSize, "{z}"); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + + // Append Suppress All Exceptions decorator. + if (pOp->Decorator.HasSae && !pOp->Decorator.HasEr) + { + // ER implies SAE, so if we have ER, we will list that. + res = nd_strcat_s(Buffer, BufferSize, ", {sae}"); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + + // Append Embedded Rounding decorator. + if (pOp->Decorator.HasEr) + { + if (Instrux->RoundingMode >= 4) + { + return ND_STATUS_INVALID_INSTRUX; + } + + status = NdSprintf(temp, sizeof(temp), ", {%s-sae}", gEmbeddedRounding[Instrux->RoundingMode]); + if (!ND_SUCCESS(status)) + { + return status; + } + + res = nd_strcat_s(Buffer, BufferSize, temp); + RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW); + } + } + + return ND_STATUS_SUCCESS; +} +#else +NDSTATUS +NdToText( + const INSTRUX *Instrux, + uint64_t Rip, + uint32_t BufferSize, + char *Buffer + ) +{ + UNREFERENCED_PARAMETER(Instrux); + UNREFERENCED_PARAMETER(Rip); + + // At least make sure the buffer is NULL-terminated so integrators can use NdToText without checking if the + // BDDISASM_NO_FORMAT macro is defined. This makes switching between versions with formatting and versions without + // formatting easier. + if (Buffer != NULL && BufferSize >= 1) + { + *Buffer = '\0'; + } + + return ND_STATUS_SUCCESS; +} +#endif // !BDDISASM_NO_FORMAT diff --git a/bddisasm/include/nd_crt.h b/bddisasm/include/nd_crt.h index da38ef8..b1281cd 100644 --- a/bddisasm/include/nd_crt.h +++ b/bddisasm/include/nd_crt.h @@ -90,4 +90,11 @@ extern void *nd_memset(void *s, int c, size_t n); #define nd_memzero(Dest, Size) nd_memset((Dest), 0, (Size)) + +// Handy macros. +#define RET_EQ(x, y, z) if ((x) == (y)) { return (z); } +#define RET_GE(x, y, z) if ((x) >= (y)) { return (z); } +#define RET_GT(x, y, z) if ((x) > (y)) { return (z); } + + #endif // ND_CRT_H diff --git a/bdshemu/bdshemu.c b/bdshemu/bdshemu.c index 2de47b0..ef89ca2 100644 --- a/bdshemu/bdshemu.c +++ b/bdshemu/bdshemu.c @@ -1693,8 +1693,18 @@ ShemuPrintContext( Context->Registers.RegR8, Context->Registers.RegR9, Context->Registers.RegR10, Context->Registers.RegR11); shemu_printf(Context, " R12 = 0x%016llx R13 = 0x%016llx R14 = 0x%016llx R15 = 0x%016llx\n", Context->Registers.RegR12, Context->Registers.RegR13, Context->Registers.RegR14, Context->Registers.RegR15); - shemu_printf(Context, " RIP = 0x%016llx RFLAGS = 0x%016llx\n", + shemu_printf(Context, " RIP = 0x%016llx RFLAGS = 0x%016llx ", Context->Registers.RegRip, Context->Registers.RegFlags); + shemu_printf(Context, " CF:%d PF:%d AF:%d ZF:%d SF:%d TF:%d IF:%d DF:%d OF:%d\n", + GET_FLAG(Context, NDR_RFLAG_CF), + GET_FLAG(Context, NDR_RFLAG_PF), + GET_FLAG(Context, NDR_RFLAG_AF), + GET_FLAG(Context, NDR_RFLAG_ZF), + GET_FLAG(Context, NDR_RFLAG_SF), + GET_FLAG(Context, NDR_RFLAG_TF), + GET_FLAG(Context, NDR_RFLAG_IF), + GET_FLAG(Context, NDR_RFLAG_DF), + GET_FLAG(Context, NDR_RFLAG_OF)); shemu_printf(Context, "Emulating: 0x%016llx %s\n", Context->Registers.RegRip, text); } @@ -1711,6 +1721,7 @@ ShemuEmulate( SHEMU_VALUE res = { 0 }, dst = { 0 }, src = { 0 }, rcx = { 0 }, aux = { 0 }; bool stop = false, cf; uint16_t cs = 0; + uint64_t tsc = 0x1248fe7a5c30; if (NULL == Context) { @@ -1753,6 +1764,8 @@ ShemuEmulate( uint64_t rip; uint32_t i; + tsc++; + // Reset all the operands to 0. nd_memzero(&dst, sizeof(dst)); nd_memzero(&src, sizeof(src)); @@ -3058,6 +3071,17 @@ check_far_branch: stop = true; break; + case ND_INS_SIDT: + if (Context->Ring == 0) + { + // Flag this only in ring0, as we treat the SHEMU_FLAG_SIDT as a ring0 specific indicator - it can be + // used to locate the kernel image. + Context->Flags |= SHEMU_FLAG_SIDT; + } + + stop = true; + break; + case ND_INS_AESIMC: case ND_INS_AESDEC: case ND_INS_AESDECLAST: @@ -3096,6 +3120,16 @@ check_far_branch: break; } + case ND_INS_RDTSC: + src.Size = 4; + // Set EAX to lower 32 bits. + src.Value.Dwords[0] = tsc & 0xFFFFFFFF; + SET_OP(Context, 0, &src); + // Set EDX to upper 32 bits. + src.Value.Dwords[0] = tsc >> 32; + SET_OP(Context, 1, &src); + break; + default: return SHEMU_ABORT_UNSUPPORTED_INSTRUX; diff --git a/bdshemu_test/bdshemu_test.zip b/bdshemu_test/bdshemu_test.zip index 5ed1f78..31cdd81 100644 Binary files a/bdshemu_test/bdshemu_test.zip and b/bdshemu_test/bdshemu_test.zip differ diff --git a/inc/version.h b/inc/version.h index 37f5a59..48c1909 100644 --- a/inc/version.h +++ b/inc/version.h @@ -7,6 +7,6 @@ #define DISASM_VERSION_MAJOR 1 #define DISASM_VERSION_MINOR 34 -#define DISASM_VERSION_REVISION 4 +#define DISASM_VERSION_REVISION 5 #endif // DISASM_VER_H