Sample 1
This commit is contained in:
712
injector.cpp
Normal file
712
injector.cpp
Normal file
@@ -0,0 +1,712 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Mykhailo Mamedov. All rights reserved.
|
||||||
|
*
|
||||||
|
* RESEARCH PREVIEW / REFERENCE ONLY:
|
||||||
|
* This source code is provided solely for the purpose of reviewing
|
||||||
|
* the author's research methods and implementation.
|
||||||
|
*
|
||||||
|
* NO LICENSE GRANTED:
|
||||||
|
* This code is NOT for distribution, modification, or use in any
|
||||||
|
* project (commercial or otherwise). Unauthorized copying or
|
||||||
|
* use of this code is strictly prohibited.
|
||||||
|
*
|
||||||
|
* For inquiries regarding use or licensing, contact: ua.modin@gmail.com
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* This is an old injector designed to help legacy processes
|
||||||
|
* play nice with modern filesystem features. Some older services
|
||||||
|
* get tripped up by reparse points when running out of the system root,
|
||||||
|
* so this steps in to patch the PEB.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "main_defs.h"
|
||||||
|
|
||||||
|
#pragma comment(lib,"ntdll.lib")
|
||||||
|
EXTERN_C NTSTATUS NTAPI NtSuspendProcess(IN HANDLE ProcessHandle);
|
||||||
|
EXTERN_C NTSTATUS NTAPI NtResumeProcess(IN HANDLE ProcessHandle);
|
||||||
|
|
||||||
|
LPCTSTR PARENT_PIPE = TEXT("\\\\.\\pipe\\Aura");
|
||||||
|
|
||||||
|
#if _WIN64
|
||||||
|
_PPEB pebPtr = NULL;
|
||||||
|
#else
|
||||||
|
PPEB32 pebPtr = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_CreateProcessInternalW TrueCreateProcessInternalW =
|
||||||
|
(_CreateProcessInternalW)GetProcAddress(GetModuleHandle(L"KernelBase"), "CreateProcessInternalW");
|
||||||
|
|
||||||
|
|
||||||
|
inline static BOOL InArray(const std::string& value, const std::vector<std::string>& array) {
|
||||||
|
return std::find(array.begin(), array.end(), value) != array.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CheckProcImage(DEBUG_EVENT debugEvent) {
|
||||||
|
// Getting executable image name from handle provided.
|
||||||
|
const size_t size = _MAX_PATH + 4;
|
||||||
|
char* resolvedPath = (char*)malloc(size);
|
||||||
|
DWORD ret = GetFinalPathNameByHandleA(debugEvent.u.CreateProcessInfo.hFile, resolvedPath, size, 0);
|
||||||
|
simpleLogger(std::format("Module path: {}", resolvedPath));
|
||||||
|
fs::path image_path(resolvedPath);
|
||||||
|
free(resolvedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CheckProcImageDLL(DEBUG_EVENT debugEvent) {
|
||||||
|
// Getting dll image name from handle provided. Can be merged with above.
|
||||||
|
const size_t size = _MAX_PATH + 4;
|
||||||
|
char* resolvedPath = (char*)malloc(size);
|
||||||
|
DWORD ret = GetFinalPathNameByHandleA(debugEvent.u.LoadDll.hFile, resolvedPath, size, 0);
|
||||||
|
simpleLogger(std::format("Module DLL path: {}", resolvedPath));
|
||||||
|
free(resolvedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckProcThreads(DWORD processID) {
|
||||||
|
// Print threads of the process specified (expensive).
|
||||||
|
// Currently not in use.
|
||||||
|
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
|
||||||
|
THREADENTRY32 te32;
|
||||||
|
|
||||||
|
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
||||||
|
if (hThreadSnap == INVALID_HANDLE_VALUE) {
|
||||||
|
simpleLogger(std::format("Error in getting thread snapshot"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
te32.dwSize = sizeof(THREADENTRY32);
|
||||||
|
if (!Thread32First(hThreadSnap, &te32)) {
|
||||||
|
simpleLogger(std::format("Error in getting first thread"));
|
||||||
|
CloseHandle(hThreadSnap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int threadCount = 0;
|
||||||
|
do {
|
||||||
|
if (te32.th32OwnerProcessID == processID) {
|
||||||
|
++threadCount;
|
||||||
|
simpleLogger(std::format("THREAD ID: {}", te32.th32ThreadID));
|
||||||
|
}
|
||||||
|
} while (Thread32Next(hThreadSnap, &te32));
|
||||||
|
|
||||||
|
simpleLogger(std::format("THREAD COUNT: {}", threadCount));
|
||||||
|
CloseHandle(hThreadSnap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HANDLE ConnectNamedPipe(LPCTSTR pipe_name) {
|
||||||
|
HANDLE hPipe = 0;
|
||||||
|
DWORD dwError;
|
||||||
|
while (TRUE) {
|
||||||
|
hPipe = ::CreateFile(pipe_name,
|
||||||
|
GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
|
||||||
|
if (hPipe != INVALID_HANDLE_VALUE)
|
||||||
|
break;
|
||||||
|
// If any error except the ERROR_PIPE_BUSY has occurred,
|
||||||
|
// we should return FALSE.
|
||||||
|
dwError = GetLastError();
|
||||||
|
if (dwError != ERROR_PIPE_BUSY) {
|
||||||
|
simpleLogger(std::format("Pipe connect failed {}", dwError));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
simpleLogger(std::format("Pipe is busy, waiting.."));
|
||||||
|
if (!WaitNamedPipe(pipe_name, 2000)) {
|
||||||
|
simpleLogger(std::format("Pipe wait failed {}", GetLastError()));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hPipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD RequestPatchProc(DWORD pid, fs::path file, DWORD thread_id, DWORD parent_pid, BOOL keepSuspended) {
|
||||||
|
std::string msg;
|
||||||
|
HANDLE hPipe = 0;
|
||||||
|
DWORD dwWritten = 0;
|
||||||
|
DWORD dwRead;
|
||||||
|
BOOL pipe_res = FALSE;
|
||||||
|
char _buffer[1024]{};
|
||||||
|
hPipe = ConnectNamedPipe(PARENT_PIPE);
|
||||||
|
if (hPipe != INVALID_HANDLE_VALUE) {
|
||||||
|
|
||||||
|
msg = std::format("patch_process_peb=[{0}][{1}][{2}]",
|
||||||
|
pid, file.filename().generic_string().c_str(), parent_pid);
|
||||||
|
|
||||||
|
pipe_res = WriteFile(hPipe,
|
||||||
|
msg.c_str(),
|
||||||
|
msg.length(),
|
||||||
|
&dwWritten,
|
||||||
|
NULL);
|
||||||
|
if (!pipe_res || dwWritten != msg.length()) {
|
||||||
|
simpleLogger(std::format("Pipe failed to write(PEB): {}", pipe_res));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReadFile(hPipe, _buffer, sizeof(_buffer) - 1, &dwRead, NULL) != FALSE) {
|
||||||
|
_buffer[dwRead] = '\0';
|
||||||
|
simpleLogger(std::format("Got back from pipe(PEB): {}", _buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = std::format("patch_inject=[{0}][{1}][{2}][{3}][{4}]",
|
||||||
|
pid, file.filename().generic_string().c_str(), thread_id, parent_pid, keepSuspended);
|
||||||
|
|
||||||
|
pipe_res = WriteFile(hPipe,
|
||||||
|
msg.c_str(),
|
||||||
|
msg.length(),
|
||||||
|
&dwWritten,
|
||||||
|
NULL);
|
||||||
|
if (!pipe_res || dwWritten != msg.length()) {
|
||||||
|
simpleLogger(std::format("Pipe failed to write(PATCH): {}", pipe_res));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either response or broken connection. We shell wait.
|
||||||
|
if (ReadFile(hPipe, _buffer, sizeof(_buffer) - 1, &dwRead, NULL) != FALSE) {
|
||||||
|
_buffer[dwRead] = '\0';
|
||||||
|
simpleLogger(std::format("Got back from pipe(PATCH): {}", _buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hPipe);
|
||||||
|
simpleLogger(std::format("Pipe done"));
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
simpleLogger(std::format("Named pipe is not available, service might be down."));
|
||||||
|
}
|
||||||
|
return pipe_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path GetFileNameProc(HANDLE proc) {
|
||||||
|
TCHAR filename[MAX_PATH];
|
||||||
|
fs::path out;
|
||||||
|
if (GetModuleFileNameEx(proc, NULL, filename, MAX_PATH) != 0) {
|
||||||
|
out = fs::path(filename);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckDbgString(DEBUG_EVENT event, DWORD pid) {
|
||||||
|
BOOL dropSub = FALSE;
|
||||||
|
DWORD dropPid = event.dwProcessId;
|
||||||
|
BOOL dropForced = FALSE;
|
||||||
|
|
||||||
|
if (!event.u.DebugString.nDebugStringLength)
|
||||||
|
return;
|
||||||
|
|
||||||
|
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
||||||
|
|
||||||
|
if (event.u.DebugString.fUnicode)
|
||||||
|
{
|
||||||
|
std::wstring debugStringExtracted;
|
||||||
|
debugStringExtracted.resize(event.u.DebugString.nDebugStringLength + 1);
|
||||||
|
ReadProcessMemory(hProcess, event.u.DebugString.lpDebugStringData, &debugStringExtracted[0],
|
||||||
|
event.u.DebugString.nDebugStringLength * 2 + 2, nullptr);
|
||||||
|
std::string dbg_str(w_wo_a((LPWSTR)debugStringExtracted.c_str()));
|
||||||
|
simpleLogger(std::format("DBG_STR(W) {}", dbg_str));
|
||||||
|
if (dbg_str.starts_with(hookSet)) {
|
||||||
|
dropSub = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string debugStringExtracted;
|
||||||
|
debugStringExtracted.resize(event.u.DebugString.nDebugStringLength + 1);
|
||||||
|
ReadProcessMemory(hProcess, event.u.DebugString.lpDebugStringData, &debugStringExtracted[0],
|
||||||
|
event.u.DebugString.nDebugStringLength + 1, nullptr);
|
||||||
|
std::string dbg_str(debugStringExtracted.c_str());
|
||||||
|
simpleLogger(std::format("DBG_STR(A) {}", dbg_str));
|
||||||
|
if (dbg_str.starts_with(hookSet)) {
|
||||||
|
dropSub = TRUE;
|
||||||
|
}
|
||||||
|
else if (dbg_str.starts_with(libDrop)) {
|
||||||
|
std::string pid_s(dbg_str.substr(libDrop.length(), std::string::npos));
|
||||||
|
if (pid_s.length()) {
|
||||||
|
dropPid = atoi(pid_s.c_str());
|
||||||
|
simpleLogger(std::format("DBG_STR(AU_lib_drop A) {}", dropPid));
|
||||||
|
dropSub = TRUE;
|
||||||
|
dropForced = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseHandle(hProcess);
|
||||||
|
|
||||||
|
if (dropSub) {
|
||||||
|
if (releaseAttachedDebuggee || dropForced) {
|
||||||
|
simpleLogger(std::format("Dropping {}", dropPid));
|
||||||
|
if (!DebugActiveProcessStop(dropPid)) {
|
||||||
|
simpleLogger(std::format("Failed to detach"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
simpleLogger(std::format("Detached"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string flattenEnvBlock(LPVOID env) {
|
||||||
|
std::string out;
|
||||||
|
UINT offset = 0;
|
||||||
|
char* env_c = static_cast<char*>(env);
|
||||||
|
while (1) {
|
||||||
|
if (std::memcmp(env_c + offset, "\0\0", 2) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (offset) {
|
||||||
|
char* env_copy = (char*)malloc(offset);
|
||||||
|
std::memcpy(env_copy, env_c, offset);
|
||||||
|
|
||||||
|
for (UINT i = 0; i < offset; i++) {
|
||||||
|
if (std::memcmp(env_copy + i, "\0", 1) == 0)
|
||||||
|
std::memcpy(env_copy + i, " ", 1);
|
||||||
|
}
|
||||||
|
simpleLogger(std::format("Env len, {}.", offset));
|
||||||
|
out = std::string(env_copy);
|
||||||
|
free(env_copy);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ProcessWrapperT(HANDLE hToken, // __in
|
||||||
|
LPCWSTR AppName, // __in_opt
|
||||||
|
LPWSTR CmdLine, // __inout_opt
|
||||||
|
LPSECURITY_ATTRIBUTES ProcessAttr,
|
||||||
|
LPSECURITY_ATTRIBUTES ThreadAttr,
|
||||||
|
BOOL bIH,
|
||||||
|
DWORD flags,
|
||||||
|
LPVOID env,
|
||||||
|
LPCWSTR CurrDir,
|
||||||
|
LPSTARTUPINFOW si,
|
||||||
|
LPPROCESS_INFORMATION pi,
|
||||||
|
PHANDLE NewToken,
|
||||||
|
PBOOL ret,
|
||||||
|
PDWORD lastErr,
|
||||||
|
BOOL* procLaunched
|
||||||
|
) {
|
||||||
|
simpleLogger(std::format("{} Start", __func__));
|
||||||
|
|
||||||
|
*ret = TrueCreateProcessInternalW(
|
||||||
|
hToken, AppName, CmdLine, ProcessAttr, ThreadAttr, bIH,
|
||||||
|
flags, env, CurrDir, si, pi, NewToken);
|
||||||
|
|
||||||
|
// Preserve original error returned from API call.
|
||||||
|
*lastErr = GetLastError();
|
||||||
|
|
||||||
|
if (!*ret && *lastErr == ERROR_NOT_SUPPORTED) { // 32bit with 64bit child proc case.
|
||||||
|
// Try one more time.
|
||||||
|
flags = flags & ~DEBUG_PROCESS;
|
||||||
|
*ret = TrueCreateProcessInternalW(
|
||||||
|
hToken, AppName, CmdLine, ProcessAttr, ThreadAttr, bIH,
|
||||||
|
flags, env, CurrDir, si, pi, NewToken);
|
||||||
|
if (ret) {
|
||||||
|
simpleLogger(std::format("Launched detached (32>64bit case)"));
|
||||||
|
*procLaunched = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*lastErr = GetLastError();
|
||||||
|
simpleLogger(std::format("Abort launch E:{:x}.", (DWORD)&lastErr));
|
||||||
|
*procLaunched = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleLogger(std::format("Launched?, E:{:x}.", (DWORD)&lastErr));
|
||||||
|
|
||||||
|
// Notify parent thread that process is started
|
||||||
|
// and returned shared pointers can be checked.
|
||||||
|
*procLaunched = 1;
|
||||||
|
|
||||||
|
// Children process continue running if this process terminated (very unlikely).
|
||||||
|
DebugSetProcessKillOnExit(FALSE);
|
||||||
|
|
||||||
|
BOOL toContinue = TRUE;
|
||||||
|
DWORD waitMax = INFINITE;
|
||||||
|
if (flags & CREATE_SUSPENDED) {
|
||||||
|
// Some processes could be created suspended and wait for parent to resume
|
||||||
|
// But parent can break and left children hanging, therefore putting a timeout
|
||||||
|
// after which process is released.
|
||||||
|
waitMax = 30000;
|
||||||
|
}
|
||||||
|
HANDLE Handle;
|
||||||
|
fs::path pModule;
|
||||||
|
simpleLogger(std::format("Waiting for events."));
|
||||||
|
while (toContinue) {
|
||||||
|
DWORD continueStatus = DBG_CONTINUE;
|
||||||
|
DWORD th_id = 0;
|
||||||
|
DEBUG_EVENT debugEvent = { 0 };
|
||||||
|
BOOL keep = FALSE;
|
||||||
|
BOOL keepSuspended = FALSE;
|
||||||
|
fs::path tempPath;
|
||||||
|
if (!::WaitForDebugEvent(&debugEvent, waitMax)) {
|
||||||
|
simpleLogger(std::format("DBG stop."));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
simpleLogger(std::format("DBG event: {} {}", debugEvent.dwDebugEventCode, debugEvent.dwProcessId));
|
||||||
|
switch (debugEvent.dwDebugEventCode) {
|
||||||
|
case EXCEPTION_DEBUG_EVENT:
|
||||||
|
// Non of exceptions expected.
|
||||||
|
simpleLogger(std::format("DBG Exception: {}",
|
||||||
|
debugEvent.u.Exception.ExceptionRecord.ExceptionCode));
|
||||||
|
continueStatus = DBG_EXCEPTION_NOT_HANDLED;
|
||||||
|
switch (debugEvent.u.Exception.ExceptionRecord.ExceptionCode)
|
||||||
|
{
|
||||||
|
case EXCEPTION_ACCESS_VIOLATION:
|
||||||
|
simpleLogger(std::format("EXCEPTION _ACCESS_VIOLATION"));
|
||||||
|
break;
|
||||||
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||||
|
simpleLogger(std::format("EXCEPTION _DATATYPE_MISALIGNMENT"));
|
||||||
|
break;
|
||||||
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||||
|
simpleLogger(std::format("EXCEPTION _ARRAY_BOUNDS_EXCEEDED"));
|
||||||
|
break;
|
||||||
|
case EXCEPTION_PRIV_INSTRUCTION:
|
||||||
|
simpleLogger(std::format("EXCEPTION _PRIV_INSTRUCTION"));
|
||||||
|
break;
|
||||||
|
case EXCEPTION_IN_PAGE_ERROR:
|
||||||
|
simpleLogger(std::format("EXCEPTION _IN_PAGE_ERROR"));
|
||||||
|
break;
|
||||||
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||||
|
simpleLogger(std::format("EXCEPTION _ILLEGAL_INSTRUCTION"));
|
||||||
|
break;
|
||||||
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||||
|
simpleLogger(std::format("EXCEPTION _NONCONTINUABLE_EXCEPTION"));
|
||||||
|
break;
|
||||||
|
case EXCEPTION_STACK_OVERFLOW:
|
||||||
|
simpleLogger(std::format("EXCEPTION _STACK_OVERFLOW"));
|
||||||
|
break;
|
||||||
|
case EXCEPTION_INVALID_DISPOSITION:
|
||||||
|
break;
|
||||||
|
case EXCEPTION_INVALID_HANDLE:
|
||||||
|
simpleLogger(std::format("EXCEPTION_INVALID_HANDLE"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXIT_PROCESS_DEBUG_EVENT: // last
|
||||||
|
simpleLogger(std::format("EXIT_PROCESS_DEBUG_EVENT"));
|
||||||
|
break;
|
||||||
|
case CREATE_THREAD_DEBUG_EVENT: //
|
||||||
|
simpleLogger(std::format("CREATE_THREAD_DEBUG_EVENT"));
|
||||||
|
break;
|
||||||
|
case CREATE_PROCESS_DEBUG_EVENT: // first
|
||||||
|
simpleLogger(std::format("CREATE_PROCESS_DEBUG_EVENT"));
|
||||||
|
tempPath = GetFileNameProc(debugEvent.u.CreateProcessInfo.hProcess);
|
||||||
|
simpleLogger(std::format("Started proc: {} {}", debugEvent.dwProcessId, tempPath.generic_string()));
|
||||||
|
th_id = 0;
|
||||||
|
if (debugEvent.u.CreateProcessInfo.hThread) {
|
||||||
|
th_id = GetThreadId(debugEvent.u.CreateProcessInfo.hThread);
|
||||||
|
keepSuspended = TRUE;
|
||||||
|
}
|
||||||
|
RequestPatchProc(
|
||||||
|
debugEvent.dwProcessId,
|
||||||
|
tempPath,
|
||||||
|
th_id,
|
||||||
|
pi->dwProcessId,
|
||||||
|
keepSuspended
|
||||||
|
);
|
||||||
|
CheckProcImage(debugEvent);
|
||||||
|
break;
|
||||||
|
case EXIT_THREAD_DEBUG_EVENT:
|
||||||
|
simpleLogger(std::format("EXIT_THREAD_DEBUG_EVENT"));
|
||||||
|
break;
|
||||||
|
case LOAD_DLL_DEBUG_EVENT: //
|
||||||
|
simpleLogger(std::format("LOAD_DLL_DEBUG_EVENT"));
|
||||||
|
if (debugMode)
|
||||||
|
CheckProcImageDLL(debugEvent);
|
||||||
|
break;
|
||||||
|
case UNLOAD_DLL_DEBUG_EVENT:
|
||||||
|
simpleLogger(std::format("UNLOAD_DLL_DEBUG_EVENT"));
|
||||||
|
break;
|
||||||
|
case OUTPUT_DEBUG_STRING_EVENT:
|
||||||
|
simpleLogger(std::format("OUTPUT_DEBUG_STRING_EVENT"));
|
||||||
|
CheckDbgString(debugEvent, debugEvent.dwProcessId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
::ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, continueStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!DebugActiveProcessStop(pi->dwProcessId)) {
|
||||||
|
simpleLogger(std::format("Failed to detach (top)"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
simpleLogger(std::format("DBG Detached (top)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_debug_string(std::string dbg_str) {
|
||||||
|
UCHAR orig_dbg_value = pebPtr->BeingDebugged;
|
||||||
|
BOOL changed = FALSE;
|
||||||
|
if (!orig_dbg_value) {
|
||||||
|
changed = TRUE;
|
||||||
|
// Required to be flipped back-forth to submit the notification.
|
||||||
|
// Original must be preserved afterwards.
|
||||||
|
pebPtr->BeingDebugged = 1;
|
||||||
|
}
|
||||||
|
OutputDebugStringA(dbg_str.c_str());
|
||||||
|
if (changed)
|
||||||
|
pebPtr->BeingDebugged = orig_dbg_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOL WINAPI HookCreateProcessInternalW(
|
||||||
|
HANDLE hToken, // __in
|
||||||
|
LPCWSTR AppName, // __in_opt
|
||||||
|
LPWSTR CmdLine, // __inout_opt
|
||||||
|
LPSECURITY_ATTRIBUTES ProcessAttr, // __in_opt
|
||||||
|
LPSECURITY_ATTRIBUTES ThreadAttr, // __in_opt
|
||||||
|
BOOL bIH, // __in
|
||||||
|
DWORD flags, // __in
|
||||||
|
LPVOID env, // __in_opt
|
||||||
|
LPCWSTR CurrDir, // __in_opt
|
||||||
|
LPSTARTUPINFOW si, // __in
|
||||||
|
LPPROCESS_INFORMATION pi, // __out
|
||||||
|
PHANDLE NewToken // __in
|
||||||
|
) {
|
||||||
|
|
||||||
|
std::wstring w_AppName(L"NOPE");
|
||||||
|
std::wstring w_CmdLine(L"NOPE");
|
||||||
|
if (AppName)
|
||||||
|
w_AppName = std::wstring(AppName);
|
||||||
|
if (CmdLine)
|
||||||
|
w_CmdLine = std::wstring(CmdLine);
|
||||||
|
|
||||||
|
simpleLogger(std::format("{} Create proc: AppName: `{}` CmdLine: `{}` flags: `{:x}`",
|
||||||
|
__func__, w_wo_a(w_AppName), w_wo_a(w_CmdLine), flags));
|
||||||
|
|
||||||
|
// Patch the flags.
|
||||||
|
DWORD newFlags = flags;
|
||||||
|
DWORD current_pid = GetCurrentProcessId();
|
||||||
|
simpleLogger(std::format("{} Original flags: `{:x}`", __func__, flags));
|
||||||
|
BOOL cacheOverrideSet = FALSE;
|
||||||
|
if (env) {
|
||||||
|
std::string flattenedEnv(flattenEnvBlock(env));
|
||||||
|
if (flattenedEnv.find(" __AU_ROOT") != std::string::npos) {
|
||||||
|
cacheOverrideSet = TRUE;
|
||||||
|
simpleLogger(std::format("Override is set."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL wrapIt = TRUE;
|
||||||
|
if (legacyRoot || cacheOverrideSet)
|
||||||
|
wrapIt = FALSE;
|
||||||
|
if (wrapIt) {
|
||||||
|
if (!(DEBUG_PROCESS & flags)) {
|
||||||
|
simpleLogger(std::format("Adding DEBUG_PROCESS flag"));
|
||||||
|
newFlags |= DEBUG_PROCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BOOL keepSuspended = FALSE;
|
||||||
|
if (flags & CREATE_SUSPENDED) {
|
||||||
|
simpleLogger(std::format("{} sub-proc requested suspended flag: `{:x}`", __func__, flags));
|
||||||
|
keepSuspended = TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (legacyRoot && !cacheOverrideSet) {
|
||||||
|
newFlags = flags | CREATE_SUSPENDED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleLogger(std::format("{} final flags: `{:x}`", __func__, newFlags));
|
||||||
|
|
||||||
|
BOOL res = 0;
|
||||||
|
DWORD lastError = 0;
|
||||||
|
|
||||||
|
if (!wrapIt) {
|
||||||
|
// Same thread launch (when we don't need to track hierarchy.
|
||||||
|
res = TrueCreateProcessInternalW(
|
||||||
|
hToken, AppName, CmdLine, ProcessAttr, ThreadAttr, bIH,
|
||||||
|
newFlags, env, CurrDir, si, pi, NewToken);
|
||||||
|
lastError = GetLastError();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BOOL trigger = 0;
|
||||||
|
std::thread(ProcessWrapperT, hToken, AppName, CmdLine, ProcessAttr, ThreadAttr, bIH,
|
||||||
|
newFlags, env, CurrDir, si, pi, NewToken, &res, &lastError, &trigger).detach();
|
||||||
|
|
||||||
|
UINT counter = 0;
|
||||||
|
while (1) {
|
||||||
|
// Not an elegant replacement for mutex, since 32bit threads having issues with it.
|
||||||
|
if (trigger)
|
||||||
|
break;
|
||||||
|
if (counter > 200) {
|
||||||
|
simpleLogger(std::format("Launch timeout reached. (unexpected)"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
Sleep(5);
|
||||||
|
}
|
||||||
|
simpleLogger(std::format("Wrapper launched."));
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD ec = 0;
|
||||||
|
DWORD thread_res = 0;
|
||||||
|
DWORD event_pid = 0;
|
||||||
|
BOOL arm_lib = FALSE;
|
||||||
|
DWORD sz_r = 0;
|
||||||
|
|
||||||
|
if (res && !wrapIt) {
|
||||||
|
// Legacy handling
|
||||||
|
fs::path _temp_path = GetFileNameProc(pi->hProcess);
|
||||||
|
simpleLogger(std::format("{} sub-proc(below) started: {} {}",
|
||||||
|
__func__, pi->dwProcessId, _temp_path.generic_string()));
|
||||||
|
if (!cacheOverrideSet) {
|
||||||
|
// Patch PEB and create remote thread.
|
||||||
|
DWORD th_id = 0;
|
||||||
|
if (legacyRoot && pi->hThread)
|
||||||
|
th_id = GetThreadId(pi->hThread);
|
||||||
|
if (isInSRoot(_temp_path.native())) {
|
||||||
|
RequestPatchProc(
|
||||||
|
pi->dwProcessId,
|
||||||
|
_temp_path,
|
||||||
|
th_id,
|
||||||
|
current_pid,
|
||||||
|
keepSuspended
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!keepSuspended) {
|
||||||
|
if (ResumeThread(pi->hThread) == -1) {
|
||||||
|
simpleLogger(std::format("{} Failed to resume: {}", __func__, pi->dwProcessId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (res) {
|
||||||
|
// Process created.
|
||||||
|
simpleLogger(std::format("{} sub-proc started: {}", __func__, pi->dwProcessId));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
simpleLogger(std::format("{} didn't start Err: {}", __func__, lastError));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheOverrideSet) {
|
||||||
|
std::string msg("AU_lib_drop");
|
||||||
|
msg.append(std::to_string(pi->dwProcessId));
|
||||||
|
send_debug_string(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleLogger(std::format("Releasing process.."));
|
||||||
|
SetLastError(lastError);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
|
||||||
|
|
||||||
|
simpleLogger(std::format("Exception: 0x{:x}", pExceptionInfo->ExceptionRecord->ExceptionCode));
|
||||||
|
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOLEAN WINAPI DllMain(IN HINSTANCE hDllHandle,
|
||||||
|
IN DWORD nReason,
|
||||||
|
IN LPVOID Reserved)
|
||||||
|
{
|
||||||
|
BOOLEAN bSuccess = TRUE;
|
||||||
|
BOOL unhook_res = FALSE;
|
||||||
|
CHAR szFileName[MAX_PATH * 3];
|
||||||
|
const wchar_t* env_p = NULL;
|
||||||
|
PTEB tebPtr = NULL;
|
||||||
|
UCHAR orig_dbg_value;
|
||||||
|
BOOL changed;
|
||||||
|
std::string msg_d("AU_lib_drop");
|
||||||
|
|
||||||
|
switch (nReason) {
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
|
||||||
|
#if _WIN64
|
||||||
|
tebPtr = reinterpret_cast<PTEB>(
|
||||||
|
__readgsqword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
|
||||||
|
pebPtr = (_PPEB)tebPtr->ProcessEnvironmentBlock;
|
||||||
|
|
||||||
|
#else
|
||||||
|
tebPtr = reinterpret_cast<PTEB>(
|
||||||
|
__readfsdword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
|
||||||
|
pebPtr = (PPEB32)tebPtr->ProcessEnvironmentBlock;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
GetModuleFileNameA(NULL, szFileName, MAX_PATH * 3);
|
||||||
|
pathCurrentModule = fs::path(szFileName);
|
||||||
|
exeName = std::string(pathCurrentModule.filename().generic_string());
|
||||||
|
env_p = _wgetenv(L"AU_API_HOOK_DEBUG");
|
||||||
|
if (env_p) {
|
||||||
|
debugMode = TRUE;
|
||||||
|
}
|
||||||
|
env_p = _wgetenv(L"AU_API_HOOK_WRITE_STD_ERR");
|
||||||
|
if (env_p) {
|
||||||
|
writeStdErr = _wtoi(env_p);
|
||||||
|
}
|
||||||
|
env_p = _wgetenv(L"AU_API_HOOK_LOG_PATH");
|
||||||
|
if (env_p) {
|
||||||
|
logPathPrefix = std::wstring(env_p);
|
||||||
|
}
|
||||||
|
env_p = _wgetenv(L"AU_API_HOOK_EX_HANDLER");
|
||||||
|
if (env_p) {
|
||||||
|
AddVectoredExceptionHandler(1, VectoredExceptionHandler);
|
||||||
|
}
|
||||||
|
env_p = _wgetenv(L"AU_API_HOOK_RELEASE_CHILD");
|
||||||
|
if (env_p) {
|
||||||
|
// Keep child process attached for debugging purposes.
|
||||||
|
releaseAttachedDebuggee = TRUE;
|
||||||
|
}
|
||||||
|
env_p = _wgetenv(L"__AU_PACKAGE_REP_ROOT");
|
||||||
|
if (env_p) {
|
||||||
|
msg_d.append(std::to_string(GetCurrentProcessId()));
|
||||||
|
send_debug_string(msg_d);
|
||||||
|
simpleLogger(std::format("Abort hook."));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveSystemRoot();
|
||||||
|
|
||||||
|
if (isSRootLegacy() != G_LOCATION::is_not_in_systemroot) {
|
||||||
|
// Consider no response as legacy as well to keep going.
|
||||||
|
legacyRoot = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleLogger(std::format("AU platform api lib({}-{}) attached: {} PID: {} legacy: {} dbg: {}\nfull path: {}",
|
||||||
|
__DATE__, __TIME__, pathCurrentModule.filename().generic_string(),
|
||||||
|
GetCurrentProcessId(), legacyRoot,
|
||||||
|
IsDebuggerPresent(), pathCurrentModule.generic_string()));
|
||||||
|
|
||||||
|
if (legacyRoot) {
|
||||||
|
pebPtr->PlaceholderCompatibilityMode = PHCM_DISGUISE_PLACEHOLDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Beware PEB patch removed from within library,
|
||||||
|
|
||||||
|
// Set hook to tweak sub-procs.
|
||||||
|
if (!hookCreateProcW)
|
||||||
|
hookCreateProcW = Mhook_SetHook((PVOID*)&TrueCreateProcessInternalW, HookCreateProcessInternalW);
|
||||||
|
simpleLogger(std::format("Hook state: {}", hookCreateProcW));
|
||||||
|
|
||||||
|
send_debug_string(hookSet); // Notify parent that DLL initialized and hook is set.
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
simpleLogger("AU platform api lib DLL_PROCESS_DETACH");
|
||||||
|
if (hookCreateProcW)
|
||||||
|
unhook_res = Mhook_Unhook((PVOID*)&TrueCreateProcessInternalW);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DLL_THREAD_ATTACH:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DLL_THREAD_DETACH:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return bSuccess;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user