/* * Copyright (c) 2020 Bitdefender * SPDX-License-Identifier: Apache-2.0 */ // // bdshemu.c // #include "nd_crt.h" #include "bddisasm.h" #include "bdshemu/bdshemu.h" // // A generic emulator value. // typedef struct _SHEMU_VALUE { union { uint8_t Bytes[ND_MAX_REGISTER_SIZE]; uint16_t Words[ND_MAX_REGISTER_SIZE / sizeof(uint16_t)]; uint32_t Dwords[ND_MAX_REGISTER_SIZE / sizeof(uint32_t)]; uint64_t Qwords[ND_MAX_REGISTER_SIZE / sizeof(uint64_t)]; struct { uint16_t FpuControlWord; uint16_t FpuStatusWord; uint16_t FpuTagWord; uint16_t Rsvd; uint32_t FpuDataPointer; uint32_t FpuInstructionPointer; uint32_t FpuLastInstructionOpcode; } FpuEnvironment; struct { uint16_t FpuControlWord; uint16_t FpuStatuwsWord; uint16_t FpuTagWord; uint16_t FpuOpcode; uint64_t FpuRip; uint64_t FpuDataPointer; uint32_t Mxcsr; uint32_t MxcsrMask; } XsaveArea; struct { uint16_t Limit; uint64_t Base; } Descriptor; } Value; ND_OPERAND_SIZE Size; } SHEMU_VALUE, *PSHEMU_VALUE; enum { FM_LOGIC, FM_SHL, FM_SHR, FM_SAR, FM_SUB, FM_ADD, } FLAGS_MODE; #define GET_OP(ctx, op, val) { \ SHEMU_STATUS status = ShemuGetOperandValue(ctx, op, val); \ if (SHEMU_SUCCESS != status) \ { \ return status; \ } \ } #define SET_OP(ctx, op, val) { \ SHEMU_STATUS status = ShemuSetOperandValue(ctx, op, val); \ if (SHEMU_SUCCESS != status) \ { \ return status; \ } \ } #define GET_FLAG(ctx, flg) (!!((ctx)->Registers.RegFlags & (flg))) #define SET_FLAG(ctx, flg, val) ((ctx)->Registers.RegFlags = (val) ? ((ctx)->Registers.RegFlags | flg) : \ ((ctx)->Registers.RegFlags & ~(flg))) #define SET_FLAGS(ctx, dst, src1, src2, fm) ShemuSetFlags(ctx, dst.Value.Qwords[0], src.Value.Qwords[0], \ src2.Value.Qwords[0], dst.Size, fm) #define SHELLBMP(ctx) ((ctx)->Intbuf) #define STACKBMP(ctx) ((ctx)->Intbuf + (ctx)->ShellcodeSize) #define SHELLBMP_SIZE(ctx) ((ctx)->ShellcodeSize) #define STACKBMP_SIZE(ctx) ((ctx)->StackSize) #define MAX(a, b) ((a) < (b) ? (b) : (a)) #define MIN(a, b) ((a) > (b) ? (b) : (a)) // // ShemuPrintf - simple version // static void shemu_printf( SHEMU_CONTEXT *Context, char *formatstring, ... ) { char buff[1024]; va_list args; if (NULL == Context->Log) { return; } nd_memzero(buff, sizeof(buff)); va_start(args, formatstring); nd_vsnprintf_s(buff, sizeof(buff), sizeof(buff) - 1, formatstring, args); va_end(args); Context->Log(buff); } // // shemu_memcpy // static void * shemu_memcpy( void *Dest, const void *Source, size_t Size ) { void *start = Dest; uint32_t index = 0; if (NULL == Dest) { return NULL; } if (NULL == Source) { return NULL; } while (Size--) { *(char *)Dest = *((char *)Source + index); Dest = (char *)Dest + 1; index++; } return start; } // // ShemuBts // inline static uint8_t ShemuBts( uint8_t *BitMap, uint64_t Position ) { uint8_t old; old = (BitMap[Position / 8] >> (Position % 8)) & 1; BitMap[Position / 8] |= 1 << (Position % 8); return old; } // // ShemuBtr // inline static uint8_t ShemuBtr( uint8_t *BitMap, uint64_t Position ) { uint8_t old; old = (BitMap[Position / 8] >> (Position % 8)) & 1; BitMap[Position / 8] &= ~(1 << (Position % 8)); return old; } // // ShemuBt // inline static uint8_t ShemuBt( uint8_t *BitMap, uint64_t Position ) { return (BitMap[Position / 8] >> (Position % 8)) & 1; } // // ShemuSetBits // static void ShemuSetBits( uint8_t *Bitmap, uint64_t Start, uint64_t Size, bool Val ) // // No size validations here; the caller has to make sure the ranges are all good. // { uint64_t i; for (i = 0; i < Size; i++) { if (Val) { ShemuBts(Bitmap, (uint64_t)(Start + i)); } else { ShemuBtr(Bitmap, (uint64_t)(Start + i)); } } } // // ShemuAllBitsSet // static bool ShemuAllBitsSet( uint8_t *Bitmap, uint64_t Start, uint32_t Size ) // // No size validations here; the caller has to make sure the ranges are all good. // { uint32_t i; for (i = 0; i < Size; i++) { if (!ShemuBt(Bitmap, (uint64_t)(Start + i))) { return false; } } return true; } // // ShemuAnyBitsSet // static bool ShemuAnyBitsSet( uint8_t *Bitmap, uint64_t Start, uint32_t Size ) // // No size validations here; the caller has to make sure the ranges are all good. // { uint32_t i; for (i = 0; i < Size; i++) { if (ShemuBt(Bitmap, (uint64_t)(Start + i))) { return true; } } return false; } // // ShemuSetFlags // static void ShemuSetFlags( SHEMU_CONTEXT *Context, uint64_t Dst, uint64_t Src1, uint64_t Src2, ND_OPERAND_SIZE Size, uint8_t FlagsMode ) { uint8_t pfArr[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; // Mask the operands with their respective size. Dst = ND_TRIM(Size, Dst); Src1 = ND_TRIM(Size, Src1); Src2 = ND_TRIM(Size, Src2); // PF set if the first bytes has an even number of 1 bits. if ((pfArr[Dst & 0xF] + pfArr[(Dst >> 4) & 0xF]) % 2 == 0) { Context->Registers.RegFlags |= NDR_RFLAG_PF; } else { Context->Registers.RegFlags &= ~NDR_RFLAG_PF; } // ZF set if the result is zero. if (Dst == 0) { Context->Registers.RegFlags |= NDR_RFLAG_ZF; } else { Context->Registers.RegFlags &= ~NDR_RFLAG_ZF; } // SF is set if the sign flag is set. if (ND_GET_SIGN(Size, Dst) != 0) { Context->Registers.RegFlags |= NDR_RFLAG_SF; } else { Context->Registers.RegFlags &= ~NDR_RFLAG_SF; } // OF and CF are handled differently for some instructions. if (FM_LOGIC == FlagsMode) { // OF and CF are cleared on logic instructions. Context->Registers.RegFlags &= ~(NDR_RFLAG_OF | NDR_RFLAG_CF); } else if (FM_SHL == FlagsMode) { // CF is the last bit shifted out of the destination. if (ND_GET_BIT(Src1, (Size * 8) - Src2)) { Context->Registers.RegFlags |= NDR_RFLAG_CF; } else { Context->Registers.RegFlags &= ~NDR_RFLAG_CF; } if (Src2 == 1) { if (ND_GET_BIT(Size * 8 - 1, Dst) ^ ND_GET_BIT(Src1, (Size * 8) - Src2)) { Context->Registers.RegFlags |= NDR_RFLAG_OF; } else { Context->Registers.RegFlags &= ~NDR_RFLAG_OF; } } } else if (FM_SHR == FlagsMode) { // CF is the last bit shifted out of the destination. if (ND_GET_BIT(Src1, Src2 - 1)) { Context->Registers.RegFlags |= NDR_RFLAG_CF; } else { Context->Registers.RegFlags &= ~NDR_RFLAG_CF; } if (Src2 == 1) { if (ND_GET_BIT(Size * 8 - 1, Dst)) { Context->Registers.RegFlags |= NDR_RFLAG_OF; } else { Context->Registers.RegFlags &= ~NDR_RFLAG_OF; } } } else if (FM_SAR == FlagsMode) { // CF is the last bit shifted out of the destination. if (ND_GET_BIT(Src1, Src2 - 1)) { Context->Registers.RegFlags |= NDR_RFLAG_CF; } else { Context->Registers.RegFlags &= ~NDR_RFLAG_CF; } Context->Registers.RegFlags &= ~NDR_RFLAG_OF; } else { // Set CF. if ((FM_SUB == FlagsMode) && (Src1 < Src2)) { Context->Registers.RegFlags |= NDR_RFLAG_CF; } else if ((FM_ADD == FlagsMode) && (Dst < Src1)) { Context->Registers.RegFlags |= NDR_RFLAG_CF; } else { Context->Registers.RegFlags &= ~NDR_RFLAG_CF; } // Set OF. if (FM_SUB == FlagsMode) { if ((ND_GET_SIGN(Size, Src1) && !ND_GET_SIGN(Size, Src2) && !ND_GET_SIGN(Size, Dst)) || (!ND_GET_SIGN(Size, Src1) && ND_GET_SIGN(Size, Src2) && ND_GET_SIGN(Size, Dst))) { Context->Registers.RegFlags |= NDR_RFLAG_OF; } else { Context->Registers.RegFlags &= ~NDR_RFLAG_OF; } } else if (FM_ADD == FlagsMode) { if (ND_GET_SIGN(Size, Src1) == ND_GET_SIGN(Size, Src2) && ND_GET_SIGN(Size, Src1) != ND_GET_SIGN(Size, Dst)) { Context->Registers.RegFlags |= NDR_RFLAG_OF; } else { Context->Registers.RegFlags &= ~NDR_RFLAG_OF; } } } } // // ShemuEvalCondition // static bool ShemuEvalCondition( SHEMU_CONTEXT *Context, uint8_t ConditionCode ) { switch (ConditionCode) { case ND_COND_OVERFLOW: // O if (GET_FLAG(Context, NDR_RFLAG_OF) == 1) { return true; } break; case ND_COND_NOT(ND_COND_OVERFLOW): // NO if (GET_FLAG(Context, NDR_RFLAG_OF) == 0) { return true; } break; case ND_COND_CARRY: // C/B/NAE if (GET_FLAG(Context, NDR_RFLAG_CF) == 1) { return true; } break; case ND_COND_NOT(ND_COND_CARRY): // NC/NB/AE if (GET_FLAG(Context, NDR_RFLAG_CF) == 0) { return true; } break; case ND_COND_ZERO: // E/Z if (GET_FLAG(Context, NDR_RFLAG_ZF) == 1) { return true; } break; case ND_COND_NOT(ND_COND_ZERO): // NE/NZ if (GET_FLAG(Context, NDR_RFLAG_ZF) == 0) { return true; } break; case ND_COND_BELOW_OR_EQUAL: // BE/NA if ((GET_FLAG(Context, NDR_RFLAG_CF) | (GET_FLAG(Context, NDR_RFLAG_ZF))) == 1) { return true; } break; case ND_COND_NOT(ND_COND_BELOW_OR_EQUAL): // A/NBE if ((GET_FLAG(Context, NDR_RFLAG_CF) | (GET_FLAG(Context, NDR_RFLAG_ZF))) == 0) { return true; } break; case ND_COND_SIGN: // S if (GET_FLAG(Context, NDR_RFLAG_SF) == 1) { return true; } break; case ND_COND_NOT(ND_COND_SIGN): // NS if (GET_FLAG(Context, NDR_RFLAG_SF) == 0) { return true; } break; case ND_COND_PARITY: // P if (GET_FLAG(Context, NDR_RFLAG_PF) == 1) { return true; } break; case ND_COND_NOT(ND_COND_PARITY): // NP if (GET_FLAG(Context, NDR_RFLAG_PF) == 0) { return true; } break; case ND_COND_LESS: // L/NGE if ((GET_FLAG(Context, NDR_RFLAG_SF) ^ GET_FLAG(Context, NDR_RFLAG_OF)) == 1) { return true; } break; case ND_COND_NOT(ND_COND_LESS): // NL/GE if ((GET_FLAG(Context, NDR_RFLAG_SF) ^ GET_FLAG(Context, NDR_RFLAG_OF)) == 0) { return true; } break; case ND_COND_LESS_OR_EQUAL: // LE/NG if (((GET_FLAG(Context, NDR_RFLAG_SF) ^ GET_FLAG(Context, NDR_RFLAG_OF)) | (GET_FLAG(Context, NDR_RFLAG_ZF))) == 1) { return true; } break; case ND_COND_NOT(ND_COND_LESS_OR_EQUAL): // NLE/G if (((GET_FLAG(Context, NDR_RFLAG_SF) ^ GET_FLAG(Context, NDR_RFLAG_OF)) | (GET_FLAG(Context, NDR_RFLAG_ZF))) == 0) { return true; } break; } return false; } // // ShemuIsShellcodePtr // inline static bool ShemuIsShellcodePtr( SHEMU_CONTEXT *Context, uint64_t Gla, uint32_t Size ) { return (Gla >= Context->ShellcodeBase && Gla < Context->ShellcodeBase + Context->ShellcodeSize && Gla + Size > Context->ShellcodeBase && Gla + Size <= Context->ShellcodeBase + Context->ShellcodeSize); } // // ShemuIsStackPtr // inline static bool ShemuIsStackPtr( SHEMU_CONTEXT *Context, uint64_t Gla, uint32_t Size ) { return (Gla >= Context->StackBase && Gla < Context->StackBase + Context->StackSize && Gla + Size > Context->StackBase && Gla + Size <= Context->StackBase + Context->StackSize); } // // ShemuGetGprValue // static uint64_t ShemuGetGprValue( SHEMU_CONTEXT *Context, uint32_t Reg, uint32_t Size, bool High8 ) { switch (Size) { case 1: if (High8) { // AH, CH, DH or BH accessed. return (*(&Context->Registers.RegRax + Reg - 4) >> 8) & 0xff; } else { return *(&Context->Registers.RegRax + Reg) & 0xff; } case 2: return *(&Context->Registers.RegRax + Reg) & 0xffff; case 4: return *(&Context->Registers.RegRax + Reg) & 0xffffffff; default: return *(&Context->Registers.RegRax + Reg); } } // // ShemuGetGprValue // static void ShemuSetGprValue( SHEMU_CONTEXT *Context, uint32_t Reg, uint32_t Size, uint64_t Value, bool High8 ) { switch (Size) { case 1: if (High8) { // AH, CH, DH or BH accessed. *((uint8_t *)(&Context->Registers.RegRax + Reg - 4) + 1) = Value & 0xFF; } else { *((uint8_t *)(&Context->Registers.RegRax + Reg)) = Value & 0xff; } break; case 2: *((uint16_t *)(&Context->Registers.RegRax + Reg)) = Value & 0xffff; break; case 4: // Higher uint32_t is always set to zero. *(&Context->Registers.RegRax + Reg) = Value & 0xffffffff; break; default: *(&Context->Registers.RegRax + Reg) = Value; break; } } // // ShemuGetSegValue // static uint64_t ShemuGetSegValue( SHEMU_CONTEXT *Context, uint32_t Reg ) { switch (Reg) { case NDR_ES: return Context->Segments.Es.Selector; case NDR_CS: return Context->Segments.Cs.Selector; case NDR_SS: return Context->Segments.Ss.Selector; case NDR_DS: return Context->Segments.Ds.Selector; case NDR_FS: return Context->Segments.Fs.Selector; case NDR_GS: return Context->Segments.Gs.Selector; } return 0; } // // ShemuSetSegValue // static void ShemuSetSegValue( SHEMU_CONTEXT *Context, uint32_t Reg, uint16_t Value ) { switch (Reg) { case NDR_ES: Context->Segments.Es.Selector = Value; break; case NDR_CS: Context->Segments.Cs.Selector = Value; break; case NDR_SS: Context->Segments.Ss.Selector = Value; break; case NDR_DS: Context->Segments.Ds.Selector = Value; break; case NDR_FS: Context->Segments.Fs.Selector = Value; break; case NDR_GS: Context->Segments.Gs.Selector = Value; break; } } // // ShemuGetSegBase // static uint64_t ShemuGetSegBase( SHEMU_CONTEXT *Context, uint32_t Reg ) { switch (Reg) { case NDR_ES: return Context->Segments.Es.Base; case NDR_CS: return Context->Segments.Cs.Base; case NDR_SS: return Context->Segments.Ss.Base; case NDR_DS: return Context->Segments.Ds.Base; case NDR_FS: return Context->Segments.Fs.Base; case NDR_GS: return Context->Segments.Gs.Base; } return 0; } // // ShemuComputeLinearAddress // static uint64_t ShemuComputeLinearAddress( SHEMU_CONTEXT *Context, PND_OPERAND Operand ) { uint64_t gla = 0; if (Operand->Info.Memory.HasBase) { gla += ShemuGetGprValue(Context, Operand->Info.Memory.Base, Operand->Info.Memory.BaseSize, false); } if (Operand->Info.Memory.HasIndex) { gla += ShemuGetGprValue(Context, Operand->Info.Memory.Index, Operand->Info.Memory.IndexSize, false) * Operand->Info.Memory.Scale; } // Note that this also handles the case where moffset (absolute addressing) is used, as HasDisp will be set when // IsDirect is also set. if (Operand->Info.Memory.HasDisp) { gla += Operand->Info.Memory.Disp; } if (Operand->Info.Memory.IsRipRel) { gla += Context->Registers.RegRip; } // Special handling for BT, BTR, BTS, BTC instructions with bitbase addressing. if (Operand->Info.Memory.IsBitbase) { uint64_t bitbase, op1size, op2size, reg; op1size = Context->Instruction.Operands[0].Size; op2size = Context->Instruction.Operands[1].Size; reg = ((uint64_t*)&Context->Registers.RegRax)[Context->Instruction.Operands[1].Info.Register.Reg]; // Note: only BT* with register source (NOT immediate) support bitbase addressing. bitbase = ND_SIGN_EX(op2size, reg); if (bitbase & (1ULL << 63)) { gla -= ((~bitbase >> 3) & ~(op1size - 1)) + op1size; } else { gla += (bitbase >> 3) & ~(op1size - 1); } } // Special handling for stack operations: if we have a PUSH, we have to subtract the accessed size, as, in fact, // [RSP - size] is accessed, not [RSP]. if (Operand->Info.Memory.IsStack) { if (Operand->Access.Write || Operand->Access.CondWrite) { gla -= Operand->Size; } } // Make sure we truncate the linear address to the address size. switch (Context->Instruction.AddrMode) { case ND_ADDR_32: gla &= 0xFFFFFFFF; break; case ND_ADDR_16: gla &= 0xFFFF; default: break; } // Memory operands usually have a segment. Note that we don't care about any segment checks, since we're most // likely be provided with flat segments. If checks should be needed, dedicated callbacks should be added. if (Operand->Info.Memory.HasSeg) { gla += ShemuGetSegBase(Context, Operand->Info.Memory.Seg); if (Context->Mode != ND_CODE_64) { // Truncate to 32 bit outside 64 bit. gla &= 0xFFFFFFFF; } } return gla; } // // ShemuGetMemValue // static SHEMU_STATUS ShemuGetMemValue( SHEMU_CONTEXT *Context, uint64_t Gla, uint32_t Size, uint8_t *Value ) { uint8_t *addr; uint32_t offset; if (ShemuIsShellcodePtr(Context, Gla, Size)) { addr = Context->Shellcode; offset = (uint32_t)(Gla - Context->ShellcodeBase); } else if (ShemuIsStackPtr(Context, Gla, Size)) { addr = Context->Stack; offset = (uint32_t)(Gla - Context->StackBase); } else { bool res = false; // We allow a maximum number of external memory accesses, due to performance reasons. if (++Context->ExtMemAccess > Context->MemThreshold) { return SHEMU_ABORT_GLA_OUTSIDE; } // NOTE: The accessed GLA may partially access an internal address (shellcode or stack) and an external address. // Since the AccessMemory callback can be provided with the full SHEMU_CONTEXT, the integrator can choose how // to handle those accesses; some options are: // - Don't handle them at all, and return error (false); // - Handle them by reading the actual memory value; this has the disadvantage that if the shellcode/stack // portion has been modified due to emulation, the AccessMemory function would return the original memory // value; // - Handle them properly, by returning the emulated values for the internal addresses, and the external // values for the external addresses. // bdshemu does not care directly about this, and lets the integrator choose his own strategy. if (NULL != Context->AccessMemory) { res = Context->AccessMemory(Context, Gla, Size, Value, false); } if (res) { return SHEMU_SUCCESS; } return SHEMU_ABORT_GLA_OUTSIDE; } switch (Size) { case 1: *Value = *(addr + offset); break; case 2: *(uint16_t *)Value = *(uint16_t *)(addr + offset); break; case 4: *(uint32_t *)Value = *(uint32_t *)(addr + offset); break; case 8: *(uint64_t *)Value = *(uint64_t *)(addr + offset); break; default: shemu_memcpy(Value, addr + offset, Size); break; } return SHEMU_SUCCESS; } // // ShemuSetMemValue // static SHEMU_STATUS ShemuSetMemValue( SHEMU_CONTEXT *Context, uint64_t Gla, uint32_t Size, uint8_t *Value ) { uint8_t *addr; uint32_t offset; if (ShemuIsShellcodePtr(Context, Gla, Size)) { addr = Context->Shellcode; offset = (uint32_t)(Gla - Context->ShellcodeBase); } else if (ShemuIsStackPtr(Context, Gla, Size)) { addr = Context->Stack; offset = (uint32_t)(Gla - Context->StackBase); } else { bool res = false; // We allow a maximum number of external memory accesses, due to performance reasons. if (++Context->ExtMemAccess > Context->MemThreshold) { return SHEMU_ABORT_GLA_OUTSIDE; } // Handling external stores made by the shellcode can be done in a variety of ways by the integrator. Some // of the solutions are: // - Abort on external stores; this will cause the emulation to immediately stop; // - Discard external stores; this is very simple, and it assumes that modified memory addresses will // not be read later on by the shellcode; // - Create a store-buffer like structure, where every external store is cached; when a load is issued on // a previously written address, the value from the store-buffer can be returned; // For obvious reasons, actually storing the value at the indicated address is a very, very bad idea. if (NULL != Context->AccessMemory) { res = Context->AccessMemory(Context, Gla, Size, Value, true); } if (res) { return SHEMU_SUCCESS; } return SHEMU_ABORT_GLA_OUTSIDE; } switch (Size) { case 1: *(addr + offset) = *Value & 0xff; break; case 2: *(uint16_t *)(addr + offset) = *(uint16_t *)Value & 0xffff; break; case 4: *(uint32_t *)(addr + offset) = *(uint32_t *)Value & 0xffffffff; break; case 8: *(uint64_t *)(addr + offset) = *(uint64_t *)Value; break; default: shemu_memcpy(addr + offset, Value, Size); break; } return SHEMU_SUCCESS; } // // IntWinShcSetOperandValue // static SHEMU_STATUS ShemuGetOperandValue( SHEMU_CONTEXT *Context, uint32_t Operand, SHEMU_VALUE *Value ) { SHEMU_STATUS status; PND_OPERAND op = &Context->Instruction.Operands[Operand]; Value->Size = op->Size; if (Value->Size > sizeof(Value->Value)) { return SHEMU_ABORT_OP_TOO_LARGE; } if (op->Type == ND_OP_REG) { switch (op->Info.Register.Type) { case ND_REG_GPR: Value->Value.Qwords[0] = ShemuGetGprValue(Context, op->Info.Register.Reg, op->Size, op->Info.Register.IsHigh8); break; case ND_REG_SEG: Value->Value.Qwords[0] = ShemuGetSegValue(Context, op->Info.Register.Reg); break; case ND_REG_MMX: Value->Value.Qwords[0] = Context->MmxRegisters[op->Info.Register.Reg]; break; case ND_REG_SSE: shemu_memcpy(Value->Value.Bytes, &Context->SseRegisters[op->Info.Register.Reg * ND_MAX_REGISTER_SIZE], op->Size); break; case ND_REG_RIP: Value->Value.Qwords[0] = ND_TRIM(Value->Size, Context->Registers.RegRip); break; case ND_REG_FLG: Value->Value.Qwords[0] = Context->Registers.RegFlags; break; case ND_REG_CR: switch (op->Info.Register.Reg) { case NDR_CR0: Value->Value.Qwords[0] = Context->Registers.RegCr0; break; case NDR_CR2: Value->Value.Qwords[0] = Context->Registers.RegCr2; break; case NDR_CR3: Value->Value.Qwords[0] = Context->Registers.RegCr3; break; case NDR_CR4: Value->Value.Qwords[0] = Context->Registers.RegCr4; break; case NDR_CR8: Value->Value.Qwords[0] = Context->Registers.RegCr8; break; default: return SHEMU_ABORT_REGISTER_NOT_SUPPORTED; } break; default: return SHEMU_ABORT_REGISTER_NOT_SUPPORTED; } } else if (op->Type == ND_OP_MEM) { uint64_t gla = ShemuComputeLinearAddress(Context, op); if (op->Info.Memory.IsAG) { // Address generation instruction, the result is the linear address itself. Value->Value.Qwords[0] = gla; } else { uint32_t offset; uint8_t seg; if (Context->Ring == 3) { // User-mode TIB offset that contains the PEB address. offset = Context->Mode == ND_CODE_32 ? 0x30 : 0x60; seg = Context->Mode == ND_CODE_32 ? ND_PREFIX_G2_SEG_FS : ND_PREFIX_G2_SEG_GS; } else { // Kernel-mode KPCR offset that contains the current KTHREAD address. offset = Context->Mode == ND_CODE_32 ? 0x124 : 0x188; seg = Context->Mode == ND_CODE_32 ? ND_PREFIX_G2_SEG_FS : ND_PREFIX_G2_SEG_GS; } // Check if this is a TIB/PCR access. Make sure the FS/GS register is used for the access, in order to avoid // false positives where legitimate code accesses a linear TIB directly. if (gla == Context->TibBase + offset && Context->Instruction.Seg == seg) { Context->Flags |= SHEMU_FLAG_TIB_ACCESS; } // Check if we are reading a previously saved RIP. Ignore RET category, which naturally uses the saved RIP. // Also, ignore RMW instruction which naturally read the current value - this could happen if the code // modifies the return value, for example "ADD qword [rsp], r8". if (Context->Instruction.Category != ND_CAT_RET && !(op->Access.Access & ND_ACCESS_ANY_WRITE) && ShemuIsStackPtr(Context, gla, op->Size) && ShemuAnyBitsSet(STACKBMP(Context), gla - Context->StackBase, op->Size)) { Context->Flags |= SHEMU_FLAG_LOAD_RIP; } // Get the memory value. status = ShemuGetMemValue(Context, gla, Value->Size, Value->Value.Bytes); if (SHEMU_SUCCESS != status) { return status; } // If this is a stack access, we need to update the stack pointer. if (op->Info.Memory.IsStack) { uint64_t regval = ShemuGetGprValue(Context, NDR_RSP, (2 << Context->Instruction.DefStack), false); regval += op->Size; ShemuSetGprValue(Context, NDR_RSP, (2 << Context->Instruction.DefStack), regval, false); } // If this is a string operation, make sure we update RSI/RDI. if (op->Info.Memory.IsString) { uint64_t regval = ShemuGetGprValue(Context, op->Info.Memory.Base, op->Info.Memory.BaseSize, false); regval = GET_FLAG(Context, NDR_RFLAG_DF) ? regval - op->Size : regval + op->Size; ShemuSetGprValue(Context, op->Info.Memory.Base, op->Info.Memory.BaseSize, regval, false); } } } else if (op->Type == ND_OP_IMM) { Value->Value.Qwords[0] = op->Info.Immediate.Imm; } else if (op->Type == ND_OP_CONST) { Value->Value.Qwords[0] = op->Info.Constant.Const; } else if (op->Type == ND_OP_OFFS) { Value->Value.Qwords[0] = op->Info.RelativeOffset.Rel; } else { return SHEMU_ABORT_UNSUPPORTED_INSTRUX; } return SHEMU_SUCCESS; } // // IntWinShcSetOperandValue // static SHEMU_STATUS ShemuSetOperandValue( SHEMU_CONTEXT *Context, uint32_t Operand, SHEMU_VALUE *Value ) { SHEMU_STATUS status; PND_OPERAND op = &Context->Instruction.Operands[Operand]; if (op->Type == ND_OP_REG) { switch (op->Info.Register.Type) { case ND_REG_GPR: ShemuSetGprValue(Context, op->Info.Register.Reg, op->Size, Value->Value.Qwords[0], op->Info.Register.IsHigh8); break; case ND_REG_SEG: ShemuSetSegValue(Context, op->Info.Register.Reg, Value->Value.Words[0]); break; case ND_REG_MMX: Context->MmxRegisters[op->Info.Register.Reg] = Value->Value.Qwords[0]; // Only log these when they're written. shemu_printf(Context, " MM%d = 0x%016llx\n", op->Info.Register.Reg, Value->Value.Qwords[0]); break; case ND_REG_SSE: shemu_memcpy(&Context->SseRegisters[op->Info.Register.Reg * ND_MAX_REGISTER_SIZE], Value->Value.Bytes, op->Size); // Only log these when they're written. shemu_printf(Context, " %cMM%d (HI_32) = 0x%016llx%016llx%016llx%016llx\n", op->Size == 16 ? 'X' : op->Size == 32 ? 'Y' : 'Z', op->Info.Register.Reg, Value->Value.Qwords[7], Value->Value.Qwords[6], Value->Value.Qwords[5], Value->Value.Qwords[4]); shemu_printf(Context, " %cMM%d (LO_32) = 0x%016llx%016llx%016llx%016llx\n", op->Size == 16 ? 'X' : op->Size == 32 ? 'Y' : 'Z', op->Info.Register.Reg, Value->Value.Qwords[3], Value->Value.Qwords[2], Value->Value.Qwords[1], Value->Value.Qwords[0]); break; case ND_REG_RIP: Context->Registers.RegRip = ND_TRIM(Value->Size, Value->Value.Qwords[0]); break; case ND_REG_FLG: Context->Registers.RegFlags = ND_TRIM(Value->Size, Value->Value.Qwords[0]); break; case ND_REG_CR: switch (op->Info.Register.Reg) { case NDR_CR0: Context->Registers.RegCr0 = Value->Value.Qwords[0]; break; case NDR_CR2: Context->Registers.RegCr2 = Value->Value.Qwords[0]; break; case NDR_CR3: Context->Registers.RegCr3 = Value->Value.Qwords[0]; break; case NDR_CR4: Context->Registers.RegCr4 = Value->Value.Qwords[0]; break; case NDR_CR8: Context->Registers.RegCr8 = Value->Value.Qwords[0]; break; default: return SHEMU_ABORT_REGISTER_NOT_SUPPORTED; } break; default: return SHEMU_ABORT_REGISTER_NOT_SUPPORTED; } } else if (op->Type == ND_OP_MEM) { // Compute the GLA. uint64_t gla = ShemuComputeLinearAddress(Context, op); // Handle self-write. We store a 1 for each written byte inside the shellcode space. Once the modified bytes // are executed, we can trigger the self-write detection. if (ShemuIsShellcodePtr(Context, gla, op->Size)) { ShemuSetBits(SHELLBMP(Context), gla - Context->ShellcodeBase, op->Size, 1); } // Handle RIP save on the stack. if (ShemuIsStackPtr(Context, gla, MAX(op->Size, Context->Instruction.WordLength))) { // Note: only Context->Instruction.WordLength bits are flagged as RIP, as that is the RIP size. if (Context->Instruction.Instruction == ND_INS_CALLNR || Context->Instruction.Instruction == ND_INS_CALLNI) { ShemuSetBits(STACKBMP(Context), gla - Context->StackBase, Context->Instruction.WordLength, 1); } else if (Context->Instruction.Instruction == ND_INS_FNSTENV) { // OK: op->Size will be the FPU state size. ShemuSetBits(STACKBMP(Context), (gla + 0xC) - Context->StackBase, Context->Instruction.WordLength, 1); } else if (Context->Instruction.Instruction == ND_INS_FXSAVE) { // OK: op->Size will be the FXSAVE size. ShemuSetBits(STACKBMP(Context), (gla + 0x8) - Context->StackBase, Context->Instruction.WordLength, 1); } else { // Something is written on a previously saved RIP; reset it. ShemuSetBits(STACKBMP(Context), gla - Context->StackBase, op->Size, 0); } // Check if a string is being saved on the stack. Typically used by shellcodes like this: // PUSH str0 // PUSH str1 // ... // PUSH strn // Other variants may exist, but all we care about are stores on the stack, and all are checked. for (uint32_t i = 0; i < Value->Size; i++) { unsigned char c = Value->Value.Bytes[i]; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '\\' || c == '/' || c == ':' || c == ' ') { Context->StrLength++; if (Context->StrLength >= Context->StrThreshold) { Context->Flags |= SHEMU_FLAG_STACK_STR; break; } } else { Context->StrLength = 0; } } } // Set the value. status = ShemuSetMemValue(Context, gla, MIN(op->Size, Value->Size), Value->Value.Bytes); if (SHEMU_SUCCESS != status) { return status; } // If this is a stack access, we need to update the stack pointer. if (op->Info.Memory.IsStack) { uint64_t regval = ShemuGetGprValue(Context, NDR_RSP, (2 << Context->Instruction.DefStack), false); regval -= op->Size; ShemuSetGprValue(Context, NDR_RSP, (2 << Context->Instruction.DefStack), regval, false); } // If this is a string operation, make sure we update RSI/RDI. if (op->Info.Memory.IsString) { uint64_t regval = ShemuGetGprValue(Context, op->Info.Memory.Base, op->Info.Memory.BaseSize, false); regval = GET_FLAG(Context, NDR_RFLAG_DF) ? regval - op->Size : regval + op->Size; ShemuSetGprValue(Context, op->Info.Memory.Base, op->Info.Memory.BaseSize, regval, false); } } else { return SHEMU_ABORT_INVALID_INSTRUX; } return SHEMU_SUCCESS; } // // ShemuMultiply64Unsigned // static void ShemuMultiply64Unsigned( uint64_t Operand1, uint64_t Operand2, uint64_t *ResHigh, uint64_t *ResLow ) { uint64_t xLow = (uint64_t)(uint32_t)Operand1; uint64_t xHigh = Operand1 >> 32; uint64_t yLow = (uint64_t)(uint32_t)Operand2; uint64_t yHigh = Operand2 >> 32; uint64_t p0 = xLow * yLow; uint64_t p1 = xLow * yHigh; uint64_t p2 = xHigh * yLow; uint64_t p3 = xHigh * yHigh; uint32_t cy = (uint32_t)(((p0 >> 32) + (uint32_t)p1 + (uint32_t)p2) >> 32); *ResLow = p0 + (p1 << 32) + (p2 << 32); *ResHigh = p3 + (p1 >> 32) + (p2 >> 32) + cy; } // // ShemuMultiply64Signed // static void ShemuMultiply64Signed( int64_t Operand1, int64_t Operand2, int64_t *ResHigh, int64_t *ResLow ) { ShemuMultiply64Unsigned((uint64_t)Operand1, (uint64_t)Operand2, (uint64_t *)ResHigh, (uint64_t *)ResLow); if (Operand1 < 0LL) *ResHigh -= Operand2; if (Operand2 < 0LL) *ResHigh -= Operand1; } // // ShemuPrintContext // static void ShemuPrintContext( SHEMU_CONTEXT *Context ) { char text[ND_MIN_BUF_SIZE] = { 0 }; NdToText(&Context->Instruction, Context->Registers.RegRip, ND_MIN_BUF_SIZE, text); shemu_printf(Context, " RAX = 0x%016llx RCX = 0x%016llx RDX = 0x%016llx RBX = 0x%016llx\n", Context->Registers.RegRax, Context->Registers.RegRcx, Context->Registers.RegRdx, Context->Registers.RegRbx); shemu_printf(Context, " RSP = 0x%016llx RBP = 0x%016llx RSI = 0x%016llx RDI = 0x%016llx\n", Context->Registers.RegRsp, Context->Registers.RegRbp, Context->Registers.RegRsi, Context->Registers.RegRdi); shemu_printf(Context, " R8 = 0x%016llx R9 = 0x%016llx R10 = 0x%016llx R11 = 0x%016llx\n", 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", Context->Registers.RegRip, Context->Registers.RegFlags); shemu_printf(Context, "Emulating: 0x%016llx %s\n", Context->Registers.RegRip, text); } // // ShemuEmulate // SHEMU_STATUS ShemuEmulate( SHEMU_CONTEXT *Context ) { NDSTATUS ndstatus; SHEMU_VALUE res = { 0 }, dst = { 0 }, src = { 0 }, rcx = { 0 }, aux = { 0 }; bool stop = false; uint64_t rip = 0; if (NULL == Context) { return SHEMU_ABORT_INVALID_PARAMETER; } if (NULL == Context->Shellcode) { return SHEMU_ABORT_INVALID_PARAMETER; } if (NULL == Context->Stack) { return SHEMU_ABORT_INVALID_PARAMETER; } if (NULL == Context->Intbuf) { return SHEMU_ABORT_INVALID_PARAMETER; } if (SHEMU_INTERNAL_BUFFER_SIZE(Context) > Context->IntbufSize) { return SHEMU_ABORT_INVALID_PARAMETER; } if (Context->NopThreshold == 0) { Context->NopThreshold = SHEMU_DEFAULT_NOP_THRESHOLD; } if (Context->StrThreshold == 0) { Context->StrThreshold = SHEMU_DEFAULT_STR_THRESHOLD; } while (Context->InstructionsCount++ < Context->MaxInstructionsCount) { // The stop flag has been set, this means we've reached a valid instruction, but that instruction cannot be // emulated (for example, SYSCALL, INT, system instructions, etc). if (stop) { return SHEMU_ABORT_CANT_EMULATE; } // If we already have a detection and we wish to stop on detections, do so now. if ((0 != Context->Flags) && (0 != (Context->Options & SHEMU_OPT_STOP_ON_EXPLOIT))) { return SHEMU_ABORT_SHELLCODE_DETECTED; } // Make sure the RIP is pointing in the right area. We test only 1 byte - the decoder will make sure it can // access as many bytes as needed and return error in case it can't. if (!ShemuIsShellcodePtr(Context, Context->Registers.RegRip, 1)) { return SHEMU_ABORT_BRANCH_OUTSIDE; } // Get the offset inside the shellcode buffer. rip = Context->Registers.RegRip - Context->ShellcodeBase; // Decode the next instruction. ndstatus = NdDecodeEx(&Context->Instruction, Context->Shellcode + rip, Context->ShellcodeSize - (size_t)rip, Context->Mode, Context->Mode); if (!ND_SUCCESS(ndstatus)) { if (ND_STATUS_BUFFER_TOO_SMALL == ndstatus) { return SHEMU_ABORT_BRANCH_OUTSIDE; } else { return SHEMU_ABORT_INVALID_INSTRUX; } } // Paranoid check... if (!ShemuIsShellcodePtr(Context, Context->Registers.RegRip, Context->Instruction.Length)) { return SHEMU_ABORT_BRANCH_OUTSIDE; } // Check if we just fetched an instruction from a previously written area, to raise self-write alert. if (ShemuAnyBitsSet(SHELLBMP(Context), rip, Context->Instruction.Length)) { Context->Flags |= SHEMU_FLAG_WRITE_SELF; } // Dump the context. if (Context->Options & SHEMU_OPT_TRACE_EMULATION) { ShemuPrintContext(Context); } // The RIP is incremented BEFORE actually emulating the instruction. This is what the CPU does as well. Context->Registers.RegRip += Context->Instruction.Length; // FPU instructions are "pass-through", we just want to save the RIP, so we can emulate FNSTENV. if ((Context->Instruction.IsaSet == ND_SET_X87) && (Context->Instruction.Instruction != ND_INS_FNSTENV)) { Context->Registers.FpuRip = Context->Registers.RegRip - Context->Instruction.Length; continue; } switch (Context->Instruction.Instruction) { case ND_INS_FNSTENV: src.Size = Context->Instruction.Operands[0].Size; src.Value.FpuEnvironment.FpuInstructionPointer = (uint32_t)Context->Registers.FpuRip; SET_OP(Context, 0, &src); break; case ND_INS_FXSAVE: src.Size = MIN(Context->Instruction.Operands[0].Size, sizeof(src.Value.XsaveArea)); src.Value.XsaveArea.FpuRip = Context->Registers.FpuRip; SET_OP(Context, 0, &src); break; case ND_INS_MOV_CR: if (Context->Ring != 0) { return SHEMU_ABORT_NO_PRIVILEGE; } // Fall through. case ND_INS_MOV: case ND_INS_MOVZX: GET_OP(Context, 1, &src); SET_OP(Context, 0, &src); break; case ND_INS_MOVSX: case ND_INS_MOVSXD: GET_OP(Context, 1, &src); GET_OP(Context, 0, &dst); dst.Value.Qwords[0] = ND_SIGN_EX(src.Size, src.Value.Qwords[0]); SET_OP(Context, 0, &dst); break; case ND_INS_CMOVcc: if (ShemuEvalCondition(Context, Context->Instruction.Condition)) { GET_OP(Context, 1, &src); SET_OP(Context, 0, &src); } break; case ND_INS_SETcc: if (ShemuEvalCondition(Context, Context->Instruction.Condition)) { src.Size = Context->Instruction.Operands[0].Size; src.Value.Qwords[0] = 1; } else { src.Size = Context->Instruction.Operands[0].Size; src.Value.Qwords[0] = 0; } SET_OP(Context, 0, &src); break; case ND_INS_XLATB: GET_OP(Context, 1, &src); SET_OP(Context, 0, &src); break; case ND_INS_XCHG: GET_OP(Context, 1, &src); GET_OP(Context, 0, &dst); SET_OP(Context, 1, &dst); SET_OP(Context, 0, &src); break; case ND_INS_XADD: GET_OP(Context, 1, &src); GET_OP(Context, 0, &dst); res.Size = dst.Size; res.Value.Qwords[0] = dst.Value.Qwords[0] + src.Value.Qwords[0]; SET_FLAGS(Context, res, dst, src, FM_ADD); SET_OP(Context, 1, &dst); SET_OP(Context, 0, &res); break; case ND_INS_CMPXCHG: GET_OP(Context, 2, &src); GET_OP(Context, 0, &dst); if (src.Value.Qwords[0] == dst.Value.Qwords[0]) { GET_OP(Context, 1, &src); SET_OP(Context, 0, &src); SET_FLAG(Context, NDR_RFLAG_ZF, 1); } else { SET_OP(Context, 2, &dst); SET_FLAG(Context, NDR_RFLAG_ZF, 0); } break; case ND_INS_ADD: case ND_INS_ADC: GET_OP(Context, 0, &dst); GET_OP(Context, 1, &src); if (ND_INS_ADC == Context->Instruction.Instruction) { src.Value.Qwords[0] += GET_FLAG(Context, NDR_RFLAG_CF); } res.Size = src.Size; res.Value.Qwords[0] = dst.Value.Qwords[0] + src.Value.Qwords[0]; SET_FLAGS(Context, res, dst, src, FM_ADD); SET_OP(Context, 0, &res); break; case ND_INS_SUB: case ND_INS_SBB: case ND_INS_CMP: GET_OP(Context, 0, &dst); GET_OP(Context, 1, &src); if (ND_INS_SBB == Context->Instruction.Instruction) { src.Value.Qwords[0] += GET_FLAG(Context, NDR_RFLAG_CF); } res.Size = src.Size; res.Value.Qwords[0] = dst.Value.Qwords[0] - src.Value.Qwords[0]; SET_FLAGS(Context, res, dst, src, FM_SUB); if (ND_INS_CMP != Context->Instruction.Instruction) { SET_OP(Context, 0, &res); } break; case ND_INS_INC: GET_OP(Context, 0, &dst); src.Size = dst.Size; src.Value.Qwords[0] = 1; res.Size = src.Size; res.Value.Qwords[0] = dst.Value.Qwords[0] + src.Value.Qwords[0]; SET_FLAGS(Context, res, dst, src, FM_ADD); SET_OP(Context, 0, &res); break; case ND_INS_DEC: GET_OP(Context, 0, &dst); src.Size = dst.Size; src.Value.Qwords[0] = 1; res.Size = src.Size; res.Value.Qwords[0] = dst.Value.Qwords[0] - src.Value.Qwords[0]; SET_FLAGS(Context, res, dst, src, FM_SUB); SET_OP(Context, 0, &res); break; case ND_INS_PUSH: case ND_INS_PUSHF: GET_OP(Context, 0, &src); SET_OP(Context, 1, &src); break; case ND_INS_POP: case ND_INS_POPF: GET_OP(Context, 1, &src); SET_OP(Context, 0, &src); break; case ND_INS_PUSHA: src.Size = 32; src.Value.Dwords[7] = (uint32_t)Context->Registers.RegRax; src.Value.Dwords[6] = (uint32_t)Context->Registers.RegRcx; src.Value.Dwords[5] = (uint32_t)Context->Registers.RegRdx; src.Value.Dwords[4] = (uint32_t)Context->Registers.RegRbx; src.Value.Dwords[3] = (uint32_t)Context->Registers.RegRsp; src.Value.Dwords[2] = (uint32_t)Context->Registers.RegRbp; src.Value.Dwords[1] = (uint32_t)Context->Registers.RegRsi; src.Value.Dwords[0] = (uint32_t)Context->Registers.RegRdi; SET_OP(Context, 1, &src); break; case ND_INS_POPA: GET_OP(Context, 1, &src); Context->Registers.RegRax = src.Value.Dwords[7]; Context->Registers.RegRcx = src.Value.Dwords[6]; Context->Registers.RegRdx = src.Value.Dwords[5]; Context->Registers.RegRbx = src.Value.Dwords[4]; Context->Registers.RegRsp = src.Value.Dwords[3]; Context->Registers.RegRbp = src.Value.Dwords[2]; Context->Registers.RegRsi = src.Value.Dwords[1]; Context->Registers.RegRdi = src.Value.Dwords[0]; break; case ND_INS_LEA: GET_OP(Context, 1, &src); SET_OP(Context, 0, &src); break; case ND_INS_SHL: case ND_INS_SAL: case ND_INS_SHR: case ND_INS_SAR: GET_OP(Context, 0, &dst); GET_OP(Context, 1, &src); if (dst.Size == 8) { src.Value.Qwords[0] &= 0x3f; } else { src.Value.Qwords[0] &= 0x1f; } res.Size = dst.Size; if (ND_INS_SHL == Context->Instruction.Instruction || ND_INS_SAL == Context->Instruction.Instruction) { res.Value.Qwords[0] = dst.Value.Qwords[0] << src.Value.Qwords[0]; } else if (ND_INS_SHR == Context->Instruction.Instruction) { res.Value.Qwords[0] = dst.Value.Qwords[0] >> src.Value.Qwords[0]; } else { int64_t val = ND_SIGN_EX(dst.Size, dst.Value.Qwords[0]); val = val >> src.Value.Qwords[0]; res.Value.Qwords[0] = (uint64_t)val; } if (src.Value.Qwords[0] != 0) { // 0 bit shifts do not affect the flags. if (ND_INS_SHL == Context->Instruction.Instruction || ND_INS_SAL == Context->Instruction.Instruction) { SET_FLAGS(Context, res, dst, src, FM_SHL); } else if (ND_INS_SHR == Context->Instruction.Instruction) { SET_FLAGS(Context, res, dst, src, FM_SHR); } else { SET_FLAGS(Context, res, dst, src, FM_SAR); } } SET_OP(Context, 0, &res); break; case ND_INS_RCL: case ND_INS_RCR: case ND_INS_ROL: case ND_INS_ROR: { uint32_t cnt, tempcnt, cntmask; uint8_t tempCF = 0; GET_OP(Context, 0, &dst); GET_OP(Context, 1, &src); cnt = (uint32_t)src.Value.Qwords[0]; tempcnt = 0; cntmask = ((dst.Size == 8) ? 0x3F : 0x1F); if (ND_INS_RCL == Context->Instruction.Instruction || ND_INS_RCR == Context->Instruction.Instruction) { switch (dst.Size) { case 1: tempcnt = (cnt & 0x1F) % 9; break; case 2: tempcnt = (cnt & 0x1F) % 17; break; case 4: tempcnt = (cnt & 0x1F); break; case 8: tempcnt = (cnt & 0x3F); break; default: break; } } else { tempcnt = (cnt & cntmask) % (dst.Size * 8); } if (ND_INS_RCL == Context->Instruction.Instruction) { while (tempcnt != 0) { tempCF = ND_MSB(dst.Size, dst.Value.Qwords[0]); dst.Value.Qwords[0] = (dst.Value.Qwords[0] << 1) + GET_FLAG(Context, NDR_RFLAG_CF); SET_FLAG(Context, NDR_RFLAG_CF, tempCF); tempcnt--; } if ((cnt & cntmask) == 1) { SET_FLAG(Context, NDR_RFLAG_OF, ND_MSB(dst.Size, dst.Value.Qwords[0]) ^ GET_FLAG(Context, NDR_RFLAG_CF)); } } else if (ND_INS_RCR == Context->Instruction.Instruction) { if ((cnt & cntmask) == 1) { SET_FLAG(Context, NDR_RFLAG_OF, ND_MSB(dst.Size, dst.Value.Qwords[0]) ^ GET_FLAG(Context, NDR_RFLAG_CF)); } while (tempcnt != 0) { tempCF = ND_LSB(dst.Size, dst.Value.Qwords[0]); dst.Value.Qwords[0] = (dst.Value.Qwords[0] >> 1) + ((uint64_t)GET_FLAG(Context, NDR_RFLAG_CF) << (dst.Size * 8 - 1)); SET_FLAG(Context, NDR_RFLAG_CF, tempCF); tempcnt--; } } else if (ND_INS_ROL == Context->Instruction.Instruction) { while (tempcnt != 0) { tempCF = ND_MSB(dst.Size, dst.Value.Qwords[0]); dst.Value.Qwords[0] = (dst.Value.Qwords[0] << 1) + tempCF; tempcnt--; } if ((cnt & cntmask) != 0) { SET_FLAG(Context, NDR_RFLAG_CF, dst.Value.Qwords[0] & 1); } if ((cnt & cntmask) == 1) { SET_FLAG(Context, NDR_RFLAG_OF, ND_MSB(dst.Size, dst.Value.Qwords[0]) ^ GET_FLAG(Context, NDR_RFLAG_CF)); } } else // ND_INS_ROR { while (tempcnt != 0) { tempCF = ND_LSB(dst.Size, dst.Value.Qwords[0]); dst.Value.Qwords[0] = (dst.Value.Qwords[0] >> 1) + ((uint64_t)tempCF << (dst.Size * 8 - 1)); tempcnt--; } if ((cnt & cntmask) != 0) { SET_FLAG(Context, NDR_RFLAG_CF, ND_MSB(dst.Size, dst.Value.Qwords[0])); } if ((cnt & cntmask) == 1) { SET_FLAG(Context, NDR_RFLAG_OF, ND_MSB(dst.Size, dst.Value.Qwords[0]) ^ tempCF); } } SET_OP(Context, 0, &dst); } break; case ND_INS_OR: case ND_INS_XOR: case ND_INS_AND: case ND_INS_TEST: GET_OP(Context, 0, &dst); GET_OP(Context, 1, &src); res.Size = dst.Size; if (ND_INS_OR == Context->Instruction.Instruction) { res.Value.Qwords[0] = dst.Value.Qwords[0] | src.Value.Qwords[0]; } else if (ND_INS_XOR == Context->Instruction.Instruction) { res.Value.Qwords[0] = dst.Value.Qwords[0] ^ src.Value.Qwords[0]; } else { res.Value.Qwords[0] = dst.Value.Qwords[0] & src.Value.Qwords[0]; } if (ND_INS_TEST != Context->Instruction.Instruction) { SET_OP(Context, 0, &res); } SET_FLAGS(Context, res, dst, src, FM_LOGIC); break; case ND_INS_NOT: GET_OP(Context, 0, &dst); dst.Value.Qwords[0] = ~dst.Value.Qwords[0]; SET_OP(Context, 0, &dst); break; case ND_INS_NEG: GET_OP(Context, 0, &dst); dst.Value.Qwords[0] = 0 - dst.Value.Qwords[0]; SET_OP(Context, 0, &dst); break; case ND_INS_BT: case ND_INS_BTS: case ND_INS_BTR: case ND_INS_BTC: GET_OP(Context, 0, &dst); GET_OP(Context, 1, &src); src.Value.Qwords[0] %= dst.Size * 8; // Store the bit inside CF. SET_FLAG(Context, NDR_RFLAG_CF, (dst.Value.Qwords[0] >> src.Value.Qwords[0]) & 1); if (ND_INS_BTS == Context->Instruction.Instruction) { dst.Value.Qwords[0] |= (1ULL << src.Value.Qwords[0]); } else if (ND_INS_BTR == Context->Instruction.Instruction) { dst.Value.Qwords[0] &= ~(1ULL << src.Value.Qwords[0]); } else if (ND_INS_BTC == Context->Instruction.Instruction) { dst.Value.Qwords[0] ^= (1ULL << src.Value.Qwords[0]); } if (ND_INS_BT != Context->Instruction.Instruction) { SET_OP(Context, 0, &dst); } break; case ND_INS_Jcc: if (ShemuEvalCondition(Context, Context->Instruction.Condition)) { // Modify the RIP if the branch is taken. GET_OP(Context, 1, &aux); aux.Value.Qwords[0] += Context->Instruction.Operands[0].Info.RelativeOffset.Rel; SET_OP(Context, 1, &aux); } break; case ND_INS_JrCXZ: // Fetch the rCX value. It could be CX, ECX or RCX, depending on address size. GET_OP(Context, 1, &rcx); if (rcx.Value.Qwords[0] == 0) { // Modify the RIP if the branch is taken. GET_OP(Context, 2, &aux); aux.Value.Qwords[0] += Context->Instruction.Operands[0].Info.RelativeOffset.Rel; SET_OP(Context, 2, &aux); } break; case ND_INS_LOOP: case ND_INS_LOOPNZ: case ND_INS_LOOPZ: // rCX is decremented first. Note that the size depends on address size. GET_OP(Context, 1, &rcx); rcx.Value.Qwords[0]--; SET_OP(Context, 1, &rcx); if (rcx.Value.Qwords[0] > 0) { if (((ND_INS_LOOPNZ == Context->Instruction.Instruction) && (0 == GET_FLAG(Context, NDR_RFLAG_ZF))) || ((ND_INS_LOOPZ == Context->Instruction.Instruction) && (0 != GET_FLAG(Context, NDR_RFLAG_ZF))) || (ND_INS_LOOP == Context->Instruction.Instruction)) { // Modify the RIP if the branch is taken. GET_OP(Context, 2, &aux); aux.Value.Qwords[0] += Context->Instruction.Operands[0].Info.RelativeOffset.Rel; SET_OP(Context, 2, &aux); } } break; case ND_INS_JMPNR: GET_OP(Context, 1, &aux); aux.Value.Qwords[0] += Context->Instruction.Operands[0].Info.RelativeOffset.Rel; SET_OP(Context, 1, &aux); break; case ND_INS_JMPNI: GET_OP(Context, 0, &src); SET_OP(Context, 1, &src); // Set the RIP to the new value. break; case ND_INS_CALLNR: // Save the EIP on the stack. GET_OP(Context, 1, &aux); SET_OP(Context, 2, &aux); aux.Value.Qwords[0] += Context->Instruction.Operands[0].Info.RelativeOffset.Rel; SET_OP(Context, 1, &aux); break; case ND_INS_CALLNI: GET_OP(Context, 0, &src); GET_OP(Context, 1, &dst); // The RIP SET_OP(Context, 2, &dst); // Save the RIP on the stack. SET_OP(Context, 1, &src); // Set the RIP to the new value. break; case ND_INS_RETN: if (!Context->Instruction.HasImm1) { // The simple RET form, 0xC3 GET_OP(Context, 1, &src); SET_OP(Context, 0, &src); } else { // The RET Imm16 form, 0xC2 GET_OP(Context, 3, &src); SET_OP(Context, 1, &src); // Patch the RSP register. GET_OP(Context, 2, &aux); aux.Value.Qwords[0] += Context->Instruction.Operands[0].Info.Immediate.Imm; SET_OP(Context, 2, &aux); } break; case ND_INS_LODS: case ND_INS_STOS: case ND_INS_MOVS: // Fetch the rCX register, which is the third operand in case of repeated instructions. while (Context->InstructionsCount < Context->MaxInstructionsCount) { GET_OP(Context, 2, &rcx); if (Context->Instruction.IsRepeated && (rcx.Value.Qwords[0] == 0)) { break; } // Load the source into the destination. GET_OP(Context, 1, &src); SET_OP(Context, 0, &src); if (Context->Instruction.IsRepeated) { // Decrement RCX. rcx.Value.Qwords[0]--; SET_OP(Context, 2, &rcx); } else { break; } Context->InstructionsCount++; } break; case ND_INS_SCAS: case ND_INS_CMPS: while (Context->InstructionsCount < Context->MaxInstructionsCount) { GET_OP(Context, 2, &rcx); if (Context->Instruction.IsRepeated && (rcx.Value.Qwords[0] == 0)) { break; } GET_OP(Context, 0, &dst); GET_OP(Context, 1, &src); res.Size = dst.Size; res.Value.Qwords[0] = dst.Value.Qwords[0] - src.Value.Qwords[0]; ShemuSetFlags(Context, res.Value.Qwords[0], dst.Value.Qwords[0], src.Value.Qwords[0], res.Size, FM_SUB); if (Context->Instruction.IsRepeated) { // Decrement RCX. rcx.Value.Qwords[0]--; SET_OP(Context, 2, &rcx); if (Context->Instruction.HasRepRepzXrelease && !GET_FLAG(Context, NDR_RFLAG_ZF)) { break; } if (Context->Instruction.HasRepnzXacquireBnd && GET_FLAG(Context, NDR_RFLAG_ZF)) { break; } } else { break; } Context->InstructionsCount++; } break; case ND_INS_MUL: case ND_INS_IMUL: if (Context->Instruction.ExpOperandsCount < 3) { // MUL or IMUL with a single explicit operand or IMUL with 2 explicit operands. GET_OP(Context, 0, &dst); GET_OP(Context, 1, &src); } else { // IMUL with 3 operands. The first operand is the write-only destination. GET_OP(Context, 0, &res); GET_OP(Context, 1, &dst); GET_OP(Context, 2, &src); } if (dst.Size == 1) { if (ND_INS_MUL == Context->Instruction.Instruction) { res.Value.Words[0] = dst.Value.Bytes[0] * src.Value.Bytes[0]; } else { res.Value.Words[0] = (int8_t)dst.Value.Bytes[0] * (int8_t)src.Value.Bytes[0]; } } else if (dst.Size == 2) { if (ND_INS_MUL == Context->Instruction.Instruction) { res.Value.Dwords[0] = dst.Value.Words[0] * src.Value.Words[0]; } else { res.Value.Dwords[0] = (int16_t)dst.Value.Words[0] * (int16_t)src.Value.Words[0]; } } else if (dst.Size == 4) { if (ND_INS_MUL == Context->Instruction.Instruction) { res.Value.Qwords[0] = dst.Value.Qwords[0] * src.Value.Qwords[0]; } else { res.Value.Qwords[0] = (int64_t)(int32_t)dst.Value.Dwords[0] * (int64_t)(int32_t)src.Value.Dwords[0]; } } else { if (ND_INS_MUL == Context->Instruction.Instruction) { ShemuMultiply64Unsigned(dst.Value.Qwords[0], src.Value.Qwords[0], &res.Value.Qwords[1], &res.Value.Qwords[0]); } else { ShemuMultiply64Signed(dst.Value.Qwords[0], src.Value.Qwords[0], (int64_t*)&res.Value.Qwords[1], (int64_t*)&res.Value.Qwords[0]); } } if (Context->Instruction.ExpOperandsCount == 1) { // The result is stored in AX, DX:AX, EDX:EAX or RDX:RAX for the single-operand form. switch (dst.Size) { case 1: *((uint16_t*)&Context->Registers.RegRax) = res.Value.Words[0]; break; case 2: *((uint16_t*)&Context->Registers.RegRdx) = res.Value.Words[1]; *((uint16_t*)&Context->Registers.RegRax) = res.Value.Words[0]; break; case 4: Context->Registers.RegRdx = res.Value.Dwords[1]; Context->Registers.RegRax = res.Value.Dwords[0]; break; case 8: Context->Registers.RegRdx = res.Value.Qwords[1]; Context->Registers.RegRax = res.Value.Qwords[0]; break; } } else { // The result is truncated and stored in the destination operand for the 2 & 3 operands forms. SET_OP(Context, 0, &res); } if (ND_INS_MUL == Context->Instruction.Instruction) { uint8_t cfof = 0; // CF and OF are set to 0 if the high part of the result is 0, otherwise they are set to 1. switch (dst.Size) { case 1: cfof = (0 == res.Value.Bytes[1]) ? 0 : 1; break; case 2: cfof = (0 == res.Value.Words[1]) ? 0 : 1; break; case 4: cfof = (0 == res.Value.Dwords[1]) ? 0 : 1; break; case 8: cfof = (0 == res.Value.Qwords[1]) ? 0 : 1; break; } SET_FLAG(Context, NDR_RFLAG_CF, cfof); SET_FLAG(Context, NDR_RFLAG_OF, cfof); } else { // The CF and OF flags are set when the signed integer value of the intermediate product differs from // the sign extended operand - size - truncated product, otherwise the CF and OF flags are cleared. uint8_t cfof = 0, sign = 0; sign = ND_MSB(res.Size, res.Value.Qwords[0]); switch (dst.Size) { case 1: cfof = (0 == res.Value.Bytes[1] && 0 == sign) || ((uint8_t)-1 == res.Value.Bytes[1] && 1 == sign) ? 0 : 1; break; case 2: cfof = (0 == res.Value.Words[1] && 0 == sign) || ((uint16_t)-1 == res.Value.Words[1] && 1 == sign) ? 0 : 1; break; case 4: cfof = (0 == res.Value.Dwords[1] && 0 == sign) || ((uint32_t)-1 == res.Value.Dwords[1] && 1 == sign) ? 0 : 1; break; case 8: cfof = (0 == res.Value.Qwords[1] && 0 == sign) || ((uint64_t)-1 == res.Value.Qwords[1] && 1 == sign) ? 0 : 1; break; } SET_FLAG(Context, NDR_RFLAG_CF, cfof); SET_FLAG(Context, NDR_RFLAG_OF, cfof); } break; case ND_INS_DIV: case ND_INS_IDIV: // DIV and IDIV only exist with a single explicit operand encoding. All flags are undefined. GET_OP(Context, 0, &src); if (src.Size == 1) { if (src.Value.Bytes[0] == 0) { // Division by zero, force emulation stop. stop = true; break; } if (ND_INS_DIV == Context->Instruction.Instruction) { res.Value.Bytes[0] = (uint8_t)((uint16_t)Context->Registers.RegRax / (uint8_t)src.Value.Bytes[0]); res.Value.Bytes[1] = (uint8_t)((uint16_t)Context->Registers.RegRax % (uint8_t)src.Value.Bytes[0]); } else { res.Value.Bytes[0] = (int8_t)((int16_t)Context->Registers.RegRax / (int8_t)src.Value.Bytes[0]); res.Value.Bytes[1] = (int8_t)((int16_t)Context->Registers.RegRax % (int8_t)src.Value.Bytes[0]); } // Result in AX (AL - quotient, AH - reminder). *((uint16_t*)&Context->Registers.RegRax) = res.Value.Words[0]; } else if (src.Size == 2) { uint32_t divident; if (src.Value.Words[0] == 0) { // Division by zero, force emulation stop. stop = true; break; } divident = ((uint32_t)(uint16_t)Context->Registers.RegRdx << 16) | (uint32_t)(uint16_t)Context->Registers.RegRax; if (ND_INS_DIV == Context->Instruction.Instruction) { res.Value.Words[0] = (uint16_t)((uint32_t)divident / (uint16_t)src.Value.Words[0]); res.Value.Words[1] = (uint16_t)((uint32_t)divident % (uint16_t)src.Value.Words[0]); } else { res.Value.Words[0] = (int16_t)((int32_t)divident / (int16_t)src.Value.Words[0]); res.Value.Words[1] = (int16_t)((int32_t)divident % (int16_t)src.Value.Words[0]); } *((uint16_t*)&Context->Registers.RegRdx) = res.Value.Words[1]; *((uint16_t*)&Context->Registers.RegRax) = res.Value.Words[0]; } else if (src.Size == 4) { uint64_t divident; if (src.Value.Dwords[0] == 0) { // Division by zero, force emulation stop. stop = true; break; } divident = ((uint64_t)(uint32_t)Context->Registers.RegRdx << 32) | (uint64_t)(uint32_t)Context->Registers.RegRax; if (ND_INS_DIV == Context->Instruction.Instruction) { res.Value.Dwords[0] = (uint32_t)((uint64_t)divident / (uint32_t)src.Value.Dwords[0]); res.Value.Dwords[1] = (uint32_t)((uint64_t)divident % (uint32_t)src.Value.Dwords[0]); } else { res.Value.Dwords[0] = (int32_t)((int64_t)divident / (int32_t)src.Value.Dwords[0]); res.Value.Dwords[1] = (int32_t)((int64_t)divident % (int32_t)src.Value.Dwords[0]); } Context->Registers.RegRdx = res.Value.Dwords[1]; Context->Registers.RegRax = res.Value.Dwords[0]; } else if (src.Size == 8) { /// Not implemented! } break; case ND_INS_CLD: SET_FLAG(Context, NDR_RFLAG_DF, 0); break; case ND_INS_STD: SET_FLAG(Context, NDR_RFLAG_DF, 1); break; case ND_INS_CLC: SET_FLAG(Context, NDR_RFLAG_CF, 0); break; case ND_INS_STC: SET_FLAG(Context, NDR_RFLAG_CF, 1); break; case ND_INS_CMC: Context->Registers.RegFlags ^= NDR_RFLAG_CF; break; case ND_INS_STI: if (Context->Ring != 0) { return SHEMU_ABORT_NO_PRIVILEGE; } SET_FLAG(Context, NDR_RFLAG_IF, 1); break; case ND_INS_CLI: if (Context->Ring != 0) { return SHEMU_ABORT_NO_PRIVILEGE; } SET_FLAG(Context, NDR_RFLAG_IF, 0); break; case ND_INS_SAHF: { uint8_t ah = (Context->Registers.RegRax >> 8) & 0xFF; // Handle reserved bits. ah |= (1 << 1); ah &= ~((1 << 3) | (1 << 5)); ((uint8_t *)&Context->Registers.RegFlags)[0] = ah; } break; case ND_INS_LAHF: { uint8_t ah = ((uint8_t *)&Context->Registers.RegFlags)[0]; ((uint8_t *)&Context->Registers.RegRax)[1] = ah; } break; case ND_INS_SALC: if (GET_FLAG(Context, NDR_RFLAG_CF)) { *((uint8_t *)&Context->Registers.RegRax) = 0xFF; } else { *((uint8_t *)&Context->Registers.RegRax) = 0x0; } break; case ND_INS_NOP: Context->NopCount++; break; case ND_INS_WAIT: break; case ND_INS_CBW: case ND_INS_CWDE: case ND_INS_CDQE: GET_OP(Context, 1, &src); dst.Size = src.Size * 2; dst.Value.Qwords[0] = ND_SIGN_EX(src.Size, src.Value.Qwords[0]); SET_OP(Context, 0, &dst); break; case ND_INS_CWD: case ND_INS_CDQ: case ND_INS_CQO: GET_OP(Context, 1, &src); dst.Size = src.Size; if (ND_GET_SIGN(src.Size, src.Value.Qwords[0])) { dst.Value.Qwords[0] = 0xFFFFFFFFFFFFFFFF; } else { dst.Value.Qwords[0] = 0; } SET_OP(Context, 0, &dst); break; case ND_INS_AAA: case ND_INS_AAD: case ND_INS_AAM: case ND_INS_AAS: case ND_INS_DAA: case ND_INS_DAS: // Ignore these for now. break; case ND_INS_ENDBR: // Acts as a NOP, it's just a hint to the decoder. break; case ND_INS_LFENCE: case ND_INS_SFENCE: case ND_INS_MFENCE: // Nothing can be done for them, really. break; case ND_INS_CPUID: // OK; EAX, EBX, ECX and EDX are modified, which also zeroes the high 32 bit. Context->Registers.RegRax = 0; Context->Registers.RegRbx = 0; Context->Registers.RegRcx = 0; Context->Registers.RegRdx = 0; break; // Some basic MMX/SSE instructions supported. case ND_INS_EMMS: nd_memzero(Context->MmxRegisters, sizeof(Context->MmxRegisters)); break; case ND_INS_MOVD: case ND_INS_MOVQ: case ND_INS_MOVDQU: case ND_INS_MOVDQA: // memzero the source; if the source size is less than the destination size, the upper bits will be zero. // Note that we don't really care about #GP on unaligned MOVDQA accesses... nd_memzero(src.Value.Bytes, sizeof(src.Value.Bytes)); GET_OP(Context, 1, &src); SET_OP(Context, 0, &src); break; case ND_INS_PUNPCKLBW: GET_OP(Context, 0, &dst); GET_OP(Context, 1, &src); if (dst.Size == 8) { // Operating on MMX register. dst.Value.Bytes[7] = src.Value.Bytes[3]; dst.Value.Bytes[6] = dst.Value.Bytes[3]; dst.Value.Bytes[5] = src.Value.Bytes[2]; dst.Value.Bytes[4] = dst.Value.Bytes[2]; dst.Value.Bytes[3] = src.Value.Bytes[1]; dst.Value.Bytes[2] = dst.Value.Bytes[1]; dst.Value.Bytes[1] = src.Value.Bytes[0]; } else { // Operating on XMM register. dst.Value.Bytes[15] = src.Value.Bytes[7]; dst.Value.Bytes[14] = dst.Value.Bytes[7]; dst.Value.Bytes[13] = src.Value.Bytes[6]; dst.Value.Bytes[12] = dst.Value.Bytes[6]; dst.Value.Bytes[11] = src.Value.Bytes[5]; dst.Value.Bytes[10] = dst.Value.Bytes[5]; dst.Value.Bytes[9] = src.Value.Bytes[4]; dst.Value.Bytes[8] = src.Value.Bytes[4]; dst.Value.Bytes[7] = src.Value.Bytes[3]; dst.Value.Bytes[6] = dst.Value.Bytes[3]; dst.Value.Bytes[5] = src.Value.Bytes[2]; dst.Value.Bytes[4] = dst.Value.Bytes[2]; dst.Value.Bytes[3] = src.Value.Bytes[1]; dst.Value.Bytes[2] = dst.Value.Bytes[1]; dst.Value.Bytes[1] = src.Value.Bytes[0]; } SET_OP(Context, 0, &dst); break; case ND_INS_PXOR: GET_OP(Context, 0, &dst); GET_OP(Context, 1, &src); for (uint32_t i = 0; i < dst.Size; i++) { dst.Value.Bytes[i] ^= src.Value.Bytes[i]; } SET_OP(Context, 0, &dst); break; // Some basic AVX/AVX2 instructions support. case ND_INS_VMOVD: case ND_INS_VMOVQ: case ND_INS_VMOVDQU: case ND_INS_VMOVDQA: // First clear out the entire register. If less than MAX_VL bits are written, the upper bits are set to 0. nd_memzero(src.Value.Bytes, sizeof(src.Value.Bytes)); nd_memzero(dst.Value.Bytes, sizeof(dst.Value.Bytes)); dst.Size = ND_MAX_REGISTER_SIZE; SET_OP(Context, 0, &dst); // Fetch the source, and write the destination. GET_OP(Context, 1, &src); SET_OP(Context, 0, &src); break; case ND_INS_VPBROADCASTB: case ND_INS_VPBROADCASTW: case ND_INS_VPBROADCASTD: case ND_INS_VPBROADCASTQ: GET_OP(Context, 1, &src); dst.Size = Context->Instruction.Operands[0].Size; for (uint32_t i = 0; i < dst.Size / src.Size; i++) { switch (src.Size) { case 1: dst.Value.Bytes[i] = src.Value.Bytes[0]; break; case 2: dst.Value.Words[i] = src.Value.Words[0]; break; case 4: dst.Value.Dwords[i] = src.Value.Dwords[0]; break; default: dst.Value.Qwords[i] = src.Value.Qwords[0]; break; } } SET_OP(Context, 0, &dst); break; case ND_INS_VPXOR: GET_OP(Context, 1, &dst); GET_OP(Context, 2, &src); for (uint32_t i = 0; i < dst.Size; i++) { dst.Value.Bytes[i] ^= src.Value.Bytes[i]; } SET_OP(Context, 0, &dst); break; // Software interrupt/SYSCALL/SYSENTER. case ND_INS_INT: if (Context->Instruction.Immediate1 == 0x80 || Context->Instruction.Immediate1 == 0x2E) { Context->Flags |= SHEMU_FLAG_SYSCALL; } stop = true; break; case ND_INS_SYSCALL: case ND_INS_SYSENTER: Context->Flags |= SHEMU_FLAG_SYSCALL; stop = true; break; // Some basic privileged instructions supported, specific to kernel-mode shellcodes. case ND_INS_SWAPGS: if (Context->Ring != 0) { return SHEMU_ABORT_NO_PRIVILEGE; } Context->Flags |= SHEMU_FLAG_SWAPGS; stop = true; break; case ND_INS_RDMSR: if (Context->Ring != 0) { return SHEMU_ABORT_NO_PRIVILEGE; } if ((Context->Registers.RegRcx == 0xC0000082 && ND_CODE_64 == Context->Mode) || (Context->Registers.RegRcx == 0x00000176 && ND_CODE_32 == Context->Mode)) { Context->Flags |= SHEMU_FLAG_SYSCALL_MSR_READ; } stop = true; break; case ND_INS_WRMSR: if (Context->Ring != 0) { return SHEMU_ABORT_NO_PRIVILEGE; } if ((Context->Registers.RegRcx == 0xC0000082 && ND_CODE_64 == Context->Mode) || (Context->Registers.RegRcx == 0x00000176 && ND_CODE_32 == Context->Mode)) { Context->Flags |= SHEMU_FLAG_SYSCALL_MSR_WRITE; } stop = true; break; default: return SHEMU_ABORT_UNSUPPORTED_INSTRUX; } } // Minimum percent of the instructions were NOPs => consider we have a NOP sled. Note that we get here only if // the maximum number of instructions has been emulated successfully; if the emulation is aborted for any reason, // this code will have no effect. if ((Context->InstructionsCount >= Context->MaxInstructionsCount / 2) && (Context->NopCount >= Context->InstructionsCount * Context->NopThreshold / 100)) { Context->Flags |= SHEMU_FLAG_NOP_SLED; return SHEMU_ABORT_SHELLCODE_DETECTED; } return SHEMU_SUCCESS; }