static InstTransResult doCmpxchgRR(InstPtr ip, BasicBlock *&b, const MCOperand &dstReg, const MCOperand &srcReg) { NASSERT(dstReg.isReg()); NASSERT(srcReg.isReg()); Value *acc; switch(width) { case 8: acc = R_READ<width>(b, X86::AL); break; case 16: acc = R_READ<width>(b, X86::AX); break; case 32: acc = R_READ<width>(b, X86::EAX); break; default: throw TErr(__LINE__, __FILE__, "Width not supported"); } Value *dstReg_v = R_READ<width>(b, dstReg.getReg()); Value *srcReg_v = R_READ<width>(b, srcReg.getReg()); doCmpVV<width>(ip, b, acc, dstReg_v); Value *Cmp = new ICmpInst(*b, CmpInst::ICMP_EQ, acc, dstReg_v); F_WRITE(b, ZF, Cmp); /// // ZF = Acc == DST // acc = select(ZF, acc, dst) // dst = select(ZF, src, dst) Value *new_acc = SelectInst::Create(Cmp, acc, dstReg_v, "", b); Value *new_dst = SelectInst::Create(Cmp, srcReg_v, dstReg_v, "", b); R_WRITE<width>(b, dstReg.getReg(), new_dst); switch(width) { case 8: R_WRITE<width>(b, X86::AL, new_acc); break; case 16: R_WRITE<width>(b, X86::AX, new_acc); break; case 32: R_WRITE<width>(b, X86::EAX, new_acc); break; default: throw TErr(__LINE__, __FILE__, "Width not supported"); } return ContinueBlock; }
SystemArchType SystemArch(llvm::Module *) { auto arch = ArchType(); if (arch == llvm::Triple::x86) { return _X86_; } else if (arch == llvm::Triple::x86_64) { return _X86_64_; } else { throw TErr(__LINE__, __FILE__, "Unsupported architecture"); } }
static bool LiftBlockIntoFunction(TranslationContext &ctx) { auto didError = false; //first, either create or look up the LLVM basic block for this native //block. we are either creating it for the first time, or, we are //going to look up a blank block auto block_name = ctx.natB->get_name(); auto curLLVMBlock = ctx.va_to_bb[ctx.natB->get_base()]; //then, create a basic block for every follow of this block, if we do not //already have that basic block in our LLVM CFG const auto &follows = ctx.natB->get_follows(); for (auto succ_block_va : follows) { if (!ctx.va_to_bb.count(succ_block_va)) { throw TErr(__LINE__, __FILE__, "Missing successor block!"); } } //now, go through each statement and translate it into LLVM IR //statements that branch SHOULD be the last statement in a block for (auto inst : ctx.natB->get_insts()) { ctx.natI = inst; switch (LiftInstIntoBlock(ctx, curLLVMBlock, true)) { case ContinueBlock: break; case EndBlock: case EndCFG: goto done; case TranslateErrorUnsupported: didError = !IgnoreUnsupportedInsts; goto done; case TranslateError: didError = true; goto done; } } done: if (curLLVMBlock->getTerminator()) { return didError; } // we may need to insert a branch inst to the successor // if the block ended on a non-terminator (this happens since we // may split blocks in cfg recovery to avoid code duplication) if (follows.size() == 1) { llvm::BranchInst::Create(ctx.va_to_bb[follows.front()], curLLVMBlock); } else { new llvm::UnreachableInst(curLLVMBlock->getContext(), curLLVMBlock); } return didError; }
NTSTATUS EXTERNAL DriverEntry( __in PDRIVER_OBJECT DrvObj, __in PUNICODE_STRING RegPath ) { NTSTATUS status = STATUS_SUCCESS; HID_MINIDRIVER_REGISTRATION hidMinidriverRegistration = {0}; TEnter(Func,("(DrvObj=%p,RegPath=%p)\n", DrvObj, RegPath)); gDriverObj = DrvObj; DrvObj->MajorFunction[IRP_MJ_CREATE] = DrvObj->MajorFunction[IRP_MJ_CLOSE] = HbtnCreateClose; DrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = HbtnInternalIoctl; DrvObj->MajorFunction[IRP_MJ_PNP] = HbtnPnp; DrvObj->MajorFunction[IRP_MJ_POWER] = HbtnPower; DrvObj->DriverUnload = HbtnUnload; DrvObj->DriverExtension->AddDevice = HbtnAddDevice; // // Register with HIDCLASS.SYS module // RtlZeroMemory(&hidMinidriverRegistration, sizeof(hidMinidriverRegistration)); hidMinidriverRegistration.Revision = HID_REVISION; hidMinidriverRegistration.DriverObject = DrvObj; hidMinidriverRegistration.RegistryPath = RegPath; hidMinidriverRegistration.DeviceExtensionSize = sizeof(DEVICE_EXTENSION); hidMinidriverRegistration.DevicesArePolled = FALSE; status = HidRegisterMinidriver(&hidMinidriverRegistration); if (!NT_SUCCESS(status)) { LogError(ERRLOG_MINIDRV_REG_FAILED, status, UNIQUE_ERRID(0x10), NULL, NULL); TErr(("failed to register mini driver.\n")); } TExit(Func,("=%x\n", status)); return status; } //DriverEntry
llvm::CallingConv::ID getLLVMCC(ExternalCodeRef::CallingConvention cc) { switch (cc) { case ExternalCodeRef::CallerCleanup: return llvm::CallingConv::C; case ExternalCodeRef::CalleeCleanup: return llvm::CallingConv::X86_StdCall; case ExternalCodeRef::FastCall: return llvm::CallingConv::X86_FastCall; case ExternalCodeRef::McsemaCall: // mcsema internal calls are cdecl with one argument return llvm::CallingConv::C; default: throw TErr(__LINE__, __FILE__, "Unknown calling convention!"); break; } return llvm::CallingConv::C; }
static InstTransResult doCmpxchgRM(InstPtr ip, BasicBlock *&b, Value *dstAddr, const MCOperand &srcReg) { NASSERT(dstAddr != NULL); NASSERT(srcReg.isReg()); Value *acc; switch(width) { case 8: acc = R_READ<width>(b, X86::AL); break; case 16: acc = R_READ<width>(b, X86::AX); break; case 32: acc = R_READ<width>(b, X86::EAX); break; default: throw TErr(__LINE__, __FILE__, "Width not supported"); } //Value *mem_v = M_READ<width>(ip, b, dstAddr); Value *m_addr = NULL; unsigned addrspace = ip->get_addr_space(); if( dstAddr->getType()->isPointerTy() == false ) { llvm::Type *ptrTy = Type::getIntNPtrTy(b->getContext(), width, addrspace); m_addr = new llvm::IntToPtrInst(dstAddr, ptrTy, "", b); } else if( dstAddr->getType() != Type::getIntNPtrTy( b->getContext(), width, addrspace) ) { //we need to bitcast the pointer value to a pointer type of the appropriate width m_addr = CastInst::CreatePointerCast(dstAddr, Type::getIntNPtrTy(b->getContext(), width, addrspace), "", b); } else { m_addr = dstAddr; } Value *srcReg_v = R_READ<width>(b, srcReg.getReg()); AtomicCmpXchgInst *cmpx = new AtomicCmpXchgInst( m_addr, acc, srcReg_v, llvm::SequentiallyConsistent, llvm::SequentiallyConsistent, llvm::CrossThread, b); cmpx->setVolatile(true); Value *cmpx_val = ExtractValueInst::Create(cmpx, 0, "cmpxchg_cmpx_val", b); Value *was_eq = ExtractValueInst::Create(cmpx, 1, "cmpxchg_was_eq", b); doCmpVV<width>(ip, b, acc, cmpx_val); F_WRITE(b, ZF, was_eq); Value *new_acc = SelectInst::Create(was_eq, acc, cmpx_val, "", b); switch(width) { case 8: R_WRITE<width>(b, X86::AL, new_acc); break; case 16: R_WRITE<width>(b, X86::AX, new_acc); break; case 32: R_WRITE<width>(b, X86::EAX, new_acc); break; default: throw TErr(__LINE__, __FILE__, "Width not supported"); } return ContinueBlock; }
VOID __cdecl LogDbgMsg( IN NTSTATUS ErrorCode, IN NTSTATUS NTStatus OPTIONAL, _In_z_ LPCSTR pszFormat, ... ) { #define MAX_ERRMSG_LEN ((ERROR_LOG_MAXIMUM_SIZE - \ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData)) \ /sizeof(WCHAR)) static char szErrMsg[MAX_ERRMSG_LEN] = {0}; TEnter(Func, ("(ErrorCode=%x,NTStatus=%x,Format=%s)", ErrorCode, NTStatus, pszFormat)); TAssert(gDriverObj != NULL); if (gDriverObj != NULL) { va_list arglist; NTSTATUS status; size_t iLen = 0; ULONG_PTR iTotalLen; PIO_ERROR_LOG_PACKET ErrEntry; va_start(arglist, pszFormat); status = RtlStringCchVPrintfA(szErrMsg, ARRAYSIZE(szErrMsg), pszFormat, arglist); va_end(arglist); if (NT_SUCCESS(status)) { status = RtlStringCchLengthA(szErrMsg, ARRAYSIZE(szErrMsg), &iLen); } if (NT_SUCCESS(status)) { iTotalLen = FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + (iLen + 1)*sizeof(WCHAR); iTotalLen = max(iTotalLen, sizeof(IO_ERROR_LOG_PACKET)); ErrEntry = IoAllocateErrorLogEntry(gDriverObj, (UCHAR)iTotalLen); if (ErrEntry) { ErrEntry->NumberOfStrings = 1; ErrEntry->ErrorCode = ErrorCode; ErrEntry->StringOffset = FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData); mbstowcs((WCHAR *)ErrEntry->DumpData, szErrMsg, iLen); ErrEntry->FinalStatus = NTStatus; IoWriteErrorLogEntry(ErrEntry); } else { TWarn(("Failed to allocate error log entry (len=%d).", (int)iTotalLen)); } if (ErrorCode == ERRLOG_DEBUG_INFORMATION) { TInfo(("%s", szErrMsg)); } else if (ErrorCode == ERRLOG_DEBUG_WARNING) { TWarn(("%s", szErrMsg)); } else if (ErrorCode == ERRLOG_DEBUG_ERROR) { TErr(("%s", szErrMsg)); } } } TExit(Func, ("!")); return; } //LogDbgMsg
// Iterate over the list of external functions and insert them as // global functions. static void InitExternalCode(NativeModulePtr natMod, llvm::Module *M) { for (auto e : natMod->getExtCalls()) { auto conv = e->getCallingConvention(); auto argCount = e->getNumArgs(); auto symName = e->getSymbolName(); auto funcSign = e->getFunctionSignature(); // Create the function if it is not already there. auto &C = M->getContext(); auto F = M->getFunction(symName); if (F) { continue; } if (ExternalCodeRef::McsemaCall == conv) { // normal mcsema function prototypes F = llvm::dyn_cast<llvm::Function>(M->getOrInsertFunction( ArchNameMcSemaCall(symName), LiftedFunctionType())); ArchSetCallingConv(M, F); F->setLinkage(llvm::GlobalValue::ExternalLinkage); continue; } std::vector<llvm::Type *> arguments; llvm::Type *returnType = nullptr; // Create arguments. const auto Arch = SystemArch(M); const auto OS = SystemOS(M); for (auto i = 0; i < argCount; i++) { if (_X86_64_ == Arch) { if (llvm::Triple::Win32 == OS) { if (funcSign.c_str()[i] == 'F') { arguments.push_back(llvm::Type::getDoubleTy(C)); } else { arguments.push_back(llvm::Type::getInt64Ty(C)); } } else if (llvm::Triple::Linux == OS) { arguments.push_back(llvm::Type::getInt64Ty(C)); } else { TASSERT(false, "Unknown OS Type!"); } } else { arguments.push_back(llvm::Type::getInt32Ty(C)); } } //create function type switch (e->getReturnType()) { case ExternalCodeRef::NoReturn: case ExternalCodeRef::VoidTy: returnType = llvm::Type::getVoidTy(C); break; case ExternalCodeRef::Unknown: case ExternalCodeRef::IntTy: if (natMod->is64Bit()) { returnType = llvm::Type::getInt64Ty(C); } else { returnType = llvm::Type::getInt32Ty(C); } break; default: throw TErr( __LINE__, __FILE__, "Encountered an unknown return type while translating function"); } auto FTy = llvm::FunctionType::get(returnType, arguments, false); if (e->isWeak()) { F = llvm::Function::Create(FTy, llvm::GlobalValue::ExternalWeakLinkage, symName, M); } else { F = llvm::Function::Create(FTy, llvm::GlobalValue::ExternalLinkage, symName, M); } if (e->getReturnType() == ExternalCodeRef::NoReturn) { F->setDoesNotReturn(); } //set calling convention if (natMod->is64Bit()) { ArchSetCallingConv(M, F); } else { F->setCallingConv(getLLVMCC(conv)); } } }
static bool InsertFunctionIntoModule(NativeModulePtr mod, NativeFunctionPtr func, llvm::Module *M) { auto &C = M->getContext(); auto F = M->getFunction(func->get_name()); if (!F) { throw TErr(__LINE__, __FILE__, "Could not get func " + func->get_name()); } if (!F->empty()) { std::cout << "WARNING: Asking to re-insert function: " << func->get_name() << std::endl << "\tReturning current function instead" << std::endl; return true; } auto entryBlock = llvm::BasicBlock::Create(F->getContext(), "entry", F); ArchAllocRegisterVars(entryBlock); TranslationContext ctx; ctx.natM = mod; ctx.natF = func; ctx.natI = nullptr; ctx.M = M; ctx.F = F; // Create basic blocks for each basic block in the original function. for (auto block_info : func->get_blocks()) { ctx.va_to_bb[block_info.first] = llvm::BasicBlock::Create( C, block_info.second->get_name(), F); } // Create a branch from the end of the entry block to the first block llvm::BranchInst::Create(ctx.va_to_bb[func->get_start()], entryBlock); try { // Lift every basic block into the functions. auto error = false; for (auto block_info : func->get_blocks()) { ctx.natB = block_info.second; error = LiftBlockIntoFunction(ctx) || error; ctx.natB = nullptr; } // For ease of debugging generated code, don't allow lifted functions to // be inlined. This will make lifted and native call graphs one-to-one. F->addFnAttr(llvm::Attribute::NoInline); //we should be done, having inserted every block into the module return !error; } catch (std::exception &e) { std::cerr << "error: " << std::endl << e.what() << std::endl << "in function " << std::hex << ctx.natF->get_start() << std::endl; if (ctx.natB) { std::cerr << "in block " << std::hex << ctx.natB->get_base() << std::endl; if (ctx.natI) { std::cerr << "in inst " << std::hex << ctx.natI->get_loc() << std::endl; } } return false; } }
static InstTransResult doCmpxchgRR(InstPtr ip, BasicBlock *&b, const MCOperand &dstReg, const MCOperand &srcReg) { NASSERT(dstReg.isReg()); NASSERT(srcReg.isReg()); Function *F = b->getParent(); BasicBlock *AccEQDest = BasicBlock::Create(b->getContext(), "AccEQDest", F); BasicBlock *AccNEDest = BasicBlock::Create(b->getContext(), "AccNEDest", F); BasicBlock *done = BasicBlock::Create(b->getContext(), "done", F); Value *acc; switch(width) { case 8: acc = R_READ<width>(b, X86::AL); break; case 16: acc = R_READ<width>(b, X86::AX); break; case 32: acc = R_READ<width>(b, X86::EAX); break; default: throw TErr(__LINE__, __FILE__, "Width not supported"); } Value *dstReg_v = R_READ<width>(b, dstReg.getReg()); Value *srcReg_v = R_READ<width>(b, srcReg.getReg()); doCmpVV<width>(ip, b, acc, dstReg_v); Value *Cmp = new ICmpInst(*b, CmpInst::ICMP_EQ, acc, dstReg_v); BranchInst::Create(AccEQDest, AccNEDest, Cmp, b); // Acc == Dst F_SET(AccEQDest, "ZF"); R_WRITE<width>(AccEQDest, dstReg.getReg(), srcReg_v); BranchInst::Create(done, AccEQDest); // Acc != Dst F_CLEAR(AccNEDest, "ZF"); switch(width) { case 8: R_WRITE<width>(AccNEDest, X86::AL, dstReg_v); break; case 16: R_WRITE<width>(AccNEDest, X86::AX, dstReg_v); break; case 32: R_WRITE<width>(AccNEDest, X86::EAX, dstReg_v); break; default: throw TErr(__LINE__, __FILE__, "Width not supported"); } BranchInst::Create(done, AccNEDest); b = done; return ContinueBlock; }
static InstTransResult doCmpxchgRM(InstPtr ip, BasicBlock *&b, Value *dstAddr, const MCOperand &srcReg) { NASSERT(dstAddr != NULL); NASSERT(srcReg.isReg()); Function *F = b->getParent(); BasicBlock *AccEQDest = BasicBlock::Create(b->getContext(), "AccEQDest", F); BasicBlock *AccNEDest = BasicBlock::Create(b->getContext(), "AccNEDest", F); BasicBlock *done = BasicBlock::Create(b->getContext(), "done", F); Value *acc; switch(width) { case 8: acc = R_READ<width>(b, X86::AL); break; case 16: acc = R_READ<width>(b, X86::AX); break; case 32: acc = R_READ<width>(b, X86::EAX); break; default: throw TErr(__LINE__, __FILE__, "Width not supported"); } //Value *mem_v = M_READ<width>(ip, b, dstAddr); Value *m_addr = NULL; unsigned addrspace = ip->get_addr_space(); if( dstAddr->getType()->isPointerTy() == false ) { llvm::Type *ptrTy = Type::getIntNPtrTy(b->getContext(), width, addrspace); m_addr = new llvm::IntToPtrInst(dstAddr, ptrTy, "", b); } else if( dstAddr->getType() != Type::getIntNPtrTy( b->getContext(), width, addrspace) ) { //we need to bitcast the pointer value to a pointer type of the appropriate width m_addr = CastInst::CreatePointerCast(dstAddr, Type::getIntNPtrTy(b->getContext(), width, addrspace), "", b); } else { m_addr = dstAddr; } Value *srcReg_v = R_READ<width>(b, srcReg.getReg()); AtomicCmpXchgInst *cmpx = new AtomicCmpXchgInst( m_addr, acc, srcReg_v, llvm::SequentiallyConsistent, llvm::CrossThread, b); cmpx->setVolatile(true); // needed for flags settings doCmpVV<width>(ip, b, acc, cmpx); Value *Cmp = new ICmpInst(*b, CmpInst::ICMP_EQ, cmpx, acc); BranchInst::Create(AccEQDest, AccNEDest, Cmp, b); // Acc == Dst F_SET(AccEQDest, "ZF"); //M_WRITE<width>(ip, AccEQDest, dstAddr, srcReg_v); BranchInst::Create(done, AccEQDest); // Acc != Dst F_CLEAR(AccNEDest, "ZF"); switch(width) { case 8: R_WRITE<width>(AccNEDest, X86::AL, cmpx); break; case 16: R_WRITE<width>(AccNEDest, X86::AX, cmpx); break; case 32: R_WRITE<width>(AccNEDest, X86::EAX, cmpx); break; default: throw TErr(__LINE__, __FILE__, "Width not supported"); } BranchInst::Create(done, AccNEDest); b = done; return ContinueBlock; }
Value *getAddrFromExpr( BasicBlock *b, NativeModulePtr mod, const MCOperand &Obase, const MCOperand &Oscale, const MCOperand &Oindex, const int64_t Odisp, const MCOperand &Oseg, bool dataOffset) { TASSERT(Obase.isReg(), ""); TASSERT(Oscale.isImm(), ""); TASSERT(Oindex.isReg(), ""); TASSERT(Oseg.isReg(), ""); unsigned baseReg = Obase.getReg(); int64_t disp = Odisp; // specific function for 64 bit Value *d = NULL; IntegerType *iTy = IntegerType::getInt64Ty(b->getContext()); if( dataOffset || (mod && disp && baseReg != X86::RBP && baseReg!= X86::RSP) ) { VA baseGlobal; if( addrIsInData(disp, mod, baseGlobal, dataOffset ? 0 : 0x1000) ) { //we should be able to find a reference to this in global data Module *M = b->getParent()->getParent(); string sn = "data_0x" + to_string<VA>(baseGlobal, hex); GlobalVariable *gData = M->getNamedGlobal(sn); //if we thought it was a global, we should be able to //pin it to a global array we made during module setup if( gData == NULL) throw TErr(__LINE__, __FILE__, "Global variable not found"); // since globals are now a structure // we cannot simply slice into them. // Need to get ptr and then add integer displacement to ptr Value *globalGEPV[] = { ConstantInt::get(Type::getInt64Ty(b->getContext()), 0), ConstantInt::get(Type::getInt32Ty(b->getContext()), 0)}; Instruction *globalGEP = GetElementPtrInst::Create(gData, globalGEPV, "", b); Type *ty = Type::getInt64Ty(b->getContext()); Value *intVal = new PtrToIntInst(globalGEP, ty, "", b); uint32_t addr_offset = disp-baseGlobal; Value *int_adjusted = BinaryOperator::CreateAdd(intVal, CONST_V<64>(b, addr_offset), "", b); //then, assign this to the outer 'd' so that the rest of the //logic picks up on that address instead of another address d = int_adjusted; } } else { //there is no disp value, or its relative to esp/ebp in which case //we might not want to do anything } if( d == NULL ) { //create a constant integer out of the raw displacement //we were unable to assign the displacement to an address d = ConstantInt::getSigned(iTy, disp); } Value *rVal = NULL; //read the base register (if given) if( baseReg != X86::NoRegister && baseReg != X86::RIP) { rVal = R_READ<64>(b, baseReg); } else { //if the base is not present, just use 0 rVal = CONST_V<64>(b, 0); } Value *dispComp; dispComp = BinaryOperator::Create( Instruction::Add, rVal, d, "", b); //add the index amount, if present if( Oindex.getReg() != X86::NoRegister ) { Value *index = R_READ<64>(b, Oindex.getReg()); int64_t scaleAmt = Oscale.getImm(); if( scaleAmt > 1 ) { index = BinaryOperator::CreateMul(index,CONST_V<64>(b, scaleAmt),"",b); } dispComp = BinaryOperator::CreateAdd(dispComp, index, "", b); } //convert the resulting integer into a pointer type PointerType *piTy = Type::getInt64PtrTy(b->getContext()); Value *dispPtr = new IntToPtrInst(dispComp, piTy, "", b); return dispPtr; }