Введение в Micro$oft Windows Sockets
by Bumblebee/29a
*************************** перевод by DrMad **************************
Оглавление
1. Введение
2. Что такое сокеты Windows?
3. Расширения от Micro$oft
4. Шаг-за-шагом: подключаемся
5. Чтение и запись
Приложение: wsocks.inc
1. Ведение
Это пособие не ориентировано на вирусы. Оно дает обзор варианта
сокетов Беркли, реализованного в Win32. Это поможет вам выполнить
удаленное соединение вашего вируса и, например, послать мыло. Это
просто описание небольшого кусочка сетевого вируса I-Worm.Anaphylaxis.
2. Что такое сокеты Windows?
Спецификация сокетов Windows описывает интерфейс Microsoft Windows
к сети, котороый основан на концепции "сокетов", ставшей известной
благодаря Berkeley Software Distribution (BSD). Они содержат как
процедуры, воспроизводящие сокеты Беркли, так и некоторые
Windows-специфические расширения, позволяющие программисту использовать
"преимущества" ориентированной на сообщения архитектуры Windows.
Так что это за фигня - сокеты? Идея BSD - это файлы, расположенные
на удаленной машине. Сокеты использовались в BSD как обычные файлы. Вы
можете использовать обычные файловые 'write' и 'read' для записи и
чтения через сокеты. Но MS изменили этот уровень абстракции и дали вам
специальные функции, чтобы вы не забывали почаще использовать их
поганый API.
3. Расширения Microsoft
Вот функции. которые есть у M$, и которых не было у Беркли:
WSAAsyncGetHostByAddr()
WSAAsyncGetHostByName()
WSAAsyncGetProtoByName()
WSAAsyncGetProtoByNumber()
WSAAsyncGetServByName()
WSAAsyncGetServByPort()
WSAAsyncSelect()
WSACancelAsyncRequest()
WSACancelBlockingCall()
*WSACleanup()
WSAGetLastError()
WSAIsBlocking()
WSASetBlockingHook()
WSASetLastError()
*WSAStartup()
WSAUnhookBlockingHook()
M$ снабжает этим дерьмом, чтобы работать в условиях
многозадачности. Что произойдет, если App ждет установления связи?
Установленная связь требует, чтобы App постоянно проверяла готовность.
Таким образом, App не может принимать сообщения, посылаемые окнами.
Упомянутые функции снабжают неблокируемой работой. Нам особо интересны
функции с '*'. Другие не нужны, потому что я собираюсь использовать
функции в стиле BSD (BSD=UNIX=LINUX=rulez!).
4. Шаг-за-шагом: подключаемся
Первый шаг - проинициализировать wsocks на компе. Мы собираемся
использовать wsock 1.1, так что проверки придется делать при помощи
WSAStartup.
push offset wsadata ; структура WSADATA
push VERSION1_1 ; нам нужна версия 1.1
call WSAStartup ; проверить установку wsocks
cmp eax,0 ; on error
jne qApp ; exit app
mov ax,VERSION1_1 ; WSAStartup возвращает версию
cmp ax,word ptr [wsadata.mVersion] ; проверить версию
jne exitAppQsocks ; закрыть сокеты и выйти
WSAStartup требует двух аргументов: указателя на структуру WSDATA и
требуемую версию. Эта API-шка возвращает в поле .mVwrsion доступный
номер версии, который вы можете сравнить с желаемым. Кроме того,
WSAStartup предупреждает Windows, что вы собираетесь использовать
wsocks. Даже если версия вас не устраивает, то все равно надо
использовать WSACleanup.
call WSACleanup ; завершить работу с сокетами
Допустим, у нас подходящая версия. Теперь нам нужно открыть сокет.
push PCL_NONE ; протокол
push SOCK_STREAM ; тип сокета
push AF_INET ; семейство
call socket ; открыть сокет
cmp eax,SOCKET_ERR ; on error
je doCleanUp ; WSACleanup
Функция socket требует 3 аргументов: протокола, типа и семейства.
Протокол может быть установлен, но об этом может узнать система
безопасности Windows. Пример: нам нужно сконнектиться с telnet-ом. Если
мы установим протокол в telnet-овское состояние и Windows не разрешит
этот протокол, то работа с сокетами обломится. Поэтому лучше
устанавливать неопределенный протокол.
Тип сокета может быть: STREAM или DATAGRAM. Первый ориентирован на
установление связи и повторные посылки пакетов, которые могут приходить
к получателю в произвольном порядке. Более того, посылающая сторона не
сможет узнать, дошел ли пакет, без помощи со стороны получателя.
Последнее - семейство, которое может быть: AI_UNIX, AF_INET... но
только адреса AF_INET поддерживаются в той версии, которую я использую.
Мы используем PCL_NONE, SOCK_STREAM и AF_INET. Функции сокетов
возвращают SOCKET_ERR (-1), если вызов обломился и хэндл сокета, если
все пучком.
Последний шаг - установить связь. Теоретически, мы связываемся с
сокетом, который используется для управления связью с удаленной
машиной. Первое, что нам надо, это заполнить структуру SOCKADDR (это
модифицированная структура: я склеил различные структуры вместе, т.к,
мы собираемся использовать только AF_INET).
SOCKADDR struct
sin_family dw 0 ; ever AF_INET
sin_port dw 0 ; the port
sin_addr dd 0 ; addr of server node
sin_zero db 8 dup(0) ; not used
SOCKADDR ends
Поле sin_family легко заполнить, но sin_port и sin_addr - гораздо
сложнее. Поле sin_port - это порт, к которому мы собираемся
приконнектиться. Но это число должно быть побайтно упорядочено в
соответствии с сетевыми соглашениями. Для этого сокеты поддерживают
htons:
push PORT ; номер порта
call htons ; получить номер порта в сетевом
mov word ptr [sockaddr.sin_port],ax ; порядке байтов
Функция htons берет порт и возвращает слово с упорядоченными
по сетевому стандарту байтами.
Поле sin_addr более сложно. Нам для связи нужны addr и host. Это
число, которое идентифицирует узел. Обычно мы имеем нечто вроде
'domain.ext' (например, IBM.com, netscape.com,...). Но мы должны
получить IP-адрес (xxx.xxx.xxx.xxx), который разделен на группы
точками.
push offset server ; адрес строки ('oeee.net')
call gethostbyname ; берем структуру hostent
cmp eax,0 ; on error:
je exitQsocksC ; закрываем сокет и отваливаем
; eax содержит указатель на HOSTENT
mov eax,dword ptr [eax+HOSTENT_IP] ; берем структуру с IP
mov eax,dword ptr [eax] ; берем собственно IP
mov dword ptr [sockaddr.sin_addr],eax ; вот и все!
push sizeOfSockaddr ; размер структуры sockaddr
push offset sockaddr ; адрес sockaddr
push dword ptr [fd] ; хэндл сокета socket
call connect ; установить связь!
cmp ax,SOCKET_ERR ; on error:
je exitQsocksC ; закрыть сокет и отвалить
Этот пример очень понятен: мы берем HOSTENT, которая содержит в
поле HOSTENT_IP адрес IP для узла. Затем мы заполняем sin_addr и
sockaddr, и после этого все готово для связи. Установка связи требует:
размер структуры SOCKADDR (для меня это константа), указатель на
структуру SOCKADDR и хэндл сокета.
Вот и все, нам осталось закрыть сокет при помощи closesocket.
push dword ptr [fd] ; хэнд сокета
call closesocket
5. Чтение и запись
Micro$офт снабжает различными API для чтения и записи, но мы будем
использовать: send и recv.
push 0 ; это так и надо (может быть также OOBD)
push ecx ; размер посылаемого сообщения
push esi ; адрес сообщения
push eax ; жэнлл сокета
call send
push 0 ; это так и надо
push 4 ; размер принимаемого сообщения
push offset response ; адрес принимемого сообщения
push eax ; хэндл сокета
call recv
Функция send работает и возвращает те же ошибки, что и _lwrite и то
же самое происходит с recv и _lread.
Функции sens и recv - синхронные. Это означает, что если вы
посылаете или принимаете данные и нет данных для приема/передачи, то
сокеты блокируют приложение до тех пор, пока данные не будут доступны,
или связь не оборвется, или процесс не закончится.
Мы создаем поток и поток устанавливает связь и принимает/посылает
сообщения (выполняет обмен данными). Главный процесс, который создал
этот поток, просто наблюдает за этим. Но если поток находится в
состоянии ожидания, время истекает. Тогда главный поток завершает
поток и продлжает работу.
Вот и все, пацаны!
ПРИЛОЖЕНИЕ: wsocks.inc
;
; WSocks.inc: включаемый файл для windows sockets.
; Разрабтоан для TASM5 и Win32.
;
; (C) 1999 Bumblebee.
;
; Этот файл содержит основные структуры и функции для рабты с
; windows sockets.
; Описания API:
; аргументы - в порядке PUSH-ей
; Только для отладки
extrn WSAGetLastError:PROC
; начинает использование winsock.dll
; addr типа WSADATA, запрашиваемая версия version
; возвращает: 0, если все нормально
extrn WSAStartup:PROC
; завершает использование winsock.dll
; возвращает: SOCK_ERR при ошибке
extrn WSACleanup:PROC
; открывает новыйц сокет
; протокол (PCL_NONE), тип (SOCK_??), формат адреса (AF_??)
; возвращает: идентификатор сокета или SOCKET_ERR (это 16 бит)
extrn socket:PROC
; закрывает сокет
; дескриптор сокета
;
extrn closesocket:PROC
; посылает даные (это просто дерьмо... Unix использует просто write)
; флаги ( 1 OOB data или 0 - норма), длина, адрес буфера, сокет
; возвращает: посланные символы или SOCKET_ERR при ошибке
extrn send:PROC
; принимает даные (это просто дерьмо... Unix использует просто read)
; флаги ( исарльзуйте 0), длина, адрес буфера, сокет
; возвращает: принятые символы или SOCKET_ERR при ошибке
extrn recv:PROC
; коннектится с сервером
; размер структуры SOCKADDR, структура SOCKADDR, сокет
; возвращает: SOCKET_ERR при ошибке
extrn connect:PROC
; принимает данные текущего хоста
; длина буфера под имя, адрес буфера под имя
; возвращает: SOCKET_ERR при ошибке
extrn gethostname:PROC
; принимает структуру hostent
; адрес имени
; возвращает: указатель на структуру или 0 при ошибке
extrn gethostbyname:PROC
; конвертирует строку вида "xxx.xxx.xxx.xxx" в новый порядок байтов
; указатель на строку
; возвращает: in_addr (32 бита)
extrn inet_addr:PROC
; конвертирует двойное слово в новый порядок байтов (обычно это port)
; возвращает: слово с сетевым порядком байтов
extrn htons:PROC
; Структуры :o
; структура sockaddr для связи
; модифицирована (для более удобного использования)
; если хотите оригинал, смотрите в winsock.h
SOCKADDR struct
sin_family dw 0 ; ex. AF_INET
sin_port dw 0 ; используйте для этого htons
sin_addr dd 0 ; сюда попадает сервер (с указанным адресом)
sin_zero db 8 dup(0)
SOCKADDR ends
; для диагностики WSAStartup
WSADATA struct
mVersion dw 0
mHighVersion dw 0
szDescription db 257 dup(0)
szSystemStatus db 129 dup(0)
iMaxSockets dw 0
iMaxUpdDg dw 0
lpVendorInfo dd 0
WSADATA ends
; Некоторые нужные константы
; какая версия winsocks нам нужна? (usually 1.1)
VERSION1_0 equ 0100h
VERSION1_1 equ 0101h
VERSION2_0 equ 0200h
AF_UNIX equ 1 ; локальный хост
AF_INET equ 2 ; интернет (используется чаще всего)
AF_IMPLINK equ 3 ; арпанет
AF_NETBIOS equ 17 ; адреса в стиле NetBios
; типы сокетов
SOCK_STREAM equ 1 ; поток (ориентирован на связь; в стиле telnet)
SOCK_DGRAM equ 2 ; дейтаграмма (пакеты, пакеты, пакеты)
; protocol
PCL_NONE equ 0 ; none (неопределенный протокол)
SOCKET_ERR equ -1 ; стандартная ошибка winsock
HOSTENT_IP equ 10h ; сесто в структуре hostent для IP
Конец приложения
|