bool MMap::RunModuleInitializers(ImageContext* pImage, DWORD dwReason) { AsmJitHelper a; uint64_t result = 0; auto hNtdll = _process.Modules().GetModule(L"ntdll.dll", LdrList, pImage->peImage.ImageType()); auto pActivateActx = _process.Modules().GetExport(hNtdll, "RtlActivateActivationContext"); auto pDeactivateeActx = _process.Modules().GetExport(hNtdll, "RtlDeactivateActivationContext"); a.GenPrologue(); // ActivateActCtx if(_pAContext.Valid() && pActivateActx.procAddress) { a->mov(a->zax, _pAContext.Ptr<size_t>()); a->mov(a->zax, asmjit::host::dword_ptr(a->zax)); a.GenCall(static_cast<size_t>(pActivateActx.procAddress), {0, a->zax, _pAContext.Ptr<size_t>() + sizeof(HANDLE)}); } // Function order // TLS first, entry point last if(dwReason == DLL_PROCESS_ATTACH || dwReason == DLL_THREAD_ATTACH) { // PTLS_CALLBACK_FUNCTION(pImage->ImageBase, dwReason, NULL); if(!(pImage->flags & NoTLS)) for(auto& pCallback : pImage->tlsCallbacks) { BLACBONE_TRACE(L"ManualMap: Calling TLS callback at 0x%p for '%ls', Reason: %d", static_cast<size_t>(pCallback), pImage->FileName.c_str(), dwReason); a.GenCall(static_cast<size_t>(pCallback), {pImage->imgMem.Ptr<size_t>(), dwReason, NULL}); } // DllMain if(pImage->EntryPoint != 0) { BLACBONE_TRACE(L"ManualMap: Calling entry point for '%ls', Reason: %d", pImage->FileName.c_str(), dwReason); a.GenCall(static_cast<size_t>(pImage->EntryPoint), {pImage->imgMem.Ptr<size_t>(), dwReason, NULL}); } } // Entry point first, TLS last else { // DllMain if(pImage->EntryPoint != 0) { BLACBONE_TRACE(L"ManualMap: Calling entry point for '%ls', Reason: %d", pImage->FileName.c_str(), dwReason); a.GenCall(static_cast<size_t>(pImage->EntryPoint), {pImage->imgMem.Ptr<size_t>(), dwReason, NULL}); } // PTLS_CALLBACK_FUNCTION(pImage->ImageBase, dwReason, NULL); if(!(pImage->flags & NoTLS)) for(auto& pCallback : pImage->tlsCallbacks) { BLACBONE_TRACE(L"ManualMap: Calling TLS callback at 0x%p for '%ls', Reason: %d", static_cast<size_t>(pCallback), pImage->FileName.c_str(), dwReason); a.GenCall(static_cast<size_t>(pCallback), {pImage->imgMem.Ptr<size_t>(), dwReason, NULL}); } } // DeactivateActCtx if(_pAContext.Valid() && pDeactivateeActx.procAddress) { a->mov(a->zax, _pAContext.Ptr<size_t>() + sizeof(HANDLE)); a->mov(a->zax, asmjit::host::dword_ptr(a->zax)); a.GenCall(static_cast<size_t>(pDeactivateeActx.procAddress), {0, a->zax}); } _process.Remote().AddReturnWithEvent(a, pImage->peImage.ImageType()); a.GenEpilogue(); _process.Remote().ExecInWorkerThread(a->make(), a->getCodeSize(), result); return true; }
bool MMap::RelocateImage(ImageContext* pImage) { BLACBONE_TRACE(L"ManualMap: Relocating image '%ls'", pImage->FilePath.c_str()); // Reloc delta size_t Delta = pImage->imgMem.Ptr<size_t>() - static_cast<size_t>(pImage->peImage.ImageBase()); // No need to relocate if(Delta == 0) { BLACBONE_TRACE(L"ManualMap: No need for relocation"); LastNtStatus(STATUS_SUCCESS); return true; } auto start = pImage->peImage.DirectoryAddress(IMAGE_DIRECTORY_ENTRY_BASERELOC); auto end = start + pImage->peImage.DirectorySize(IMAGE_DIRECTORY_ENTRY_BASERELOC); RelocData* fixrec = reinterpret_cast<RelocData*>(start); if(fixrec == nullptr) { // TODO: return proper error code BLACBONE_TRACE(L"ManualMap: Can't relocate image, no relocation data"); LastNtStatus(STATUS_IMAGE_NOT_AT_BASE); return false; } while((size_t)fixrec < end && fixrec->BlockSize) { DWORD count = (fixrec->BlockSize - 8) >> 1; // records count for(DWORD i = 0; i < count; ++i) { WORD fixtype = (fixrec->Item[i].Type); // fixup type WORD fixoffset = (fixrec->Item[i].Offset) % 4096; // offset in 4K block // no fixup required if(fixtype == IMAGE_REL_BASED_ABSOLUTE) continue; // add delta if(fixtype == IMAGE_REL_BASED_HIGHLOW || fixtype == IMAGE_REL_BASED_DIR64) { size_t fixRVA = fixoffset + fixrec->PageRVA; size_t val = *reinterpret_cast<size_t*>(pImage->peImage.ResolveRVAToVA(fixoffset + fixrec->PageRVA)) + Delta; auto status = STATUS_SUCCESS; if(pImage->flags & HideVAD) status = Driver().WriteMem(_process.Id(), pImage->imgMem.Ptr() + fixRVA, sizeof(val), &val); else status = pImage->imgMem.Write(fixRVA, val); // Apply relocation if(!NT_SUCCESS(status)) { BLACBONE_TRACE(L"ManualMap: Failed to apply relocation at offset 0x%x. Status = 0x%x", fixRVA, status); return false; } } else { // TODO: support for all remaining relocations BLACBONE_TRACE(L"ManualMap: Abnormal relocation type %d. Aborting", fixtype); LastNtStatus(STATUS_INVALID_IMAGE_FORMAT); return false; } } // next reloc entry fixrec = reinterpret_cast<RelocData*>(reinterpret_cast<size_t>(fixrec) + fixrec->BlockSize); } return true; }
/// <summary> /// Reload driver /// </summary> /// <param name="path">Path to the driver file</param> /// <returns>Status code</returns> NTSTATUS DriverControl::Reload( std::wstring path /*= L"" */ ) { NTSTATUS status = STATUS_SUCCESS; Unload(); // Use default path if (path.empty()) { const wchar_t* filename = nullptr; if (IsWindows10OrGreater()) filename = L"BlackBoneDrv10.sys"; else if (IsWindows8Point1OrGreater()) filename = L"BlackBoneDrv81.sys"; else if (IsWindows8OrGreater()) filename = L"BlackBoneDrv8.sys"; else if (IsWindows7OrGreater()) filename = L"BlackBoneDrv7.sys"; else filename = L"BlackBoneDrv.sys"; path = Utils::GetExeDirectory() + L"\\" + filename; } status = _loadStatus = LoadDriver( DRIVER_SVC_NAME, path ); if (!NT_SUCCESS( status )) { BLACBONE_TRACE( L"Failed to load driver %ls. Status 0x%X", path.c_str(), status ); return LastNtStatus( status ); } _hDriver = CreateFileW( BLACKBONE_DEVICE_FILE, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (_hDriver == INVALID_HANDLE_VALUE) { status = LastNtStatus(); BLACBONE_TRACE( L"Failed to open driver handle. Status 0x%X", status ); return status; } return status; }
bool MMap::InitializeCookie(ImageContext* pImage) { auto pLC = reinterpret_cast<PIMAGE_LOAD_CONFIG_DIRECTORY>(pImage->peImage.DirectoryAddress(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG)); // // Cookie generation based on MSVC++ compiler // if(pLC && pLC->SecurityCookie) { BLACBONE_TRACE(L"ManualMap: Performing security cookie initializtion for image '%ls'", pImage->FileName.c_str()); FILETIME systime = {0}; LARGE_INTEGER PerformanceCount = {{0}}; uintptr_t cookie = 0; GetSystemTimeAsFileTime(&systime); QueryPerformanceCounter(&PerformanceCount); cookie = _process.Id() ^ _process.Remote().GetWorkerThread()->Id() ^ reinterpret_cast<uintptr_t>(&cookie); #ifdef USE64 cookie ^= *reinterpret_cast<uint64_t*>(&systime); cookie ^= (PerformanceCount.QuadPart << 32) ^ PerformanceCount.QuadPart; cookie &= 0xFFFFFFFFFFFF; if(cookie == 0x2B992DDFA232) cookie++; #else cookie ^= systime.dwHighDateTime ^ systime.dwLowDateTime; cookie ^= PerformanceCount.LowPart; cookie ^= PerformanceCount.HighPart; if(cookie == 0xBB40E64E) cookie++; else if(!(cookie & 0xFFFF0000)) cookie |= (cookie | 0x4711) << 16; #endif _process.Memory().Write(REBASE(pLC->SecurityCookie, pImage->peImage.ImageBase(), pImage->imgMem.Ptr<ptr_t>()), cookie); } return true; }
NTSTATUS MMap::DisableExceptions(ImageContext* pImage) { BLACBONE_TRACE(L"ManualMap: Disabling exception support for image '%ls'", pImage->FileName.c_str()); #ifdef USE64 if(pImage->pExpTableAddr) { AsmJitHelper a; size_t result = 0; auto pRemoveTable = _process.Modules().GetExport( _process.Modules().GetModule(L"ntdll.dll", LdrList, pImage->peImage.ImageType()), "RtlDeleteFunctionTable" ); a.GenPrologue(); // RtlDeleteFunctionTable(pExpTable); a.GenCall(static_cast<size_t>(pRemoveTable.procAddress), {pImage->pExpTableAddr}); _process.Remote().AddReturnWithEvent(a); a.GenEpilogue(); auto status = _process.Remote().ExecInWorkerThread(a->make(), a->getCodeSize(), result); if(!NT_SUCCESS(status)) return status; return MExcept::RemoveVEH((pImage->flags & CreateLdrRef) != 0); } else return STATUS_NOT_FOUND; #else return MExcept::RemoveVEH((pImage->flags & PartialExcept) != 0); #endif }
/// <summary> /// Copies image into target process /// </summary> /// <param name="pImage">Image data</param> /// <returns>true on success</returns> bool MMap::CopyImage(ImageContext* pImage) { NTSTATUS status = STATUS_SUCCESS; BLACBONE_TRACE(L"ManualMap: Performing image copy"); // offset to first section equals to header size size_t dwHeaderSize = pImage->peImage.HeadersSize(); // Copy header if(pImage->flags & HideVAD) status = Driver().WriteMem(_process.Id(), pImage->imgMem.Ptr(), dwHeaderSize, pImage->peImage.FileBase()); else status = pImage->imgMem.Write(0, dwHeaderSize, pImage->peImage.FileBase()); if(!NT_SUCCESS(status)) { BLACBONE_TRACE(L"ManualMap: Failed to copy image headers. Status = 0x%x", status); return false; } // Set header protection if(!(pImage->flags & HideVAD) && pImage->imgMem.Protect(PAGE_READONLY, 0, dwHeaderSize) != STATUS_SUCCESS) { BLACBONE_TRACE(L"ManualMap: Failed to set header memory protection. Status = 0x%x", LastNtStatus()); return false; } auto& sections = pImage->peImage.Sections(); // Copy sections for(auto& section : sections) { // Skip discardable sections if(section.Characteristics & (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE)) { if(section.SizeOfRawData == 0) continue; uint8_t* pSource = reinterpret_cast<uint8_t*>(pImage->peImage.ResolveRVAToVA(section.VirtualAddress)); // Copy section data if(pImage->flags & HideVAD) { status = Driver().WriteMem( _process.Id(), pImage->imgMem.Ptr() + section.VirtualAddress, section.SizeOfRawData, pSource ); } else { status = pImage->imgMem.Write(section.VirtualAddress, section.SizeOfRawData, pSource); } if(!NT_SUCCESS(status)) { BLACBONE_TRACE( L"ManualMap: Failed to copy image section at offset 0x%x. Status = 0x%x", section.VirtualAddress, status ); return false; } } } return true; }
bool MMap::InitStaticTLS(ImageContext* pImage) { IMAGE_TLS_DIRECTORY *pTls = reinterpret_cast<decltype(pTls)>(pImage->peImage.DirectoryAddress(IMAGE_DIRECTORY_ENTRY_TLS)); auto pRebasedTls = reinterpret_cast<IMAGE_TLS_DIRECTORY*>(REBASE(pTls, pImage->peImage.FileBase(), pImage->imgMem.Ptr<ptr_t>())); // Use native TLS initialization if(pTls && pTls->AddressOfIndex) { BLACBONE_TRACE(L"ManualMap: Performing static TLS initialization for image '%ls'", pImage->FileName.c_str()); _process.NativeLdr().AddStaticTLSEntry(pImage->imgMem.Ptr<void*>(), pRebasedTls); } return true; }
const ModuleData* MMap::FindOrMapDependency(ImageContext* pImage, std::wstring& path) { // Already loaded auto hMod = _process.Modules().GetModule(path, LdrList, pImage->peImage.ImageType(), pImage->FileName.c_str()); if(hMod) return hMod; BLACBONE_TRACE(L"ManualMap: Loading new dependency '%ls'", path.c_str()); auto basedir = pImage->peImage.NoPhysFile() ? Utils::GetExeDirectory() : Utils::GetParent(pImage->FilePath); auto status = NameResolve::Instance().ResolvePath(path, pImage->FileName, basedir, NameResolve::EnsureFullPath, _process.Id(), pImage->peImage.ActCtx()); if(!NT_SUCCESS(status)) { BLACBONE_TRACE(L"ManualMap: Failed to resolve dependency path '%ls'", path.c_str()); return nullptr; } BLACBONE_TRACE(L"ManualMap: Dependency path resolved to '%ls'", path.c_str()); LoadData data; if(_mapCallback != nullptr) { ModuleData tmpData; tmpData.baseAddress = 0; tmpData.manual = ((pImage->flags & ManualImports) != 0); tmpData.fullPath = path; tmpData.name = Utils::StripPath(path); tmpData.size = 0; tmpData.type = mt_unknown; data = _mapCallback(PreCallback, _userContext, _process, tmpData); } // Loading method if(data.mtype == MT_Manual || (data.mtype == MT_Default && pImage->flags & ManualImports)) { return FindOrMapModule(path, nullptr, 0, false, pImage->flags | NoSxS | NoDelayLoad | PartialExcept); } else if(data.mtype != MT_None) { return _process.Modules().Inject(path); } // Aborted by user else { LastNtStatus(STATUS_REQUEST_CANCELED); return nullptr; } };
bool MMap::ProtectImageMemory(ImageContext* pImage) { // Set section memory protection for(auto& section : pImage->peImage.Sections()) { auto prot = GetSectionProt(section.Characteristics); if(prot != PAGE_NOACCESS) { if(pImage->imgMem.Protect(prot, section.VirtualAddress, section.Misc.VirtualSize) != STATUS_SUCCESS) { BLACBONE_TRACE( L"ManualMap: Failed to set section memory protection at offset 0x%x. Status = 0x%x", section.VirtualAddress, LastNtStatus() ); return false; } } // Decommit pages with NO_ACCESS protection else { _process.Memory().Free(pImage->imgMem.Ptr() + section.VirtualAddress, section.Misc.VirtualSize, MEM_DECOMMIT); } } return true; }
NTSTATUS MMap::EnableExceptions(ImageContext* pImage) { BLACBONE_TRACE(L"ManualMap: Enabling exception support for image '%ls'", pImage->FileName.c_str()); bool partial = (pImage->flags & PartialExcept) != 0; #ifdef USE64 size_t size = pImage->peImage.DirectorySize(IMAGE_DIRECTORY_ENTRY_EXCEPTION); auto pExpTable = reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY>(pImage->peImage.DirectoryAddress(IMAGE_DIRECTORY_ENTRY_EXCEPTION)); // Invoke RtlAddFunctionTable if(pExpTable) { AsmJitHelper a; uint64_t result = 0; pImage->pExpTableAddr = REBASE(pExpTable, pImage->peImage.FileBase(), pImage->imgMem.Ptr<ptr_t>()); auto pAddTable = _process.Modules().GetExport( _process.Modules().GetModule(L"ntdll.dll", LdrList, pImage->peImage.ImageType()), "RtlAddFunctionTable" ); a.GenPrologue(); a.GenCall( static_cast<size_t>(pAddTable.procAddress), { pImage->pExpTableAddr, size / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY), pImage->imgMem.Ptr<size_t>()} ); _process.Remote().AddReturnWithEvent(a, pImage->peImage.ImageType()); a.GenEpilogue(); auto status = _process.Remote().ExecInWorkerThread(a->make(), a->getCodeSize(), result); if(!NT_SUCCESS(status)) return status; return (pImage->flags & CreateLdrRef) ? STATUS_SUCCESS : MExcept::CreateVEH(pImage->imgMem.Ptr<size_t>(), pImage->peImage.ImageSize(), pImage->peImage.ImageType(), partial); } // No exception table else return STATUS_NOT_FOUND; #else bool safeseh = false; if(!_process.NativeLdr().InsertInvertedFunctionTable(pImage->imgMem.Ptr<void*>(), pImage->peImage.ImageSize(), safeseh)) return STATUS_UNSUCCESSFUL; return safeseh ? STATUS_SUCCESS : MExcept::CreateVEH(pImage->imgMem.Ptr<size_t>(), pImage->peImage.ImageSize(), pImage->peImage.ImageType(), partial); #endif }
/// <summary> /// Unmap all manually mapped modules /// </summary> /// <returns>true on success</returns> bool MMap::UnmapAllModules() { for(auto img = _images.rbegin(); img != _images.rend(); ++img) { auto pImage = img->get(); BLACBONE_TRACE(L"ManualMap: Unmapping image '%ls'", pImage->FileName.c_str()); // Call main RunModuleInitializers(pImage, DLL_PROCESS_DETACH); // Remove VEH if(!(pImage->flags & NoExceptions)) DisableExceptions(pImage); // Remove from loader auto mod = _process.Modules().GetModule(pImage->FileName); _process.Modules().Unlink(mod); // Free memory pImage->imgMem.Free(); // Remove reference from local modules list _process.Modules().RemoveManualModule(pImage->FilePath, pImage->peImage.ImageType()); } Cleanup(); Reset(); return true; }
bool MMap::CreateActx(const std::wstring& path, int id /*= 2 */, bool asImage /*= true*/) { AsmJitHelper a; uint64_t result = 0; ACTCTXW act = {0}; _pAContext = _process.Memory().Allocate(512, PAGE_READWRITE); act.cbSize = sizeof(act); act.lpSource = reinterpret_cast<LPCWSTR>(_pAContext.Ptr<size_t>() + sizeof(HANDLE) + sizeof(act)); // Ignore some fields for pure manifest file if(asImage) { act.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; act.lpResourceName = MAKEINTRESOURCEW(id); } bool switchMode = (_process.Core().GetNative()->GetWow64Barrier().type == wow_64_32); auto pCreateActx = _process.Modules().GetExport(_process.Modules().GetModule(L"kernel32.dll"), "CreateActCtxW"); if(pCreateActx.procAddress == 0) { BLACBONE_TRACE(L"ManualMap: Failed to create activation context for image '%ls'. 'CreateActCtxW' is absent", path.c_str()); return false; } // CreateActCtx(&act) // Emulate Wow64 if(switchMode) { _ACTCTXW_T<DWORD> act32 = {0}; act32.cbSize = sizeof(act32); act32.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; act32.lpSource = _pAContext.Ptr<uint32_t>() + sizeof(HANDLE) + sizeof(act32); act32.lpResourceName = id; a->push(_pAContext.Ptr<uint32_t>() + static_cast<uint32_t>(sizeof(HANDLE))); a->mov(asmjit::host::eax, static_cast<uint32_t>(pCreateActx.procAddress)); a->call(a->zax); a->mov(asmjit::host::edx, _pAContext.Ptr<uint32_t>()); //a->mov( asmjit::host::dword_ptr( asmjit::host::edx ), asmjit::host::eax ); a->dw('\x01\x02'); auto pTermThd = _process.Modules().GetExport(_process.Modules().GetModule(L"ntdll.dll"), "NtTerminateThread"); a->push(a->zax); a->push(uint32_t(0)); a->mov(asmjit::host::eax, static_cast<uint32_t>(pTermThd.procAddress)); a->call(a->zax); a->ret(4); // Write path to file _pAContext.Write(sizeof(HANDLE), act32); _pAContext.Write(sizeof(HANDLE) + sizeof(act32), (path.length() + 1) * sizeof(wchar_t), path.c_str()); auto pCode = _process.Memory().Allocate(0x1000); pCode.Write(0, a->getCodeSize(), a->make()); result = _process.Remote().ExecDirect(pCode.Ptr<ptr_t>(), _pAContext.Ptr<size_t>() + sizeof(HANDLE)); } // Native way else { a.GenPrologue(); a.GenCall(static_cast<size_t>(pCreateActx.procAddress), {_pAContext.Ptr<size_t>() + sizeof(HANDLE)}); a->mov(a->zdx, _pAContext.Ptr<size_t>()); a->mov(a->intptr_ptr(a->zdx), a->zax); _process.Remote().AddReturnWithEvent(a); a.GenEpilogue(); // Write path to file _pAContext.Write(sizeof(HANDLE), act); _pAContext.Write(sizeof(HANDLE) + sizeof(act), (path.length() + 1) * sizeof(wchar_t), path.c_str()); _process.Remote().ExecInWorkerThread(a->make(), a->getCodeSize(), result); } if(reinterpret_cast<HANDLE>(result) == INVALID_HANDLE_VALUE) { _pAContext.Free(); // SetLastError( err::mapping::CantCreateActx ); BLACBONE_TRACE(L"ManualMap: Failed to create activation context for image '%ls'. Status: 0x%x", path.c_str(), _process.Remote().GetLastStatus()); return false; } return true; }
bool MMap::ResolveImport(ImageContext* pImage, bool useDelayed /*= false */) { auto imports = pImage->peImage.GetImports(useDelayed); if(imports.empty()) return true; // Traverse entries for(auto& importMod : imports) { std::wstring wstrDll = importMod.first; // Load dependency if needed auto hMod = FindOrMapDependency(pImage, wstrDll); if(!hMod) { // TODO: Add error code BLACBONE_TRACE(L"ManualMap: Failed to load dependency '%ls'. Status = 0x%x", wstrDll.c_str(), LastNtStatus()); return false; } for(auto& importFn : importMod.second) { exportData expData; if(importFn.importByOrd) expData = _process.Modules().GetExport(hMod, reinterpret_cast<const char*>(importFn.importOrdinal)); else expData = _process.Modules().GetExport(hMod, importFn.importName.c_str()); // Still forwarded, load missing modules while(expData.procAddress && expData.isForwarded) { std::wstring wdllpath = expData.forwardModule; // Ensure module is loaded auto hFwdMod = FindOrMapDependency(pImage, wdllpath); if(!hFwdMod) { // TODO: Add error code BLACBONE_TRACE(L"ManualMap: Failed to load forwarded dependency '%ls'. Status = 0x%x", wstrDll.c_str(), LastNtStatus()); return false; } if(expData.forwardByOrd) expData = _process.Modules().GetExport(hFwdMod, reinterpret_cast<const char*>(expData.forwardOrdinal), wdllpath.c_str()); else expData = _process.Modules().GetExport(hFwdMod, expData.forwardName.c_str(), wdllpath.c_str()); } // Failed to resolve import if(expData.procAddress == 0) { LastNtStatus(STATUS_ORDINAL_NOT_FOUND); if(importFn.importByOrd) BLACBONE_TRACE(L"ManualMap: Failed to get import #%d from image '%ls'", importFn.importOrdinal, wstrDll.c_str()); else BLACBONE_TRACE(L"ManualMap: Failed to get import '%ls' from image '%ls'", Utils::AnsiToWstring(importFn.importName).c_str(), wstrDll.c_str()); return false; } auto status = STATUS_SUCCESS; if(pImage->flags & HideVAD) { size_t address = static_cast<size_t>(expData.procAddress); status = Driver().WriteMem(_process.Id(), pImage->imgMem.Ptr() + importFn.ptrRVA, sizeof(address), &address); } else status = pImage->imgMem.Write(importFn.ptrRVA, static_cast<size_t>(expData.procAddress)); // Write function address if(!NT_SUCCESS(status)) { BLACBONE_TRACE(L"ManualMap: Failed to write import function address at offset 0x%x. Status = 0x%x", importFn.ptrRVA, status); return false; } } } return true; }
const ModuleData* MMap::MapImageInternal( const std::wstring& path, void* buffer, size_t size, bool asImage /*= false*/, eLoadFlags flags /*= NoFlags*/, MapCallback mapCallback /*= nullptr*/, void* context /*= nullptr*/ ) { // Already loaded if(auto hMod = _process.Modules().GetModule(path)) return hMod; // Prepare target process if(!NT_SUCCESS(_process.Remote().CreateRPCEnvironment())) { Cleanup(); return nullptr; } // No need to support exceptions if DEP is disabled if(_process.Core().DEP() == false) flags |= NoExceptions; // Ignore MapInHighMem for native x64 process if(!_process.Core().IsWOW64()) flags &= ~MapInHighMem; // Set native loader callback _mapCallback = mapCallback; _userContext = context; BLACBONE_TRACE(L"ManualMap: Mapping image '%ls' with flags 0x%x", path.c_str(), flags); // Map module and all dependencies auto mod = FindOrMapModule(path, buffer, size, asImage, flags); if(mod == nullptr) { Cleanup(); return nullptr; } // Change process base module address if needed if(flags & RebaseProcess && !_images.empty() && _images.rbegin()->get()->peImage.IsExecutable()) { BLACBONE_TRACE(L"ManualMap: Rebasing process to address 0x%p", static_cast<size_t>(mod->baseAddress)); // Managed path fix if(_images.rbegin()->get()->peImage.PureIL() && !path.empty()) FixManagedPath(_process.Memory().Read<size_t>(_process.Core().GetPEB() + 2 * WordSize), path); _process.Memory().Write(_process.Core().GetPEB() + 2 * WordSize, WordSize, &mod->baseAddress); } auto wipeMemory = [](Process& proc, ImageContext* img, size_t offset, size_t size) { size = Align(size, 0x1000); std::unique_ptr<uint8_t[]> zeroBuf(new uint8_t[size]()); if(img->flags & HideVAD) { Driver().WriteMem(proc.Id(), img->imgMem.Ptr() + offset, size, zeroBuf.get()); } else { img->imgMem.Write(offset, size, zeroBuf.get()); if(!NT_SUCCESS(proc.Memory().Free(img->imgMem.Ptr() + offset, size))) proc.Memory().Protect(img->imgMem.Ptr() + offset, size, PAGE_NOACCESS); } }; // Run initializers for(auto& img : _images) { // Init once if(!img->initialized) { // Hack for IL dlls if(!img->peImage.IsExecutable() && img->peImage.PureIL()) { DWORD flOld = 0; auto flg = img->imgMem.Read(img->peImage.ILFlagOffset(), 0); img->imgMem.Protect(PAGE_EXECUTE_READWRITE, img->peImage.ILFlagOffset(), sizeof(flg), &flOld); img->imgMem.Write(img->peImage.ILFlagOffset(), flg & ~COMIMAGE_FLAGS_ILONLY); img->imgMem.Protect(flOld, img->peImage.ILFlagOffset(), sizeof(flg), &flOld); } if(!RunModuleInitializers(img.get(), DLL_PROCESS_ATTACH)) return nullptr; // Wipe header if(img->flags & WipeHeader) wipeMemory(_process, img.get(), 0, img->peImage.HeadersSize()); // Wipe discardable sections for non pure IL images for(auto& sec : img->peImage.Sections()) if(sec.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) wipeMemory(_process, img.get(), sec.VirtualAddress, sec.Misc.VirtualSize); img->initialized = true; } } Cleanup(); return mod; }
/// <summary> /// Get existing module or map it if absent /// </summary> /// <param name="path">Image path</param> /// <param name="buffer">Image data buffer</param> /// <param name="size">Buffer size.</param> /// <param name="asImage">If set to true - buffer has image memory layout</param> /// <param name="flags">Mapping flags</param> /// <returns>Module info</returns> const ModuleData* MMap::FindOrMapModule( const std::wstring& path, void* buffer, size_t size, bool asImage, eLoadFlags flags /*= NoFlags*/ ) { NTSTATUS status = STATUS_SUCCESS; std::unique_ptr<ImageContext> pImage(new ImageContext()); pImage->FilePath = path; pImage->FileName = Utils::StripPath(pImage->FilePath); pImage->flags = flags; // Load and parse image status = buffer ? pImage->peImage.Load(buffer, size, !asImage) : pImage->peImage.Load(path, flags & NoSxS ? true : false); if(!NT_SUCCESS(status)) { BLACBONE_TRACE(L"ManualMap: Failed to load image '%ls'/0x%p. Status 0x%X", path.c_str(), buffer, status); pImage->peImage.Release(); return nullptr; } // Check if already loaded if(auto hMod = _process.Modules().GetModule(path, LdrList, pImage->peImage.ImageType())) { pImage->peImage.Release(); return hMod; } BLACBONE_TRACE(L"ManualMap: Loading new image '%ls'", path.c_str()); // Try to map image in high (>4GB) memory range if(flags & MapInHighMem) { AllocateInHighMem(pImage->imgMem, pImage->peImage.ImageSize()); } // Try to map image at it's original ASRL-aware base else if(flags & HideVAD) { ptr_t base = pImage->peImage.ImageBase(); ptr_t isize = pImage->peImage.ImageSize(); if(!NT_SUCCESS(Driver().EnsureLoaded())) { pImage->peImage.Release(); return nullptr; } // Allocate as physical at desired base status = Driver().AllocateMem(_process.Id(), base, isize, MEM_COMMIT, PAGE_EXECUTE_READWRITE, true); // Allocate at any base if(!NT_SUCCESS(status)) { base = 0; size = pImage->peImage.ImageSize(); status = Driver().AllocateMem(_process.Id(), base, isize, MEM_COMMIT, PAGE_EXECUTE_READWRITE, true); } // Store allocated region if(NT_SUCCESS(status)) { pImage->imgMem = MemBlock(&_process.Memory(), base, static_cast<size_t>(isize), PAGE_EXECUTE_READWRITE, true, true); } // Stop mapping else { //flags &= ~HideVAD; BLACBONE_TRACE(L"ManualMap: Failed to allocate physical memory for image, status 0x%d", status); pImage->peImage.Release(); return nullptr; } } // Allocate normally if something went wrong if(pImage->imgMem == 0) pImage->imgMem = _process.Memory().Allocate(pImage->peImage.ImageSize(), PAGE_EXECUTE_READWRITE, pImage->peImage.ImageBase()); BLACBONE_TRACE(L"ManualMap: Image base allocated at 0x%p", pImage->imgMem.Ptr<size_t>()); if(!pImage->imgMem.Valid()) return nullptr; // Create Activation context for SxS if(pImage->peImage.ManifestID() == 0) flags |= NoSxS; if(!(flags & NoSxS)) CreateActx(pImage->peImage.ManifestFile(), pImage->peImage.ManifestID(), !pImage->peImage.NoPhysFile()); // Core image mapping operations if(!CopyImage(pImage.get()) || !RelocateImage(pImage.get())) { pImage->peImage.Release(); return nullptr; } auto mt = pImage->peImage.ImageType(); auto pMod = _process.Modules().AddManualModule(pImage->FilePath, pImage->imgMem.Ptr<module_t>(), pImage->imgMem.Size(), mt); // Import tables if(!ResolveImport(pImage.get()) || (!(flags & NoDelayLoad) && !ResolveImport(pImage.get(), true))) { pImage->peImage.Release(); _process.Modules().RemoveManualModule(pImage->FileName, mt); return nullptr; } // Apply proper memory protection for sections if(!(flags & HideVAD)) ProtectImageMemory(pImage.get()); // Make exception handling possible (C and C++) if(!(flags & NoExceptions)) { status = EnableExceptions(pImage.get()); if(!NT_SUCCESS(status) && status != STATUS_NOT_FOUND) { BLACBONE_TRACE(L"ManualMap: Failed to enable exception handling for image %ls", pImage->FileName.c_str()); pImage->peImage.Release(); _process.Modules().RemoveManualModule(pImage->FileName, mt); return nullptr; } } // Unlink image from VAD list if(flags & HideVAD && !NT_SUCCESS(ConcealVad(pImage->imgMem))) { pImage->peImage.Release(); _process.Modules().RemoveManualModule(pImage->FileName, mt); return nullptr; } // Initialize security cookie if(!InitializeCookie(pImage.get())) { pImage->peImage.Release(); _process.Modules().RemoveManualModule(pImage->FileName, mt); return nullptr; } // Get entry point pImage->EntryPoint = pImage->peImage.EntryPoint(pImage->imgMem.Ptr<ptr_t>()); // Create reference for native loader functions LdrRefFlags ldrFlags = flags & CreateLdrRef ? Ldr_All : Ldr_None; if(_mapCallback != nullptr) { auto mapData = _mapCallback(PostCallback, _userContext, _process, *pMod); ldrFlags = mapData.ldrFlags; } if(ldrFlags != Ldr_None) { _process.NativeLdr().CreateNTReference( pImage->imgMem.Ptr<HMODULE>(), pImage->peImage.ImageSize(), pImage->FilePath, static_cast<size_t>(pImage->EntryPoint), ldrFlags ); } // Static TLS data if(!(flags & NoTLS) && !InitStaticTLS(pImage.get())) { pImage->peImage.Release(); _process.Modules().RemoveManualModule(pImage->FileName, mt); return nullptr; } // Fill TLS callbacks pImage->peImage.GetTLSCallbacks(pImage->imgMem.Ptr<ptr_t>(), pImage->tlsCallbacks); // Unload local copy pImage->peImage.Release(); // Release ownership of image memory block pImage->imgMem.Release(); // Store image _images.emplace_back(std::move(pImage)); return pMod; }
NTSTATUS MExcept::CreateVEH(size_t pTargetBase, size_t imageSize, eModType mt, bool partial) { AsmJitHelper a; uint64_t result = 0; auto& mods = _proc.Modules(); #ifdef USE64 // Add module to module table if(!_pModTable.Valid()) { _pModTable = _proc.Memory().Allocate(0x1000); _pModTable.Release(); if(!_pModTable.Valid()) return LastNtStatus(); } ModuleTable table; _pModTable.Read(0, table); // Add new entry to the table table.entry[table.count].base = pTargetBase; table.entry[table.count].size = imageSize; table.count++; _pModTable.Write(0, table); // No handler required if(partial) return STATUS_SUCCESS; // VEH codecave _pVEHCode = _proc.Memory().Allocate(0x2000); _pVEHCode.Release(); if(!_pVEHCode.Valid()) return LastNtStatus(); BLACBONE_TRACE("ManualMap: Vectored hander: 0x%p\n", _pVEHCode.Ptr()); asmjit::Label lExit = a->newLabel(); asmjit::Label lLoop1 = a->newLabel(); asmjit::Label skip1 = a->newLabel(); asmjit::Label found1 = a->newLabel(); // // Assembly code for VectoredHandler64 // 0x10 - EXCEPTION_RECORD.ExceptionAddress // 0x20 - EXCEPTION_RECORD.ExceptionInformation[0] // 0x30 - EXCEPTION_RECORD.ExceptionInformation[2] // 0x38 - EXCEPTION_RECORD.ExceptionInformation[3] // a->mov(asmjit::host::rax, asmjit::host::qword_ptr(asmjit::host::rcx)); a->cmp(asmjit::host::dword_ptr(asmjit::host::rax), EH_EXCEPTION_NUMBER); // Exception code a->jne(lExit); a->cmp(asmjit::host::qword_ptr(asmjit::host::rax, 0x20), EH_PURE_MAGIC_NUMBER1); // Sub code a->jne(lExit); a->cmp(asmjit::host::qword_ptr(asmjit::host::rax, 0x38), 0); // Image base a->jne(lExit); a->mov(asmjit::host::r9, _pModTable.Ptr()); a->mov(asmjit::host::rdx, asmjit::host::qword_ptr(asmjit::host::r9)); // Record count a->add(asmjit::host::r9, sizeof(table.count)); a->xor_(asmjit::host::r10, asmjit::host::r10); a->bind(lLoop1); a->mov(asmjit::host::r8, asmjit::host::qword_ptr(asmjit::host::rax, 0x30)); a->mov(asmjit::host::r11, asmjit::host::qword_ptr(asmjit::host::r9)); a->cmp(asmjit::host::r8, asmjit::host::r11); a->jl(skip1); a->add(asmjit::host::r11, asmjit::host::qword_ptr(asmjit::host::r9, sizeof(table.entry[0].base))); // Size a->cmp(asmjit::host::r8, asmjit::host::r11); a->jg(skip1); a->jmp(found1); a->bind(skip1); a->add(asmjit::host::r9, sizeof(ExceptionModule)); a->add(asmjit::host::r10, 1); a->cmp(asmjit::host::r10, asmjit::host::rdx); a->jne(lLoop1); a->jmp(lExit); a->bind(found1); a->mov(asmjit::host::qword_ptr(asmjit::host::rax, 0x20), EH_MAGIC_NUMBER1); a->mov(asmjit::host::rcx, asmjit::host::qword_ptr(asmjit::host::rcx)); a->mov(asmjit::host::rdx, asmjit::host::qword_ptr(asmjit::host::r9)); a->mov(asmjit::host::qword_ptr(asmjit::host::rax, 0x38), asmjit::host::rdx); a->bind(lExit); a->xor_(asmjit::host::rax, asmjit::host::rax); a->ret(); a->db(0xCC); a->db(0xCC); a->db(0xCC); if(_pVEHCode.Write(0, a->getCodeSize(), a->make()) != STATUS_SUCCESS) { _pVEHCode.Free(); return LastNtStatus(); } #else UNREFERENCED_PARAMETER(pTargetBase); UNREFERENCED_PARAMETER(imageSize); // No handler required if(partial) return STATUS_SUCCESS; // VEH codecave _pVEHCode = _proc.Memory().Allocate(0x2000); _pVEHCode.Release(); if(!_pVEHCode.Valid()) return LastNtStatus(); // Resolve compiler incremental table address, if any void *pFunc = ResolveJmp(&VectoredHandler); size_t fnSize = static_cast<size_t>(SizeOfProc(pFunc)); size_t dataOfs = 0, code_ofs = 0, code_ofs2 = 0;; // Find and replace magic values for(uint8_t *pData = reinterpret_cast<uint8_t*>(pFunc); pData < reinterpret_cast<uint8_t*>(pFunc) + fnSize - 4; pData++) { // LdrpInvertedFunctionTable if(*(size_t*)pData == 0xDEADDA7A) { dataOfs = pData - reinterpret_cast<uint8_t*>(pFunc); continue; } // DecodeSystemPointer address if(*(size_t*)pData == 0xDEADC0DE) { code_ofs = pData - reinterpret_cast<uint8_t*>(pFunc); break; } // LdrProtectMrdata address if(*(size_t*)pData == 0xDEADC0D2) { code_ofs2 = pData - reinterpret_cast<uint8_t*>(pFunc); continue; } } auto pDecode = mods.GetExport(mods.GetModule(L"ntdll.dll", Sections, mt), "RtlDecodeSystemPointer").procAddress; // Write handler data into target process if(!NT_SUCCESS(_pVEHCode.Write(0, fnSize, pFunc)) || !NT_SUCCESS(_pVEHCode.Write(dataOfs, _proc.NativeLdr().LdrpInvertedFunctionTable())) || !NT_SUCCESS(_pVEHCode.Write(code_ofs, static_cast<size_t>(pDecode))) || !NT_SUCCESS(_pVEHCode.Write(code_ofs2, _proc.NativeLdr().LdrProtectMrdata()))) { _pVEHCode.Free(); return LastNtStatus(); } #endif // AddVectoredExceptionHandler(0, pHandler); auto pAddHandler = mods.GetExport(mods.GetModule(L"ntdll.dll", Sections, mt), "RtlAddVectoredExceptionHandler").procAddress; if(pAddHandler == 0) return STATUS_NOT_FOUND; a->reset(); a.GenPrologue(); a.GenCall(static_cast<size_t>(pAddHandler), {0, _pVEHCode.Ptr<size_t>()}); _proc.Remote().AddReturnWithEvent(a, mt); a.GenEpilogue(); _proc.Remote().ExecInWorkerThread(a->make(), a->getCodeSize(), result); _hVEH = static_cast<size_t>(result); return (_hVEH == 0 ? STATUS_NOT_FOUND : STATUS_SUCCESS); }