转载:Iceberg's Blog
先要申明一下,这里所说的"隐藏"服务器程序的端口,并不是使用rootkit将服务器程序的端口令用户使用netstat.exe,fport.exe,mport.exe,ActivePort等第三方查看程序打开端口的程序无法查出程序打开的端口,这里所说的"隐藏"是指对远程用户的扫描或连接而言。
现在先温习一下一些基本的TCP连接的网络知识:
一般大家熟知的TCP连接是涉及三次握手的,首先客户先发送一个SYN包给服务器端,然后服务器端就发送回一个SYN-ACK数据包给客户(如果客户连接过来的端口是有服务在处理连接的话),最后是客户端发回一个ACK数据包给服务器端,然后TCP连接就建立了,连接建立后,客户端和服务端就可以互传数据到对方中去。
然后去看看传统服务器代码:
1.绑定一个端口
2.等待新的连接,并用不同的方法处理这些新的连接请求(同步,异步,阻塞,非阻塞等不同方法的不同组合使用)
int main()
{
WSADATA wsaData;
SOCKET ListenSocket, AcceptSocket;
strUCt sockaddr_in Client;
int ClientSize = sizeof(Client);
USHORT port = 1234;
struct sockaddr_in service;
WSAStartup(MAKEWord(2,2), &wsaData);
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
service.sin_family = AF_INET;
service.sin_port = htons(port);
service.sin_addr.s_addr = htonl(INADDR_ANY);
bind(ListenSocket, (SOCKADDR *) &service, sizeof(SOCKADDR));
listen(ListenSocket, 8);
while(TRUE)
{
AcceptSocket = WSAAccept(ListenSocket, &Client, &ClientSize, NULL, 0);
// Trap The Error & Handle The New Client Request
}
closesocket(ListenSocket);
WSACleanup();
return 0;
}
以上代码基本上在是95%以上的服务器程序都是使用这样方式,或者说就算代码不同,也都是异曲同工.这种方法并没有什么不妥,但最大的缺点就是无论新的连接是服务器所允许还是拒绝的,客户端都能知道服务器那个端口是打开的(上面的例子是TCP端口1234),因为这样方式是完成了三次握手,所以远程用户无论是使用传统的TCP连接扫描,还是SYN扫描,都可以知道TCP端口1234是打开的。
问题:难道没有办法不让服务器程序拒绝的远程地址得知服务器的端口是打开并且进行服务的吗?
答案是有的.微软本身就有API可以做到,而且我们上面的代码也有用到这个API的,那就是WSAAccept().我们所需要做的就是改变一下这个API的第四个参数,并且多加点代码,就可以实现题目所说的"隐藏"服务器端口了。
complete test source code:
#include <windows.h>
#include <winsock2.h>
int CALLBACK MyCondition(LPWSABUF lpCallerId,LPWSABUF lpCallerData,LPQOS lpSQOS,LPQOS lpGQOS,LPWSABUF lpCalleeId,LPWSABUF lpCalleeData,GROUP FAR *g,DWORD_PTR dwCallbackData)
{
return CF_REJECT; // Just Reject All The Connections
}
int main()
{
WSADATA wsaData;
SOCKET ListenSocket, AcceptSocket;
struct sockaddr_in Client;
int ClientSize = sizeof(Client);
USHORT port = 1234;
struct sockaddr_in service;
WSAStartup(MAKEWORD(2,2), &wsaData);
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
service.sin_family = AF_INET;
service.sin_port = htons(port);
service.sin_addr.s_addr = htonl(INADDR_ANY);
BOOL bReUse = TRUE;
if (setsockopt(ListenSocket,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(char *)&bReUse,sizeof(bReUse)) != 0)
{
closesocket(ListenSocket);
return -1;
}
bind(ListenSocket, (SOCKADDR *) &service, sizeof(SOCKADDR));
listen(ListenSocket, 8);
while(TRUE)
{
AcceptSocket = WSAAccept(ListenSocket, &Client, &ClientSize, &MyCondition, 0);
if (AcceptSocket != INVALID_SOCKET)
{
send(AcceptSocket,"You Are Accepted\r\n",strlen("You Are Accepted\r\n"),0):
closesocket(AcceptSocket);
}
else
{
printf("The Connection Is Rejected\n");
}
// Trap The Error & Handle The New Client Request
}
closesocket(ListenSocket);
WSACleanup();
return 0;
}
可以清楚看到,加了的是一个函数,以及在bind()前面加了几行代码.就是加了这点儿代码,所以远程用户都无法使用连接还是扫描的方法得知TCP端口1234是打开的。只要在MyCondition()函数中再加入代码,就可以检查哪些用户是允许连接服务器的,哪些用户是不允许的。很酷的方法吧。
有人会说使用防火墙也能实现,那是当然的,但如果服务器程序本身就能实现的话,那又何必再装防火墙来实现这样的功能呢?
以上信息通过写代码测试过,应该是没有问题的,方法是在寻找服务器程序本身代码编写时反抗Syn攻击研究中无意中发现。如果有什么错误,请指出。
(出处:http://www.sheup.com)