Статья ASM для x86. (5.4) Протокол ARP

Тропа повествований привела меня к протоколу самого нижнего уровня из всех – протокол преобразования сетевых адресов ARP. Этот мир прикрыт железным куполом контролёра сетевой карты и большинству рядовых пользователей просто не нужен – пусть с ним разбираются инженеры. Но мы не входим в число рядовых, поэтому рассмотрим его с точки зрения не обывателей, а Low-Level программиста.

На самом деле, чем дальше мы по стеку отдаляемся от физических устройств типа кабель и сетевая карта, тем больше погружаемся в логическую абстракцию виртуальной памяти системы, которая уводит от реальной действительности. Например, вот как выглядит структурная схема типичного сетевого адаптера.. Он состоит из двух модулей: PHY (Physical, физический) и МАС (Medium Access Control) – их связывает независимый интерфейс MII или Media Independent Interface:

nic.png


За исключением некоторых нюансов, модуль PHY не доступен для программиста и работает на аппаратном уровне. Главная его задача – обеспечить качественный доступ к линии передачи. Нужно сказать, что дело это не из простых и требует кодирования бинарного потока данных таким образом, чтобы максимально уменьшить в нём кол-во единиц, не потеряв при этом информации. В противном случае, даже на частоте 100 Мбит/сек (100Base-TX) частые всплески единиц превратят линию передачи в антенну, которая будет создавать помехи себе и окружающим.

Поэтому модулю PHY приходится напречь свои ягодицы, трижды кодируя один поток различными методами. В древних 10 Мбитных сетях "10Base-T" применялся только манчестерский код и этого было достаточно. С появлением 100Base-TX к нему добавили кодеры NRZI + MLT3 (двуполярная дифференциальная передача +/0/-, что снижает размах сигнала на линии), а гигабитные адаптеры 1000Base-FX кодируют фреймы ещё более продвинутым алгоритмом РАМ-5. Одним словом, чем дальше – тем страшнее:

NRZI.png


В отличии от PHY, модуль МАС расположен ближе к оперативной памяти и подобраться к нему на программном уровне проще - он управляет массовым доступом к линии передачи. В своей тушке, МАС имеет клиента LLC - "Logical Link Control", который управляет логическими связями. Блок логики контролёра нельзя недооценивать – это мозг сетевой карты со своими регистрами среди которых выделяются: DA – регистры данных/адреса, Control – регистр управления и Status – информационный регистр (и по совместительству регистр ошибок). Доступ к регистрам контролёра осуществляется через конфигурационное пространство PCI.

• В модуле МАС, у стучащихся в карту фреймов проверяется контрольная сумма и если логика обнаружит несовпадение, контролёр отбрасывает пакет или генерит прерывание процессору об ошибке. Посредством прерываний сигнализируются и факты окончания приёма/передачи данных, чтобы процессор знал, когда можно забрать пакет у карты.

• МАС-контролёр имеет встроенный FIFO-буфер (First-In-First-Out – первым зашёл, первым выйдешь) для хранения принимаемых с уровня IP пакетов данных. Это 48-Кбайтный "Пинг-Понг" буфер, когда один и тот-же буфф используется и для приёма и для передачи, переключаясь в зависимости от режима работы.

• Когда карта работает в режиме передачи данных Transmit, блок фильтра добавляет в пакеты Ethernet-заголовок в виде: МАС-адресов отправителя/получателя, типа кадра, и его контрольной суммы – этот процесс известен как "инкапсуляция канального уровня". Именно на этом тёплом уровне беззаботно живёт протокол ARP, который всего-то меняет значение поля Type в заголовке на 0x0806, вместо дефолтных 0x0800. Заголовок Ethernet-кадра и возможные варианты его поля Type перечислены ниже (см.инклуд):

eth.png


• Манипулируя регистром управления контролёра, сетевую карту можно перевести в неразборчивый режим "Promiscuous". В этом режиме фильтр-пакетов в LLC уже не проверяет MAC-адрес получателя, что позволяет собирать пакеты адресуемые не только нашему узлу, но и чужие. Эту фишку взяли на вооружение воины тёмных сил типа: Wireshark, Nmap, WinDump, etc. В следующей части мы попробуем написать свой сниффер, тогда и рассмотрим promisc подробней. Из доков на эту тему, можно полистать

В современной архитектуре материнских плат, шина PCI является основной транспортной магистралью, на которой висят буквально все физические девайсы. Для каждого PCI-устройства, система выделяет своё конфигурационное CFG-пространство в памяти размером мин 256 байт, и макс 4 Кб. Таким образом, каждый контролёр имеет свой базовый адрес в памяти, на который в cfg указывает его BARBase Address Register. Получив базу любого из контролёров, мы можем управлять его регистрами с одним ограничением – нужно спуститься на уровень драйверов, т.к. юзеру туда дорога закрыта.


5.4.0. Что такое МАС-адрес сетевого адаптера

Начиная с транспортного уровня(2) стека TCP/IP, все остальные уровни находятся в оперативной памяти компьютера – это протоколы IP, ICMP, IGMP, TCP, UDP и как саранча заселившая прикладной уровень(4) протоколы типа http, telnet, etc.. К прикладным протоколам мы подбираемся через их порты (макс. 65535 портов, а значит столько-же возможных протоколов). Однако на следующем уровне TCP/IP фигирирует IP-адрес и порты уходят уже на второй план. В свою очередь, когда приходит время передать пакет по физ.линии связи, теперь уже IP-адрес отправляется на скамейку запасных и системным механизмам требуется исключительно МАС-адрес конкретной сетевой карты.

МАС зашит в ПЗУ контролёра сети и представляет из себя уникальный номер, который присвоил данному контролёру, его производитель. Он кодируется шестью байтами (48-бит), где старшие три определяют производителя, и младшие три – порядковой номер изделия. В настоящее время, распределением физических адресов занимается международная организация IEEE - она и присваивает каждому производителю свой 24-битный код OUI (Organization Unique Identifier). Оставшиеся 24 бита МАС-адреса, производитель может использовать в качестве порядкового номера своего контролёра.

Трёх-байтный код OUI позволяет назначить ID одному из 16.777.215 производителей (значение 0xFFFFFF в десятичном). И соответственно каждый производитель может изготовить столько-же своих контролёров. Когда разработчик исчерпает этот лимит, он сообщает об этом в IEEE, и зарегистрировав у себя, она выделяет производителю новый код из своего резерва. В сети можно найти , в которых перечислены коды OUI производителей. Пример из этой базы для моего МАС-адреса выглядит так:

C-подобный:
00-19-66   Asiarock Technology Limited
           P.O. Box957, Offshore Incorporations Centre
           Road Town Tortola VG

Довольно быстро производители пришли к выводу, что организация IEEE берёт завышенную мзду за свои услуги. Надвигалась буря и чтобы оправдать доверие, IEEE отдаёт два бита из своего OUI в распоряжение протокола канального уровня(1). Теперь это "управляющие биты" и в старшей половине МАС-адреса под ID производителя осталось только 22, из 24-х бит:

mac.png


• Бит(0) старшего байта является признаком или "индивидуального", или "группового" адреса I/G – нуль означает индивидуальный Unicast-адрес. Если-же он равен единице, значит кадр адресуется группе узлов – это т.н. Multicast (групповая рассылка). Если все 24-бита OUI заполнены единицами, то получателями являются все станции текущей сети – такую адресацию назвали Broadcast или широковещательной.

• Бит(1) структуры OUI назвали U/L. Этот маяк зажигается, когда аппаратный адрес карты задаёт сам администратор сети вручную (бывает и такое). Но в большинстве случаях он сброшен, что говорит о назначении МАС-адреса организацией IEEE.

Кстати судя по докам, в природе имеется два вида представления МАС-адреса – канонический (байты резделяются чертой, что характерно для Win), и бит-реверсный (байты разделяются двоеточием). Последний присущь Unix-системам, и тогда два управляющих бита становятся старшими в байте. Другими словами, одна и та-же карта в Win будет иметь один МАС, и она-же установленная в Unix будет иметь этот-же МАС, только в бит-реверсном виде:

C-подобный:
0000.1000 – 0000.0000 – 0010.0111 – 0000.0000 - 0001.0100 – 1101.0101  = 08-00-27-00-14-D5 (Win)
0001.0000 : 0000.0000 : 1110.0100 : 0000.0000 : 0010.1000 : 1010.1011  = 10:00:E4:00:28:AB (Unix)

broadcast.png



5.4.1. Описание протокола ARP

Ну теперь ближе к делу…
Протокол ARP занимается тем, что по IP-адресу удалённого узла, вычисляет его МАС-адрес. Такое преобразование требуется когда пакет поступает с транспортного уровня(2) стека протоколов, на канальный уровень(1). В этот момент, протокол ARP посылает в сеть широковещательный "Broadcast запрос", в котором прописан только IP-адрес получателя. Все узлы сети получают этот запрос (см.рис.выше), и сравнивают свои IP с тем, что указан в запросе. Если IP не совпал, узел тупо игнорирует пакет, а если совпал – добавляет в него-же свой МАС-адрес, и возвращает его отправителю:

arp.png


На рисунке, красным выделены поля, которые модифицируются в запросе. Удалённый узел меняет только код операции с единицы на 2, и вставляет в полученный запрос свой МАС. Отправитель анализирует ответ и по коду(2) делает вывод, что он валидный.

Теперь этот МАС заносится в ARP-таблицу и хранится в ней всего 10 минут (на винде). Если в течении этого времени к удалённому узлу обращений больше не было, его МАС из таблицы отправляется прямиком в топку (коммутаторы и маршрутизаторы хранят записи в ARP-таблицах дольше.. около 30 минут). ARP-запросы не могут маршрутизироваться, т.е. никогда не выходят за пределы маршрутизатора своей сети. Если запрашиваемый хост находится в удалённой сети, отправителю нужно узнать сначала аппаратный адрес порта маршрутизатора, и только потом выйти во-внешнюю сеть.


5.4.2. Практика – собираем свою ARP-таблицу

Под виндой, как и в случае с протоколом ICMP, сетевые сокеты на этом уровне не доступны, и мастдай предлагает нам альтернативу в виде нескольких API из той-же библиотеки iphlpapi.dll - здесь нужно выделить функцию SendARP(). Она отличается от остальным тем, что именно посылает широковещательный запрос, а не читает готовые данные из ARP-таблицы. Посмотрим на её прототип:

C-подобный:
SendARP (
  DestIP          ; IP-адрес получателя
  SrcIP           ; IP-адрес отправителя
  pMacAddr        ; приёмный буфер под МАС
  PhyAddrLen      ; его длина (мин 8 байт)
);

Как видим, функция простая.. Фиктивный source адрес-отправителя подставлять в неё мы не сможем – всё равно Win запихает туда валидный наш IP, вычислив его на автомате. Поэтому можно установить этот аргумент хоть в ноль. А вот IP-получателя нужно будет задать явно, используя функцию Winsock gethostbyname(). Но это скучно, поэтому мы пойдём другим путём и напишем небольшой ARP-спуфер.

Суть в том, что как упоминалось выше, ARP-запросы не покидают пределов своей сети. Поэтому мы узнаем свой IP и обнулив последний его байт, получим наименьший IP своей сети, т.е. применим к нему маску. Теперь начиная с нуля и до самого подвала(255) можем в цикле спуфить ARP-запросами свою сеть, и если узел с очередным IP обнаружится, то клюнет на запрос и вернёт нам свой МАС. Пример реализации подобного рода софтины, представлен ниже:

C-подобный:
format   pe console
entry    start
include 'win32ax.inc'
;----------
.data
wsa          WSADATA
capt         db  13,10,' ARP example v0.1'
             db  13,10,' **********************'
             db  13,10,' MAC database..',13,10,0
ipMac        db  13,10,' Host-> %s   MAC->  %02X-%02X-%02X-%02X-%02X-%02X',0
succes       db  13,10
             db  13,10,' ~~~~~~'
             db  13,10,' ARP successful..'
             db  13,10,' Press any key for exit...',0

frmt         db  '%s',0          ; для scanf()
host         dd  0               ; 4 байта для хранения IP-адреса
buff         db  32 dup(0)       ; под строку с IP-адресом (он-же приёмный буфф)
bSize        dd  8               ; длина буфера
count        dd  6               ; счётчик ARP-запросов
;----------
.code
start:
         invoke  WSAStartup,0x0101,wsa
        cinvoke  printf,capt

;//---- Вычисляем свой IP-адрес ---------
         invoke  gethostname,buff,32  ; возвращает локальное имя узла
         invoke  gethostbyname,buff   ; возвращает адрес структуры "hostent"
         mov     eax,[eax+12]         ;
         mov     eax,[eax]            ;
         mov     eax,[eax]            ; EAX = наш IP-адрес
         mov     [host],eax           ; запомнить его..

;//---- Циклический ARP-спуфинг ---------
@mac:    mov     eax,[host]           ; берём IP назначения
         push    eax                  ; +
         invoke  SendARP,eax,0,buff,bSize   ;// отправляем ARP-запрос!

;//---- Вывод инфы об очередном IP/МАС ---------
;// функция SendARP() сбросила МАС клиента в наш буфер
         pop     eax                  ; -
         invoke  inet_ntoa,eax        ; IP в строку
         push    eax                  ; запомнить указатель на неё

         xor     eax,eax              ; МАС из буфера в строку
         mov     esi,buff             ; адрес буфа
         lodsb                        ; берём от туда первый байт
         mov     ebx,eax              ; запоминаем в EABX
         lodsb                        ; второй байт..
         mov     ecx,eax              ;
         lodsb                        ; третий..
         mov     edx,eax              ;
         lodsb                        ; червёртый
         mov     edi,eax              ;
         lodsb                        ; пятый..
         mov     ebp,eax              ;
         lodsb                        ; и шестой
         mov     esi,eax              ;

; Вывести всю бодягу на консоль
         pop     eax
        cinvoke  printf,ipMac,eax,ebx,ecx,edx,edi,ebp,esi

;//---- Очистить 8 байтный буфф для следующего мак'а ---
         mov     ecx,2          ; повторов
         mov     edi,buff       ; что очищаем
         xor     eax,eax        ; чем забиваем (нуль)
         rep     stosd          ; из EAX в EDI, ECX-раз..

;//---- Меняем IP на следующий ---------
         rol     [host],8       ; прокрутить влево на 8 бит
         inc     [host]         ; увеличить на 1 мл.байт
         ror     [host],8       ; восстановить порядок вправо на 8 бит
         mov     [bSize],8      ; восстановить размер приёмного буфера
         dec     [count]        ; уменьшить счётчик повторов цикла
         jnz     @mac           ; повторить, если счётчик не нуль..

;//---- GAME over !!! ---------//
        cinvoke  printf,succes        ; мессага ОК!
@exit:  cinvoke  scanf,frmt,frmt+5    ; ждём клаву..
         invoke  ExitProcess,0        ; на выход!

;//--- Секция импорта  ------
section '.idata' import data readable     ;
library  kernel32,'kernel32.dll',\        ; импортируемые библиотеки
         user32,'user32.dll',\
         wsock32,'wsock32.dll',\          ;  ^^^
         msvcrt,'msvcrt.dll',\            ;    ^^^
         iphlp,'iphlpapi.dll'             ;      ^^^

import   iphlp,SendARP,'SendARP'
import   msvcrt, printf,'printf',scanf,'scanf'
include 'api\kernel32.inc'
include 'api\user32.inc'
include 'api\wsock32.inc'

arpReq.png


Плюс такого способа в том, что мы сможем получить МАС-адреса буквально всех участников в своей сети. Например в ARP-таблице и кэш нет аппаратных адресов тех узлов, к которым мы ещё ни разу не обращались – эта таблица заполняется динамически, по конкретным запросам и они хранятся там всего 10 минут. А в предложенном способе таблица уже не нужна, т.к. мы пелингуем каждый узел индивидуально. Для работы с таблицами есть свои функции из той-же iphlpapi.dll – вот из список и назначение:
  1. GetIpNetTable() извлекает таблицу ARP на локальном компьютере.
  2. CreateIpNetEntry() создает запись ARP в таблице.
  3. DeleteIpNetEntry() удаляет запись ARP из таблицы.
  4. SetIpNetEntry() изменяет существующую запись ARP.
  5. FlushIpNetTable() удаляет все записи ARP для указанного интерфейса из таблицы.

5.4.3. Практика – собираем информацию об активных адаптерах

Бывают ситуации, когда нужно получить сведения обо всех имеющихся интерфейсах локальной машины (паспорт и данные сетевого адаптера), например в случае, если хотим прослушивать трафик конкретной сети, если их несколько. Здесь можно воспользоваться функцией GetAdaptersInfo() опять из той-же либы iphlpapi.dll. По сравнению с предыдущей SendARP() эта функция сложней, зато и возвращает намного больше информации, включая и МАС-адрес выбранного интерфейса:

C-подобный:
GetAdaptersInfo (
   AdapterInfo     ; адрес приёмного буфера
   SizePointer     ; адрес переменной с размером этого буфера
);

Буфером для неё является структура IP_ADAPTER_INFO, которая имеет вложенную структуру IP_ADDR_STRING для хранения назначенного адаптеру IP-адреса в текстовом виде. Если посчитать кол-во символов в строке с макс. IPv4-адресом 255.255.255.255, то она занимает ровно один 16-байтный параграф памяти. Для хранения такой строки и ввели структуру IP_ADDR_STRING – вот её члены:

C-подобный:
struct IP_ADDR_STRING
    Next         dd  0          ; указатель на сл.адрес
    IpAddress    db  16 dup(0)  ; строка с IP-адресом
    IpMask       db  16 dup(0)  ; строка с маской сети
    Context      dd  0          ; запись сетевой таблицы NTE
ends

Ого.. Да сюда не только айпишник сбрасывается, а бонусум ещё и маска подсети – как раз то-что доктор прописал. Одному адаптеру может быть назначено несколько IP-адресов, поэтому структура имеет поле "Next", в котором лежит указатель на следующую строку с IP-адресом (т.н. список связей). Если IP один, то в поле next будет лежать дырка от бублика. Но это только вложенная структура.. Теперь посмотрим на основную:

C-подобный:
struct IP_ADAPTER_INFO           ;// Всего 640 байт
  Next           dd  0             ; указатель на сл.структуру в цепочке
  ComboIndex     dd  0             ; резерв..
  AdapterName    db  260 dup(0)    ; UID адаптера (регистрационное число для реестра)
  Description    db  132 dup(0)    ; имя адаптера в текстовом виде
  AddressLength  dd  0             ; длина МАС-адреса в байта =6
  Address        db  8 dup(0)      ; МАС-адрес адаптера
  Index          dd  0             ; индекс (меняется при вкл/выкл)
  Type           dd  0
  DhcpEnabled    dd  0
  CurrentIpAddr  dd  0                  ; резерв..
  IpAddressList      IP_ADDR_STRING     ; вложеные структуры
  GatewayList        IP_ADDR_STRING     ;
  DhcpServer         IP_ADDR_STRING     ;
  HaveWins       dd  0                  ; флаг активности WINS
  PriWinsServer      IP_ADDR_STRING     ;
  SecWinsServer      IP_ADDR_STRING     ;
  LeaseObtained  dd  0,0                ; структура "time_t" 8-байт
  LeaseExpires   dd  0,0                ; ^^^^^
ends

Судя по содержимому, здесь уже есть где развернуться..
Тут и мак, и айпи, и шлюз, и dhcp-сервер и ещё куча всего – нормальный куш..
Чтобы не забивать пространство исходника, я не буду выводить все поля структуры, а сделаю упор только на основные. Если что не понятно, можете спросить у всё-знающего MSDN, или оставить вопрос в этой теме. Вот исходник и результат его работы на моём узле:

C-подобный:
format   pe console
entry    start
include 'win32ax.inc'
;----------
.data
struct IP_ADDR_STRING
    Next         dd  0          ; указатель на сл.адрес
    IpAddress    db  16 dup(0)  ; строка с IP-адресом
    IpMask       db  16 dup(0)  ; строка с маской сети
    Context      dd  0          ; запись сетевой таблицы NTE
ends

struct IP_ADAPTER_INFO        ;// Всего 640 байт
  Next           dd  0          ; указатель на сл.структуру в цепочке
  ComboIndex     dd  0          ; резерв..
  AdapterName    db  260 dup(0)    ; UID адаптера
  Description    db  132 dup(0)    ; имя адаптера
  AddressLength  dd  0             ; длина МАС-адреса в байта =6
  Address        db  8 dup(0)      ; МАС-адрес адаптера
  Index          dd  0             ; индекс (меняется при вкл/выкл)
  Type           dd  0
  DhcpEnabled    dd  0
  CurrentIpAddr  dd  0                  ; резерв..
  IpAddressList      IP_ADDR_STRING     ; вложеные структуры
  GatewayList        IP_ADDR_STRING     ;
  DhcpServer         IP_ADDR_STRING     ;
  HaveWins       dd  0                  ; флаг активности WINS
  PriWinsServer      IP_ADDR_STRING     ;
  SecWinsServer      IP_ADDR_STRING     ;
  LeaseObtained  dd  0,0                ; структура "time_t" 8-байт
  LeaseExpires   dd  0,0                ; ^^^^^
ends

capt     db  13,10,' Adapters info v0.1'
         db  13,10,' **********************',0
nicName  db  13,10
         db  13,10,' Name....: %s',0
nicUin   db  13,10,' UIName..: %s',0
nicMac   db  13,10,' MAC.....: ',0
nicIp    db  13,10,' Addr....: %s',0
nicMask  db  13,10,' Mask....: %s',0
nicGtw   db  13,10,' Gateway.: %s',0

succes   db  13,10
         db  13,10,' ~~~~~~'
         db  13,10,' Operation successful'
         db  13,10,' Press any key for exit...',0

flag     db  1                 ; флаг последней структуры в списке
size     dd  3072              ; размер приёмного буфера 3 Кб
len      dd  0                 ; будет смещением на сл.структуру
addrStr  IP_ADDR_STRING        ; определяем вложеную структуру
align    16                    ; выравнивание буфера на параграф
buff     IP_ADAPTER_INFO       ; приёмный буфер! (до конца секции-данных)
;----------
.code
start:
;//--- Зовём на помощь функцию! ----------
;// она возвращает инфу сразу обо всех активных адаптерах
        cinvoke  printf,capt
         invoke  GetAdaptersInfo,buff,size

;//--- Цикл вывода информации на экран ---
@printInfo:
         mov     esi,buff                ; адрес буффа
         add     esi,[len]               ; прибавить текущее смещение
         cmp     dword[esi],0            ; проверить поле "Next" на нуль
         jnz     @next                   ; пропустить, если не нуль
         dec     [flag]                  ; значит последняя структура - сбросить флаг

@next:   mov     esi,buff.Description    ; берём имя адаптера
         add     esi,[len]               ; + текущее смещение
        cinvoke  printf,nicName,esi      ; на консоль его!

         mov     esi,buff.AdapterName    ; UIN адаптера
         add     esi,[len]               ;
        cinvoke  printf,nicUin,esi       ;

        cinvoke  printf,nicMac           ; МАС выводить будем в цикле.
         mov     esi,buff.Address        ; указатель на него
         add     esi,[len]               ;
         mov     ecx,6                   ; длина в байтах
         and     eax,0                   ; EAX = 0 (сбросить все биты)
@mac:    lodsb                           ; AL = очередной байт из ESI
         push    eax ecx esi             ; + + + printf() портит все регистры
        cinvoke  printf,<'%02X:',0>,eax  ; вывести очередной байт МАС'a
         pop     esi ecx eax             ; - - -
         loop    @mac                    ; промотать ECX-раз..

         mov     esi,buff.IpAddressList.IpAddress    ; IP через вложеную структуру
         add     esi,[len]                           ;
        cinvoke  printf,nicIp,esi                    ;
         mov     esi,buff.IpAddressList.IpMask       ; маска подсети от туда-же
         add     esi,[len]                           ;
        cinvoke  printf,nicMask,esi                  ;

         mov     esi,buff.GatewayList.IpAddress      ; шлюз по-умолчанию
         add     esi,[len]                           ;
        cinvoke  printf,nicGtw,esi                   ;

;//--- Перемещаем указатель на сл.структуру "IP_ADAPTER_INFO" в буфере ---
         add     [len],640         ; прибавить к смещению длину "IP_ADAPTER_INFO"
         cmp     [flag],0          ; проверить флаг на False
         jnz     @printInfo        ; повторить, если не нуль..

;//--- Конец программы -----------
@exit:  cinvoke  printf,succes
        cinvoke  scanf,nicIp,nicIp      ; ждём клаву..
         invoke  ExitProcess,0          ; на выход!

;//****************************************
;//--- Секция импорта ---------------------
;//****************************************
section '.idata' import data readable     ;
library  kernel32,'kernel32.dll',\        ; импортируемые библиотеки
         user32,'user32.dll',\            ; ^^^
         wsock32,'wsock32.dll',\          ;   ^^^
         msvcrt,'msvcrt.dll',\            ;     ^^^
         iphlp,'iphlpapi.dll'             ;       ^^^

import   iphlp,GetAdaptersInfo,'GetAdaptersInfo'
import   msvcrt, printf,'printf',scanf,'scanf'
include 'api\kernel32.inc'
include 'api\user32.inc'
include 'api\wsock32.inc'

getAdapt.png


В следующей части поговорим о том, какие возможности предоставляют сырые RAW-сокеты под виндой, на чём и закончим обзор протоколов стека TCP/IP. В операционной системе имеются гораздо более интересные механизмы для низкоуровневых программистов ассемблера, чем сеть – их и подвергнем трепанации, медленно но верно продвигаясь в глубь. Снифте иногда этот раздел форума, может и найдёте для себя что-то полезное.
 
Последнее редактирование:

voldim

One Level
10.07.2017
5
1
BIT
0
;//---- Вычисляем свой IP-адрес ---------
invoke gethostname,buff,32 ; возвращает локальное имя узла
invoke gethostbyname,buff ; возвращает адрес структуры "hostent"
mov eax,[eax+12] ;
mov eax,[eax] ;
mov eax,[eax] ; EAX = наш IP-адрес
mov [host],eax ; запомнить его..

А можно по подробней откуда это и почему(выделено красным не совсем понятно)? Какой смысл в двойном mov eax,[eax] ;?
 

Marylin

Mod.Assembler
Red Team
05.06.2019
326
1 451
BIT
696
А можно по подробней откуда это и почем
Судя по документации, функция gethostbyname() в единственном аргументе требует имени узла в текстовом виде, и возвращает в EAX указатель на структуру "hostent". В этой структуре будет лежать подноготная указанного узла. Чтобы получить имя, мы сперва вызываем gethostname(), а потом вскармливаем её выхлоп функции gethostbyname().

Чтобы разобраться подробней, нужно просто запустить отладчик и посмотреть, что он покажет - вот скрин:

hostent.png


Теперь посмотрим на код..

C-подобный:
mov   eax,[eax+12]       ;// EAX = 0x0008bd7c (указатель на список)
mov   eax,[eax]          ;// EAX = 0x0008bd88 (берём первый указатель из списка)
mov   eax,[eax]          ;// EAX = 0xA9FE488A = наш IP (169.254.72.138)
mov   [host],eax         ;// запомнить его..

Если посмотреть на выделенный красным первый указатель, то после него имеется ещё один, и только потом терминальный нуль, что означает "конец списка IP-адресов". Таким образом, к данной сетевой карте (интерфейсу) у меня привязано 2 айпишника.

Нужно сказать, что это дамп когда я не подключён к инету. Когда подключусь, провайдер мне выделит IP и он появится первым в структуре "hostent", а эти/два сдвинутся вправо, ..т.е. в этом списке их станет уже три. Отладчик - это наши глаза и руки..
 

voldim

One Level
10.07.2017
5
1
BIT
0
А почему смещение +12 (не +4 или +8), как вы узнали что именно по этому смещению указатель на список?
 
Последнее редактирование:

Marylin

Mod.Assembler
Red Team
05.06.2019
326
1 451
BIT
696
Структуру hostent придумал не я, а разработчик программного обеспечения, т.е. это утверждённая структура (см.описание выше). Если посчитать смещение поля "h_addr_list" в ней, то это +12 от начала, на которое указывает регистр EAX.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!