Поиски свободного «почтальона Печкина». Часть 46.2. «Нюхач» - на службе отечеству.Автор: _follower/TPOC Конечно, сканировать всю сетку на предмет доступного SMTP – сервера – утомительно и долго. Как это дело ускорить? Выход есть. Для наших целей мы можем использовать такого необычного но при всем этом не редкого зверя, который уже достаточно размножился в сетевых джунглях. Этот зверь принадлежит распространенному роду – “нюхачей”. На английском – Sniffer. Sniffer ("Нюхач") – в сетевой терминологии термин, обозначающий программу перехвата трафика. В нашем уроке наша цель создать такой, заточенный под наши нужды, сниффер. Наш “нюхач” должен слушать (или может скорее вынюхивать), проходящие через сетевой интерфейс, пакеты и собирать для нас e-mail адреса отправителей писем и IP – адреса доступных SMTP – серверов. Итак, программа должна: · запустить процесс прослушивания; · получить IP – адрес и адрес отправителя; Итак приступим. Для начала стоит ознакомиться со статьей Snorting coke (and packets) by GriYo / 29A (29a-6.004) [1.1]. Она есть на сайте (перевод конечно не очень, но как говорится - “чем богаты …”). Далее полезно ознакомиться с уже работающим сниффером. Мне очень понравилась программа CommView (http://www.tamos.ru/products/commview/). Если скачать ее демонстрационную версию, то ее ограничением будет – прием пакетов “через один”. Этот режим нас вполне удовлетворит, хотя я бы ее купил. Программа дает возможность очень наглядно просмотреть структуру IP, TCP, UDP, ICMP, ARP – пакетов. Посмотрите и сами увидите: … Рис. 1
 Вот видно как в уловленном ARP – пакете мы наглядно видим, куда направлен пакет, что в пакете, структура пакета. Тоже и для остальных протоколов. Зеленым я затер свой MAC – адрес (что бы не узнал никто :) Все на русском и удобно. Что еще нужно от программы? Ее полная версия? Вы правы, но мы же хотим писать свой сниффер. И после всех приготовлений мы можем рассмотреть текст программы. .586 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\ws2_32.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc
includelib \masm32\lib\ws2_32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib
;**************************************************************************** IP_HEADER STRUCT Version_and_HdrLen db ? ;+0; версия и длина заголовка ServiceType db ? ;+1; тип сервиса TotalLen dw ? ;+2; длина всего пакета ID dw ? ;+4; Идентификация Flags_and_FragOff dw ? ;+6; флаги и смещения TimeToLive db ? ;+8; время жизни пакета Protocol db ? ;+9; протокол HdrChksum dw ? ;+A; контрольная сумма SrcAddr dd ? ;+C; IP-адрес отправителя DstAddr dd ? ;+E; IP-адрес назначения IP_HEADER ENDS
;**************************************************************************** Main proto ZeroFill proto :DWORD,:DWORD ParseBuffer proto :DWORD,:DWORD,:DWORD ;**************************************************************************** .data _char_str db 'MAIL FRO',0 _tab db ' ',0 crlf db 13,10
;**************************************************************************** .data? DATA db 523 dup(?) if_LIST db 2048 dup(?) buf db 2048 dup(?) buf_in db 128 dup(?)
;**************************************************************************** .const WSA_FLAG_OVERLAPPED equ 00000001h SIO_ADDRESS_LIST_QUERY equ 48000016h SIO_RCVALL equ 98000001h
;**************************************************************************** .code ; ######################################################################### start: invoke Main invoke ExitProcess,0 ; #########################################################################
Main proc LOCAL s :SOCKET LOCAL if0 :sockaddr_in LOCAL optval :DWORD LOCAL dwBytesRet :DWORD LOCAL dwFlags :DWORD LOCAL wbuf[8] :BYTE LOCAL BytesRet :DWORD ;Инициируем Winsocket версии 2.2+ invoke WSAStartup,00000202h,addr DATA test eax,eax ;******************************************************************** ;создаем сокет связанный с транспортной службой invoke WSASocketA, AF_INET,; семейство SOCK_RAW,;тип используемого сокета IPPROTO_IP,;используемый протокол 0,;указатель на структуру содержащую характеристики создаваемого сокета 0,;зарезервировано WSA_FLAG_OVERLAPPED; атрибуты спецификации сокета .if eax == INVALID_SOCKET ;Error: WSASocket xor eax,eax ret .endif mov s,eax ;Если OK, то определяем количество существующих интерфейсов invoke ZeroFill,addr if0,SIZE sockaddr_in invoke ZeroFill,addr if_LIST,2048 mov BytesRet,0 ;Получим список доступных интерфейсов в виде выходной структуры ...
invoke WSAIoctl, s, ; дескриптор сокета SIO_ADDRESS_LIST_QUERY, ; код выполняемой операции NULL, ; указатель на входной буфер 0, ; размер выходного буфера addr if_LIST, ; указатель на выходной буфер 2048, ; размер выходного буфера addr BytesRet, ; указатель на число байт которые реально возвращениы NULL, ; указатель на структуру WSAOVERLAPPED NULL ; указатель на функцию завершения операции
.if eax == SOCKET_ERROR ;Error: WSAIoctl xor eax,eax ret .endif
xor ecx,ecx lea esi,if_LIST cld lodsd
;заполняем IP adress shl ecx,3 add esi,ecx cld lodsd add eax,sockaddr_in.sin_addr mov esi,eax
lea edi,if0 add edi,sockaddr_in.sin_addr
cld lodsd stosd
;******************************************************************** mov if0.sin_family,AF_INET invoke htons,0 mov if0.sin_port,ax ;********************************************************************
invoke bind,s,addr if0,SIZE sockaddr_in .if eax == SOCKET_ERROR ;Error: bind xor eax,eax ret .endif
mov dwBytesRet,0 mov optval,1 ;******************************************************************** ;Включаем сокет на прием всех IP пакетов из сети
invoke WSAIoctl, s, ; дескриптор сокета SIO_RCVALL, ; код выполняемой операции addr optval, ; указатель на входной буфер 4, ; размер выходного буфера 0, ; указатель на выходной буфер 0, ; размер выходного буфера addr dwBytesRet, ; указатель на число байт которые реально возвращениы 0, ; указатель на структуру WSAOVERLAPPED 0 ; указатель на функцию завершения операции
.if eax == SOCKET_ERROR ;Error: WSAIoctl xor eax,eax ret .endif ;*************************************************************************** do2: ;***************************************************************************
invoke ZeroFill,addr buf,SIZE buf invoke ZeroFill,addr buf_in,SIZE buf_in invoke recv, s, addr buf, sizeof buf, 0
;############################################################################ ;############################################################################ lea EDX,buf assume EDX : PTR IP_HEADER invoke inet_ntoa, dword ptr [EDX].DstAddr invoke lstrcpy,ADDR buf_in,eax
invoke ParseBuffer,addr buf,sizeof buf,ADDR _char_str .IF eax!=-1 push eax invoke StdOut,addr buf_in invoke StdOut,ADDR _tab pop eax invoke StdOut,eax .ENDIF assume EDX : NOTHING jmp do2 ret Main endp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ZeroFill proc adr : DWORD, num :DWORD mov ecx,num mov esi,adr add esi,ecx do1: dec esi mov [esi],byte ptr 0 loop do1 ret ZeroFill endp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ParseBuffer proc uses ebx edx ecx edi esi _str:DWORD,\ _szstr:DWORD,\ _sub:DWORD align 16
mov esi,_str mov edi,_sub mov eax,dword ptr[edi] ;"MAIL" mov ebx,dword ptr[edi+4] ;" FRO" mov ecx,_szstr dec esi
@@: inc esi cmp eax,dword ptr[esi] je @Fnd dec ecx cmp ecx,0 je @No jmp @B
@Fnd: cmp ebx,dword ptr[esi+4] jne @B ;*************************************** push esi
xor ecx,ecx mov cx,100 cld ___search3E: mov al,byte ptr [esi] cmp al,3eh ; '>' jz ___formatESI inc esi loop ___search3E
___formatESI: inc esi mov byte ptr [esi],0dh mov byte ptr [esi+1],0ah mov byte ptr [esi+2],00h
;*************************************** pop esi mov eax,esi ret @No: or eax,-1 ret ParseBuffer endp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; end start |
Результат: Наша ОС принадлежит множеству {Win95 OSR2, Win98, NT, …}. Запускаем программку и сворачиваем ее в трей. При попытке отослать письмо с этой машины с помощью программы TheBat! или Outlook или еще каким-то, но не on-line!, почтовиком – в окне консоли нашего сниффера появится e-mail адрес отправителя.
192.168.1.13 MAIL FROM:<_follower@mail.ru>
192.168.1.12 MAIL FROM:< GorinM@orel.bk.ru>
192.168.1.12 MAIL FROM:<_follower@mail.ru>
192.168.1.11 MAIL FROM:<_follower@mail.ru> |
Анализ работы: Наш пример строился на основании положенном уважаемым / 29A. На сайте (http://www.tpoc.h15.ru/) есть попытка перевода этой статьи, поэтому мы ограничимся тут лишь краткими замечаниями, относящимися к особенностям адаптации примера GriYo к нашим нуждам. 1)
invoke WSAIoctl,
s, ; дескриптор сокета
SIO_ADDRESS_LIST_QUERY, ; код выполняемой операции
NULL, ; указатель на входной буфер
0, ; размер выходного буфера
addr if_LIST, ; указатель на выходной буфер
2048, ; размер выходного буфера
addr BytesRet, ; указатель на число байт которые реально возвращениы
NULL, ; указатель на структуру WSAOVERLAPPED
NULL ; указатель на функцию завершения операции |
Тут мы хотим получить список доступных интерфейсов. Дело в том что на выходе функции WSAIoctl() мы получаем указатель на структуру. Как выглядит эта структура? Для того чтобы сней познакомиться поближе найдите с помощью программы поиска на своих локальных дисках заголовочный файл с именем Winsock2.h и, предварительно открыв его, выполните поиск по слову “typedef struct _SOCKET_ADDRESS_LIST”. Вы найдете там примерно такое:
;/* Winsock2.h -- definitions to be used with the WinSock 2 DLL and
; * WinSock 2 applications.
;/*
; * Address list returned via SIO_ADDRESS_LIST_QUERY
; */
; typedef struct _SOCKET_ADDRESS_LIST {
; INT iAddressCount;
; SOCKET_ADDRESS Address[1];
;} SOCKET_ADDRESS_LIST, FAR * LPSOCKET_ADDRESS_LIST; |
… где iAddressCount – количество сетевых интерфейсов; Address[1] - IP последнего. Далее следуют мероприятия по вычленению этого IP из структуры - SOCKET_ADDRESS_LIST и перенесение его в структуру - sockaddr_in. Наверное, Вы спросите: “ Что за ерунда! У меня воткнута одна сетевая карта. Откуда возьмутся иные IP – адреса кроме как тот, который присвоен мною (или специальным сервисом DHCP, PPP …)?” Но не спешите с выводами! Вы вероятно забываете об интерфейсе так называемой “обратной петли”, ей то и присвоен IP = 127.0.0.1. Вот Вам и еще один адресованный сетевой интерфейс.
2)
invoke bind,s,addr if0,SIZE sockaddr_in |
Tут можно задать вопрос: - “Я в инет хожу через диалап и мой внешний IP постоянно меняется провайдером. Если мне нужен снифер могу ли я провязать созданный сокет к IP = 127.0.0.1?”
Ответ может звучать примерно так: - “если вы "привязали" (bind) сокет на интерфейс -127.0.0.1 то параметр SIO_RCVALL не отработает. RAW- сокет “привязать” (bind) можно будет, но WSAIoctl() с параметром SIO_RCVALL будет выдавать ошибку №10022”
3)
invoke recv, s, addr buf, sizeof buf, 0
;############################################################################
;############################################################################
lea EDX,buf
assume EDX : PTR IP_HEADER
invoke inet_ntoa, dword ptr [EDX].DstAddr
invoke lstrcpy,ADDR buf_in,eax
invoke ParseBuffer,addr buf,sizeof buf,ADDR _char_str |
Тут у прославленного GriYo использовалась функция WSARecv(). Посмотрим в справочник [1.2]:
int WSARecv(
SOCKET s,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
typedef struct __WSABUF
{
u_long len;
char FAR *buf;
} WSABUF, FAR * LPWSABUF; |
Буферы, в которые нужно поместить данные, передаются функции WSARecv через параметр lpBuffers. Он содержит указатель на начало массива структур TWSABuf, а параметр dwBufferCount - количество элементов в этом массиве. Выше мы знакомились со структурой TWSABuf: она содержит указатель на начало буфера и его размер. Соответственно, массив таких структур определяет набор буферов. При чтении данных заполнение буферов начинается с первого буфера в массиве lpBuffers, затем, если в нём не хватает места, используется второй буфер и т.д. Функция не переходит к следующему буферу, пока не заполнит предыдущий до последнего байта. Таким образом, данные, получаемые с помощью функции WSARecv, могут быть помещены в несколько несвязных областей памяти, что иногда бывает удобно, если принимаемые сообщения имеют строго определённый формат с фиксированными размерами компонентов пакета: в этом случае можно каждый компонент поместить в свой независимый буфер.
Однако нам ради наших, более чем скромных, целей не нужно строить связный список этих посылок, полученных от принимающей функции. Чем проще – тем надежнее! Вот почему мы используем старую добрую Recv(). Далее применим стандартный прием - наложение структуры на область данных в памяти. Мне лично этот прием очень нравится. Вот и все, теперь мы можем обращаться в куску данных как к структурированной совокупности, и это удобно. Далее мы вызываем функцию ParseBuffer() – функцию, которая в принятом объеме пакета находит строку с именем отправителя – MAIL FROM: …
4)
push esi
xor ecx,ecx
mov cx,100
cld
___search3E:
mov al,byte ptr [esi]
cmp al,3eh ; '>'
jz ___formatESI
inc esi
loop ___search3E
___formatESI:
inc esi
mov byte ptr [esi],0dh
mov byte ptr [esi+1],0ah
mov byte ptr [esi+2],00h |
Для того чтобы адрес отправителя получился чистым мы проводим поиск символа – ‘>’ и отсекаем часть стоки после него. Отсекающим “мечем” служит последовательность 0Ah,0Dh,00.
Вот ты какой – сниффер! :) Просто, не правда ли? Теперь как только пользователь отправить письмо мы сразу узнаем и IP – SMTP – сервера, и имя зарегистрированного на этом сервере пользователя. Но! Если бы не эти - Но! А что если пользователь долго не отсылает никаких писем? Во всех, известных мне, червях IP – сервера SMTP получают из реестра. Имя зарегистрированного пользователя из файлов на локальных ресурсах. Да это быстро и не накладно. Но что если применить несколько способов получения “жизненно – необходимой” информации, может на выходе будет результат?
Но хватит уже возиться с этими локальными сетями. Пора, встряхнувшись, выйти в мир! Итак, в следующей главе мы с Вами посмотрим, что творится на необъятных просторах всемирной паутины :)
А пока - до свиданья! Перечень используемой литературы. [1.1] – статья Snorting coke (and packets) by GriYo / 29A (29a-6.004). (http://vx.netlux.org/29a). Перевод. (http://www.tpoc.h15.ru/Articles/Sniffers.htm) [1.2] - Использование сокетов в Delphi. Часть вторая: сокеты Windows (http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1060#top) Перечень используемого программного обеспечения: [1.1] – Программа сниффер - CommView (http://www.tamos.ru/products/commview/). Исходник [C] _follower / TPOC | Новые события из жизни нашей лаборатории Статьи и переводы лаборатории TPOC Программы лаборатории TPOC Здесь мы сообщаем Вам, какие творения скоро появятся Ссылки на сайты, где можно найти больше информации История нашей лаборатории и ее члены |