Введение в 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 Конец приложения |