/// <summary> /// Generate assembly code for remote call. /// </summary> /// <param name="a">Underlying assembler object</param> /// <param name="pfn">Remote function pointer</param> /// <param name="args">Function arguments</param> /// <param name="cc">Calling convention</param> /// <param name="retType">Return type</param> /// <returns>true on success</returns> bool RemoteExec::PrepareCallAssembly( AsmHelperBase& a, const void* pfn, std::vector<AsmVariant>& args, eCalligConvention cc, eReturnType retType ) { size_t data_offset = ARGS_OFFSET; // Invalid calling convention if (cc < cc_cdecl || cc > cc_fastcall) { LastNtStatus( STATUS_INVALID_PARAMETER_3 ); return false; } // Copy structures and strings for (auto& arg : args) { if (arg.type == AsmVariant::dataStruct || arg.type == AsmVariant::dataPtr) { _userData.Write( data_offset, arg.size, reinterpret_cast<const void*>(arg.imm_val) ); arg.new_imm_val = _userData.ptr<size_t>() + data_offset; // Add some padding after data data_offset += arg.size + 0x10; } } // Insert hidden variable if return type is struct. // This variable contains address of buffer in which return value is copied if (retType == rt_struct) { args.emplace( args.begin(), AsmVariant( _userData.ptr<size_t>() + ARGS_OFFSET ) ); args.front().new_imm_val = args.front().imm_val; args.front().type = AsmVariant::structRet; } a.GenPrologue(); a.GenCall( pfn, args, cc ); // Retrieve result from XMM0 or ST0 if (retType == rt_float || retType == rt_double) { a->mov( asmjit::host::zax, _userData.ptr<size_t>() + RET_OFFSET ); #ifdef USE64 if (retType == rt_double) a->movsd( asmjit::host::Mem( asmjit::host::zax, 0 ), asmjit::host::xmm0 ); else a->movss( asmjit::host::Mem( asmjit::host::zax, 0 ), asmjit::host::xmm0 ); #else a->fstp( asmjit::host::Mem( asmjit::host::zax, 0, retType * sizeof(float) ) ); #endif } AddReturnWithEvent( a, mt_default, retType ); a.GenEpilogue(); 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; }
/// <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; }