26 июня 2012 г.

ICMP Chat

В продолжении изучения сокетов посетила меня мысль, что при помощи icmp можно сделать управляющий протокол для чего нибуть, ну саму управлялку я не стал делать, а вот чат написал.



Смысл тут в чем, icmp достаточно прост, в него можно зашифровать достаточно много данных и он особа не привлекает внимание. Например некоторые даже поднимают целый тунель только на icmp чтобы обойти фаерволы и злобных админов.
Программа достаточно проста, в ней нет никакого шифрования, она не ведет лог, но позволяет обмениваться сообщениями в локальной сети. Программу достаточно легко переделать в универсальный снифер для любых пакетов, заменив пару параметров и сделав обертку в виде разбора пакетов. И все это всего в нескольких не оптимизированных килобайтах, мой проект например получился ровно 4кб.
А теперь собственно основные части программы:
Инициализация:
init_ws proc ; инициируем винсок и прочее
    invoke    gethostname, offset compname, sizeof compname ; получаем адрес на структуру с именем и ип адресом
    invoke    WSAStartup, WSVERSION, offset ws ; инициируем винсок
    invoke     socket, AF_INET, SOCK_RAW, IPPROTO_ICMP ; открываем сокет на отправку
    mov        sock, eax ; копируем сокет в переменную
    ret
init_ws endp

Процедура отправки сообщения:
sendmsg proc ; процедура отправки сообщения
    invoke    inet_addr, offset addr_ip ; переводим ип адрес получателя в hex
    mov     saddr.sin_addr,eax ; заносим уго в структуру sockaddr_in
    invoke  htons, PORT_ADDR ; переводим порт в hex
    mov     saddr.sin_port,ax ; и его в структуру sockaddr_in
    mov     saddr.sin_family,AF_INET ; заносим тип сети в структуру sockaddr_in
    invoke    setsockopt, sock, SOL_SOCKET, SO_RCVTIMEO, optval, sizeof optval ; устанавливаем параметры
    mov        packet.typ, 8h ; создаем icmp request пакет
    mov        packet.chksum, 0h ; чексумма при создании должна быть 0
    mov        packet.ident, 6863h ; 6863h - id пакета
    invoke    lstrcpy, offset packet.dat, offset textstr ; копируем строчку в созданный пакет
    invoke    icmpchecksum, offset packet, sizeof packet ; считаем получившуюся чексумму
    mov        packet.chksum, ax ; заносим чексуму в соотвествующее поле
    invoke    sendto, sock, offset packet, sizeof packet, 0, offset saddr, sizeof saddr ; отправляем пакет
    ret
sendmsg endp

Для того чтобы пакет был валидный, надо посчитать его чек сумму, хотя конечно можно и забить, но такой пакет могут не пропустить через себя маршрутизаторы, так что будем делать по правилам.
Процедурка подсчета чек суммы:
icmpchecksum proc icmppacket:DWORD, psize:DWORD ; процедурка подсчета чек суммы
    mov        edi, icmppacket ; в edi указатель на начало пакета
    mov        ax, WORD PTR [edi] ; в ax первые 2 байта из пакета
    xchg    al, ah ; поменять местами 
    mov        ecx, 2h ; увеличить счетчик на 2
nextword:    
    mov        bx, WORD PTR [edi+ecx] ; в bx поместить следующие 2 байта
    xchg    bl, bh ; их тоже поменять местами
    add        ax, bx ; сложить ax + bx
    jnc        noverflow ; если нет переполнения прыгнуть на noverflow 
    inc        ax ; иначе увеличить ax на 1
noverflow:
    add        ecx, 2h ; прибавить к счетчику 2
    .if    ecx >= psize ; сравнить, превысили или равно колчество байт
        jmp    endchk ; если превысили/равно - прыгнуть на выход из функции
    .endif
    jmp    nextword ; повторить со следующими 2мя байтами
endchk:
    not        ax ; инвертировать биты
    xchg    al, ah ; снова поменять местами
    ret
icmpchecksum endp

И наконец, процедурка приема сообщений, которая к стати без проблем переделывается в простой снифер, без написания всяких дров и использования библ pcap:
recvmsg proc hWin:DWORD ; процедурка приема сообщений
LOCAL sock2:DWORD ; локальная переменная для второй копии сокета
    mov        rflag, 1 ; флаг для треда
    invoke     socket, AF_INET, SOCK_RAW, IPPROTO_ICMP ; откроем сокет
    mov        sock2, eax ; закопируем в переменную
    invoke    gethostbyname, offset compname ; получим ссылку на структуру со совим ип адресом
    mov        eax, DWORD PTR [eax+1Ch] ; теперь сам ип адрес
    mov     inaddr.sin_addr,eax ; копируем ip адрес в структуру sockaddr_in
    mov     inaddr.sin_family,AF_INET ; копируем тип сети в структуру sockaddr_in
    invoke    bind, sock2, offset inaddr, sizeof inaddr ; биндим сокет
    mov        lsaddr, sizeof inaddr ; получаем размер структуры lsaddr
    mov        sflag, TRUE ; поставим флаг в TRUE 
    invoke     ioctlsocket, sock2, SIO_RCVALL,offset sflag ; переведем сокет в режим прослушивания
nextpacket:    
    invoke    recvfrom,sock2, offset buf, sizeof buf, 0, ADDR inaddr, ADDR lsaddr ; ждем пакет
    mov        eax, DWORD PTR [buf+0Ch] ; копируем ип адрес пришедшего пакета
    .if    saddr.sin_addr != eax ; не совпадает с ип адресом абонента
        jmp    fchk ; прыгаем на fchk
    .endif
    mov        al, BYTE PTR [buf+14h] ; копируем тип
    .if    al != 8h ; если не icmp request
        jmp    fchk ; прыгаем на выход
    .endif
    mov        ax, WORD PTR [buf+18h] ; копируем идентификатор
    .if ax != 6863h ; если пакет не из нашей программы
        jmp    fchk ; прыгаем на выход
    .endif
    lea        eax, DWORD PTR [buf+1ch] ; получаем ссылку на тело сообщения
    invoke    PostMessage, hWin, WM_FINISH,NULL, NULL ; сигнализируем основной программе, что получили валидный пакет
fchk:
    jmp        nextpacket ; ждем следующего пакета
    ret
recvmsg endp

А теперь код целиком для ручной сборки: http://pastebin.com/QVRuLnMm
Ну или готовый проект с откомпилированным исходником: http://depositfiles.com/files/wp692b99a

Комментариев нет:

Отправить комментарий