Welcome to The Passion Of Code Laboratory!!!Статьи

Поиски свободного «почтальона Печкина». Часть 4

6.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

Релизы

Здесь мы сообщаем Вам, какие творения скоро появятся

Ссылки

Ссылки на сайты, где можно найти больше информации

Наша лаборатория

История нашей лаборатории и ее члены

Дата последнего обновления: 17 августа 2005 года
У вас есть предложения по нашему сайту?
Напишите сюда
Любимые сайты вирмейкеров:
(WASM)   (RSDN)