SOCKS сервер – очень нужная и полезная вещь. Он служит как бы шлюзом для соединения с удаленным хостом, в случае если прямое соединение невозможно (файрвол) или нежелательно (светиться не хочешь).

Существуют проги, которые служат как бы переходником между обычной, не знающей о существовании SOCKS
программой и SOCKS сервером. Они ловят все пакеты, устанавливают соединение с SOCKS сервером и шлют пакеты на него.
В этой статье рассказано о работе SOCKS сервера и приведена прога, реализующая SOCKS. Вообще, сначала я хотел реализовать SOCKS 5, но понял, что я не совсем понимаю смысл команды UDP ASSOCIATE, а также не могу найти библиотеку, реализующую GSS API под винду.
В общем, читай про нечто среднее – наш сервер будет поддерживать аутентификацию Username/Password, адресацию по имени, а больше ничем от SOCKS 4 отличаться не будет. 

Когда клиент начинает подключаться к SOCKS серверу, он шлет ему пакет, содержащий версию SOCKS протокола (в нашем случае 5), количество методов аутентификации, которые он поддерживает и дальше массив идентификаторов методов (подробней читай RFC 1928).
Сервер должен выбрать нужный метод и отправить клиенту ответ, содержащий номер версии и идентификатор выбранного метода. 

После этого клиент шлет запрос-команду. Она содержит команду (что сделать), адрес хоста таргета и порта на таргете. Команды бывают CONNECT и BIND (еще бывает UDP ASSOCIATE, но что должен по ней сделать сервак не знаю – глуп). По команде CONNECT сервак должен подключиться к указанному таргету
(по имени или IP), уведомить клиента и наладить передачу данных от клиента к таргету. По команде BIND – почти то же самое, только наоборот: сервак должен открыть указанный порт, уведомить клиента и ждать, пока кто-нибудь туда подключиться, а когда это случится, уведомить клиента еще раз. 

Программу я написал на чистом API, поэтому экзешник получился на 26 килобайт, а жатый ASPack’ом – 18,5 килобайт.
Теперь прикинь – запускаешь эту вещь на чужом серваке, ставишь себе
SocksCap и делаешь черные дела абсолютно анонимно. Если запалят IP, то придут туда, где стоит твой сервак (который чужой). А там, понятное дело уже чисто… Ж8-).

///////////////////////////////////////////////////////////////////////////////
// socks.cpp
// Малышев А.В. aka "k41n"
// 

#include <windows.h>
#define NOAUTH 0
#define GSSAPI 1//
Не реализовано
#define USERNAME_PASSWORD 2
#define AUTHSCHEME USERNAME_PASSWORD

//структура для передачи сокетов другому потоку
struct CONN_PARAM
{
SOCKET sockSession;
SOCKET sockClient;
};

//Функция инициализации сокетов.
bool InitSockets();
//
Функция отсылки пакета с ответом, информирующим о ошибке
void SendNegativeResponse(SOCKET sock,int iError);
//
Функция отсылки пакета с ответом, информирующим о успехе
void SendPositiveResponse(SOCKET sockSession,SOCKET sockClient);
//
Функция потока для установленной связи между удаленным сервером и клиентом нашего SOCKSа
DWORD WINAPI SessionEstablished(LPVOID lpConnParam);
//
Функция потока для установления связи между удаленным сервером и клиентом нашего SOCKSа
DWORD WINAPI Session(LPVOID lpParameter);

int main(void)
{
//
Инициализировать сокеты
if (!InitSockets()) 
{
return -1;
}
//
Создадим сокет, который будет слушать 31337 Ж8-) порт
SOCKET socketListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (socketListen==INVALID_SOCKET) 
{
return -2;
}

//Забиндуем его на 31337 и внешний сетевой интерфейс
sockaddr_in a;
struct hostent *h;
h=gethostbyname("localhost");
h=gethostbyname(h->h_name);
a.sin_family=AF_INET;
a.sin_port=htons(31337);
memcpy(&(a.sin_addr.S_un.S_addr),h->h_addr,sizeof(int));
if (0!=bind(socketListen,(struct sockaddr *)&a,sizeof(a)))
{
return -2;
}
//
Начинаем слушать порт
listen(socketListen,50);
//
по мере получения запросов на соединение создаем для каждого индивидуальный поток
while (true)
{
sockaddr sa;
int iLength=sizeof(sa);
SOCKET sockSession=accept(socketListen,&sa,&iLength);
if (sockSession==INVALID_SOCKET) 
{
return -3;
}
DWORD dwThread;
CreateThread(NULL,0,Session,(LPVOID)sockSession,0,&dwThread);
}
}

//Инициализация сокетов
bool InitSockets()
{
WSADATA wsadata;
if (0==WSAStartup(2,&wsadata)) return true;
else return false;
}

//Послать отрицательный ответ клиенту
void SendNegativeResponse(SOCKET sock,int iError)
{
//
Готовим 10 - октетный пакет
char ResponsePacket[10];
//
Версия
ResponsePacket[0]=5;
//
В зависимости от ошибки придумываем разные причины для отказа Ж8-)
switch (iError)
{
case WSAENETDOWN:
ResponsePacket[1]=3;//
Network unreachable
break;
case WSAENETUNREACH:
ResponsePacket[1]=3;//
Network unreachable
break;
case WSAECONNREFUSED:
ResponsePacket[1]=5;//
Connection refused
break;
case WSAETIMEDOUT:
ResponsePacket[1]=4;//
Host unreachable
break;
default:
ResponsePacket[1]=1;//
general SOCKS server failure
}

//Этот октет зарезервирован, туда надо 0
ResponsePacket[2]=0;
//
Тип адреса
ResponsePacket[3]=1;//
IPv4
hostent *h=gethostbyname("localhost");
h=gethostbyname(h->h_name);
//
На самом деле соединения не произошло, поэтому никому не интересно, 
//
На каком IP и порту висит сокет, присоединенный к таргету
*((int *)(ResponsePacket+4))=*((int *)(h->h_addr));
*(short *)(ResponsePacket+8)=htons(4000);
int nReceived;
nReceived=send(sock,ResponsePacket,10,0);
//
По инструкции (RFC1928) надо немедленно закрыть сокет
//
Мне лень закрывать его корректно, так что просто
closesocket(sock);
}

//Послать положительный ответ клиенту
void SendPositiveResponse(SOCKET sockSession,SOCKET sockClient)
{
//
Готовим 10 - октетный пакет 
char ResponsePacket[10];
//
Версия
ResponsePacket[0]=5;
//
Сообщение об успехе
ResponsePacket[1]=0;
//
Этот октет зарезервирован, туда надо 0
ResponsePacket[2]=0;
//
Тип адреса
ResponsePacket[3]=1;//
IPv4
//
Определяем IP и порт, на котором висит сокет, присоединенный к таргету
hostent *h=gethostbyname("localhost");
h=gethostbyname(h->h_name);
*((int *)(ResponsePacket+4))=*((int *)(h->h_addr));
sockaddr_in sa;
int iSize=sizeof(sa);
getsockname(sockClient,(sockaddr *)&sa,&iSize);
*(short *)(ResponsePacket+8)=sa.sin_port;
//
Шлем пакет
int nReceived=send(sockSession,ResponsePacket,10,0);
if (SOCKET_ERROR==nReceived) //
А если ошибка случилась - закрываем сокет. Грубо.
closesocket(sockSession);
}

DWORD WINAPI SessionEstablished(LPVOID lpConnParam)
{
//
В этом потоке гоним полученные от таргета октеты
//
клиенту
//
CONN_PARAM *pcp=(CONN_PARAM *)lpConnParam;
SOCKET sockSession=pcp->sockSession;
SOCKET sockClient=pcp->sockClient;
int nReceived;
while (true)//
Если все идет хорошо, то этот цикл будет выполняться вечно
{
char c;
nReceived=recv(sockClient,&c,1,0);//
Получаем данные с таргета
if (SOCKET_ERROR==nReceived) //
Если произошла ошибка, закрываем сокет
{
closesocket(sockSession);
return 2;
}
nReceived=send(sockSession,&c,1,0);//
Шлем на клиента
if (SOCKET_ERROR==nReceived) //
Если произошла ошибка, закрываем сокет
{
closesocket(sockSession);
return 2;
}
}
return 0;
}

DWORD WINAPI Session(LPVOID lpParameter)
{
SOCKET sockSession=(SOCKET)lpParameter;
//
Начать переговоры с клиентом
char cVersion;
//
Получить версию, которая ему нужна
int nReceived=recv(sockSession,&cVersion,1,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
//
Получить методы, которые поддерживает клиент
char cMethodsCount;
nReceived=recv(sockSession,&cMethodsCount,1,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
char *cMethods=new char[cMethodsCount];
for (int i=0;i<cMethodsCount;i++)
{
nReceived=recv(sockSession,&cMethods[i],1,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
}
//
Ответить клиенту, послать ему нашу версию и метод 00 - No Auth
switch (AUTHSCHEME)
{
case NOAUTH:
{
char *packet=new char[2];
packet[0]=5;
//
Если клиент послал не 5 версию, то шлем ему FF 
if (cVersion!=5) packet[1]=(char)0xFF;
else packet[1]=0;
nReceived=send(sockSession,packet,2,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
}
case USERNAME_PASSWORD:
{
char *packet=new char[2];
packet[0]=5;
//
Если клиент послал не 5 версию, то шлем ему FF 
if (cVersion!=5) packet[1]=(char)0xFF;
else packet[1]=2;
nReceived=send(sockSession,packet,2,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
char cVersion;
nReceived=recv(sockSession,&cVersion,1,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
char cNameLength;
nReceived=recv(sockSession,&cNameLength,1,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
char *sName=new char[cNameLength+1];
nReceived=recv(sockSession,sName,cNameLength,0);
sName[cNameLength]=0;
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
char cPassLength;
nReceived=recv(sockSession,&cPassLength,1,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
char *sPass=new char[cPassLength+1];
nReceived=recv(sockSession,sPass,cPassLength,0);
sPass[cPassLength]=0;
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
if (!strcmp(sName,"Andrew"))
if (!strcmp(sPass,"170838"))
{
char *Packet=new char[2];
Packet[0]=1;
Packet[1]=0;
nReceived=send(sockSession,Packet,2,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
}
else
{
char *Packet=new char[2];
Packet[0]=1;
Packet[1]=0;
nReceived=send(sockSession,Packet,2,0);
closesocket(sockSession);
return 3;
}
else
{
char *Packet=new char[2];
Packet[0]=1;
Packet[1]=0;
nReceived=send(sockSession,Packet,2,0);
closesocket(sockSession);
return 3;
}
}
case GSSAPI:
//
Еще не сделано
closesocket(sockSession);
return 4;
}

//Получаем запрос клиента с командой
//
Версия
nReceived=recv(sockSession,&cVersion,1,0); 
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
//
Команда
char cCommand;
nReceived=recv(sockSession,&cCommand,1,0); 
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
//
Получаем нулевой октет - резервед
char cReserved;
nReceived=recv(sockSession,&cReserved,1,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
//
В каком формате будет передан адрес?
char cAddrType;
nReceived=recv(sockSession,&cAddrType,1,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
char *Address;
//
В зависимости от того, в каком формате клиент шлет адрес, выделяем на него память и 
//
получаем
switch (cAddrType)
{
case 1://
IPv4
{
Address=new char[4];
nReceived=recv(sockSession,Address,4,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
break;
}
case 3://
FQDN - Fully Qualified Domain Name
{
char iLength;
//
В первом октете записана длина адреса
nReceived=recv(sockSession,&iLength,1,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
Address=new char[iLength+1];
//
Получаем адрес
nReceived=recv(sockSession,Address,iLength,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
Address[iLength]=0;
break;
}
}
short iPort;
//
Получаем порт
nReceived=recv(sockSession,(char *)&iPort,2,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
//
Обрабатываем запрос
//
Создать сокет для соединения к таргету
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

switch (cCommand)
{
//
Команда 1 - CONNECT
case 1:
{
//
Соединяемся
sockaddr_in a;
a.sin_family=AF_INET;
a.sin_port=iPort;
//
Если адрес дан как FQDN, то надо его отресолвить
if (cAddrType==3)
{
hostent *h=gethostbyname(Address);
memcpy(&(a.sin_addr.S_un.S_addr),h->h_addr,sizeof(int));
}
else
a.sin_addr.S_un.S_addr=*(int *)Address;
//
Пробуем подключиться
nReceived=connect(sockClient,(sockaddr *)&a,sizeof(a));
if (nReceived)
{
//
Если не вышло, то шлем клиенту отрицательный пакет
SendNegativeResponse(sockSession,nReceived);
return -1;
}
//
А иначе шлем клиенту положительный пакет
SendPositiveResponse(sockSession,sockClient);
//
Теперь клиент будет слать и получать данные на таргет
//
Запустим поток, который будет передавать данные
//
от таргета к клиенту
CONN_PARAM cp;
cp.sockClient=sockClient;
cp.sockSession=sockSession;
DWORD dwThreadID;
CreateThread(NULL,0,SessionEstablished,&cp,0,&dwThreadID);
}
break;
//
Команда 2 - BIND
case 2:
{
//
Создать сокет для соединения к таргету
sockaddr_in a;
a.sin_family=AF_INET;
a.sin_port=iPort;
//
Если адрес дан как FQDN, то надо его отресолвить
if (cAddrType==3)
{
hostent *h=gethostbyname(Address);
memcpy(&(a.sin_addr.S_un.S_addr),h->h_addr,sizeof(int));
}
else
a.sin_addr.S_un.S_addr=*(int *)Address;
//
Пробуем связать сокет с нужным портом
nReceived=bind(sockClient,(sockaddr *)&a,sizeof(a));
//
Если не вышло, то шлем клиенту отрицательный пакет
if (nReceived) 
{
SendNegativeResponse(sockSession,nReceived);
return -1;
}
//
Пробуем начать слушать
nReceived=listen(sockClient,5);
//
Если не вышло, то шлем клиенту отрицательный пакет
if (nReceived) 
{
SendNegativeResponse(sockSession,nReceived); 
return -1;
}
//
А иначе шлем клиенту положительный пакет
SendPositiveResponse(sockSession,sockClient);
//
Принимаем входящее соединение от таргета
int iSize=sizeof(a);
sockClient=accept(sockClient,(sockaddr *)&a,&iSize);
//
Если не вышло, то шлем клиенту отрицательный пакет
if (sockClient==INVALID_SOCKET) 
{
SendNegativeResponse(sockSession,nReceived); 
return -1;
}
//
А иначе шлем клиенту ВТОРОЙ положительный пакет 
SendPositiveResponse(sockSession,sockClient); 
}
}
//
Создаем поток для пересылки данных от таргета к клиенту
CONN_PARAM cp;
cp.sockClient=sockClient;
cp.sockSession=sockSession;
DWORD dwThreadID;
CreateThread(NULL,0,SessionEstablished,&cp,0,&dwThreadID);
//
Теперь в этом потоке мы будем
//
гнать данные от клиента к таргету
while (true)//
Если все идет хорошо, то этот цикл будет выполняться вечно
{
char c;
//
Получаем от клиента
nReceived=recv(sockSession,&c,1,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
//
посылаем таргету
nReceived=send(sockClient,&c,1,0);
if (SOCKET_ERROR==nReceived) 
{
closesocket(sockSession);
return 2;
}
}
return 0;
}

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии