Работа с протоколом Socks5 на MASM
Недавно столкнулся с реализацией работы через прокси сервер по протоколу Socks5 на языке ассемблера.
К моему удивлению, на эту тему я не нашел какого либо завершенного материала (даже на wasm'овском форуме !).
Это и послужило поводом для написательства данной статьи.
На первый взгляд, все кажется сложным, но на самом деле все просто =)
Полное описание протокола можно найти в rfc 1928 и rfc 1929, а я на простом примере покажу
как реализуется коннект, отправка и прием данных, через не требующий авторизации Socks5 прокси сервер.
Печатая эти строки, я предполагаю, что у читателя есть навыки работы с
WinSock, а так же начальные знания языка Ассемблера (MASM).
И так, приступаем:
1. Инициализируем структуру WSAStartup (используя версию сокетов 1.1):
invoke WSAStartup, 101h, addr lpWSAData
2. Создаем сокет и зполняем структуру sockaddr_in:
invoke socket, AF_INET, SOCK_STREAM, 0 cmp eax, INVALID_SOCKET ; Если ошибка... jz ERRSOCK ; .. выходим mov hSocket, eax ; сохраняем хендл нашего сокета
mov SinStructSocks.sin_family, AF_INET invoke htons, 1080d ; Порт прокси сервера преобразуем в сетевой порядок бит mov SinStructSocks.sin_port, ax ; Теперь кидаем в структуру
invoke inet_addr, addr SocksHost ; IP адрес Прокси сервера, берем из переменной...
mov SinStructSocks.sin_addr.S_un.S_addr, eax ; .. Преобразуем, и кидаем в структуру
3. Пробуем приконектиться к заданному серверу:
invoke connect, hSocket, offset SinStructSocks, sizeof SinStructSocks
cmp eax, 0 jne ERRCON ; Если облом - выход...
4. И так, коннект прошел успешно. Теперь начинается самое интересное =):
Что бы начать работу через Прокси, мы должны послать запрос авторизации.
Выдержка из RFC 1928:
Клиент соединяется с сервером и посылает сообщение с номером версии и выбором соответствующего метода аутентификации:
+----+----------+----------+ |VER | NMETHODS | METHODS | +----+----------+----------+ | 1 | 1 | 1 to 255 | +----+----------+----------+
Значения для поля METHOD:
o X'00' аутентификация не требуется o X'01' GSSAPI o X'02' USERNAME/PASSWORD (см. RFC1929) o X'03' до X'7F' зарезервировано IANA o X'80' до X'FE' преднозначено для частных методов o X'FF' нет применимых методов
Поле NMETHODS содержит число октетов в идентификаторах методов авторизации в поле METHODS.
В нашем случае, авторизация не нужна, по этому, запрос будет выглядеть так:
0x05 0x01 0x00
в памяти:
.... .DATA
szNOAUTH db 05h, 01h, 00h ....
В ответ, Прокся пошлет нам всего 2 байта:
- 1 байт - версия протокола (0х05)
- 2 байт - выбранный метод авторизации (в нашем случае 0х00)
invoke send, hSocket, offset szNOAUTH, 3, 0 ;шлем данные
invoke recv, hSocket, addr szSocksRecvBuff, sizeof szSocksRecvBuff, 0 ;тут ответ
5. Далее мы должны послать запрос на соединение с "целевым хостом".
Под "целевым хостом" я подразумеваю конечный узел\ресурс, с
которым мы собственно и будем работать через упомянутый выше Прокси %)
Для примера я взял SMTP сервер Yandex'a, с которым будет происходить
обмен данными.
Снова выдержка из RFC 1928:
SOCKS-запрос формируется следующим образом:
+----+-----+-------+------+----------+----------+ |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+
Где:
o VER версия протокола: X'05' o CMD o CONNECT X'01' o BIND X'02' o UDP ASSOCIATE X'03' o RSV зарезервировано o ATYP тип адреса, следующего вида: o IP v4 адрес: X'01' o имя домена: X'03' o IP v6 адрес: X'04' o DST.ADDR требуемый адрес o DST.PORT требуемый порт (в сетевом порядке октетов)
Адресация
Тип адреса содержащегося в адресном поле (DST.ADDR, BND.ADDR), определяется содержимым поля ATYP:
o X'01'
адрес является адресом IP v4, длинна адреса 4 октета
o X'03'
поле адреса содержит имя домена. Первый октет адресного поля содержит число октетов в последующем за ним имени.
o X'04'
адрес является адресом IP v6, длинна адреса 16 октет
В нашем случае, нам нужно соединиться с узлом smtp.yandex.ru через порт 25.
Отсюда следует, что:
- тип соединения CONNECT (0х01)
- тип адреса - имя домена (0х03)
получаем запрос вида:
0х05 0х01 0х00 0х03 0х0E 'smtp.yandex.ru' 0х00 0х19 0х00
где:
- первые 4 байта Вам должны быть уже понтяны ;)
- пятый - 0х0Е это количество символов в имени "целевого хоста"
- далее само имя "целевого хоста"
- завершающий NUL - 0 (об этом скажу подробнее, в конце стати.)
- и последние 2 байта есть не что иное как порт 25, но только в сетевом порядке бит (как он получается догадайтесь сами ;) )
в памяти наш запрос выглядит так:
.... .DATA
szCONNECT db 05h,01h,00h,03h, sizeof nmSMTP-1, SMTPNAME,0,19h,00h ....
Теперь отсылаем все это серверу:
invoke send, hSocket, offset szCONNECT, sizeof szCONNECT, 0 invoke recv, hSocket, addr szSocksRecvBuff, 30, 0 ; тут ответ сервера
6. Получение ответа. В области памяти по имени szSocksRecvBuff, будет находится ответ сервера.
Выглядит он примерно так:
0х05 0х00 0х00 0х01 0х0A 0х0A 0х0A 0х64 0х20 0х04
Внимательно смотрим на 2-ой байт ! Если он НЕ РАВЕН нулю, значит произошла ошибка.. =(
За получением всех возможных вариантов..
..Обратимся к RFC 1928:
Сервер обрабатывает запрос и посылает ответ в следующей форме:
+----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable | 2 | +----+-----+-------+------+----------+----------+
Где:
o VER версия протокола: X'05' o REP код ответа: o X'00' успешный o X'01' ошибка SOCKS-сервера o X'02' соединение запрещено набором правил o X'03' сеть недоступна o X'04' хост недоступен o X'05' отказ в соединении o X'06' истечение TTL o X'07' команда не поддерживается o X'08' тип адреса не поддерживается o X'09' до X'FF' не определены o RSV зарезервирован o ATYP тип последующего адреса o IP v4 адрес: X'01' o имя домена: X'03' o IP v6 адрес: X'04' o BND.ADDR выданный сервером адрес o BND.PORT выданный сервером порт (в сетевом порядке октетов)
Значения зарезервированных (RSV) полей должны быть установлены в X'00'.
7. Ошибки нет ? Тогда нас можно поздравить, ведь теперь мы наконец можем работать черз Прокси =)
Что бы доказать это, мы пошлем SMTP серверу Yandex'a приветственное сообщение, и примем от него ответ.
.... .DATA szEHLO db 'HELO localhost', 13, 10
....
invoke recv, hSocket, addr szRecvTrashBuff, sizeof szRecvTrashBuff, 0 ;получаем приветствие яндекса
invoke send, hSocket, addr szEHLO, sizeof szEHLO, 0 ; посылаем наше приветствие..
invoke recv, hSocket, addr szRecvBuff, sizeof szRecvBuff, 0 ;..и получаем ответ.
Буфер szRecvBuff содержит такой ответ:
250 smtp16.yandex.ru Hello localhost
То же самое Вы можете проделать используя утилиту telnet:
telnet smtp.yandex.ru 25
HELO localhost
8. По завершении работ вызываем WinApi функции closesocket и WSACleanup:
invoke closesocket, hSocket invoke WSACleanup
Хочу обратить Ваше внимание на добавление нулевого октета в запросе,
после имени "целевого хоста".. В оригинальном рфц написанно:
"The first octet of the address field contains the number of octets of name that
follow, there is no terminating NUL octet."
|