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 Reply-To: icq-devel@tjsgroup.com To: "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; }