/// <summary> /// Create activation context /// Target memory layout: /// ----------------------------- /// | hCtx | ACTCTX | file_path | /// ----------------------------- /// </summary> /// <param name="path">Image file path.</param> /// <param name="id">Manifest resource id</param> /// <returns>true on success</returns> bool MMap::CreateActx( const std::wstring& path, int id /*= 2 */ ) { AsmJit::Assembler a; AsmJitHelper ah( a ); uint64_t result = 0; ACTCTXW act = { 0 }; _pAContext = _process.memory().Allocate( 512, PAGE_READWRITE ); act.cbSize = sizeof(act); act.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; act.lpSource = reinterpret_cast<LPCWSTR>(_pAContext.ptr<size_t>() + sizeof(HANDLE) + sizeof(act)); act.lpResourceName = MAKEINTRESOURCEW( id ); bool switchMode = (_process.core().native()->GetWow64Barrier().type == wow_64_32); auto pCreateActx = _process.modules().GetExport( _process.modules().GetModule( L"kernel32.dll" ), "CreateActCtxW" ); if (pCreateActx.procAddress == 0) return nullptr; // 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<DWORD>() + sizeof(HANDLE) + sizeof(act32); act32.lpResourceName = id ; a.push( _pAContext.ptr<DWORD>() + static_cast<DWORD>(sizeof(HANDLE)) ); a.mov( AsmJit::eax, static_cast<DWORD>(pCreateActx.procAddress) ); a.call( AsmJit::nax ); a.mov( AsmJit::edx, _pAContext.ptr<DWORD>() ); a.mov( AsmJit::dword_ptr( AsmJit::edx ), AsmJit::eax ); auto pTermThd = _process.modules().GetExport( _process.modules().GetModule( L"ntdll.dll" ), "NtTerminateThread" ); a.push( AsmJit::nax ); a.push( DWORD( 0 ) ); a.mov( AsmJit::eax, static_cast<DWORD>(pTermThd.procAddress) ); a.call( AsmJit::nax ); 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 { ah.GenPrologue(); ah.GenCall( static_cast<size_t>(pCreateActx.procAddress), { _pAContext.ptr<size_t>() + sizeof(HANDLE) } ); a.mov( AsmJit::ndx, _pAContext.ptr<size_t>() ); a.mov( AsmJit::sysint_ptr( AsmJit::ndx ), AsmJit::nax ); _process.remote().AddReturnWithEvent( ah ); ah.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 ); 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 )) { AsmJit::Assembler a; AsmJitHelper ah( a ); #ifdef _M_AMD64 const int count = 15; AsmJit::GPReg regs[] = { AsmJit::rax, AsmJit::rbx, AsmJit::rcx, AsmJit::rdx, AsmJit::rsi, AsmJit::rdi, AsmJit::r8, AsmJit::r9, AsmJit::r10, AsmJit::r11, AsmJit::r12, AsmJit::r13, AsmJit::r14, AsmJit::r15, AsmJit::rbp }; // // Preserve thread context // I don't care about FPU, XMM and anything else // a.sub(AsmJit::rsp, count * WordSize); // Stack must be aligned on 16 bytes a.pushfq(); // // Save registers for (int i = 0; i < count; i++) a.mov( AsmJit::Mem( AsmJit::rsp, i * WordSize ), regs[i] ); ah.GenCall( _userCode.ptr<size_t>(), { _userData.ptr<size_t>() } ); AddReturnWithEvent( ah, rt_int32, INTRET_OFFSET ); // Restore registers for (int i = 0; i < count; i++) a.mov( regs[i], AsmJit::Mem( AsmJit::rsp, i * WordSize ) ); a.popfq(); a.add( AsmJit::rsp, count * WordSize ); a.jmp( ctx.Rip ); #else a.pushad(); a.pushfd(); ah.GenCall( _userCode.ptr<size_t>(), { _userData.ptr<size_t>() } ); AddReturnWithEvent( ah, rt_int32, INTRET_OFFSET ); a.popfd(); a.popad(); a.push( 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; }