TaNkist
19.10.2006, 13:06
Работа с цепочкой SOCKS-проксей.
Не мне тебе объяснять, что значит анонимность для хакера. Ты, наверное, не раз задумывался о лучшем способе ее сохранения. Не самый лучший, но, безусловно, самый популярный – это использование цепочки SOCKS-серверов. Ведь если хотя бы одно звено-сервер из этой цепи не пишет логов, то у взломщика остаются неплохие шансы сохранить анонимность.
Итак, как ты уже догадался сегодня мы познакомимся с работой по передаче данных в цепочке SOCKS-проксей. Для того чтобы Socks-сервер соединился с нужным нам сайтом необходимо указать адрес сайта и его порт. Но сначала придется поприветствовать сервер и пройти аутентификацию (она не обязательна, и на большинстве анонимных соксах отсутствует).
[Приветствие] На этом этапе нужно отправить специальный пакет:
1 байт – номер версии
1 байт – количество методов (N)
N байт – перечисление методов
Т.к. мы будем работать с соксами 5 версии, то нужно послать $05, следующий байт – число поддерживаемых методов соединения / аутенфикации. За ним последовательность байт, описывающих эти самые методы. Мы не будем заморачиваться с аутенфикацией, поэтому напишем тут 00.
На это приветствие сокс отвечает двумя байтами: номером своей версии и выбранным методом. Если соксу ничего не понравилось, то второй байт будет равняться $FF, если мы получим $00, то можно смело продолжить работу.
[Соединение] на следующем этапе мы должны указать серверу с кем соединиться. Для этого нужно отправить такой пакет.
1 байт – номер версии
1 байт – команда
1 байт – зарезервировано до лучших времен
1 байт – тип адреса
N байт – адрес
2 байта – порт
Для заполнения этого пакета я создал специальную структуру:
request = record
version:byte;
cmd:byte;
null:byte;
typeofaddr:byte;
addr:integer;
port:word;
end;
Байт команды у нас будет равен $01, это значит, что мы просто хотим соединиться (о других значениях следует прочитать в RFC).
Следующий байт всегда равен нулю.
Типом адреса может быть:
$01 – ip v4 адрес, заданный 4 байтами
$03 – Нерезолвленное имя хоста (всегда лучше резолвить удаленно, т.к. иначе ты можешь пропалить свой DNS-сервер).
$04 –ip v6 адрес
На это сокс-сервер вернет пакет той же структуры, но с другими значениями. Например. нерезолвленный хост станет ip-шником.
Если все прошло нормально, то socks переходит в прозрачный режим и начинает передовать все данные по адресу.
[Составляем цепочку] Теперь на практике разбирем, то о чем говорилось выше и реализуем минимальный функционал socks-клиента.
Информацию о соксах будем хранить в следующей структуре.
server = record
ip:integer;
port:word;
end;
Нужно заполнить структуру SockAddrIn, указав данные о первом соксе.
SockAddrIn.sin_addr.S_addr:=socks[0].ip;
SockAddrIn.sin_port:=Socks[0].port;
Коннектимся к первому серверу
connect(FSocket,SockAddrIn,SizeOf(SockAddrIn));
Затем в цикле запускаем коннект к следующем соксам.
For i:=1 to sockcount do
begin
send(Fsocket,hello,SizeOf(hello),0);
recv(FSocket,ans,Sizeof(ans),0);
if (ans[0]<>$05) or (ans[1]<>$FF) then halt;
//Заполняем структуру req
req.version:=$05;
req.cmd:=$01;
req.null:=0;
req.typeofaddr:=$01;
req.addr:=socks[i].ip;
req.port:=socks[i].port;
send(Fsocket,req,sizeof(req),0);
recv(Fsocket,req,sizeof(req),0);
end;
Вот мы и написали в общем-то нехитрый код для работы с SOCKS-проксями. Для более подробного изучения рекомендую прочитать RFC 1928, в котором описан протокол SOCKS 5. Напоследок, я выложу полный исходный код со всеми подробными комментариями.
program Sock5clinet;
uses
Windows,
WinSock;
type server = record //Структура для описания адресов соксов
ip:integer;//ip-адрес
port:word; //порт
end;
request = record // Пакет для соединения с сервером
version:byte; // номер версии
cmd:byte; // команда
null:byte; // зарезервировано до лучших времен
typeofaddr:byte; // тип адреса
addr:integer; // адрес
port:word; // порт
end;
var
socks:array [0..4] of server;
FSocket:Tsocket;
WSAData:TWSADATA;
procedure connecttoSocks(sockcount:integer);
var
i:byte;
ans:array[0..1] of byte; //Буфер для ответов сервера
SockAddrIn: TSockAddrIn;
hello:array [0..2]of char;//Буфер приветсвия
req:request; //Структура в которой содержиться запрос на соединение
begin
hello:=#050100;//Последовательность байт 05, 01, 00
SockAddrIn.sin_addr.S_addr:=socks[0].ip;
SockAddrIn.sin_port:=Socks[0].port;
connect(FSocket,SockAddrIn,SizeOf(SockAddrIn));
For i:=1 to sockcount do
begin
send(Fsocket,hello,SizeOf(hello),0);
recv(FSocket,ans,Sizeof(ans),0);
if (ans[0]<>$05) or (ans[1]<>$FF) then halt;
//Заполняем структуру req
req.version:=$05;
req.cmd:=$01;
req.null:=0;
req.typeofaddr:=$01;
req.addr:=socks[i].ip;
req.port:=socks[i].port;
send(Fsocket,req,sizeof(req),0);
recv(Fsocket,req,sizeof(req),0);
end;
end;
begin
//Первый сокс
socks[0].ip:=inet_addr('127.0.0.1');
socks[0].port:=htons(80);
//Второй сокс
socks[1].ip:=inet_addr('127.0.0.1');
socks[1].port:=htons(81);
//Третий сокс
socks[2].ip:=inet_addr('127.0.0.1');
socks[2].port:=htons(82);
//Четвертый сокс
socks[3].ip:=inet_addr('127.0.0.1');
socks[3].port:=htons(83);
//Хост куда нужно подключиться
socks[4].ip:=inet_addr('127.0.0.1');
socks[4].port:=htons(110);
//Инициализируем WinSock
WSAStartUp($202,WSAData);
//Подготавляиваем сокет
FSocket:=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//Соединяемся по цепочке соксов с нужным хостом
connecttoSocks(4);
end.
Не мне тебе объяснять, что значит анонимность для хакера. Ты, наверное, не раз задумывался о лучшем способе ее сохранения. Не самый лучший, но, безусловно, самый популярный – это использование цепочки SOCKS-серверов. Ведь если хотя бы одно звено-сервер из этой цепи не пишет логов, то у взломщика остаются неплохие шансы сохранить анонимность.
Итак, как ты уже догадался сегодня мы познакомимся с работой по передаче данных в цепочке SOCKS-проксей. Для того чтобы Socks-сервер соединился с нужным нам сайтом необходимо указать адрес сайта и его порт. Но сначала придется поприветствовать сервер и пройти аутентификацию (она не обязательна, и на большинстве анонимных соксах отсутствует).
[Приветствие] На этом этапе нужно отправить специальный пакет:
1 байт – номер версии
1 байт – количество методов (N)
N байт – перечисление методов
Т.к. мы будем работать с соксами 5 версии, то нужно послать $05, следующий байт – число поддерживаемых методов соединения / аутенфикации. За ним последовательность байт, описывающих эти самые методы. Мы не будем заморачиваться с аутенфикацией, поэтому напишем тут 00.
На это приветствие сокс отвечает двумя байтами: номером своей версии и выбранным методом. Если соксу ничего не понравилось, то второй байт будет равняться $FF, если мы получим $00, то можно смело продолжить работу.
[Соединение] на следующем этапе мы должны указать серверу с кем соединиться. Для этого нужно отправить такой пакет.
1 байт – номер версии
1 байт – команда
1 байт – зарезервировано до лучших времен
1 байт – тип адреса
N байт – адрес
2 байта – порт
Для заполнения этого пакета я создал специальную структуру:
request = record
version:byte;
cmd:byte;
null:byte;
typeofaddr:byte;
addr:integer;
port:word;
end;
Байт команды у нас будет равен $01, это значит, что мы просто хотим соединиться (о других значениях следует прочитать в RFC).
Следующий байт всегда равен нулю.
Типом адреса может быть:
$01 – ip v4 адрес, заданный 4 байтами
$03 – Нерезолвленное имя хоста (всегда лучше резолвить удаленно, т.к. иначе ты можешь пропалить свой DNS-сервер).
$04 –ip v6 адрес
На это сокс-сервер вернет пакет той же структуры, но с другими значениями. Например. нерезолвленный хост станет ip-шником.
Если все прошло нормально, то socks переходит в прозрачный режим и начинает передовать все данные по адресу.
[Составляем цепочку] Теперь на практике разбирем, то о чем говорилось выше и реализуем минимальный функционал socks-клиента.
Информацию о соксах будем хранить в следующей структуре.
server = record
ip:integer;
port:word;
end;
Нужно заполнить структуру SockAddrIn, указав данные о первом соксе.
SockAddrIn.sin_addr.S_addr:=socks[0].ip;
SockAddrIn.sin_port:=Socks[0].port;
Коннектимся к первому серверу
connect(FSocket,SockAddrIn,SizeOf(SockAddrIn));
Затем в цикле запускаем коннект к следующем соксам.
For i:=1 to sockcount do
begin
send(Fsocket,hello,SizeOf(hello),0);
recv(FSocket,ans,Sizeof(ans),0);
if (ans[0]<>$05) or (ans[1]<>$FF) then halt;
//Заполняем структуру req
req.version:=$05;
req.cmd:=$01;
req.null:=0;
req.typeofaddr:=$01;
req.addr:=socks[i].ip;
req.port:=socks[i].port;
send(Fsocket,req,sizeof(req),0);
recv(Fsocket,req,sizeof(req),0);
end;
Вот мы и написали в общем-то нехитрый код для работы с SOCKS-проксями. Для более подробного изучения рекомендую прочитать RFC 1928, в котором описан протокол SOCKS 5. Напоследок, я выложу полный исходный код со всеми подробными комментариями.
program Sock5clinet;
uses
Windows,
WinSock;
type server = record //Структура для описания адресов соксов
ip:integer;//ip-адрес
port:word; //порт
end;
request = record // Пакет для соединения с сервером
version:byte; // номер версии
cmd:byte; // команда
null:byte; // зарезервировано до лучших времен
typeofaddr:byte; // тип адреса
addr:integer; // адрес
port:word; // порт
end;
var
socks:array [0..4] of server;
FSocket:Tsocket;
WSAData:TWSADATA;
procedure connecttoSocks(sockcount:integer);
var
i:byte;
ans:array[0..1] of byte; //Буфер для ответов сервера
SockAddrIn: TSockAddrIn;
hello:array [0..2]of char;//Буфер приветсвия
req:request; //Структура в которой содержиться запрос на соединение
begin
hello:=#050100;//Последовательность байт 05, 01, 00
SockAddrIn.sin_addr.S_addr:=socks[0].ip;
SockAddrIn.sin_port:=Socks[0].port;
connect(FSocket,SockAddrIn,SizeOf(SockAddrIn));
For i:=1 to sockcount do
begin
send(Fsocket,hello,SizeOf(hello),0);
recv(FSocket,ans,Sizeof(ans),0);
if (ans[0]<>$05) or (ans[1]<>$FF) then halt;
//Заполняем структуру req
req.version:=$05;
req.cmd:=$01;
req.null:=0;
req.typeofaddr:=$01;
req.addr:=socks[i].ip;
req.port:=socks[i].port;
send(Fsocket,req,sizeof(req),0);
recv(Fsocket,req,sizeof(req),0);
end;
end;
begin
//Первый сокс
socks[0].ip:=inet_addr('127.0.0.1');
socks[0].port:=htons(80);
//Второй сокс
socks[1].ip:=inet_addr('127.0.0.1');
socks[1].port:=htons(81);
//Третий сокс
socks[2].ip:=inet_addr('127.0.0.1');
socks[2].port:=htons(82);
//Четвертый сокс
socks[3].ip:=inet_addr('127.0.0.1');
socks[3].port:=htons(83);
//Хост куда нужно подключиться
socks[4].ip:=inet_addr('127.0.0.1');
socks[4].port:=htons(110);
//Инициализируем WinSock
WSAStartUp($202,WSAData);
//Подготавляиваем сокет
FSocket:=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//Соединяемся по цепочке соксов с нужным хостом
connecttoSocks(4);
end.