#pragma once #include "../../Include/Winheaders.h" #include "../../Asm/AsmFactory.h" #include "../Threads/Threads.h" #include "../MemBlock.h" // User data offsets #define INTRET_OFFSET 0x00 #define RET_OFFSET 0x08 #define ERR_OFFSET 0x10 #define EVENT_OFFSET 0x18 #define ARGS_OFFSET 0x20 namespace blackbone { enum WorkerThreadMode { Worker_None, // No worker thread Worker_CreateNew, // Create dedicated worker thread Worker_UseExisting, // Hijack existing thread }; class RemoteExec { using vecArgs = std::vector; public: BLACKBONE_API RemoteExec( class Process& proc ); BLACKBONE_API ~RemoteExec(); /// /// Create environment for future remote procedure calls /// /// _userData layout (x86/x64): /// -------------------------------------------------------------------------------------------------------------------------- /// | Internal return value | Return value | Last Status code | Event handle | Space for copied arguments and strings | /// ------------------------------------------------------------------------------------------------------------------------- /// | 8/8 bytes | 8/8 bytes | 8/8 bytes | 8/8 bytes | | /// -------------------------------------------------------------------------------------------------------------------------- /// /// Worket thread mode /// Create sync event for worker thread /// Status BLACKBONE_API NTSTATUS CreateRPCEnvironment( WorkerThreadMode mode = Worker_None, bool bEvent = false ); /// /// Create new thread and execute code in it. Wait until execution ends /// /// Code to execute /// Code size /// Code return value /// Switch wow64 thread to long mode upon creation /// Status BLACKBONE_API NTSTATUS ExecInNewThread( PVOID pCode, size_t size, uint64_t& callResult, eThreadModeSwitch modeSwitch = AutoSwitch ); /// /// Execute code in context of our worker thread /// /// Cde to execute /// Code size. /// Execution result /// Status BLACKBONE_API NTSTATUS ExecInWorkerThread( PVOID pCode, size_t size, uint64_t& callResult ); /// /// Execute code in context of any existing thread /// /// Cde to execute /// Code size. /// Execution result /// Target thread /// Status BLACKBONE_API NTSTATUS ExecInAnyThread( PVOID pCode, size_t size, uint64_t& callResult, ThreadPtr& thread ); /// /// Create new thread with specified entry point and argument /// /// Entry point /// Thread function argument /// Thread exit code BLACKBONE_API DWORD ExecDirect( ptr_t pCode, ptr_t arg ); /// /// Generate assembly code for remote call. /// /// Underlying assembler object /// Remote function pointer /// Function arguments /// Calling convention /// Return type /// Status code BLACKBONE_API NTSTATUS PrepareCallAssembly( IAsmHelper& a, ptr_t pfn, std::vector& args, eCalligConvention cc, eReturnType retType ); /// /// Generate return from function with event synchronization /// /// Target assembly helper /// 32/64bit loader /// Function return type /// Return value offset BLACKBONE_API void AddReturnWithEvent( IAsmHelper& a, eModType mt = mt_default, eReturnType retType = rt_int32, uint32_t retOffset = RET_OFFSET ); /// /// Save value in rax to user buffer /// /// Target assembly helper BLACKBONE_API void SaveCallResult( IAsmHelper& a, uint32_t retOffset = RET_OFFSET ) { a->mov( a->zdx, _userData.ptr() + retOffset ); a->mov( asmjit::host::dword_ptr( a->zdx ), a->zax ); } /// /// Retrieve call result /// /// Retrieved result /// true on success template NTSTATUS GetCallResult( T& result ) { if constexpr (sizeof( T ) > sizeof( uint64_t )) { if constexpr (std::is_reference_v) return _userData.Read( _userData.Read( RET_OFFSET, 0 ), sizeof( T ), reinterpret_cast(&result) ); else return _userData.Read( ARGS_OFFSET, sizeof( T ), reinterpret_cast(&result) ); } else return _userData.Read( RET_OFFSET, sizeof( T ), reinterpret_cast(&result) ); } /// /// Retrieve last NTSTATUS code /// /// BLACKBONE_API NTSTATUS GetLastStatus() { return _userData.Read( ERR_OFFSET, STATUS_NOT_FOUND ); } /// /// Terminate existing worker thread /// BLACKBONE_API void TerminateWorker(); /// /// Get worker thread /// /// BLACKBONE_API ThreadPtr getWorker() { return _workerThread; } /// /// Get execution thread /// /// BLACKBONE_API ThreadPtr getExecThread() { return _hijackThread ? _hijackThread : _workerThread; } /// /// Ge memory routines /// /// BLACKBONE_API class ProcessMemory& memory() { return _memory; } /// /// Reset instance /// BLACKBONE_API void reset(); private: /// /// Create worker RPC thread /// /// Thread ID call_result_t CreateWorkerThread(); /// /// Create event to synchronize APC procedures /// /// The thread identifier. /// Status code NTSTATUS CreateAPCEvent( DWORD threadID ); /// /// Copy executable code into remote codecave for future execution /// /// Code to copy /// Code size /// Status NTSTATUS CopyCode( PVOID pCode, size_t size ); RemoteExec( const RemoteExec& ) = delete; RemoteExec& operator =(const RemoteExec&) = delete; private: // Process routines class Process& _process; class ProcessModules& _mods; class ProcessMemory& _memory; class ProcessThreads& _threads; ThreadPtr _workerThread; // Worker thread handle ThreadPtr _hijackThread; // Thread to use for hijacking HANDLE _hWaitEvent; // APC sync event handle MemBlock _workerCode; // Worker thread address space MemBlock _userCode; // Codecave for code execution MemBlock _userData; // Region to store copied structures and strings bool _apcPatched; // KiUserApcDispatcher was patched }; }