# keepalive_f5.ps1 # Pulses F5 to the AC client window every N seconds to defeat Coldeve's # idle-kick. Finds the window by class "Turbine Device Class" so it's # robust to title-string encoding quirks. param( [int]$IntervalSec = 60, [string]$LogFile = "C:\Users\acbot\leakhunt\artifacts\phase1\keepalive.log" ) $ErrorActionPreference = "Stop" Add-Type @" using System; using System.Runtime.InteropServices; using System.Text; public class LhKA { public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); [DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetClassName(IntPtr hWnd, StringBuilder text, int count); [DllImport("user32.dll")] public static extern int GetWindowThreadProcessId(IntPtr hWnd, out uint pid); [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); } "@ -ErrorAction SilentlyContinue function W([string]$msg) { $t = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") "[$t] $msg" | Out-File -Append -Encoding utf8 -FilePath $LogFile } function Find-AcWindow { param([int]$ProcessId) $found = $null $cb = [LhKA+EnumWindowsProc]{ param($h, $l) $pid_ = 0 [void][LhKA]::GetWindowThreadProcessId($h, [ref]$pid_) if ($pid_ -eq $ProcessId -and [LhKA]::IsWindowVisible($h)) { $cls = New-Object System.Text.StringBuilder 256 [void][LhKA]::GetClassName($h, $cls, $cls.Capacity) if ($cls.ToString() -eq "Turbine Device Class") { $script:found = $h return $false } } return $true } [void][LhKA]::EnumWindows($cb, [IntPtr]::Zero) return $script:found } $WM_KEYDOWN = 0x0100 $WM_KEYUP = 0x0101 $VK_F5 = 0x74 W "keepalive_f5 starting, pulse every $IntervalSec s" while ($true) { $ac = Get-Process -Name acclient -ErrorAction SilentlyContinue | Sort-Object StartTime -Descending | Select-Object -First 1 if (-not $ac) { W "acclient gone - exiting"; break } $hwnd = Find-AcWindow -ProcessId $ac.Id if (-not $hwnd) { W "AC window not found for PID $($ac.Id); waiting"; Start-Sleep -Seconds $IntervalSec; continue } $ok1 = [LhKA]::PostMessage($hwnd, $WM_KEYDOWN, [IntPtr]$VK_F5, [IntPtr]0) Start-Sleep -Milliseconds 50 $ok2 = [LhKA]::PostMessage($hwnd, $WM_KEYUP, [IntPtr]$VK_F5, [IntPtr]0) W "F5 pulse -> hwnd=0x$($hwnd.ToInt64().ToString('X')) pid=$($ac.Id) PM=$([math]::Round($ac.PrivateMemorySize64/1MB,1))MB ok=$ok1,$ok2" Start-Sleep -Seconds $IntervalSec }