/// <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> /// 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; }
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); }
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; }
/// <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); }