ZF

             Введение в 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

Конец приложения



(C) NF, 1998-2004