text: #include "precomp.h"
  #include "common_metapi.h"
  
  DWORD copy_memory_to_process(HANDLE process, BOOLEAN allocate,
  	LPVOID *buffer, DWORD length, DWORD prot);
  
  /*
   * Executes a portion of code in the address space of the supplied process
   * and returns the exit code of the thread that is created
   *
   * FIXME: can-block
   */
  DWORD execute_code_stub_in_process(HANDLE process, PVOID buffer, ULONG length,
  	LPVOID parameter, DWORD parameterLength, LPDWORD rv)
  {
  	HANDLE thread = NULL;
  	LPVOID paramInProcess = (LPVOID)parameter;
  	LPVOID codeInProcess  = (LPVOID)buffer;
  	DWORD  threadId;
  	DWORD  result = ERROR_SUCCESS;
  	DWORD  wait;
  
  	do
  	{ 
  		// Copy the code and parameter storage
  		if ((result = copy_memory_to_process(process, TRUE, &codeInProcess,
  			length, PAGE_EXECUTE_READ)) != ERROR_SUCCESS)
  		{
  			break;
  		}
  		
  		if ((result = copy_memory_to_process(process, TRUE, &paramInProcess,
  			parameterLength, PAGE_EXECUTE_READWRITE)) != ERROR_SUCCESS)
  		{
  			break;
  		}
  
  		// Create the thread in the target process
  		if (!(thread = met_api->thread.create_remote(process, 0, codeInProcess, paramInProcess, 0, &threadId)))
  		{
  			result = GetLastError();
  			break;
  		}
  
  		// Wait for the thread to terminate
  		while ((wait = WaitForSingleObjectEx(thread, 1000, TRUE)) != WAIT_OBJECT_0)
  		{
  			if (wait == WAIT_FAILED)
  			{
  				result = GetLastError();
  				break;
  			}
  		}
  		
  		if (rv)
  		{
  			GetExitCodeThread(thread, rv);
  		}
  
  		// Free the memory in the process
  		if ((!VirtualFreeEx(process, codeInProcess, 0, MEM_RELEASE)) ||
  		    (!VirtualFreeEx(process, paramInProcess, 0, MEM_RELEASE)))
  		{
  			result = GetLastError();
  			break;
  		}
  	} while (0);
  
  	// Close the thread handle if one was obtained
  	if (thread)
  	{
  		CloseHandle(thread);
  	}
  
  	return result;
  }
  
  /*
   * Copies memory to the target process, optionally allocating it
   */
  DWORD copy_memory_to_process(HANDLE process, BOOLEAN allocate,
  		LPVOID *buffer, DWORD length, DWORD prot)
  {
  	LPVOID remoteBuffer = *buffer;
  	SIZE_T  written;
  	DWORD  result = ERROR_SUCCESS;
  
  	do
  	{
  		if (allocate)
  		{
  			// Allocate storage for the buffer
  			if (!(remoteBuffer = VirtualAllocEx(process, NULL, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE)))
  			{
  				result = GetLastError();
  				break;
  			}
  		}
  
  		// Copy the memory from local to remote
  		if (!WriteProcessMemory(process, remoteBuffer, *buffer, length, &written))
  		{
  			result = GetLastError();
  			break;
  		}
  
  		// Re-protect the region to have the protection mask specified
  		if (prot != PAGE_EXECUTE_READWRITE)
  		{
  			DWORD old;
  
  			if (!VirtualProtectEx(process, remoteBuffer, length, prot, &old))
  			{
  				result = GetLastError();
  				break;
  			}
  		}
  	} while (0);
  
  	// Update the buffer pointer
  	*buffer = remoteBuffer;
  
  	return result;
  }
  