背景
部分服务器只开80/443端口,那么只往服务器的80/443发送数据,而有的情况下,我们需要在内部监听一个9999端口,怎么让数据能正常通过80然后到9999呢?
利用
驱动使用的是WinDivert,开源,之前的blog有相关的介绍。
代码如下,作为demo只处理80端口。
简单说一下,tcp连接都会先进行三次握手,我们刻意开的9999端口和默认开启的80端口都是基于tcp协议的,怎么分辨我们的特殊数据呢?这里比较随意(改成异或或者位移不难吧?),当第一位是字符1的时候,就认为是需要转发到9999的数据,这里不能直接转发到9999,必须要先给80发一个rst中止连接。
为什么?因为我们收到特殊数据的时候80和你的客户端已经完成了三次握手,你和9999都完全没有握手。tcp是可靠的协议,在没有和9999握手的前提下,直接发数据9999是不会理你的,所以我们先中止和80的连接,记录下src的ip,当你下一次来的时候,直接发往9999进行三次握手和数据传输。
#include <winsock2.h>
#include <windows.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include "windivert.h"
#define MAXBUF WINDIVERT_MTU_MAX
typedef struct
{
WINDIVERT_IPHDR ip;
WINDIVERT_TCPHDR tcp;
} PACKET, * PPACKET;
void PacketInit(PPACKET packet)
{
memset(packet, 0, sizeof(PACKET));
packet->ip.Version = 4;
packet->ip.HdrLength = sizeof(WINDIVERT_IPHDR) / sizeof(UINT32);
packet->ip.Length = htons(sizeof(PACKET));
packet->ip.TTL = 64;
packet->ip.Protocol = IPPROTO_TCP;
packet->tcp.HdrLength = sizeof(WINDIVERT_TCPHDR) / sizeof(UINT32);
}
BOOLEAN g_ok = FALSE;
int main()
{
PACKET reset0;
PPACKET reset = &reset0;
PacketInit(reset);
reset->tcp.Rst = 1;
reset->tcp.Ack = 1;
UINT32 pyloadIP = NULL;
PVOID payload = NULL;
UINT payload_len = NULL;
UINT packet_len = NULL;
unsigned char packet[MAXBUF];
WINDIVERT_ADDRESS addr = { 0 };
PWINDIVERT_IPHDR ip_header = NULL;
PWINDIVERT_TCPHDR tcp_header = NULL;
HANDLE handle = WinDivertOpen(" tcp.DstPort == 80 or tcp.SrcPort == 9999", WINDIVERT_LAYER_NETWORK, 0, 0);
if (handle == INVALID_HANDLE_VALUE)
return 0;
while (1)
{
//这里一直在接收数据包
if (!WinDivertRecv(handle, packet, sizeof(packet), &packet_len, &addr))
{
printf("failed to read packet (%d) \n", GetLastError());
continue;
}
WinDivertHelperParsePacket(packet, packet_len, &ip_header, NULL, NULL, NULL, NULL, &tcp_header, NULL, &payload, &payload_len, NULL, NULL);
if (ip_header == NULL || tcp_header == NULL)
{
printf("failed to parse packet (%d) \n", GetLastError());
continue;
}
char* buf = (char*)payload;
if (payload && buf[0] == '1' && !pyloadIP)
{
if (!addr.Outbound)
{
//首先中止第一个连接 -->告诉80端口已经结束了
reset->ip.SrcAddr = ip_header->SrcAddr;
reset->ip.DstAddr = ip_header->DstAddr;
reset->tcp.SrcPort = tcp_header->SrcPort;
reset->tcp.DstPort = htons(80);
reset->tcp.SeqNum = tcp_header->SeqNum;
reset->tcp.AckNum = tcp_header->AckNum;
WinDivertHelperCalcChecksums((PVOID)reset, sizeof(PACKET), &addr, 0);
if (!WinDivertSend(handle, (PVOID)reset, sizeof(PACKET), NULL, &addr))
{
printf("error reset\n");
}
pyloadIP = ip_header->SrcAddr;
}
continue;
}
//这里将端口重定向
if (pyloadIP && (pyloadIP == ip_header->SrcAddr || pyloadIP == ip_header->DstAddr))
{
if (addr.Outbound)
{
if (tcp_header->SrcPort == htons(9999))
{
tcp_header->SrcPort = htons(80);
}
}
else
{
if (tcp_header->DstPort == htons(80))
{
tcp_header->DstPort = htons(9999);
}
}
}
WinDivertHelperCalcChecksums(packet, packet_len, &addr, 0);
UINT writeLen = NULL;
if (!WinDivertSend(handle, packet, packet_len, &writeLen, &addr))
{
printf("failed to send packet (%d) \n", GetLastError());
continue;
}
}
return 0;
}
client测试代码
#include<stdio.h>
#include<windows.h>
#include<winsock.h>
#define SERVERPORT 80
#define SERVERIP "192.168.1.2"
#pragma comment(lib,"ws2_32.lib")
int main(int argc, char* argv[])
{
struct sockaddr_in serAddr;
SOCKET cliSocket;
int conn;
WSADATA Ws;
char sendBuf[1024];
int sen;
int recvR;
fd_set rfds;/
struct timeval time;///
int maxrd = 0;
int ret;/
if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0)
{
printf("init windows socket failed:%d\n", GetLastError());
return -1;
}
cliSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (cliSocket == INVALID_SOCKET)
{
printf("window socket failed:%d\n", GetLastError());
return -1;
}
serAddr.sin_family = AF_INET;
serAddr.sin_addr.s_addr = inet_addr(SERVERIP);
serAddr.sin_port = htons(SERVERPORT);
memset(serAddr.sin_zero, 0x00, 8);
conn = connect(cliSocket, (struct sockaddr*) & serAddr, sizeof(serAddr));
if (conn == SOCKET_ERROR)
{
printf("connect socket failed:%d\n", GetLastError());
return -1;
}
else
{
printf("connect successfully!\n");
}
while (true)
{
FD_ZERO(&rfds);/
FD_SET(0, &rfds);/
FD_SET(cliSocket, &rfds);/
time.tv_sec = 1;/
time.tv_usec = 0;//
maxrd = ((maxrd > cliSocket) ? maxrd : cliSocket);
ret = select(maxrd + 1, &rfds, NULL, NULL, &time);
if (ret == 0)
continue;
else {
if (FD_ISSET(0, &rfds))
{
memset(sendBuf, 0x00, sizeof(sendBuf));
system("pause");
sen = send(cliSocket, "111111199999", (int)strlen("111111199999"), 0);
if (sen == SOCKET_ERROR)
{
printf("send info error:%d\n", GetLastError());
break;
}
}
if (FD_ISSET(cliSocket, &rfds))
{
memset(sendBuf, 0x00, sizeof(sendBuf));
recvR = recv(cliSocket, sendBuf, 1024, 0);
printf("recieve from server:%s\n", sendBuf);
}
}
}
closesocket(cliSocket);
WSACleanup();
return 0;
}
server的测试代码
#include<windows.h>
#include<stdio.h>
#include<winsock.h>
#define SERVER_PORT 80
#define SERVER_IP "0.0.0.0"
#pragma comment(lib,"ws2_32.lib")
DWORD Demo()
{
SOCKET serSocket, cliSocket;
struct sockaddr_in localAddr, clientAddr;
WSADATA Ws;
int Bit;
int Lit;
char recvBuf[1024];
int recvR, sen;
int cliLen;
fd_set rfds;//
struct timeval time;//
int maxrd = 0;//
int ret;//
if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0)
{
printf("init windows socket failed:%d\n", GetLastError());
return -1;
}
serSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serSocket == INVALID_SOCKET)
{
printf("create socket failed:%d\n", GetLastError());
return -1;
}
localAddr.sin_family = AF_INET;
localAddr.sin_port = htons(SERVER_PORT);
localAddr.sin_addr.s_addr = inet_addr(SERVER_IP);
memset(localAddr.sin_zero, 0x00, 8);
Bit = bind(serSocket, (struct sockaddr*) & localAddr, sizeof(localAddr));
if (Bit != 0)
{
printf("bind failed:%d\n", GetLastError());
return -1;
}
Lit = listen(serSocket, 5);
if (Lit != 0)
{
printf("listen failed:%d\n", GetLastError());
return -1;
}
printf("server has been startup\n");
while (true)
{
cliLen = sizeof(clientAddr);
cliSocket = accept(serSocket, (struct sockaddr*) & clientAddr, &cliLen);
if (cliSocket == INVALID_SOCKET)
{
printf("accept failed:%d\n", GetLastError());
return -1;
}
//printf("client connect%d:%d\n", inet_ntoa(clientAddr.sin_addr), clientAddr.sin_port);/
while (true)
{
FD_ZERO(&rfds);///
FD_SET(0, &rfds);
FD_SET(cliSocket, &rfds);
time.tv_sec = 1;
time.tv_usec = 0;/
maxrd = (maxrd > cliSocket ? maxrd : cliSocket);/
ret = select(maxrd + 1, &rfds, NULL, NULL, &time);
if (ret == 0)//
continue;
else {
if (FD_ISSET(0, &rfds))
{ /
memset(recvBuf, 0x00, sizeof(recvBuf));
recvR = recv(cliSocket, recvBuf, 1024, 0);
if (recvR == 0 || recvR == SOCKET_ERROR)
{
printf("the client had quit\n");
break;
}
printf("recieve message:%s\n", recvBuf);
}
if (FD_ISSET(cliSocket, &rfds))
{
memset(recvBuf, 0x00, sizeof(recvBuf));
//gets(recvBuf);
sen = send(cliSocket, "19999", sizeof("19999"), 0);
}
}
}
return 0;
}
closesocket(serSocket);
closesocket(cliSocket);
WSACleanup();
return 0;
}
int main()
{
Demo();
}