mirror of
https://github.com/8damon/Blackbird-Platform
synced 2026-06-21 13:41:12 +00:00
3159249e08
Replace Blackbird_controller_injection.c with injection.cpp. Add core/util/handles.h with RAII handle wrappers (UniqueHandle, UniqueProcessHandle) used across the C++ modules to avoid manual CloseHandle call paths. Refs #11.
1167 lines
35 KiB
C++
1167 lines
35 KiB
C++
#include "blackbird_controller_injection.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <strsafe.h>
|
|
#include <tlhelp32.h>
|
|
#include <wchar.h>
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
DWORD ControllerWaitForHookReady(_In_ DWORD ProcessId);
|
|
VOID ControllerLog(_In_z_ _Printf_format_string_ PCSTR Format, ...);
|
|
|
|
using NtSuspendProcessFn = LONG(NTAPI *)(HANDLE ProcessHandle);
|
|
using RtlNtStatusToDosErrorFn = ULONG(WINAPI *)(LONG Status);
|
|
|
|
static PCWSTR ControllerInjectionFileNameFromPath(_In_z_ PCWSTR Path)
|
|
{
|
|
PCWSTR slash = NULL;
|
|
PCWSTR fileName = Path;
|
|
|
|
if (Path == NULL)
|
|
{
|
|
return L"";
|
|
}
|
|
|
|
slash = wcsrchr(Path, L'\\');
|
|
if (slash != NULL && slash[1] != L'\0')
|
|
{
|
|
fileName = slash + 1;
|
|
}
|
|
|
|
slash = wcsrchr(fileName, L'/');
|
|
if (slash != NULL && slash[1] != L'\0')
|
|
{
|
|
fileName = slash + 1;
|
|
}
|
|
|
|
return fileName;
|
|
}
|
|
|
|
static DWORD ControllerInjectionSuspendProcessHandle(_In_ HANDLE ProcessHandle)
|
|
{
|
|
HMODULE ntdll = NULL;
|
|
NtSuspendProcessFn suspendProcess = nullptr;
|
|
RtlNtStatusToDosErrorFn mapStatus = nullptr;
|
|
LONG status;
|
|
|
|
if (ProcessHandle == NULL || ProcessHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
ntdll = GetModuleHandleW(L"ntdll.dll");
|
|
if (ntdll == NULL)
|
|
{
|
|
return GetLastError() != ERROR_SUCCESS ? GetLastError() : ERROR_MOD_NOT_FOUND;
|
|
}
|
|
|
|
suspendProcess = reinterpret_cast<NtSuspendProcessFn>(GetProcAddress(ntdll, "NtSuspendProcess"));
|
|
if (suspendProcess == nullptr)
|
|
{
|
|
return ERROR_PROC_NOT_FOUND;
|
|
}
|
|
|
|
status = suspendProcess(ProcessHandle);
|
|
if (status >= 0)
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
mapStatus = reinterpret_cast<RtlNtStatusToDosErrorFn>(GetProcAddress(ntdll, "RtlNtStatusToDosError"));
|
|
if (mapStatus != nullptr)
|
|
{
|
|
DWORD mapped = static_cast<DWORD>(mapStatus(status));
|
|
if (mapped != ERROR_MR_MID_NOT_FOUND && mapped != ERROR_SUCCESS)
|
|
{
|
|
return mapped;
|
|
}
|
|
}
|
|
|
|
return ERROR_GEN_FAILURE;
|
|
}
|
|
|
|
static bool ControllerInjectionEnvironmentNamesEqual(_In_z_ PCWSTR Left, _In_z_ PCWSTR Right) noexcept
|
|
{
|
|
PCWSTR leftEq = wcschr(Left, L'=');
|
|
PCWSTR rightEq = wcschr(Right, L'=');
|
|
size_t leftChars = leftEq ? (size_t)(leftEq - Left) : wcslen(Left);
|
|
size_t rightChars = rightEq ? (size_t)(rightEq - Right) : wcslen(Right);
|
|
return (leftChars == rightChars) && (_wcsnicmp(Left, Right, leftChars) == 0);
|
|
}
|
|
|
|
static BOOL ControllerInjectionBuildEnvironmentBlock(_In_reads_or_z_(OverrideChars) PCWSTR Overrides,
|
|
_In_ BOOL EnableLaunchGate,
|
|
_In_ BOOL DeferLaunchGateRelease,
|
|
_Outptr_result_nullonfailure_ PWSTR *EnvironmentBlockOut)
|
|
{
|
|
if (EnvironmentBlockOut == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*EnvironmentBlockOut = NULL;
|
|
if ((Overrides == NULL || Overrides[0] == L'\0') && !EnableLaunchGate && !DeferLaunchGateRelease)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
LPWCH currentEnvironment = GetEnvironmentStringsW();
|
|
if (currentEnvironment == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
std::vector<std::wstring> entries;
|
|
|
|
for (PCWSTR cursor = currentEnvironment; *cursor != L'\0'; cursor += wcslen(cursor) + 1)
|
|
{
|
|
entries.emplace_back(cursor);
|
|
}
|
|
FreeEnvironmentStringsW(currentEnvironment);
|
|
|
|
if (Overrides != NULL)
|
|
{
|
|
for (PCWSTR cursor = Overrides; *cursor != L'\0';)
|
|
{
|
|
PCWSTR lineEnd = cursor;
|
|
while (*lineEnd != L'\0' && *lineEnd != L'\r' && *lineEnd != L'\n')
|
|
{
|
|
lineEnd += 1;
|
|
}
|
|
|
|
PCWSTR lineStart = cursor;
|
|
while (lineStart < lineEnd && (*lineStart == L' ' || *lineStart == L'\t'))
|
|
{
|
|
lineStart += 1;
|
|
}
|
|
while (lineEnd > lineStart && (lineEnd[-1] == L' ' || lineEnd[-1] == L'\t'))
|
|
{
|
|
lineEnd -= 1;
|
|
}
|
|
|
|
if (lineEnd > lineStart)
|
|
{
|
|
std::wstring line(lineStart, lineEnd);
|
|
const wchar_t *eq = wcschr(line.c_str(), L'=');
|
|
if (eq == NULL || eq == line.c_str())
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
bool replaced = false;
|
|
for (auto &e : entries)
|
|
{
|
|
if (ControllerInjectionEnvironmentNamesEqual(e.c_str(), line.c_str()))
|
|
{
|
|
e = std::move(line);
|
|
replaced = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!replaced)
|
|
{
|
|
entries.push_back(std::move(line));
|
|
}
|
|
}
|
|
|
|
cursor = lineEnd;
|
|
while (*cursor == L'\r' || *cursor == L'\n')
|
|
{
|
|
cursor += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (EnableLaunchGate)
|
|
{
|
|
const wchar_t *launchGate = L"BLACKBIRD_HOOK_LAUNCH_GATE=1";
|
|
const wchar_t *deferLaunchGate = L"BLACKBIRD_HOOK_LAUNCH_GATE_DEFER_OPEN=1";
|
|
bool replaced = false;
|
|
for (auto &e : entries)
|
|
{
|
|
if (ControllerInjectionEnvironmentNamesEqual(e.c_str(), launchGate))
|
|
{
|
|
e = launchGate;
|
|
replaced = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!replaced)
|
|
{
|
|
entries.emplace_back(launchGate);
|
|
}
|
|
|
|
if (DeferLaunchGateRelease)
|
|
{
|
|
replaced = false;
|
|
for (auto &e : entries)
|
|
{
|
|
if (ControllerInjectionEnvironmentNamesEqual(e.c_str(), deferLaunchGate))
|
|
{
|
|
e = deferLaunchGate;
|
|
replaced = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!replaced)
|
|
{
|
|
entries.emplace_back(deferLaunchGate);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
std::sort(entries.begin(), entries.end(),
|
|
[](const std::wstring &a, const std::wstring &b) { return _wcsicmp(a.c_str(), b.c_str()) < 0; });
|
|
|
|
size_t totalChars = 1;
|
|
for (const auto &e : entries)
|
|
{
|
|
totalChars += e.size() + 1;
|
|
}
|
|
|
|
PWSTR block = (PWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, totalChars * sizeof(WCHAR));
|
|
if (block == NULL)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
PWSTR write = block;
|
|
for (const auto &e : entries)
|
|
{
|
|
CopyMemory(write, e.c_str(), e.size() * sizeof(WCHAR));
|
|
write += e.size() + 1;
|
|
}
|
|
*write = L'\0';
|
|
*EnvironmentBlockOut = block;
|
|
return TRUE;
|
|
}
|
|
|
|
static DWORD ControllerInjectionMapPriorityClass(_In_ UINT32 PriorityClass)
|
|
{
|
|
switch (PriorityClass)
|
|
{
|
|
case IDLE_PRIORITY_CLASS:
|
|
case BELOW_NORMAL_PRIORITY_CLASS:
|
|
case NORMAL_PRIORITY_CLASS:
|
|
case ABOVE_NORMAL_PRIORITY_CLASS:
|
|
case HIGH_PRIORITY_CLASS:
|
|
case REALTIME_PRIORITY_CLASS:
|
|
return PriorityClass;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static BOOL ControllerInjectionBuildParentProcessAttribute(
|
|
_In_ DWORD ParentProcessId, _Outptr_result_nullonfailure_ LPPROC_THREAD_ATTRIBUTE_LIST *AttributeListOut,
|
|
_Out_ HANDLE *ParentHandleOut)
|
|
{
|
|
SIZE_T attributeBytes = 0;
|
|
HANDLE parentHandle = NULL;
|
|
LPPROC_THREAD_ATTRIBUTE_LIST attributeList = NULL;
|
|
|
|
if (AttributeListOut == NULL || ParentHandleOut == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*AttributeListOut = NULL;
|
|
*ParentHandleOut = NULL;
|
|
if (ParentProcessId == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
parentHandle = OpenProcess(PROCESS_CREATE_PROCESS | PROCESS_DUP_HANDLE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
|
|
ParentProcessId);
|
|
if (parentHandle == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
(void)InitializeProcThreadAttributeList(NULL, 1, 0, &attributeBytes);
|
|
attributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, attributeBytes);
|
|
if (attributeList == NULL)
|
|
{
|
|
CloseHandle(parentHandle);
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!InitializeProcThreadAttributeList(attributeList, 1, 0, &attributeBytes))
|
|
{
|
|
DWORD err = GetLastError();
|
|
HeapFree(GetProcessHeap(), 0, attributeList);
|
|
CloseHandle(parentHandle);
|
|
SetLastError(err);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!UpdateProcThreadAttribute(attributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parentHandle,
|
|
sizeof(parentHandle), NULL, NULL))
|
|
{
|
|
DWORD err = GetLastError();
|
|
DeleteProcThreadAttributeList(attributeList);
|
|
HeapFree(GetProcessHeap(), 0, attributeList);
|
|
CloseHandle(parentHandle);
|
|
SetLastError(err);
|
|
return FALSE;
|
|
}
|
|
|
|
*AttributeListOut = attributeList;
|
|
*ParentHandleOut = parentHandle;
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL ControllerInjectionReadImageMachine(_In_z_ PCWSTR ImagePath, _Out_ USHORT *MachineOut)
|
|
{
|
|
HANDLE fileHandle = INVALID_HANDLE_VALUE;
|
|
IMAGE_DOS_HEADER dosHeader;
|
|
DWORD signature = 0;
|
|
IMAGE_FILE_HEADER fileHeader;
|
|
LARGE_INTEGER ntHeaderOffset;
|
|
DWORD bytesRead = 0;
|
|
DWORD err = ERROR_SUCCESS;
|
|
|
|
if (ImagePath == NULL || ImagePath[0] == L'\0' || MachineOut == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*MachineOut = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
ZeroMemory(&dosHeader, sizeof(dosHeader));
|
|
ZeroMemory(&fileHeader, sizeof(fileHeader));
|
|
|
|
fileHandle = CreateFileW(ImagePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (fileHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ReadFile(fileHandle, &dosHeader, sizeof(dosHeader), &bytesRead, NULL) || bytesRead != sizeof(dosHeader))
|
|
{
|
|
err = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE || dosHeader.e_lfanew <= 0)
|
|
{
|
|
err = ERROR_BAD_EXE_FORMAT;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ntHeaderOffset.QuadPart = dosHeader.e_lfanew;
|
|
if (!SetFilePointerEx(fileHandle, ntHeaderOffset, NULL, FILE_BEGIN))
|
|
{
|
|
err = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!ReadFile(fileHandle, &signature, sizeof(signature), &bytesRead, NULL) || bytesRead != sizeof(signature))
|
|
{
|
|
err = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
if (signature != IMAGE_NT_SIGNATURE)
|
|
{
|
|
err = ERROR_BAD_EXE_FORMAT;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!ReadFile(fileHandle, &fileHeader, sizeof(fileHeader), &bytesRead, NULL) || bytesRead != sizeof(fileHeader))
|
|
{
|
|
err = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
if (fileHeader.Machine == IMAGE_FILE_MACHINE_UNKNOWN)
|
|
{
|
|
err = ERROR_BAD_EXE_FORMAT;
|
|
goto Cleanup;
|
|
}
|
|
|
|
*MachineOut = fileHeader.Machine;
|
|
CloseHandle(fileHandle);
|
|
return TRUE;
|
|
|
|
Cleanup:
|
|
CloseHandle(fileHandle);
|
|
SetLastError((err == ERROR_SUCCESS) ? ERROR_BAD_EXE_FORMAT : err);
|
|
return FALSE;
|
|
}
|
|
|
|
typedef BOOL(WINAPI *BLACKBIRD_IS_WOW64_PROCESS2_FN)(_In_ HANDLE ProcessHandle, _Out_ USHORT *ProcessMachine,
|
|
_Out_ USHORT *NativeMachine);
|
|
|
|
static BOOL ControllerInjectionQueryProcessMachine(_In_ HANDLE ProcessHandle, _Out_ USHORT *ProcessMachineOut,
|
|
_Out_opt_ USHORT *NativeMachineOut)
|
|
{
|
|
HMODULE kernel32Module = NULL;
|
|
BLACKBIRD_IS_WOW64_PROCESS2_FN isWow64Process2Fn = NULL;
|
|
USHORT processMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
USHORT nativeMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
BOOL wow64 = FALSE;
|
|
|
|
if (ProcessHandle == NULL || ProcessHandle == INVALID_HANDLE_VALUE || ProcessMachineOut == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
kernel32Module = GetModuleHandleW(L"kernel32.dll");
|
|
isWow64Process2Fn = (kernel32Module != NULL)
|
|
? (BLACKBIRD_IS_WOW64_PROCESS2_FN)GetProcAddress(kernel32Module, "IsWow64Process2")
|
|
: NULL;
|
|
if (isWow64Process2Fn != NULL)
|
|
{
|
|
if (!isWow64Process2Fn(ProcessHandle, &processMachine, &nativeMachine))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!IsWow64Process(ProcessHandle, &wow64))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#if defined(_WIN64)
|
|
processMachine = wow64 ? IMAGE_FILE_MACHINE_I386 : IMAGE_FILE_MACHINE_AMD64;
|
|
nativeMachine = IMAGE_FILE_MACHINE_AMD64;
|
|
#elif defined(_M_IX86)
|
|
processMachine = IMAGE_FILE_MACHINE_I386;
|
|
nativeMachine = IMAGE_FILE_MACHINE_I386;
|
|
#elif defined(_M_ARM64)
|
|
processMachine = wow64 ? IMAGE_FILE_MACHINE_I386 : IMAGE_FILE_MACHINE_ARM64;
|
|
nativeMachine = IMAGE_FILE_MACHINE_ARM64;
|
|
#else
|
|
processMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
nativeMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
#endif
|
|
}
|
|
|
|
*ProcessMachineOut = processMachine;
|
|
if (NativeMachineOut != NULL)
|
|
{
|
|
*NativeMachineOut = nativeMachine;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static DWORD ControllerInjectionValidateHookArchitecture(_In_ HANDLE ProcessHandle, _In_z_ PCWSTR HookDllPath)
|
|
{
|
|
USHORT hookMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
USHORT processMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
USHORT nativeMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
USHORT effectiveProcessMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
DWORD err;
|
|
|
|
if (ProcessHandle == NULL || ProcessHandle == INVALID_HANDLE_VALUE || HookDllPath == NULL ||
|
|
HookDllPath[0] == L'\0')
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (!ControllerInjectionReadImageMachine(HookDllPath, &hookMachine))
|
|
{
|
|
err = GetLastError();
|
|
return (err == ERROR_SUCCESS) ? ERROR_BAD_EXE_FORMAT : err;
|
|
}
|
|
|
|
if (!ControllerInjectionQueryProcessMachine(ProcessHandle, &processMachine, &nativeMachine))
|
|
{
|
|
err = GetLastError();
|
|
return (err == ERROR_SUCCESS) ? ERROR_GEN_FAILURE : err;
|
|
}
|
|
|
|
effectiveProcessMachine = (processMachine != IMAGE_FILE_MACHINE_UNKNOWN) ? processMachine : nativeMachine;
|
|
#if defined(_WIN64)
|
|
if (effectiveProcessMachine == IMAGE_FILE_MACHINE_UNKNOWN)
|
|
{
|
|
effectiveProcessMachine = IMAGE_FILE_MACHINE_AMD64;
|
|
}
|
|
#endif
|
|
|
|
if (hookMachine != IMAGE_FILE_MACHINE_UNKNOWN && effectiveProcessMachine != IMAGE_FILE_MACHINE_UNKNOWN &&
|
|
hookMachine != effectiveProcessMachine)
|
|
{
|
|
return ERROR_BAD_EXE_FORMAT;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
BOOL ControllerInjectionPathPointsToFile(_In_z_ PCWSTR Path)
|
|
{
|
|
DWORD attrs;
|
|
|
|
if (Path == NULL || Path[0] == L'\0')
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
attrs = GetFileAttributesW(Path);
|
|
if (attrs == INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0);
|
|
}
|
|
|
|
BOOL ControllerInjectionResolveHookDllPath(_In_ const BLACKBIRD_IPC_SET_USER_HOOK_TARGET_REQUEST *Request,
|
|
_Out_writes_z_(OutputChars) PWSTR Output, _In_ size_t OutputChars)
|
|
{
|
|
WCHAR modulePath[BLACKBIRD_MAX_IMAGE_PATH_CHARS];
|
|
PWSTR slash;
|
|
DWORD chars;
|
|
|
|
if (Request == NULL || Output == NULL || OutputChars == 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
Output[0] = L'\0';
|
|
if (Request->HookDllPath[0] != L'\0' && ControllerInjectionPathPointsToFile(Request->HookDllPath))
|
|
{
|
|
return SUCCEEDED(StringCchCopyW(Output, OutputChars, Request->HookDllPath));
|
|
}
|
|
|
|
ZeroMemory(modulePath, sizeof(modulePath));
|
|
chars = GetModuleFileNameW(NULL, modulePath, RTL_NUMBER_OF(modulePath));
|
|
if (chars == 0 || chars >= RTL_NUMBER_OF(modulePath))
|
|
{
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
slash = wcsrchr(modulePath, L'\\');
|
|
if (slash == NULL)
|
|
{
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
slash[1] = L'\0';
|
|
|
|
if (FAILED(StringCchCatW(modulePath, RTL_NUMBER_OF(modulePath), L"SR71.dll")))
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ControllerInjectionPathPointsToFile(modulePath))
|
|
{
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
if (FAILED(StringCchCopyW(Output, OutputChars, modulePath)))
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
static BOOL ControllerInjectionAcquirePrimaryTokenFromPipe(_In_ HANDLE PipeHandle, _Out_ HANDLE *TokenOut)
|
|
{
|
|
HANDLE token = NULL;
|
|
HANDLE primaryToken = NULL;
|
|
|
|
if (TokenOut == NULL || PipeHandle == NULL || PipeHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*TokenOut = NULL;
|
|
if (!ImpersonateNamedPipeClient(PipeHandle))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!OpenThreadToken(GetCurrentThread(),
|
|
TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_DEFAULT |
|
|
TOKEN_ADJUST_SESSIONID,
|
|
FALSE, &token))
|
|
{
|
|
DWORD err = GetLastError();
|
|
(void)RevertToSelf();
|
|
SetLastError(err);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!RevertToSelf())
|
|
{
|
|
DWORD err = GetLastError();
|
|
CloseHandle(token);
|
|
SetLastError(err);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!DuplicateTokenEx(
|
|
token, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID,
|
|
NULL, SecurityImpersonation, TokenPrimary, &primaryToken))
|
|
{
|
|
DWORD err = GetLastError();
|
|
CloseHandle(token);
|
|
SetLastError(err);
|
|
return FALSE;
|
|
}
|
|
|
|
CloseHandle(token);
|
|
*TokenOut = primaryToken;
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL ControllerInjectionLaunchTargetProcess(_In_ HANDLE ClientPipe,
|
|
_In_ const BLACKBIRD_IPC_SET_USER_HOOK_TARGET_REQUEST *Request,
|
|
_Out_ PROCESS_INFORMATION *ProcessInformation)
|
|
{
|
|
STARTUPINFOEXW startupInfo;
|
|
PROCESS_INFORMATION processInfo;
|
|
WCHAR commandLine[BLACKBIRD_MAX_IMAGE_PATH_CHARS + 4];
|
|
HANDLE clientToken = NULL;
|
|
HANDLE parentHandle = NULL;
|
|
LPPROC_THREAD_ATTRIBUTE_LIST attributeList = NULL;
|
|
PWSTR environmentBlock = NULL;
|
|
BOOL launched = FALSE;
|
|
BOOL inheritHandles = FALSE;
|
|
DWORD err = ERROR_SUCCESS;
|
|
DWORD priorityClass = 0;
|
|
DWORD creationFlags = CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT;
|
|
PCWSTR currentDirectory = NULL;
|
|
PCWSTR imagePath = NULL;
|
|
|
|
if (ClientPipe == NULL || ClientPipe == INVALID_HANDLE_VALUE || Request == NULL || Request->ImagePath[0] == L'\0' ||
|
|
ProcessInformation == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
imagePath = Request->ImagePath;
|
|
inheritHandles = (Request->InheritHandles != 0);
|
|
currentDirectory = (Request->WorkingDirectory[0] != L'\0') ? Request->WorkingDirectory : NULL;
|
|
priorityClass = ControllerInjectionMapPriorityClass(Request->PriorityClass);
|
|
|
|
ZeroMemory(&startupInfo, sizeof(startupInfo));
|
|
ZeroMemory(&processInfo, sizeof(processInfo));
|
|
startupInfo.StartupInfo.cb = sizeof(startupInfo);
|
|
ZeroMemory(commandLine, sizeof(commandLine));
|
|
(void)StringCchPrintfW(commandLine, RTL_NUMBER_OF(commandLine), L"\"%s\"", imagePath);
|
|
|
|
if (!ControllerInjectionBuildParentProcessAttribute(Request->ParentProcessId, &attributeList, &parentHandle))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (attributeList != NULL)
|
|
{
|
|
startupInfo.lpAttributeList = attributeList;
|
|
creationFlags |= EXTENDED_STARTUPINFO_PRESENT;
|
|
}
|
|
|
|
if (!ControllerInjectionBuildEnvironmentBlock(
|
|
Request->EnvironmentOverrides, TRUE,
|
|
(Request->Flags & BLACKBIRD_IPC_USER_HOOK_FLAG_DEFER_LAUNCH_GATE_RELEASE) != 0, &environmentBlock))
|
|
{
|
|
err = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (ControllerInjectionAcquirePrimaryTokenFromPipe(ClientPipe, &clientToken))
|
|
{
|
|
launched = CreateProcessAsUserW(clientToken, imagePath, commandLine, NULL, NULL, inheritHandles, creationFlags,
|
|
environmentBlock, currentDirectory, &startupInfo.StartupInfo, &processInfo);
|
|
if (!launched)
|
|
{
|
|
err = GetLastError();
|
|
if (err == ERROR_PRIVILEGE_NOT_HELD)
|
|
{
|
|
launched =
|
|
CreateProcessWithTokenW(clientToken, LOGON_WITH_PROFILE, imagePath, commandLine, creationFlags,
|
|
environmentBlock, currentDirectory, &startupInfo.StartupInfo, &processInfo);
|
|
if (!launched)
|
|
{
|
|
err = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle(clientToken);
|
|
clientToken = NULL;
|
|
}
|
|
else
|
|
{
|
|
err = GetLastError();
|
|
}
|
|
|
|
if (!launched)
|
|
{
|
|
launched = CreateProcessW(imagePath, commandLine, NULL, NULL, inheritHandles, creationFlags, environmentBlock,
|
|
currentDirectory, &startupInfo.StartupInfo, &processInfo);
|
|
if (!launched)
|
|
{
|
|
DWORD fallbackErr = GetLastError();
|
|
if (fallbackErr != ERROR_SUCCESS)
|
|
{
|
|
err = fallbackErr;
|
|
}
|
|
SetLastError(err == ERROR_SUCCESS ? ERROR_ACCESS_DENIED : err);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (priorityClass != 0 && !SetPriorityClass(processInfo.hProcess, priorityClass))
|
|
{
|
|
err = GetLastError();
|
|
(void)TerminateProcess(processInfo.hProcess, 1);
|
|
(void)CloseHandle(processInfo.hThread);
|
|
(void)CloseHandle(processInfo.hProcess);
|
|
processInfo.hThread = NULL;
|
|
processInfo.hProcess = NULL;
|
|
SetLastError(err == ERROR_SUCCESS ? ERROR_GEN_FAILURE : err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (Request->AffinityMask != 0 && !SetProcessAffinityMask(processInfo.hProcess, (DWORD_PTR)Request->AffinityMask))
|
|
{
|
|
err = GetLastError();
|
|
(void)TerminateProcess(processInfo.hProcess, 1);
|
|
(void)CloseHandle(processInfo.hThread);
|
|
(void)CloseHandle(processInfo.hProcess);
|
|
processInfo.hThread = NULL;
|
|
processInfo.hProcess = NULL;
|
|
SetLastError(err == ERROR_SUCCESS ? ERROR_GEN_FAILURE : err);
|
|
goto Cleanup;
|
|
}
|
|
|
|
*ProcessInformation = processInfo;
|
|
processInfo.hProcess = NULL;
|
|
processInfo.hThread = NULL;
|
|
err = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
if (clientToken != NULL)
|
|
{
|
|
CloseHandle(clientToken);
|
|
}
|
|
if (processInfo.hThread != NULL)
|
|
{
|
|
CloseHandle(processInfo.hThread);
|
|
}
|
|
if (processInfo.hProcess != NULL)
|
|
{
|
|
CloseHandle(processInfo.hProcess);
|
|
}
|
|
if (environmentBlock != NULL)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, environmentBlock);
|
|
}
|
|
if (attributeList != NULL)
|
|
{
|
|
DeleteProcThreadAttributeList(attributeList);
|
|
HeapFree(GetProcessHeap(), 0, attributeList);
|
|
}
|
|
if (parentHandle != NULL)
|
|
{
|
|
CloseHandle(parentHandle);
|
|
}
|
|
if (err != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(err);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD ControllerInjectionAttachAndVerify(_In_ DWORD ProcessId, _In_z_ PCWSTR HookDllPath, _In_ DWORD VerifyTimeoutMs)
|
|
{
|
|
HANDLE processHandle = NULL;
|
|
DWORD err;
|
|
|
|
if (ProcessId == 0 || HookDllPath == NULL || HookDllPath[0] == L'\0')
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
processHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION |
|
|
PROCESS_VM_WRITE | PROCESS_VM_READ,
|
|
FALSE, ProcessId);
|
|
if (processHandle == NULL)
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
err = ControllerInjectionValidateHookArchitecture(processHandle, HookDllPath);
|
|
if (err != ERROR_SUCCESS)
|
|
{
|
|
CloseHandle(processHandle);
|
|
return err;
|
|
}
|
|
|
|
err = ControllerInjectionInjectHookDllIntoProcessHandle(processHandle, HookDllPath);
|
|
CloseHandle(processHandle);
|
|
processHandle = NULL;
|
|
if (err != ERROR_SUCCESS)
|
|
{
|
|
return err;
|
|
}
|
|
|
|
if (ControllerInjectionIsStealthHookModule(HookDllPath))
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if (!ControllerInjectionVerifyHookLoaded(ProcessId, HookDllPath, VerifyTimeoutMs))
|
|
{
|
|
err = GetLastError();
|
|
return (err == ERROR_SUCCESS) ? ERROR_DLL_NOT_FOUND : err;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD ControllerInjectionLaunchAndVerify(_In_ HANDLE ClientPipe,
|
|
_In_ const BLACKBIRD_IPC_SET_USER_HOOK_TARGET_REQUEST *Request,
|
|
_In_z_ PCWSTR HookDllPath, _In_ DWORD VerifyTimeoutMs,
|
|
_Out_ DWORD *ProcessIdOut)
|
|
{
|
|
PROCESS_INFORMATION processInfo;
|
|
DWORD err = ERROR_SUCCESS;
|
|
|
|
if (ClientPipe == NULL || ClientPipe == INVALID_HANDLE_VALUE || Request == NULL || Request->ImagePath[0] == L'\0' ||
|
|
HookDllPath == NULL || HookDllPath[0] == L'\0' || ProcessIdOut == NULL)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
*ProcessIdOut = 0;
|
|
ZeroMemory(&processInfo, sizeof(processInfo));
|
|
|
|
if (!ControllerInjectionPathPointsToFile(Request->ImagePath))
|
|
{
|
|
return ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
if (!ControllerInjectionLaunchTargetProcess(ClientPipe, Request, &processInfo))
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
err = ControllerInjectionValidateHookArchitecture(processInfo.hProcess, HookDllPath);
|
|
if (err != ERROR_SUCCESS)
|
|
{
|
|
(void)TerminateProcess(processInfo.hProcess, 1);
|
|
(void)CloseHandle(processInfo.hThread);
|
|
(void)CloseHandle(processInfo.hProcess);
|
|
return err;
|
|
}
|
|
|
|
err = ControllerInjectionQueueHookDllEarlyBirdApc(processInfo.hProcess, processInfo.hThread, HookDllPath);
|
|
if (err != ERROR_SUCCESS)
|
|
{
|
|
(void)TerminateProcess(processInfo.hProcess, 1);
|
|
(void)CloseHandle(processInfo.hThread);
|
|
(void)CloseHandle(processInfo.hProcess);
|
|
return err;
|
|
}
|
|
|
|
if (ResumeThread(processInfo.hThread) == (DWORD)-1)
|
|
{
|
|
err = GetLastError();
|
|
(void)TerminateProcess(processInfo.hProcess, 1);
|
|
(void)CloseHandle(processInfo.hThread);
|
|
(void)CloseHandle(processInfo.hProcess);
|
|
return err == ERROR_SUCCESS ? ERROR_GEN_FAILURE : err;
|
|
}
|
|
|
|
err = ControllerWaitForHookReady(processInfo.dwProcessId);
|
|
if (err != ERROR_SUCCESS)
|
|
{
|
|
(void)TerminateProcess(processInfo.hProcess, 1);
|
|
(void)CloseHandle(processInfo.hThread);
|
|
(void)CloseHandle(processInfo.hProcess);
|
|
return err;
|
|
}
|
|
|
|
err = ControllerInjectionSuspendProcessHandle(processInfo.hProcess);
|
|
if (err != ERROR_SUCCESS)
|
|
{
|
|
(void)TerminateProcess(processInfo.hProcess, 1);
|
|
(void)CloseHandle(processInfo.hThread);
|
|
(void)CloseHandle(processInfo.hProcess);
|
|
return err;
|
|
}
|
|
|
|
if (ControllerInjectionIsStealthHookModule(HookDllPath))
|
|
{
|
|
*ProcessIdOut = processInfo.dwProcessId;
|
|
(void)CloseHandle(processInfo.hThread);
|
|
(void)CloseHandle(processInfo.hProcess);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if (!ControllerInjectionVerifyHookLoaded(processInfo.dwProcessId, HookDllPath, VerifyTimeoutMs))
|
|
{
|
|
err = GetLastError();
|
|
(void)TerminateProcess(processInfo.hProcess, 1);
|
|
(void)CloseHandle(processInfo.hThread);
|
|
(void)CloseHandle(processInfo.hProcess);
|
|
return (err == ERROR_SUCCESS) ? ERROR_DLL_NOT_FOUND : err;
|
|
}
|
|
|
|
*ProcessIdOut = processInfo.dwProcessId;
|
|
(void)CloseHandle(processInfo.hThread);
|
|
(void)CloseHandle(processInfo.hProcess);
|
|
return ERROR_SUCCESS;
|
|
}
|