mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 13:18:51 +00:00
183 lines
5.5 KiB
C
183 lines
5.5 KiB
C
/*
|
|
* PROJECT: ReactOS Local Spooler API Tests
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Functions needed to run our code as a service. This is needed to run in SYSTEM security context.
|
|
* COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org)
|
|
*/
|
|
|
|
#include <apitest.h>
|
|
|
|
#define WIN32_NO_STATUS
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <wingdi.h>
|
|
#include <winreg.h>
|
|
#include <winsvc.h>
|
|
#include <winspool.h>
|
|
#include <winsplp.h>
|
|
#include <tlhelp32.h>
|
|
|
|
#include "localspl_apitest.h"
|
|
|
|
//#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
|
|
static void
|
|
_DoDLLInjection()
|
|
{
|
|
DWORD cbDLLPath;
|
|
HANDLE hProcess;
|
|
HANDLE hSnapshot;
|
|
HANDLE hThread;
|
|
PROCESSENTRY32W pe;
|
|
PVOID pLoadLibraryAddress;
|
|
PVOID pLoadLibraryArgument;
|
|
PWSTR p;
|
|
WCHAR wszFilePath[MAX_PATH];
|
|
|
|
// Get the full path to our EXE file.
|
|
if (!GetModuleFileNameW(NULL, wszFilePath, _countof(wszFilePath)))
|
|
{
|
|
DPRINT("GetModuleFileNameW failed with error %lu!\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
// Replace the extension.
|
|
p = wcsrchr(wszFilePath, L'.');
|
|
if (!p)
|
|
{
|
|
DPRINT("File path has no file extension: %S\n", wszFilePath);
|
|
return;
|
|
}
|
|
|
|
wcscpy(p, L".dll");
|
|
cbDLLPath = (wcslen(wszFilePath) + 1) * sizeof(WCHAR);
|
|
|
|
// Create a snapshot of the currently running processes.
|
|
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
if (hSnapshot == INVALID_HANDLE_VALUE)
|
|
{
|
|
DPRINT("CreateToolhelp32Snapshot failed with error %lu!\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
// Enumerate through all running processes.
|
|
pe.dwSize = sizeof(pe);
|
|
if (!Process32FirstW(hSnapshot, &pe))
|
|
{
|
|
DPRINT("Process32FirstW failed with error %lu!\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
do
|
|
{
|
|
// Check if this is the spooler server process.
|
|
if (wcsicmp(pe.szExeFile, L"spoolsv.exe") != 0)
|
|
continue;
|
|
|
|
// Open a handle to the process.
|
|
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID);
|
|
if (!hProcess)
|
|
{
|
|
DPRINT("OpenProcess failed with error %lu!\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
// Get the address of LoadLibraryW.
|
|
pLoadLibraryAddress = (PVOID)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW");
|
|
if (!pLoadLibraryAddress)
|
|
{
|
|
DPRINT("GetProcAddress failed with error %lu!\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
// Allocate memory for the DLL path in the spooler process.
|
|
pLoadLibraryArgument = VirtualAllocEx(hProcess, NULL, cbDLLPath, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
if (!pLoadLibraryArgument)
|
|
{
|
|
DPRINT("VirtualAllocEx failed with error %lu!\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
// Write the DLL path to the process memory.
|
|
if (!WriteProcessMemory(hProcess, pLoadLibraryArgument, wszFilePath, cbDLLPath, NULL))
|
|
{
|
|
DPRINT("WriteProcessMemory failed with error %lu!\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
// Create a new thread in the spooler process that calls LoadLibraryW as the start routine with our DLL as the argument.
|
|
// This effectively injects our DLL into the spooler process and we can inspect localspl.dll there just like the spooler.
|
|
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibraryAddress, pLoadLibraryArgument, 0, NULL);
|
|
if (!hThread)
|
|
{
|
|
DPRINT("CreateRemoteThread failed with error %lu!\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
CloseHandle(hThread);
|
|
break;
|
|
}
|
|
while (Process32NextW(hSnapshot, &pe));
|
|
}
|
|
|
|
static DWORD WINAPI
|
|
_ServiceControlHandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
static void WINAPI
|
|
_ServiceMain(DWORD dwArgc, LPWSTR* lpszArgv)
|
|
{
|
|
SERVICE_STATUS_HANDLE hServiceStatus;
|
|
SERVICE_STATUS ServiceStatus;
|
|
|
|
UNREFERENCED_PARAMETER(dwArgc);
|
|
UNREFERENCED_PARAMETER(lpszArgv);
|
|
|
|
// Register our service for control.
|
|
hServiceStatus = RegisterServiceCtrlHandlerExW(SERVICE_NAME, _ServiceControlHandlerEx, NULL);
|
|
|
|
// Report SERVICE_RUNNING status.
|
|
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
|
ServiceStatus.dwServiceSpecificExitCode = 0;
|
|
ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
|
ServiceStatus.dwWaitHint = 4000;
|
|
ServiceStatus.dwWin32ExitCode = NO_ERROR;
|
|
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
|
|
SetServiceStatus(hServiceStatus, &ServiceStatus);
|
|
|
|
// Do our funky crazy stuff.
|
|
_DoDLLInjection();
|
|
|
|
// Our work is done.
|
|
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
SetServiceStatus(hServiceStatus, &ServiceStatus);
|
|
}
|
|
|
|
START_TEST(service)
|
|
{
|
|
int argc;
|
|
char** argv;
|
|
|
|
SERVICE_TABLE_ENTRYW ServiceTable[] =
|
|
{
|
|
{ SERVICE_NAME, _ServiceMain },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
// This is no real test, but an easy way to integrate the service handler routines into the API-Test executable.
|
|
// Therefore, bail out if someone tries to run "service" as a usual test.
|
|
argc = winetest_get_mainargs(&argv);
|
|
if (argc != 3)
|
|
return;
|
|
|
|
// If we have exactly 3 arguments, we're run as a service, so initialize the corresponding service handler functions.
|
|
StartServiceCtrlDispatcherW(ServiceTable);
|
|
|
|
// Prevent the testing framework from outputting a "0 tests executed" line here.
|
|
ExitProcess(0);
|
|
}
|