Files
2026-05-06 09:11:06 +10:00

241 lines
7.3 KiB
C++

#include "internal.h"
DWORD ControllerInjectionInjectHookDllIntoProcessHandle(_In_ HANDLE ProcessHandle, _In_z_ PCWSTR HookDllPath)
{
SIZE_T pathBytes;
HMODULE kernel32Module = NULL;
FARPROC loadLibraryProc = NULL;
LPVOID remotePath = NULL;
HANDLE threadHandle = NULL;
DWORD waitResult;
DWORD remoteExit = 0;
SIZE_T bytesWritten = 0;
DWORD err = ERROR_SUCCESS;
if (ProcessHandle == NULL || ProcessHandle == INVALID_HANDLE_VALUE || HookDllPath == NULL ||
HookDllPath[0] == L'\0')
{
return ERROR_INVALID_PARAMETER;
}
pathBytes = (wcslen(HookDllPath) + 1u) * sizeof(WCHAR);
kernel32Module = GetModuleHandleW(L"kernel32.dll");
loadLibraryProc = (kernel32Module != NULL) ? GetProcAddress(kernel32Module, "LoadLibraryW") : NULL;
if (loadLibraryProc == NULL)
{
return GetLastError() != ERROR_SUCCESS ? GetLastError() : ERROR_PROC_NOT_FOUND;
}
remotePath = VirtualAllocEx(ProcessHandle, NULL, pathBytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (remotePath == NULL)
{
return GetLastError();
}
if (!WriteProcessMemory(ProcessHandle, remotePath, HookDllPath, pathBytes, &bytesWritten) ||
bytesWritten != pathBytes)
{
err = GetLastError();
(void)VirtualFreeEx(ProcessHandle, remotePath, 0, MEM_RELEASE);
return err == ERROR_SUCCESS ? ERROR_WRITE_FAULT : err;
}
threadHandle =
CreateRemoteThread(ProcessHandle, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibraryProc, remotePath, 0, NULL);
if (threadHandle == NULL)
{
err = GetLastError();
(void)VirtualFreeEx(ProcessHandle, remotePath, 0, MEM_RELEASE);
return err;
}
waitResult = WaitForSingleObject(threadHandle, 20000);
if (waitResult != WAIT_OBJECT_0)
{
err = (waitResult == WAIT_TIMEOUT) ? ERROR_TIMEOUT : GetLastError();
CloseHandle(threadHandle);
(void)VirtualFreeEx(ProcessHandle, remotePath, 0, MEM_RELEASE);
return err == ERROR_SUCCESS ? ERROR_GEN_FAILURE : err;
}
if (!GetExitCodeThread(threadHandle, &remoteExit))
{
err = GetLastError();
CloseHandle(threadHandle);
(void)VirtualFreeEx(ProcessHandle, remotePath, 0, MEM_RELEASE);
return err == ERROR_SUCCESS ? ERROR_GEN_FAILURE : err;
}
CloseHandle(threadHandle);
(void)VirtualFreeEx(ProcessHandle, remotePath, 0, MEM_RELEASE);
if (remoteExit == 0)
{
return ERROR_DLL_INIT_FAILED;
}
return ERROR_SUCCESS;
}
DWORD ControllerInjectionQueueHookDllEarlyBirdApc(_In_ HANDLE ProcessHandle, _In_ HANDLE ThreadHandle,
_In_z_ PCWSTR HookDllPath)
{
#if !defined(_M_X64)
UNREFERENCED_PARAMETER(ProcessHandle);
UNREFERENCED_PARAMETER(ThreadHandle);
UNREFERENCED_PARAMETER(HookDllPath);
return ERROR_NOT_SUPPORTED;
#else
SIZE_T pathBytes;
HMODULE kernel32Module = NULL;
FARPROC loadLibraryProc = NULL;
LPVOID remotePath = NULL;
SIZE_T bytesWritten = 0;
DWORD err = ERROR_SUCCESS;
if (ProcessHandle == NULL || ProcessHandle == INVALID_HANDLE_VALUE || ThreadHandle == NULL ||
ThreadHandle == INVALID_HANDLE_VALUE || HookDllPath == NULL || HookDllPath[0] == L'\0')
{
return ERROR_INVALID_PARAMETER;
}
pathBytes = (wcslen(HookDllPath) + 1u) * sizeof(WCHAR);
kernel32Module = GetModuleHandleW(L"kernel32.dll");
loadLibraryProc = (kernel32Module != NULL) ? GetProcAddress(kernel32Module, "LoadLibraryW") : NULL;
if (loadLibraryProc == NULL)
{
return GetLastError() != ERROR_SUCCESS ? GetLastError() : ERROR_PROC_NOT_FOUND;
}
remotePath = VirtualAllocEx(ProcessHandle, NULL, pathBytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (remotePath == NULL)
{
return GetLastError();
}
if (!WriteProcessMemory(ProcessHandle, remotePath, HookDllPath, pathBytes, &bytesWritten) ||
bytesWritten != pathBytes)
{
err = GetLastError();
(void)VirtualFreeEx(ProcessHandle, remotePath, 0, MEM_RELEASE);
return err == ERROR_SUCCESS ? ERROR_WRITE_FAULT : err;
}
if (QueueUserAPC((PAPCFUNC)(ULONG_PTR)loadLibraryProc, ThreadHandle, (ULONG_PTR)remotePath) == 0)
{
err = GetLastError();
(void)VirtualFreeEx(ProcessHandle, remotePath, 0, MEM_RELEASE);
return err == ERROR_SUCCESS ? ERROR_GEN_FAILURE : err;
}
ControllerLog("[INJ] early-bird LoadLibraryW APC queued thread=%p loadLibrary=%p remotePath=%p pathBytes=%llu\n",
ThreadHandle, loadLibraryProc, remotePath, (unsigned long long)pathBytes);
return ERROR_SUCCESS;
#endif
}
static BOOL ControllerInjectionProbeHookLoaded(_In_ DWORD ProcessId, _In_z_ PCWSTR HookDllPath)
{
HANDLE snapshot = INVALID_HANDLE_VALUE;
MODULEENTRY32W module;
PCWSTR expectedName;
BOOL found = FALSE;
DWORD probeErr = ERROR_NOT_FOUND;
expectedName = ControllerInjectionFileNameFromPath(HookDllPath);
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, ProcessId);
if (snapshot == INVALID_HANDLE_VALUE)
{
return FALSE;
}
ZeroMemory(&module, sizeof(module));
module.dwSize = sizeof(module);
if (Module32FirstW(snapshot, &module))
{
do
{
if ((module.szModule[0] != L'\0' && _wcsicmp(module.szModule, expectedName) == 0) ||
(module.szExePath[0] != L'\0' && _wcsicmp(module.szExePath, HookDllPath) == 0))
{
found = TRUE;
break;
}
} while (Module32NextW(snapshot, &module));
}
probeErr = GetLastError();
if (probeErr == ERROR_SUCCESS || probeErr == ERROR_NO_MORE_FILES || probeErr == ERROR_BAD_LENGTH ||
probeErr == ERROR_PARTIAL_COPY)
{
probeErr = ERROR_NOT_FOUND;
}
CloseHandle(snapshot);
if (found)
{
SetLastError(ERROR_SUCCESS);
}
else
{
SetLastError(probeErr);
}
return found;
}
BOOL ControllerInjectionIsStealthHookModule(_In_z_ PCWSTR HookDllPath)
{
PCWSTR fileName;
if (HookDllPath == NULL || HookDllPath[0] == L'\0')
{
return FALSE;
}
fileName = ControllerInjectionFileNameFromPath(HookDllPath);
if (fileName == NULL || fileName[0] == L'\0')
{
return FALSE;
}
return (_wcsicmp(fileName, L"SR71.dll") == 0);
}
BOOL ControllerInjectionVerifyHookLoaded(_In_ DWORD ProcessId, _In_z_ PCWSTR HookDllPath, _In_ DWORD TimeoutMs)
{
ULONGLONG startTick;
ULONGLONG now;
DWORD pollSleepMs = 75;
DWORD lastErr = ERROR_NOT_FOUND;
if (ProcessId == 0 || HookDllPath == NULL || HookDllPath[0] == L'\0')
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
startTick = GetTickCount64();
now = startTick;
do
{
if (ControllerInjectionProbeHookLoaded(ProcessId, HookDllPath))
{
return TRUE;
}
lastErr = GetLastError();
if (lastErr == ERROR_PARTIAL_COPY || lastErr == ERROR_BAD_LENGTH || lastErr == ERROR_NO_MORE_FILES ||
lastErr == ERROR_SUCCESS)
{
lastErr = ERROR_NOT_FOUND;
}
Sleep(pollSleepMs);
now = GetTickCount64();
} while (now - startTick < (ULONGLONG)TimeoutMs);
SetLastError(lastErr == ERROR_SUCCESS ? ERROR_NOT_FOUND : lastErr);
return FALSE;
}