mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-03 21:46:32 +00:00
367 lines
14 KiB
C
367 lines
14 KiB
C
#include <stdarg.h>
|
|
#include <windows.h>
|
|
#include <shlwapi.h>
|
|
|
|
/* What the setups of older frameworks rely on: a heap made to run code runs the code written into it, a folder marked read-only still
|
|
takes new files, the ANSI event log calls answer, the 64-bit .NET switch keeps the value it was given, a program whose path has
|
|
spaces starts from a command line that does not quote it, a service's optional settings read back as they were set, a critical
|
|
section shows its owner where code that tests it inline looks, a program of either width loads as a data file, wnsprintf stops at
|
|
the size it is given, regedit /s imports registration entries in both of their formats, lodctr's library takes counter names, and
|
|
a process of the services' session sees its window station is not visible. */
|
|
|
|
static int run_from_heap(void)
|
|
{
|
|
/* mov eax, 42; ret */
|
|
static const unsigned char code[] = {0xb8, 0x2a, 0x00, 0x00, 0x00, 0xc3};
|
|
HANDLE heap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
|
|
if (!heap)
|
|
return 1;
|
|
unsigned char *block = HeapAlloc(heap, 0, sizeof code);
|
|
if (!block)
|
|
return 2;
|
|
for (unsigned i = 0; i < sizeof code; ++i)
|
|
block[i] = code[i];
|
|
int (*answer)(void) = (int (*)(void))(void *)block;
|
|
return answer() == 42 ? 0 : 3;
|
|
}
|
|
|
|
static int read_only_folder(void)
|
|
{
|
|
WCHAR folder[MAX_PATH], file[MAX_PATH];
|
|
if (!GetTempPathW(MAX_PATH, folder))
|
|
return 10;
|
|
lstrcatW(folder, L"raven-read-only");
|
|
CreateDirectoryW(folder, NULL);
|
|
if (!SetFileAttributesW(folder, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
|
|
return 11;
|
|
lstrcpyW(file, folder);
|
|
lstrcatW(file, L"\\desktop.ini");
|
|
HANDLE handle = CreateFileW(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
return 12;
|
|
CloseHandle(handle);
|
|
DeleteFileW(file);
|
|
return RemoveDirectoryW(folder) ? 0 : 13;
|
|
}
|
|
|
|
static int event_log(void)
|
|
{
|
|
const char *lines[] = {"from the legacy client"};
|
|
HANDLE source = RegisterEventSourceA(NULL, "Raven");
|
|
if (!source)
|
|
return 20;
|
|
if (!ReportEventA(source, EVENTLOG_INFORMATION_TYPE, 0, 1, NULL, 1, 0, lines, NULL))
|
|
return 21;
|
|
return DeregisterEventSource(source) ? 0 : 22;
|
|
}
|
|
|
|
static int complus_switch(void)
|
|
{
|
|
HMODULE kernel = GetModuleHandleW(L"kernel32.dll");
|
|
ULONG (WINAPI *get)(void) = (ULONG (WINAPI *)(void))(void *)GetProcAddress(kernel, "GetComPlusPackageInstallStatus");
|
|
BOOL (WINAPI *set)(ULONG) = (BOOL (WINAPI *)(ULONG))(void *)GetProcAddress(kernel, "SetComPlusPackageInstallStatus");
|
|
if (!get || !set)
|
|
return 30;
|
|
const ULONG before = get();
|
|
if (set(2) || GetLastError() != ERROR_INVALID_PARAMETER)
|
|
return 31;
|
|
if (!set(0) || get() != 0 || !set(1) || get() != 1)
|
|
return 32;
|
|
return set(before) ? 0 : 33;
|
|
}
|
|
|
|
static int unquoted_path(void)
|
|
{
|
|
WCHAR self[MAX_PATH], folder[MAX_PATH], copy[MAX_PATH], line[MAX_PATH + 16];
|
|
if (!GetModuleFileNameW(NULL, self, MAX_PATH) || !GetTempPathW(MAX_PATH, folder))
|
|
return 40;
|
|
lstrcatW(folder, L"raven spaced folder");
|
|
CreateDirectoryW(folder, NULL);
|
|
lstrcpyW(copy, folder);
|
|
lstrcatW(copy, L"\\legacy copy.exe");
|
|
if (!CopyFileW(self, copy, FALSE))
|
|
return 41;
|
|
lstrcpyW(line, copy);
|
|
lstrcatW(line, L" --child");
|
|
STARTUPINFOW startup = {sizeof startup};
|
|
PROCESS_INFORMATION started;
|
|
if (!CreateProcessW(NULL, line, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &started))
|
|
return 42;
|
|
DWORD code = 0;
|
|
WaitForSingleObject(started.hProcess, 30000);
|
|
GetExitCodeProcess(started.hProcess, &code);
|
|
CloseHandle(started.hThread);
|
|
CloseHandle(started.hProcess);
|
|
DeleteFileW(copy);
|
|
RemoveDirectoryW(folder);
|
|
return code == 77 ? 0 : 43;
|
|
}
|
|
|
|
static int service_settings(void)
|
|
{
|
|
SC_HANDLE manager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
|
if (!manager)
|
|
return 50;
|
|
SC_HANDLE service = CreateServiceW(manager, L"RavenLegacy", L"Raven legacy", SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
|
|
SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, L"C:\\Windows\\System32\\svchost.exe", NULL, NULL, NULL, NULL, NULL);
|
|
if (!service)
|
|
service = OpenServiceW(manager, L"RavenLegacy", SERVICE_ALL_ACCESS);
|
|
if (!service)
|
|
return 51;
|
|
SERVICE_DESCRIPTIONW description = {L"Kept by the service manager"};
|
|
if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &description))
|
|
return 52;
|
|
DWORD needed = 0;
|
|
if (QueryServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, NULL, 0, &needed) || GetLastError() != ERROR_INSUFFICIENT_BUFFER || !needed)
|
|
return 53;
|
|
BYTE buffer[512];
|
|
SERVICE_DESCRIPTIONW *read = (SERVICE_DESCRIPTIONW *)buffer;
|
|
if (needed > sizeof buffer || !QueryServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, buffer, sizeof buffer, &needed) ||
|
|
!read->lpDescription || lstrcmpW(read->lpDescription, description.lpDescription))
|
|
return 54;
|
|
SERVICE_DELAYED_AUTO_START_INFO delayed = {TRUE};
|
|
if (!QueryServiceConfig2W(service, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (BYTE *)&delayed, sizeof delayed, &needed) || delayed.fDelayedAutostart)
|
|
return 55;
|
|
DeleteService(service);
|
|
CloseServiceHandle(service);
|
|
CloseServiceHandle(manager);
|
|
return 0;
|
|
}
|
|
|
|
static int section_fields(void)
|
|
{
|
|
CRITICAL_SECTION section;
|
|
InitializeCriticalSection(§ion);
|
|
if (section.LockCount != -1 || section.OwningThread)
|
|
return 60;
|
|
EnterCriticalSection(§ion);
|
|
if (section.LockCount == -1 || (DWORD)(ULONG_PTR)section.OwningThread != GetCurrentThreadId() || section.RecursionCount != 1)
|
|
return 61;
|
|
LeaveCriticalSection(§ion);
|
|
if (section.LockCount != -1 || section.OwningThread || section.RecursionCount)
|
|
return 62;
|
|
DeleteCriticalSection(§ion);
|
|
return 0;
|
|
}
|
|
|
|
static int data_files(void)
|
|
{
|
|
WCHAR self[MAX_PATH], other[MAX_PATH];
|
|
if (!GetModuleFileNameW(NULL, self, MAX_PATH))
|
|
return 70;
|
|
HMODULE image = LoadLibraryExW(self, NULL, LOAD_LIBRARY_AS_DATAFILE);
|
|
if (!image)
|
|
return 71;
|
|
FreeLibrary(image);
|
|
/* Two files of the same name in two folders, the first gone before the second loads, as fusion stages assemblies. */
|
|
static WCHAR folder[MAX_PATH], copy[MAX_PATH];
|
|
for (int round = 0; round < 2; ++round) {
|
|
GetTempPathW(MAX_PATH, folder);
|
|
lstrcatW(folder, round ? L"raven-stage-2" : L"raven-stage-1");
|
|
CreateDirectoryW(folder, NULL);
|
|
lstrcpyW(copy, folder);
|
|
lstrcatW(copy, L"\\staged.dll");
|
|
if (!CopyFileW(self, copy, FALSE) || !(image = LoadLibraryExW(copy, NULL, LOAD_LIBRARY_AS_DATAFILE)))
|
|
return 73 + round;
|
|
FreeLibrary(image);
|
|
DeleteFileW(copy);
|
|
RemoveDirectoryW(folder);
|
|
}
|
|
#ifdef _WIN64
|
|
/* The 32-bit build of this program sits in the x86 folder beside this one's. */
|
|
lstrcpyW(other, self);
|
|
WCHAR *slash = other;
|
|
for (WCHAR *at = other; *at; ++at)
|
|
if (*at == L'\\')
|
|
slash = at;
|
|
*slash = 0;
|
|
for (WCHAR *at = other; *at; ++at)
|
|
if (*at == L'\\')
|
|
slash = at;
|
|
lstrcpyW(slash + 1, L"x86\\legacy.exe");
|
|
if (GetFileAttributesW(other) != INVALID_FILE_ATTRIBUTES) {
|
|
if (!(image = LoadLibraryExW(other, NULL, LOAD_LIBRARY_AS_DATAFILE)))
|
|
return 72;
|
|
FreeLibrary(image);
|
|
}
|
|
#else
|
|
(void)other;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int format_list(WCHAR *out, int size, const WCHAR *pattern, ...)
|
|
{
|
|
va_list list;
|
|
va_start(list, pattern);
|
|
const int written = wvnsprintfW(out, size, pattern, list);
|
|
va_end(list);
|
|
return written;
|
|
}
|
|
|
|
static int bounded_format(void)
|
|
{
|
|
char narrow[8];
|
|
WCHAR wide[32];
|
|
if (wnsprintfA(narrow, 8, "%s-%d", "abcdef", 42) != 7 || lstrcmpA(narrow, "abcdef-"))
|
|
return 80;
|
|
if (wnsprintfW(wide, 32, L"%s=%04x", L"key", 255) != 8 || lstrcmpW(wide, L"key=00ff"))
|
|
return 81;
|
|
if (format_list(wide, 6, L"%d.%s", 1234, L"tail") != 5 || lstrcmpW(wide, L"1234."))
|
|
return 82;
|
|
return 0;
|
|
}
|
|
|
|
static int run_and_wait(WCHAR *line)
|
|
{
|
|
STARTUPINFOW startup = {sizeof startup};
|
|
PROCESS_INFORMATION started;
|
|
if (!CreateProcessW(NULL, line, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &started))
|
|
return -1;
|
|
DWORD code = 1;
|
|
WaitForSingleObject(started.hProcess, 30000);
|
|
GetExitCodeProcess(started.hProcess, &code);
|
|
CloseHandle(started.hThread);
|
|
CloseHandle(started.hProcess);
|
|
return (int)code;
|
|
}
|
|
|
|
static BOOL write_file(const WCHAR *path, const void *bytes, DWORD size)
|
|
{
|
|
HANDLE file = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
DWORD written = 0;
|
|
const BOOL done = file != INVALID_HANDLE_VALUE && WriteFile(file, bytes, size, &written, NULL) && written == size;
|
|
if (file != INVALID_HANDLE_VALUE)
|
|
CloseHandle(file);
|
|
return done;
|
|
}
|
|
|
|
static int registration_entries(void)
|
|
{
|
|
static WCHAR folder[MAX_PATH], unicode[MAX_PATH], ansi[MAX_PATH], removal[MAX_PATH], line[3 * MAX_PATH];
|
|
if (!GetTempPathW(MAX_PATH, folder))
|
|
return 90;
|
|
lstrcpyW(unicode, folder);
|
|
lstrcatW(unicode, L"raven-unicode.reg");
|
|
lstrcpyW(ansi, folder);
|
|
lstrcatW(ansi, L"raven ansi.reg");
|
|
lstrcpyW(removal, folder);
|
|
lstrcatW(removal, L"raven-removal.reg");
|
|
static const WCHAR text[] = L"\xfeffWindows Registry Editor Version 5.00\r\n\r\n[HKEY_CURRENT_USER\\Software\\RavenRegedit]\r\n"
|
|
L"@=\"default\"\r\n\"Quoted\"=\"a\\\"b\\\\c\"\r\n\"Count\"=dword:0000002a\r\n\"Path\"=hex(2):25,00,41,00,25,00,00,00\r\n"
|
|
L"\"List\"=hex(7):61,00,00,00,62,00,00,00,\\\r\n 00,00\r\n\"Gone\"=\"x\"\r\n\"Gone\"=-\r\n";
|
|
static const char narrow[] = "REGEDIT4\r\n\r\n[HKEY_CURRENT_USER\\Software\\RavenRegedit\\Old]\r\n\"Name\"=\"value\"\r\n";
|
|
static const WCHAR remove_text[] = L"\xfeffWindows Registry Editor Version 5.00\r\n\r\n[-HKEY_CURRENT_USER\\Software\\RavenRegedit]\r\n";
|
|
if (!write_file(unicode, text, sizeof text - sizeof(WCHAR)) || !write_file(ansi, narrow, sizeof narrow - 1) ||
|
|
!write_file(removal, remove_text, sizeof remove_text - sizeof(WCHAR)))
|
|
return 91;
|
|
lstrcpyW(line, L"regedit.exe /s \"");
|
|
lstrcatW(line, unicode);
|
|
lstrcatW(line, L"\" \"");
|
|
lstrcatW(line, ansi);
|
|
lstrcatW(line, L"\"");
|
|
if (run_and_wait(line))
|
|
return 92;
|
|
HKEY key;
|
|
WCHAR value[64];
|
|
DWORD size = sizeof value, type = 0, number = 0;
|
|
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\RavenRegedit", 0, KEY_READ, &key))
|
|
return 93;
|
|
if (RegQueryValueExW(key, NULL, NULL, &type, (BYTE *)value, &size) || type != REG_SZ || lstrcmpW(value, L"default"))
|
|
return 94;
|
|
size = sizeof value;
|
|
if (RegQueryValueExW(key, L"Quoted", NULL, &type, (BYTE *)value, &size) || lstrcmpW(value, L"a\"b\\c"))
|
|
return 95;
|
|
size = sizeof number;
|
|
if (RegQueryValueExW(key, L"Count", NULL, &type, (BYTE *)&number, &size) || type != REG_DWORD || number != 42)
|
|
return 96;
|
|
size = sizeof value;
|
|
if (RegQueryValueExW(key, L"Path", NULL, &type, (BYTE *)value, &size) || type != REG_EXPAND_SZ || lstrcmpW(value, L"%A%"))
|
|
return 97;
|
|
size = sizeof value;
|
|
if (RegQueryValueExW(key, L"List", NULL, &type, (BYTE *)value, &size) || type != REG_MULTI_SZ || size != 10 || value[0] != L'a' || value[2] != L'b')
|
|
return 98;
|
|
size = sizeof value;
|
|
if (RegQueryValueExW(key, L"Gone", NULL, &type, (BYTE *)value, &size) != ERROR_FILE_NOT_FOUND)
|
|
return 99;
|
|
RegCloseKey(key);
|
|
size = sizeof value;
|
|
if (RegGetValueW(HKEY_CURRENT_USER, L"Software\\RavenRegedit\\Old", L"Name", RRF_RT_REG_SZ, NULL, value, &size) || lstrcmpW(value, L"value"))
|
|
return 100;
|
|
lstrcpyW(line, L"regedit /s \"");
|
|
lstrcatW(line, removal);
|
|
lstrcatW(line, L"\"");
|
|
if (run_and_wait(line) || !RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\RavenRegedit", 0, KEY_READ, &key))
|
|
return 101;
|
|
DeleteFileW(unicode);
|
|
DeleteFileW(ansi);
|
|
DeleteFileW(removal);
|
|
return 0;
|
|
}
|
|
|
|
static int counter_names(void)
|
|
{
|
|
HMODULE library = LoadLibraryW(L"loadperf.dll");
|
|
if (!library)
|
|
return 110;
|
|
DWORD (WINAPI *load)(LPWSTR, BOOL) = (DWORD (WINAPI *)(LPWSTR, BOOL))(void *)GetProcAddress(library, "LoadPerfCounterTextStringsW");
|
|
DWORD (WINAPI *unload)(LPWSTR, BOOL) = (DWORD (WINAPI *)(LPWSTR, BOOL))(void *)GetProcAddress(library, "UnloadPerfCounterTextStringsW");
|
|
WCHAR load_line[] = L"lodctr counters.ini", unload_line[] = L"unlodctr RavenService";
|
|
if (!load || !unload || load(load_line, TRUE) || unload(unload_line, TRUE))
|
|
return 111;
|
|
/* The registration tools of the frameworks ask for their console window, which is none here. */
|
|
return GetConsoleWindow() ? 112 : 0;
|
|
}
|
|
|
|
/* Installers take some shell functions by number: shell32 89 and 85, shlwapi 437. A number the system does not publish finds nothing. Under
|
|
* WOW64 a 32-bit setup learns so from IsOS and IsWow64Process, and turns to the 64-bit program folder. */
|
|
static int system_numbers(void)
|
|
{
|
|
HMODULE shell = LoadLibraryW(L"shell32.dll"), light = LoadLibraryW(L"shlwapi.dll");
|
|
if (!shell || !light)
|
|
return 130;
|
|
FARPROC clone = GetProcAddress(shell, (LPCSTR)89), stream = GetProcAddress(shell, (LPCSTR)85);
|
|
if (!clone || clone != GetProcAddress(shell, "SHCloneSpecialIDList") || !stream || stream != GetProcAddress(shell, "OpenRegStream"))
|
|
return 131;
|
|
if (GetProcAddress(shell, (LPCSTR)300) || GetProcAddress(light, (LPCSTR)300))
|
|
return 132;
|
|
BOOL (WINAPI *is_os)(DWORD) = (BOOL (WINAPI *)(DWORD))(void *)GetProcAddress(light, (LPCSTR)437);
|
|
const BOOL wow64 = sizeof(void *) == 4;
|
|
BOOL process_wow64 = !wow64;
|
|
if (!is_os || is_os != (BOOL (WINAPI *)(DWORD))(void *)GetProcAddress(light, "IsOS") || !is_os(1) || is_os(29) || !is_os(30) != !wow64)
|
|
return 133;
|
|
if (!IsWow64Process(GetCurrentProcess(), &process_wow64) || !process_wow64 != !wow64)
|
|
return 134;
|
|
void *(WINAPI *clone_list)(HWND, int, BOOL) = (void *(WINAPI *)(HWND, int, BOOL))(void *)clone;
|
|
BOOL (WINAPI *list_path)(const void *, WCHAR *) = (BOOL (WINAPI *)(const void *, WCHAR *))(void *)GetProcAddress(shell, "SHGetPathFromIDListW");
|
|
WCHAR path[MAX_PATH] = L"";
|
|
void *list = clone_list(NULL, 0x26, FALSE);
|
|
if (!list || !list_path || !list_path(list, path) || lstrcmpiW(path, wow64 ? L"C:\\Program Files (x86)" : L"C:\\Program Files"))
|
|
return 135;
|
|
return clone_list(NULL, 0x7f, FALSE) ? 136 : 0;
|
|
}
|
|
|
|
int raven_entry(void)
|
|
{
|
|
const WCHAR *line = GetCommandLineW();
|
|
for (const WCHAR *at = line; *at; ++at) {
|
|
if (at[0] == L'-' && at[1] == L'-' && at[2] == L'c' && at[3] == L'h')
|
|
return 77;
|
|
/* Started in the services' session, as the custom actions of an installer are: the window station is not visible. */
|
|
if (at[0] == L'-' && at[1] == L'-' && at[2] == L's' && at[3] == L'e') {
|
|
USEROBJECTFLAGS flags = {0};
|
|
DWORD needed = 0;
|
|
if (!GetUserObjectInformationW(GetProcessWindowStation(), UOI_FLAGS, &flags, sizeof flags, &needed))
|
|
return 120;
|
|
return flags.dwFlags & WSF_VISIBLE ? 121 : 0;
|
|
}
|
|
}
|
|
int result;
|
|
if ((result = run_from_heap()) || (result = read_only_folder()) || (result = event_log()) || (result = complus_switch()) ||
|
|
(result = unquoted_path()) || (result = service_settings()) || (result = section_fields()) || (result = data_files()) ||
|
|
(result = bounded_format()) || (result = registration_entries()) || (result = counter_names()) ||
|
|
(result = system_numbers()))
|
|
return result;
|
|
return 0;
|
|
}
|