
From mds1281@ritvax.isc.rit.edu Sat Jun 13 10:36:33 1998
Date: Sat, 16 May 1998 03:15:06 +0000
From: Matt Smith <mds1281@ritvax.isc.rit.edu>
Reply-To: icq-devel@tjsgroup.com
To: "icq-devel@tjsgroup.com" <icq-devel@tjsgroup.com>
Subject: [ICQdev] [Fwd: ICQ version 3 and 4 UDP portion details]

This was sent to me by wumpus@innocent.com
This looks like it will be a big hel with V4!

-- Matt
  [ Part 2: "Included Message" ]

Date: Fri, 15 May 1998 17:01:41 -0700
From: wumpus@innocent.com
To: mds1281@ritvax.isc.rit.edu
Subject: ICQ version 3 and 4 UDP portion details

You posted on the ICQ-Devel list (which I saw archived on the web page at
d95-mih's page, in reference to version 4  of the ICQ protocol.  This 
information should be enough for you to continue your adventuring with
the ICQ protocol in more interesting ways then trying to reverse engineer
their pitiful encryption.  

This comes from some source code to an exploit which will 'hijaak' an ICQ 
account(and which I am not releasing at the present time. Mirabilis has been
made aware of these issues and hopefully will fix them in a future protocol
revision ).   Please repost this information in any and all venues which it
is appropriate -- I'd hate to see people wasting their time on this stuff...
On the other hand, I'd appreciate being informed of how you are using
this, and other things which you find out about the protocol...

Please write back if you find this useful  and tell me if you have any more
questions about the protocol.

For the record - 
  I am NOT affiliated with Mirabilis in any way shape or form.

  This code is my own creation, and I am releasing it for informational purposes.
   
  If it doesn't work, or if you do something illegal with it and get screwed
   it is YOUR fault - not mine.

Here goes...  some rather poorly coded C...

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

  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] );
        }
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;
        int     len2;
        int     err;
        u32     check;
        u32     check2;
        int     ofs;
        int     val;

        err = WritePacket( data_ptr,&len, "WWWWL",
                0x03,   /* Version, Constant */
                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;
}

                                  

#define ICQ_VER         0
#define ICQ_CMD         2
#define ICQ_SEQ         4
#define ICQ_SEQ2        6
#define ICQ_UID         8
#define ICQ_UNKNOWN     12
#define ICQ_END         16

int decode_icq3_header( u8 * data_ptr,
                        u16 * command,
                        u16 * sequence,
                        u16 * sequence2,
                        u8 ** icqdata )
{
        u16     version;
        u32     check;
        u8      ofs;
        u8      val;

        version = word( *data_ptr, 0 );
        if ( version != 3 ) {
                fprintf(stderr,"Unknown version %04lx\n", version );
                return FAILURE;
        }
        check = ( *(data_ptr + 8) << 24) |
                ( *(data_ptr + 4) << 16 ) |
                ( *(data_ptr + 2) << 8 ) |
                ( *(data_ptr + 6) );
        check ^= dword( *data_ptr, ICQ_UNKNOWN );
        check ^= 0x00FF00FF;
        ofs = check >> 24;
        val = ( check >> 16) & 0xFF;
        if ( data_ptr[ ofs ] != val ) {
                printf("**** ICQ3 CHECK MISMATCH %d is %02X not %02X\n",
                        ofs, data_ptr[ofs], val );
        }
        ofs = (check >> 8) & 0xFF;
        val = check & 0xFF;
        if ( icq_check_data[ ofs ] != val ) {
                printf("**** ICQ3 CHECK MISMATCH %d is %02X not %02X\n",
                        ofs, icq_check_data[ofs], val );
        }
        *command = word( *data_ptr, ICQ_CMD );
        *sequence = word( *data_ptr, ICQ_SEQ );
        *sequence2 = word( *data_ptr, ICQ_SEQ2 );
        if ( dword(*data_ptr,ICQ_UID) != icq_uin ) {
                fprintf(stderr,"Error!  Packet uid %08lx isn't %08lx \n",
                        dword(*data_ptr, ICQ_UID), icq_uin );
                return FAILURE;
        }
        *icqdata = data_ptr + ICQ_END;
        return SUCCESS;
}
                                           

