Ethereal-dev: [ethereal-dev] packet-icq.c ICQ dissector
Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.
From: Kojak <kojak@xxxxxxxxxx>
Date: Sat, 23 Oct 1999 23:32:06 +0200
Hi, I built an icq dissector, just to see if it was easy. And, yes it is. Maybe you can add this to the tree, or you can just throw it away. It does the decoding of client v5 packets so far, but it does not yet display the different message formats (like CMD_LOGIN). That's for the time when I (or someone else) has an idea how to handle a zero-terminated string somewhere in the middle of a structure... <Sigh> The developer of ICQ must have been taking some serious chemicals when he created the packet format. Two parts after this; the patch for the other files (i.e. Makefile.am), and packet-icq.c Have fun, -- Kojak Index: Makefile.am =================================================================== RCS file: /cvsroot/ethereal/Makefile.am,v retrieving revision 1.96 diff -u -r1.96 Makefile.am --- Makefile.am 1999/10/22 08:57:24 1.96 +++ Makefile.am 1999/10/23 21:17:02 @@ -55,6 +55,7 @@ packet-gre.c \ packet-http.c \ packet-icmpv6.c\ + packet-icq.c \ packet-icp.c \ packet-ip.c \ packet-ip.h \ Index: configure.in =================================================================== RCS file: /cvsroot/ethereal/configure.in,v retrieving revision 1.54 diff -u -r1.54 configure.in --- configure.in 1999/10/20 18:11:52 1.54 +++ configure.in 1999/10/23 21:17:02 @@ -109,6 +109,7 @@ AC_CHECK_HEADERS(fcntl.h sys/ioctl.h sys/time.h unistd.h stdarg.h netdb.h) AC_CHECK_HEADERS(sys/stat.h sys/sockio.h sys/types.h netinet/in.h sys/socket.h net/if.h) AC_CHECK_HEADERS(sys/wait.h) +AC_CHECK_HEADERS(stddef.h) dnl SNMP Check AC_ARG_ENABLE(snmp, Index: packet-udp.c =================================================================== RCS file: /cvsroot/ethereal/packet-udp.c,v retrieving revision 1.31 diff -u -r1.31 packet-udp.c --- packet-udp.c 1999/10/22 07:17:45 1.31 +++ packet-udp.c 1999/10/23 21:17:03 @@ -79,6 +79,7 @@ #define UDP_PORT_RADACCT 1646 #define UDP_PORT_RADACCT_NEW 1813 #define UDP_PORT_ICP 3130 +#define UDP_PORT_ICQ 4000 #define UDP_PORT_RX_LOW 7000 #define UDP_PORT_RX_HIGH 7009 #define UDP_PORT_RX_AFS_BACKUPS 7021 @@ -264,7 +265,9 @@ dissect_radius(pd, offset, fd, tree); } else if ( PORT_IS(UDP_PORT_ICP)) { dissect_icp(pd,offset,fd,tree); - } else { + } else if ( PORT_IS(UDP_PORT_ICQ)) { + dissect_icq(pd,offset,fd,tree); + } else { /* OK, find a routine in the table, else use the default */ if ((dissect_routine = udp_find_hash_ent(uh_sport))) { Index: packet.h =================================================================== RCS file: /cvsroot/ethereal/packet.h,v retrieving revision 1.119 diff -u -r1.119 packet.h --- packet.h 1999/10/22 08:30:04 1.119 +++ packet.h 1999/10/23 21:17:03 @@ -250,6 +250,9 @@ ETT_DNS_QD, ETT_DNS_ANS, ETT_DNS_RR, + ETT_CL_ICQ, + ETT_CL_ICQ_DECODE, + ETT_SRV_ICQ, ETT_ISAKMP, ETT_ISAKMP_FLAGS, ETT_ISAKMP_PAYLOAD, @@ -565,6 +568,7 @@ void dissect_pppoed(const u_char *, int, frame_data *, proto_tree *); void dissect_pppoes(const u_char *, int, frame_data *, proto_tree *); void dissect_icp(const u_char *,int, frame_data *, proto_tree *); +void dissect_icq(const u_char *,int, frame_data *, proto_tree *); void dissect_isakmp(const u_char *, int, frame_data *, proto_tree *); void dissect_pim(const u_char *, int, frame_data *, proto_tree *); void dissect_radius(const u_char *, int, frame_data *, proto_tree *); packet-icq.c: /* packet-icq.c * Routines for ICQ packet disassembly * * $Id: $ * * Ethereal - Network traffic analyzer * By Johan Feyaerts * Copyright 1999 Johan Feyaerts * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * This file: by Kojak <kojak@xxxxxxxxxx> * * Decoding code ripped, reference to the original author at the * appropriate place with the code itself. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_SYS_TYPES_H # include <sys/types.h> #endif #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif #ifdef HAVE_STDDEF_H #include <stddef.h> #endif #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <ctype.h> #include <glib.h> #include "packet.h" #include "resolv.h" int proto_icq = -1; int hf_icq_uin =-1; int hf_icq_cmd =-1; int hf_icq_sessionid =-1; int hf_icq_checkcode =-1; int hf_icq_decode = -1; /* Offsets of fields in the ICQ headers */ /* Can be 0x0002 or 0x0005 */ #define ICQ_VERSION 0x00 /* Is either one (server) or four (client) bytes long */ /* Client header offsets */ #define ICQ5_UNKNOWN 0x02 #define ICQ5_CL_UIN 0x06 #define ICQ5_CL_SESSIONID 0x0a #define ICQ5_CL_CMD 0x0e #define ICQ5_CL_SEQNUM1 0x10 #define ICQ5_CL_SEQNUM2 0x12 #define ICQ5_CL_CHECKCODE 0x14 #define ICQ5_CL_PARAM 0x18 #define ICQ5_CL_HDRSIZE 0x18 /* Server header offsets */ #define ICQ5_SRV_SESSIONID 0x03 #define ICQ5_SRV_CMD 0x07 #define ICQ5_SRV_SEQNUM1 0x09 #define ICQ5_SRV_SEQNUM2 0x0b #define ICQ5_SRV_UIN 0x0d #define ICQ5_SRV_CHECKCODE 0x11 #define ICQ5_SRV_PARAM 0x15 #define ICQ5_SRV_HDRSIZE 0x15 typedef struct _cmdcode { char* descr; int code; } cmdcode; cmdcode serverCmdCode[] = { { "SRV_ACK", 10 }, { "SRV_GO_AWAY", 40 }, { "SRV_NEW_UIN", 70 }, { "SRV_LOGIN_REPLY", 90 }, { "SRV_BAD_PASS", 100 }, { "SRV_USER_ONLINE", 110 }, { "SRV_USER_OFFLINE", 120 }, { "SRV_QUERY", 130 }, { "SRV_USER_FOUND", 140 }, { "SRV_END_OF_SEARCH", 160 }, { "SRV_NEW_USER", 180 }, { "SRV_UPDATE_EXT", 200 }, { "SRV_RECV_MESSAGE", 220 }, { "SRV_X2", 230 }, { "SRV_NOT_CONNECTED", 240 }, { "SRV_TRY_AGAIN", 250 }, { "SRV_SYS_DELIVERED_MESS", 260 }, { "SRV_INFO_REPLY", 280 }, { "SRV_EXT_INFO_REPLY", 290 }, { "SRV_STATUS_UPDATE", 420 }, { "SRV_SYSTEM_MESSAGE", 450 }, { "SRV_UPDATE_SUCCESS", 480 }, { "SRV_UPDATE_FAIL", 490 }, { "SRV_AUTH_UPDATE", 500 }, { "SRV_MULTI_PACKET", 530 }, { "SRV_X1", 540 }, { "SRV_RAND_USER", 590 }, { "SRV_META_USER", 990 }, { NULL, 0 } }; cmdcode clientCmdCode[] = { { "CMD_ACK", 10 }, { "CMD_SEND_MESSAGE", 270 }, { "CMD_LOGIN", 1000 }, { "CMD_REG_NEW_USER", 1020 }, { "CMD_CONTACT_LIST", 1030 }, { "CMD_SEARCH_UIN", 1050 }, { "CMD_SEARCH_USER", 1060 }, { "CMD_KEEP_ALIVE", 1070 }, { "CMD_SEND_TEXT_CODE", 1080 }, { "CMD_ACK_MESSAGES", 1090 }, { "CMD_LOGIN_1", 1100 }, { "CMD_MSG_TO_NEW_USER", 1110 }, { "CMD_INFO_REQ", 1120 }, { "CMD_EXT_INFO_REQ", 1130 }, { "CMD_CHANGE_PW", 1180 }, { "CMD_NEW_USER_INFO", 1190 }, { "CMD_UPDATE_EXT_INFO", 1200 }, { "CMD_QUERY_SERVERS", 1210 }, { "CMD_QUERY_ADDONS", 1220 }, { "CMD_STATUS_CHANGE", 1240 }, { "CMD_NEW_USER_1", 1260 }, { "CMD_UPDATE_INFO", 1290 }, { "CMD_AUTH_UPDATE", 1300 }, { "CMD_KEEP_ALIVE2", 1310 }, { "CMD_LOGIN_2", 1320 }, { "CMD_ADD_TO_LIST", 1340 }, { "CMD_RAND_SET", 1380 }, { "CMD_RAND_SEARCH", 1390 }, { "CMD_META_USER", 1610 }, { "CMD_INVIS_LIST", 1700 }, { "CMD_VIS_LIST", 1710 }, { "CMD_UPDATE_LIST", 1720 }, { NULL, 0 } }; typedef struct { u_int32_t random; } cl_cmd_ack; typedef struct _cl_cmd_send_msg { #define MSG_TEXT 0x0100 #define MSG_URL 0x0400 #define MSG_AUTH_REQ 0x0600 #define MSG_AUTH 0x0800 #define MSG_USER_ADDED 0x0c00 #define MSG_CONTACTS 0x1300 u_int32_t receiverUIN; u_int16_t msgType; u_int16_t msgLen; /* * Followed by char[msgLen] */ } cl_cmd_send_msg; /* * All ICQv5 decryption code thanx to Sebastien Dault (daus01@xxxxxxxxxxxxx) */ const u_char table_v5 [] = { 0x59, 0x60, 0x37, 0x6B, 0x65, 0x62, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x60, 0x57, 0x5B, 0x3D, 0x5E, 0x34, 0x6D, 0x36, 0x50, 0x3F, 0x6F, 0x67, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x47, 0x63, 0x39, 0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x43, 0x69, 0x48, 0x33, 0x31, 0x64, 0x35, 0x5A, 0x4A, 0x42, 0x56, 0x40, 0x67, 0x53, 0x41, 0x07, 0x6C, 0x49, 0x58, 0x3B, 0x4D, 0x46, 0x68, 0x43, 0x69, 0x48, 0x33, 0x31, 0x44, 0x65, 0x62, 0x46, 0x48, 0x53, 0x41, 0x07, 0x6C, 0x69, 0x48, 0x33, 0x51, 0x54, 0x5D, 0x4E, 0x6C, 0x49, 0x38, 0x4B, 0x55, 0x4A, 0x62, 0x46, 0x48, 0x33, 0x51, 0x34, 0x6D, 0x36, 0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x63, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64, 0x35, 0x5A, 0x6A, 0x52, 0x6E, 0x3C, 0x51, 0x34, 0x6D, 0x36, 0x50, 0x5F, 0x5F, 0x3F, 0x4F, 0x37, 0x4B, 0x35, 0x5A, 0x4A, 0x62, 0x66, 0x58, 0x3B, 0x4D, 0x66, 0x58, 0x5B, 0x5D, 0x4E, 0x6C, 0x49, 0x58, 0x3B, 0x4D, 0x66, 0x58, 0x3B, 0x4D, 0x46, 0x48, 0x53, 0x61, 0x4C, 0x59, 0x40, 0x67, 0x33, 0x31, 0x64, 0x55, 0x6A, 0x32, 0x3E, 0x44, 0x45, 0x52, 0x6E, 0x3C, 0x31, 0x64, 0x55, 0x6A, 0x52, 0x4E, 0x6C, 0x69, 0x48, 0x53, 0x61, 0x4C, 0x39, 0x30, 0x6F, 0x47, 0x63, 0x59, 0x60, 0x57, 0x5B, 0x3D, 0x3E, 0x64, 0x35, 0x3A, 0x3A, 0x5A, 0x6A, 0x52, 0x4E, 0x6C, 0x69, 0x48, 0x53, 0x61, 0x6C, 0x49, 0x58, 0x3B, 0x4D, 0x46, 0x68, 0x63, 0x39, 0x50, 0x5F, 0x5F, 0x3F, 0x6F, 0x67, 0x53, 0x41, 0x25, 0x41, 0x3C, 0x51, 0x54, 0x3D, 0x5E, 0x54, 0x5D, 0x4E, 0x4C, 0x39, 0x50, 0x5F, 0x5F, 0x5F, 0x3F, 0x6F, 0x47, 0x43, 0x69, 0x48, 0x33, 0x51, 0x54, 0x5D, 0x6E, 0x3C, 0x31, 0x64, 0x35, 0x5A, 0x00, 0x00 }; static char* findcmd(cmdcode* c, int num) { static char buf[16]; cmdcode* p = c; while (p->code != 0) { if (p->code == num) { return p->descr; } p++; } snprintf(buf, sizeof(buf), "(%x)", num); return buf; } void proto_tree_add_hexdump(proto_tree* t, u_int32_t offset, u_char *data, int size) { int i; char buf[96]; int n; int done = 0, line = 0; int added = 0; if (size==0) return; line = size / 16; for (i=0;i<line;i++) { added = 0; done = 0; for (n = i * 16; n < (i+1)*16; n++) { added = sprintf(buf+done, "%02x", data[n]); if ((n%8)==7) added += sprintf(buf + done + added, " "); else added += sprintf(buf + done + added, " "); done += added; } for (n = i * 16; n < (i+1)*16; n++) { if (isprint(data[n])) added = sprintf(buf + done, "%c", data[n]); else added = sprintf(buf + done, "."); done += added; } proto_tree_add_text(t, offset + i*16, 16, buf); } if ((size%16)!=0) { done = 0; for (n = line * 16 ; n < size ; n++) { added = sprintf(buf+done, "%02x", data[n]); if ((n%8)==7) added += sprintf(buf + done + added, " "); else added += sprintf(buf + done + added, " "); done += added; } for (n = size ; (n%16)!=0;n++) { added = 0; if ((n%8)==7) added += sprintf(buf + done + added, " "); else added += sprintf(buf + done + added, " "); done += added; } for (n = line * 16; n < (line+1)*16; n++) { added = 0; if (n<size) { if (isprint(data[n])) added = sprintf(buf + done, "%c", data[n]); else added = sprintf(buf + done, "."); } else { added = sprintf(buf + done, " "); } done += added; } proto_tree_add_text(t, offset + line*16, size % 16, buf); } } static u_int32_t get_v5key(const u_char* pd, int len) { u_int32_t a1, a2, a3, a4, a5; u_int32_t code, check, key; code = pletohl(&pd[ICQ5_CL_CHECKCODE]); a1 = code & 0x0001f000; a2 = code & 0x07c007c0; a3 = code & 0x003e0001; a4 = code & 0xf8000000; a5 = code & 0x0000083e; a1 = a1 >> 0x0c; a2 = a2 >> 0x01; a3 = a3 << 0x0a; a4 = a4 >> 0x10; a5 = a5 << 0x0f; check = a5 + a1 + a2 + a3 + a4; key = len * 0x68656C6C; key += check; return key; } static void decrypt_v5(u_char *bfr, u_int32_t size,u_int32_t key) { u_int32_t i; u_int32_t k; for (i=0x0a; i < size+3; i+=4 ) { k = key+table_v5[i&0xff]; if ( i != 0x16 ) { bfr[i] ^= (u_char)(k & 0xff); bfr[i+1] ^= (u_char)((k & 0xff00)>>8); } if ( i != 0x12 ) { bfr[i+2] ^= (u_char)((k & 0xff0000)>>16); bfr[i+3] ^= (u_char)((k & 0xff000000)>>24); } } } static void dissect_icqv2(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) { /* Not really implemented yet */ if (check_col(fd, COL_PROTOCOL)) { col_add_str(fd, COL_PROTOCOL, "ICQv2 (UDP)"); } if (check_col(fd, COL_INFO)) { col_add_str(fd, COL_INFO, "ICQ Version 2 protocol"); } } /* * Dissect all the v5 client traffic. This is encrypted, so be careful. */ static void dissect_icqv5Client(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) { proto_tree *icq_tree = NULL; proto_tree *icq_decode_tree = NULL; proto_item *ti = NULL; u_int16_t version = -1, cmd = -1; u_int16_t seqnum1 = 0 , seqnum2 = 0; u_int32_t uin = -1, sessionid = -1; u_int32_t key = -1; u_int16_t pktsize = -1; /* The size of the ICQ content */ u_char decr_pd[1600]; /* Decrypted content, size should be dynamic */ pktsize = fd->pkt_len - offset; /* First copy the memory, we don't want to overwrite the old content */ memcpy(decr_pd, &pd[offset], pktsize); if (fd->pkt_len > fd->cap_len) { pktsize -= (fd->pkt_len - fd->cap_len); } if (pktsize>0x14) { key = get_v5key(decr_pd, pktsize); decrypt_v5(decr_pd, pktsize, key); /* This information only makes sense in the decrypted version */ uin = pletohl(&decr_pd[ICQ5_CL_UIN]); cmd = pletohs(&decr_pd[ICQ5_CL_CMD]); sessionid = pletohl(&decr_pd[ICQ5_CL_SESSIONID]); version = pletohs(&decr_pd[ICQ_VERSION]); seqnum1 = pletohs(&decr_pd[ICQ5_CL_SEQNUM1]); seqnum2 = pletohs(&decr_pd[ICQ5_CL_SEQNUM2]); } if (tree) { ti = proto_tree_add_item_format(tree, proto_icq, offset, pktsize, NULL, "ICQv5 Client: len %d", pktsize); icq_tree = proto_item_add_subtree(ti, ETT_CL_ICQ); proto_tree_add_item_format(icq_tree, hf_icq_cmd, offset+ICQ5_CL_CMD, 2, cmd, "Command: %d (%s)", cmd, findcmd(clientCmdCode, cmd)); proto_tree_add_item_format(icq_tree, hf_icq_sessionid, offset+ICQ5_CL_SESSIONID, 4, sessionid, "Session ID: 0x%08x", sessionid); proto_tree_add_item_format(icq_tree, hf_icq_checkcode, offset+ICQ5_CL_CHECKCODE, 4, key, "Key: 0x%08x", key); proto_tree_add_item_format(icq_tree, hf_icq_uin, offset+ICQ5_CL_UIN, 4, uin, "UIN: %ld (0x%08X)", uin, uin); proto_tree_add_text(icq_tree, offset + ICQ5_CL_SEQNUM1, 2, "Seqnum1: 0x%04x", seqnum1); proto_tree_add_text(icq_tree, offset + ICQ5_CL_SEQNUM1, 2, "Seqnum2: 0x%04x", seqnum2); ti = proto_tree_add_text(icq_tree, offset, pktsize, "Decoded packet"); icq_decode_tree = proto_item_add_subtree(ti, ETT_CL_ICQ_DECODE); proto_tree_add_hexdump(icq_decode_tree, offset, decr_pd, pktsize); } } static void dissect_icqv5Server(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) { /* Server traffic is easy, not encrypted */ proto_tree *icq_tree = NULL; proto_tree *icq_decode_tree = NULL; proto_item *ti = NULL; u_int16_t version, cmd; u_int32_t uin, sessionid; u_int32_t pktsize; uin = pletohl(&pd[offset + ICQ5_SRV_UIN]); cmd = pletohs(&pd[offset + ICQ5_SRV_CMD]); sessionid = pletohl(&pd[offset + ICQ5_SRV_SESSIONID]); version = pletohs(&pd[offset + ICQ_VERSION]); pktsize = fd->pkt_len - offset; if (tree) { ti = proto_tree_add_item_format(tree, proto_icq, offset, pktsize, NULL, "ICQv5 Server: len %d", pktsize); icq_tree = proto_item_add_subtree(ti, ETT_SRV_ICQ); proto_tree_add_item_format(icq_tree, hf_icq_cmd, offset + ICQ5_SRV_CMD, 2, cmd, "Command: %d (%s)", cmd, findcmd(serverCmdCode, cmd)); proto_tree_add_item_format(icq_tree, hf_icq_uin, offset+ICQ5_SRV_UIN, 4, uin, "UIN: %ld", uin); proto_tree_add_item_format(icq_tree, hf_icq_sessionid, offset+ICQ5_SRV_SESSIONID, 4, sessionid, "Session ID: 0x%08x", sessionid); ti = proto_tree_add_text(icq_tree, offset, pktsize, "Decoded packet"); icq_decode_tree = proto_item_add_subtree(ti, ETT_CL_ICQ_DECODE); proto_tree_add_hexdump(icq_decode_tree, offset, pd+offset, pktsize); } } void dissect_icqv5(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) { u_int32_t unknown = pletohl(&pd[offset + ICQ5_UNKNOWN]); if (check_col(fd, COL_PROTOCOL)) col_add_str(fd, COL_PROTOCOL, "ICQv5 (UDP)"); if (check_col(fd, COL_INFO)) col_add_str(fd, COL_INFO, "ICQv5 packet"); if (unknown == 0x0L) { dissect_icqv5Client(pd, offset, fd, tree); } else { dissect_icqv5Server(pd, offset, fd, tree); } } void dissect_icq(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) { int version = 0; version = pletohs(&pd[offset + ICQ_VERSION]); switch (version) { case 0x0005: dissect_icqv5(pd, offset, fd, tree); break; case 0x0002: dissect_icqv2(pd, offset, fd, tree); break; default: fprintf(stderr, "ICQ: Unknown version\n"); break; } } /* registration with the filtering engine */ void proto_register_icq(void) { static hf_register_info hf[] = { { &hf_icq_uin, {"UIN", "icq.uin", FT_UINT32, BASE_DEC, NULL, 0x0, ""}}, { &hf_icq_sessionid, {"SessionID", "icq.sessionid", FT_UINT32, BASE_HEX, NULL, 0x0, ""}}, { &hf_icq_cmd, {"Command", "icq.cmd", FT_UINT16, BASE_DEC, NULL, 0x0, ""}}, { &hf_icq_checkcode, {"Checkcode", "icq.checkcode", FT_UINT32, BASE_HEX, NULL, 0x0, ""}}, { &hf_icq_decode, {"Decode", "icq.decode", FT_STRING, BASE_NONE, NULL, 0x0, ""}} }; proto_icq = proto_register_protocol ("ICQ Protocol", "icq"); proto_register_field_array(proto_icq, hf, array_length(hf)); }
- Follow-Ups:
- Re: [ethereal-dev] packet-icq.c ICQ dissector
- From: Guy Harris
- Re: [ethereal-dev] packet-icq.c ICQ dissector
- Prev by Date: [ethereal-dev] SIGSEGV while "Match Selected"
- Next by Date: Re: [ethereal-dev] 4s printed on Quit
- Previous by thread: Re: [ethereal-dev] SIGSEGV while "Match Selected"
- Next by thread: Re: [ethereal-dev] packet-icq.c ICQ dissector
- Index(es):