/// <summary> /// Create new thread and execute code in it. Wait until execution ends /// </summary> /// <param name="pCode">Code to execute</param> /// <param name="size">Code size</param> /// <param name="callResult">Code return value</param> /// <returns>Status</returns> NTSTATUS RemoteExec::ExecInNewThread( PVOID pCode, size_t size, uint64_t& callResult ) { AsmJitHelper a; NTSTATUS dwResult = STATUS_SUCCESS; // Write code dwResult = CopyCode( pCode, size ); if (dwResult != STATUS_SUCCESS) return dwResult; bool switchMode = (_proc.core().native()->GetWow64Barrier().type == wow_64_32); auto pExitThread = _mods.GetExport( _mods.GetModule( L"ntdll.dll", LdrList, switchMode ? mt_mod64 : mt_default ), "NtTerminateThread" ).procAddress; if (pExitThread == 0) return LastNtStatus( STATUS_NOT_FOUND ); a.GenPrologue( switchMode ); // Prepare thread to run in x64 mode if(switchMode) { // Allocate new x64 activation stack auto createActStack = _mods.GetExport( _mods.GetModule( L"ntdll.dll", LdrList, mt_mod64 ), "RtlAllocateActivationContextStack" ).procAddress; if (createActStack) { a.GenCall( static_cast<size_t>(createActStack), { _userData.ptr<size_t>() + 0x3100 } ); a->mov( a->zax, _userData.ptr<size_t>( ) + 0x3100 ); a->mov( a->zax, a->intptr_ptr( a->zax ) ); a.SetTebPtr(); a->mov( a->intptr_ptr( a->zdx, 0x2C8 ), a->zax ); } } a.GenCall( _userCode.ptr<size_t>(), { } ); a.ExitThreadWithStatus( (size_t)pExitThread, _userData.ptr<size_t>() + INTRET_OFFSET ); // Execute code in newly created thread if (_userCode.Write( size, a->getCodeSize(), a->make() ) == STATUS_SUCCESS) { auto thread = _threads.CreateNew( _userCode.ptr<ptr_t>() + size, _userData.ptr<ptr_t>()/*, HideFromDebug*/ ); dwResult = thread.Join(); callResult = _userData.Read<uint64_t>( INTRET_OFFSET, 0 ); } else dwResult = LastNtStatus(); return dwResult; }
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 }
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; }
/// <summary> /// Removes VEH from target process /// </summary> /// <returns></returns> NTSTATUS MExcept::RemoveVEH() { AsmJitHelper a; uint64_t result = 0; a.GenPrologue(); // RemoveVectoredExceptionHandler(pHandler); a.GenCall( reinterpret_cast<void*>(&RemoveVectoredExceptionHandler), { _hVEH } ); _proc.remote().AddReturnWithEvent( a ); a.GenEpilogue(); _proc.remote().ExecInWorkerThread( a->make(), a->getCodeSize(), result ); _pVEHCode.Free(); return STATUS_SUCCESS; }
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 }
NTSTATUS MExcept::RemoveVEH(bool partial) { AsmJitHelper a; uint64_t result = 0; auto& mods = _proc.Modules(); auto pRemoveHandler = mods.GetExport(mods.GetModule(L"ntdll.dll"), "RtlRemoveVectoredExceptionHandler").procAddress; if(pRemoveHandler == 0) return STATUS_NOT_FOUND; a.GenPrologue(); // RemoveVectoredExceptionHandler(pHandler); a.GenCall(static_cast<size_t>(pRemoveHandler), {_hVEH}); _proc.Remote().AddReturnWithEvent(a); a.GenEpilogue(); // Destroy table and handler if(!partial) { _proc.Remote().ExecInWorkerThread(a->make(), a->getCodeSize(), result); _pVEHCode.Free(); _hVEH = 0; _pModTable.Free(); } return STATUS_SUCCESS; }
/// <summary> /// Create event to synchronize APC procedures /// </summary> /// <param name="threadID">The thread identifier.</param> /// <returns>true on success</returns> bool RemoteExec::CreateAPCEvent( DWORD threadID ) { if(_hWaitEvent == NULL) { AsmJitHelper a; wchar_t pEventName[128] = { 0 }; uint64_t dwResult = NULL; size_t len = sizeof(pEventName); OBJECT_ATTRIBUTES obAttr = { 0 }; UNICODE_STRING ustr = { 0 }; // Detect ntdll type eModType mt = mt_default; if (_memory.core().native()->GetWow64Barrier().type == wow_64_32) mt = mt_mod64; // Event name swprintf_s( pEventName, ARRAYSIZE( pEventName ), L"\\BaseNamedObjects\\_MMapEvent_0x%x_0x%x", threadID, GetTickCount() ); // Prepare Arguments ustr.Length = static_cast<USHORT>(wcslen( pEventName ) * sizeof(wchar_t)); ustr.MaximumLength = static_cast<USHORT>(sizeof(pEventName)); ustr.Buffer = reinterpret_cast<PWSTR>(_userData.ptr<size_t>() + ARGS_OFFSET + sizeof(obAttr) + sizeof(ustr)); obAttr.ObjectName = reinterpret_cast<PUNICODE_STRING>(_userData.ptr<size_t>() + ARGS_OFFSET + sizeof(obAttr)); obAttr.Length = sizeof(obAttr); auto pCreateEvent = _mods.GetExport( _mods.GetModule( L"ntdll.dll", Sections, mt ), "NtCreateEvent" ).procAddress; if (pCreateEvent == 0) return false; a.GenCall( static_cast<size_t>(pCreateEvent), { _userData.ptr<size_t>( ) + EVENT_OFFSET, EVENT_ALL_ACCESS, _userData.ptr<size_t>() + ARGS_OFFSET, 0, FALSE } ); // Save status a->mov( asmjit::host::zdx, _userData.ptr<size_t>() + ERR_OFFSET ); a->mov( asmjit::host::dword_ptr( asmjit::host::zdx ), asmjit::host::eax ); a->ret(); NTSTATUS status = _userData.Write( ARGS_OFFSET, obAttr ); status |= _userData.Write( ARGS_OFFSET + sizeof(obAttr), ustr ); status |= _userData.Write( ARGS_OFFSET + sizeof(obAttr) + sizeof(ustr), len, pEventName ); if (status != STATUS_SUCCESS) return false; ExecInNewThread( a->make(), a->getCodeSize(), dwResult ); status = _userData.Read<NTSTATUS>( ERR_OFFSET, -1 ); if (status != STATUS_SUCCESS) return false; ustr.Buffer = pEventName; obAttr.ObjectName = &ustr; // Open created event status = GET_IMPORT( NtOpenEvent )( &_hWaitEvent, SYNCHRONIZE | EVENT_MODIFY_STATE, &obAttr ); if (status != STATUS_SUCCESS) return false; } return true; }
/// <summary> /// Create worker RPC thread /// </summary> /// <returns>Thread ID</returns> DWORD RemoteExec::CreateWorkerThread() { AsmJitHelper a; asmjit::Label l_loop = a->newLabel(); // // Create execution thread // if(!_hWorkThd.valid()) { eModType mt = mt_default; if (_memory.core().native()->GetWow64Barrier().type == wow_64_32) { mt = mt_mod64; a.SwitchTo64(); // Align stack on 16 byte boundary a->and_( asmjit::host::zsp, -16 ); // Allocate new x64 activation stack auto createActStack = _mods.GetExport( _mods.GetModule( L"ntdll.dll", LdrList, mt ), "RtlAllocateActivationContextStack" ).procAddress; if(createActStack) { a.GenCall( static_cast<size_t>(createActStack), { _userData.ptr<size_t>() + 0x3000 } ); a->mov( asmjit::host::zax, _userData.ptr<size_t>() + 0x3000 ); a->mov( asmjit::host::zax, asmjit::host::intptr_ptr( asmjit::host::zax ) ); a.SetTebPtr(); a->mov( asmjit::host::intptr_ptr( asmjit::host::zdx, 0x2c8 ), asmjit::host::zax ); } } auto ntdll = _mods.GetModule( L"ntdll.dll", Sections, mt ); auto proc = _mods.GetExport( ntdll, "NtDelayExecution" ).procAddress; auto pExitThread = _mods.GetExport( ntdll, "NtTerminateThread" ).procAddress; if (proc == 0 || pExitThread == 0) return 0; /* for(;;) SleepEx(5, TRUE); ExitThread(SetEvent(m_hWaitEvent)); */ a->bind( l_loop ); a.GenCall( static_cast<size_t>(proc), { TRUE, _workerCode.ptr<size_t>() } ); a->jmp( l_loop ); a.ExitThreadWithStatus( (size_t)pExitThread, _userData.ptr<size_t>() ); // Write code into process LARGE_INTEGER liDelay = { 0 }; liDelay.QuadPart = -10 * 1000 * 5; _workerCode.Write( 0, liDelay ); _workerCode.Write( sizeof(LARGE_INTEGER), a->getCodeSize(), a->make() ); _hWorkThd = _threads.CreateNew( _workerCode.ptr<size_t>() + sizeof(LARGE_INTEGER), _userData.ptr<size_t>() ); } return _hWorkThd.id(); }
/// <summary> /// Execute code in context of any existing thread /// </summary> /// <param name="pCode">Cde to execute</param> /// <param name="size">Code size.</param> /// <param name="callResult">Execution result</param> /// <param name="thd">Target thread</param> /// <returns>Status</returns> NTSTATUS RemoteExec::ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callResult, Thread& thd ) { NTSTATUS dwResult = STATUS_SUCCESS; CONTEXT_T ctx; // Prepare for remote exec CreateRPCEnvironment( true ); // Write code dwResult = CopyCode( pCode, size ); if (dwResult != STATUS_SUCCESS) return dwResult; if (_hWaitEvent) ResetEvent( _hWaitEvent ); if (!thd.Suspend()) return LastNtStatus(); if (thd.GetContext( ctx, CONTEXT_ALL, true )) { AsmJitHelper a; #ifdef USE64 const int count = 15; asmjit::host::GpReg regs[] = { asmjit::host::rax, asmjit::host::rbx, asmjit::host::rcx, asmjit::host::rdx, asmjit::host::rsi, asmjit::host::rdi, asmjit::host::r8, asmjit::host::r9, asmjit::host::r10, asmjit::host::r11, asmjit::host::r12, asmjit::host::r13, asmjit::host::r14, asmjit::host::r15, asmjit::host::rbp }; // // Preserve thread context // I don't care about FPU, XMM and anything else // a->sub( asmjit::host::rsp, count * WordSize ); // Stack must be aligned on 16 bytes a->pushf(); // // Save registers for (int i = 0; i < count; i++) a->mov( asmjit::host::Mem( asmjit::host::rsp, i * WordSize ), regs[i] ); a.GenCall( _userCode.ptr<size_t>(), { _userData.ptr<size_t>() } ); AddReturnWithEvent( a, mt_default, rt_int32, INTRET_OFFSET ); // Restore registers for (int i = 0; i < count; i++) a->mov( regs[i], asmjit::host::Mem( asmjit::host::rsp, i * WordSize ) ); a->popf(); a->add( asmjit::host::rsp, count * WordSize ); a->jmp( asmjit::Imm( ctx.Rip ) ); #else a->pusha(); a->pushf(); a.GenCall( _userCode.ptr<size_t>(), { _userData.ptr<size_t>() } ); AddReturnWithEvent( a, mt_default, rt_int32, INTRET_OFFSET ); a->popf(); a->popa(); a->push( (size_t)ctx.NIP ); a->ret(); #endif if (_userCode.Write( size, a->getCodeSize(), a->make() ) == STATUS_SUCCESS) { ctx.NIP = _userCode.ptr<size_t>() + size; if (!thd.SetContext( ctx, true )) dwResult = LastNtStatus(); } else dwResult = LastNtStatus(); } else dwResult = LastNtStatus(); thd.Resume(); if (dwResult == STATUS_SUCCESS) { WaitForSingleObject( _hWaitEvent, INFINITE ); callResult = _userData.Read<size_t>( INTRET_OFFSET, 0 ); } return dwResult; }
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; }
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); }
/// <summary> /// Inject VEH wrapper into process /// Used to enable execution of SEH handlers out of image /// </summary> /// <param name="pTargetBase">Target image base address</param> /// <param name="imageSize">Size of the image</param> /// <returns>Error code</returns> NTSTATUS MExcept::CreateVEH( size_t pTargetBase, size_t imageSize ) { AsmJitHelper ea; uint64_t result = 0; // VEH codecave _pVEHCode = _proc.memory().Allocate( 0x2000 ); _pVEHCode.Release(); if (!_pVEHCode.valid()) return LastNtStatus(); #ifdef USE64 asmjit::Label lExit = ea->newLabel(); // // Assembly code for VectoredHandler64 // 0x10 - EXCEPTION_RECORD.ExceptionAddress // 0x20 - EXCEPTION_RECORD.ExceptionInformation[0] // 0x30 - EXCEPTION_RECORD.ExceptionInformation[2] // 0x38 - EXCEPTION_RECORD.ExceptionInformation[3] // ea->mov( asmjit::host::rax, qword_ptr( asmjit::host::rcx ) ); ea->cmp( asmjit::host::dword_ptr( asmjit::host::rax ), EH_EXCEPTION_NUMBER ); ea->jne( lExit ); ea->mov( asmjit::host::rdx, pTargetBase ); ea->mov( asmjit::host::r8, asmjit::host::qword_ptr( asmjit::host::rax, 0x30 ) ); ea->cmp( asmjit::host::r8, asmjit::host::rdx );; ea->jl( lExit ); ea->add( asmjit::host::rdx, imageSize ); ea->cmp( asmjit::host::r8, asmjit::host::rdx );; ea->jg( lExit ); ea->cmp( asmjit::host::qword_ptr( asmjit::host::rax, 0x20 ), EH_PURE_MAGIC_NUMBER1 );; ea->jne( lExit ); ea->cmp( asmjit::host::qword_ptr( asmjit::host::rax, 0x38 ), 0 ); ea->jne( lExit ); ea->mov( asmjit::host::qword_ptr( asmjit::host::rax, 0x20 ), EH_MAGIC_NUMBER1 ); ea->mov( asmjit::host::rcx, qword_ptr( asmjit::host::rcx ) ); ea->mov( asmjit::host::rdx, pTargetBase ); ea->mov( asmjit::host::qword_ptr( asmjit::host::rax, 0x38 ), asmjit::host::rdx ); ea->bind( lExit ); ea->xor_( asmjit::host::rax, asmjit::host::rax ); ea->ret(); ea->emit( 0xCC ); ea->emit( 0xCC ); ea->emit( 0xCC ); if (_pVEHCode.Write( 0, ea->getCodeSize(), ea->make() ) != STATUS_SUCCESS) { _pVEHCode.Free(); return LastNtStatus(); } #else UNREFERENCED_PARAMETER( pTargetBase ); UNREFERENCED_PARAMETER( imageSize ); // 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; // 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; } } // Write handler data into target process if (_pVEHCode.Write( 0, fnSize, pFunc ) != STATUS_SUCCESS || _pVEHCode.Write( dataOfs, _proc.nativeLdr().LdrpInvertedFunctionTable() ) != STATUS_SUCCESS || _pVEHCode.Write( code_ofs, &DecodeSystemPointer ) != STATUS_SUCCESS) { _pVEHCode.Free(); return LastNtStatus(); } #endif ea.GenPrologue(); // AddVectoredExceptionHandler(0, pHandler); ea.GenCall( reinterpret_cast<void*>(&AddVectoredExceptionHandler), { 0, _pVEHCode.ptr<size_t>() } ); _proc.remote().AddReturnWithEvent( ea ); ea.GenEpilogue(); _proc.remote().ExecInWorkerThread( ea->make(), ea->getCodeSize(), result ); _hVEH = static_cast<size_t>(result); return (result == 0 ? STATUS_NOT_FOUND : STATUS_SUCCESS); }