/*
    ettercap -- dissector for ICQ -- 2000 v5 -- UDP 4000
                                  -- 2001 v7 -- TCP 5190

    Copyright (C) 2001 ALoR <alor@users.sourceforge.net>, NaGA <crwm@freemail.it>

    Additional Copyright for this file:  LnZ Lorenzo Porro <lporro@libero.it>

    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.

    $Id: ec_dissector_icq.c,v 1.19 2002/04/19 14:57:49 alor Exp $
*/

#include "include/ec_main.h"

#include "include/ec_dissector.h"
#include "include/ec_inet_structures.h"

#define ICQ_HEADER_LENGTH              0x0018
#define CMD_LOGIN                      0x03E8
#define CMD_LOGIN_OFFSET               0x000E
#define CMD_LOGIN_PASS_LENGHT_OFFSET   0x0008
#define CMD_LOGIN_UIN_OFFSET           0x0006
#define CMD_LOGIN_PASS_OFFSET          0x000A


const u_char table [] = {
   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
};

const u_char pwdKey[] = {           // XORed with the password
   0xF3, 0x26, 0x81, 0xC4, 0x39, 0x86, 0xDB, 0x92,
   0x71, 0xA3, 0xB9, 0xE6, 0x53, 0x7A, 0x95, 0x7C
};


typedef struct {
   u_char type[2];
   u_char len[2];
} TLV_HEADER;

typedef struct {
   u_char cmd;
   u_char chan;
   u_char seq[2];
   u_char dlen[2];
} FLAP_HEADER;       //FLAP Header at the beginning of the packet

typedef struct {
   u_char family[2];
   u_char command[2];
   u_char flags[2];
   u_char reference[4];
} SNAC_HEADER;

// protos

FUNC_DISSECTOR(Dissector_icqv5);
FUNC_DISSECTOR(Dissector_icqv7);

unsigned long get_key(u_char *data, short datalen);
int Decode_icq(u_char *data, short datalen);
void Decode_Pwdv7(char *pwd, char *outpwd);

// --------------------


unsigned long get_key(u_char *data, short datalen)
{
   u_long A[6] = {0, 0, 0, 0, 0, 0};
   u_long key;
   u_long check;

   check = *(u_long *)(data + 0x14);

   A[1] = check & 0x0001F000;
   A[2] = check & 0x07C007C0;
   A[3] = check & 0x003E0001;
   A[4] = check & 0xF8000000;
   A[5] = check & 0x0000083E;
   A[1] = A[1] >> 0x0C;
   A[2] = A[2] >> 0x01;
   A[3] = A[3] << 0x0A;
   A[4] = A[4] >> 0x10;
   A[5] = A[5] << 0x0F;
   check = A[5] + A[1] + A[2] + A[3] + A[4];
   key = datalen * 0x68656C6C;
   key += check;
   return key;
}



int Decode_icq(u_char *data, short datalen )
{
   unsigned long key,i,k;

   if (datalen <= 0x14 + sizeof(unsigned long)) return 0;// Not enough data to decode
   key = get_key(data, datalen);

   for (i=0x0a; i < datalen+3; i+=4 )
   {
      k = key+table[i&0xff];
      if ( i != 0x16 )
      {
         data[i] ^= (u_char)(k & 0xff);
         data[i+1] ^= (u_char)((k & 0xff00)>>8);
      }
      if ( i != 0x12 ) {
         data[i+2] ^= (u_char)((k & 0xff0000)>>16);
         data[i+3] ^= (u_char)((k & 0xff000000)>>24);
      }
   }
   return 0;
}



void Decode_Pwdv7(char *pwd, char *outpwd)
{
   int x;
   for(x=0; x< strlen(pwd); x++)
      *(outpwd+x) = pwd[x] ^ pwdKey[x];
   return;
}



FUNC_DISSECTOR(Dissector_icqv5)
{
   UDP_header *udp;
   u_char *payload;
   char *password = NULL; // ;)
   u_long pwdlen = -1;
   u_char collector[MAX_DATA];
   DATA_DISSECTOR;

   udp = (UDP_header *) data;

   payload = (char *) (int)udp + UDP_HEADER;

   //if (ntohs(udp->source) == SERV_PORT) return 0;  // Skip server messages...
   if (data_to_ettercap->datalen == 0) return 0;   // No data...

   memset(collector, 0, MAX_DATA);

   if (ptohs(payload) == 5) { // version 5

      if (Conn_Mode) {

         memcpy(collector, payload, data_to_ettercap->datalen);

         Decode_icq(collector, data_to_ettercap->datalen); // decrypting....

         if ( ptohs(collector + CMD_LOGIN_OFFSET) != CMD_LOGIN) return 0; // Not a login packet

         snprintf(data_to_ettercap->user, sizeof(data_to_ettercap->user)-1, "%d (ICQ UIN)\n", ptohl(collector + CMD_LOGIN_UIN_OFFSET));  // the login (UIN)

         DEBUG_MSG("\tDissector_ICQ 5 - LOGIN ");

         pwdlen = ptohs(collector + ICQ_HEADER_LENGTH + CMD_LOGIN_PASS_LENGHT_OFFSET);

         if (pwdlen > 28) pwdlen = 28;

         password = (char *) calloc(pwdlen+1, sizeof(char) );

         strlcpy(password,(char *)(collector + ICQ_HEADER_LENGTH + CMD_LOGIN_PASS_OFFSET), pwdlen+1);

         snprintf(data_to_ettercap->pass, sizeof(data_to_ettercap->pass), "%s\n", password);
         sprintf(data_to_ettercap->type, "ICQ");

         free(password);

         DEBUG_MSG("\tDissector_ICQ 5 - PASS ");

      } else {

         memcpy(collector, payload, sniff_data_to_ettercap->datasize);

         Decode_icq(collector, sniff_data_to_ettercap->datasize); // decrypting....
         memcpy(sniff_data_to_ettercap->data, collector, sniff_data_to_ettercap->datasize);
      }
   } // end of version 5


   return 0;
}


FUNC_DISSECTOR(Dissector_icqv7)
{
   TCP_header *tcp;
   FLAP_HEADER *flap;
   u_char *payload;
   char *password = NULL; // ;)
   char *pwdtemp = NULL;
   u_char collector[MAX_DATA];
   DATA_DISSECTOR;

   tcp = (TCP_header *) data;

   payload = (char *)((int)tcp + tcp->doff * 4);

   //if (ntohs(udp->source) == SERV_PORT) return 0;  // Skip server messages...
   if (data_to_ettercap->datalen == 0) return 0;   // No data...

   memset(collector, 0, MAX_DATA);

   if (payload[0] == 0x2a && payload[1] <= 4) {   // version 7 ??
                                                  // we try to recognize the protocol
      flap = (FLAP_HEADER *) payload;

      if (Conn_Mode) {

         if(flap->chan == 1) {   //channel 1 -> login sequence

            TLV_HEADER *tlv;

            payload += sizeof(FLAP_HEADER);  //jump the flap header
            memcpy(collector, payload, data_to_ettercap->datalen);

            tlv = (TLV_HEADER *) collector;

            if (tlv->type[1] != 0 || tlv->len[1] != 1) return 0;  // server HELLO 0000 0001

            tlv = tlv + 1;

            DEBUG_MSG("\tTLV TYPE [%d] should be [1]", tlv->type[1]);
            
            if (tlv->type[1] == 1) {   // login type

               DEBUG_MSG("\tDissector_ICQ 7 - LOGIN ");

               snprintf(data_to_ettercap->user, sizeof(data_to_ettercap->user)-1, "%s (ICQ UIN)\n", (char *)(tlv+1) );

            }

            tlv = (TLV_HEADER *) ((char*)tlv + sizeof(TLV_HEADER) + tlv->len[1]);

            DEBUG_MSG("\tTLV TYPE [%d] should be [2]", tlv->type[1]);
            
            if (tlv->type[1] == 2) {   // login type

               pwdtemp = strdup((char *)(tlv + 1));
               password = calloc(strlen(pwdtemp), sizeof(char));

               //DEBUG_MSG("\tDissector_ICQ 7 - PASS - [%s][%s][%d]", pwdtemp, password, strlen(pwdtemp));

               Decode_Pwdv7(pwdtemp, password);

               snprintf(data_to_ettercap->pass, sizeof(data_to_ettercap->pass), "%s\n", password);

               DEBUG_MSG("\tDissector_ICQ 7 - PASS");

               free(password);
            }

            sprintf(data_to_ettercap->type, "ICQ v7/8");

         }

      } else {
         
         return 0;  /* not yet implemented */

#if 0         
         SNAC_HEADER *snac;
         u_char *p, *end;
         char *uin, *message;
         
         memcpy(collector, payload, sniff_data_to_ettercap->datasize);

         end = collector + sniff_data_to_ettercap->datasize;
         
         flap = (FLAP_HEADER *) collector;
         snac = (SNAC_HEADER *) (flap + 1);
         
         if (snac->family[1] != 4 || snac->command[1] != 6) return 0;   /* not a message */
        
         DEBUG_MSG("ICQ DATA -- SNAC 4 6");
         
         for(p = (u_char *)snac; memcmp(p, "\x00\x00\x00\x01", 4) && p < end; p++); /* find the server hello */
         if (p == end) return 0;
         p += 4;

         DEBUG_MSG("ICQ DATA -- UIN");
         
         uin = strdup ( p + 1 );

         for(p = (u_char *)snac; memcmp(p, "\x00\x00\xff\xff", 4) && p < end; p++); /* find the message */
         if (p == end) return 0;
         p += 4;

         DEBUG_MSG("ICQ DATA -- message");
         
         message = strdup ( p );

         memset(sniff_data_to_ettercap->data, 0, sizeof(sniff_data_to_ettercap->data));
         sprintf(sniff_data_to_ettercap->data, "*** ICQ MESSAGE ***\n To: %s\n\n Message: %s\n\n", uin, message);
         sniff_data_to_ettercap->datasize = strlen(sniff_data_to_ettercap->data);

         free(uin);
         free(message);
#endif
         
      }

   } // end of version 7

   return 0;
}


/* EOF */

// vim:ts=3:expandtab
