czat.onet -- opis protokołu (DRAFT)

Wstęp

Czat onet'u jest jednym z najpopularniejszych polskich czatów via WWW. Do korzystania z niego konieczna jest graficzna przeglądarka i maszyna wirtualna Javy. Autor artykułu z czystej ciekawości kilkukrotnie podchodził do problemu reverse engineeringu protokołów używanych przez popularne czaty. Od samego początku onet.czat wykorzystywał protokół dość zbliżony do IRCa, uzupełniony o proste metody autoryzacji. Na początku polegały one na przekazaniu komendy ZUOAUTH oraz nick'a w postaci heksadecymalnego łańcucha wraz z pewną stałą. Metoda ta została dość szybko wycofana po mojej pierwszej publikacji na ten temat. Kolejne wersje zawierają nieco bardziej skomplikowany schemat autoryzacji typu challenge-and-response. Informacje opublikowane w artykule pochodzą wyłącznie z reverse engineeringu klienta.

Protokół

Po ustanowieniu połączenia, autoryzacja przebiega w następujący sposób:

klient: AUTHKEY
serwer: [hostname] 006 * :[key]
klient: AUTHKEY [key2]
klient: ONETAUTH LOCAL [nick] * [wersja]

Gdzie [key] jest 16 znakowym łańcuchem tekstowym podanym przez serwer, a [key2] podobnym łancuchem, poddanym pewnym przekształceniom przez klienta.

Patch

Poniżej publikuję patcha na klienta IRC BitchX 1.0c19, pozwalającego na korzystanie z czat.onet. Użycie wygląda następująco:

$ BitchX -p 5013 nick czat-s.onet.pl

--- BitchX/source/numbers.c.orig	2002-02-28 05:22:50.000000000 +0100
+++ BitchX/source/numbers.c	2002-12-09 02:35:26.000000000 +0100
@@ -70,6 +70,92 @@
 
 char	*thing_ansi = NULL;
 
+
+    static char s1[17];
+
+    static int f1[] = {
+        14, 1, 48, 40, 34, 30, 52, 59, 2, 59,
+        6, 60, 22, 13, 9, 33, 19, 14, 58, 27,
+        22, 15, 36, 46, 8, 11, 44, 59, 30, 0,
+        23, 45, 2, 10, 23, 37, 41, 13, 34, 43,
+        11, 40, 42, 34, 53, 52, 6, 11, 4, 2,
+        38, 26, 18, 12, 10, 27, 24, 55, 25, 54,
+        55, 49, 38, 58, 59, 0, 33, 39, 14, 5,
+        21, 25, 45, 1, 60, 37, 53, 4
+    };
+    static int f2[] = {
+        25, 43, 43, 47, 41, 36, 32, 9, 44, 20,
+        49, 7, 55, 22, 10, 20, 15, 53, 5, 59,
+        30, 37, 20, 27, 11, 14, 41, 3, 17, 1,
+        23, 42, 45, 4, 28, 24, 41, 60, 34, 23,
+        19, 21, 31, 12, 43, 41, 32, 59, 32, 37,
+        56, 0, 13, 15, 28, 25, 29, 8, 28, 47,
+        9, 51, 27, 55, 55, 55, 18, 34, 54, 52,
+        58, 11, 12, 28, 24, 56, 7, 57
+    };
+    static int f3[] = {
+        27, 48, 27, 16, 3, 29, 56, 44, 5, 37,
+        45, 60, 59, 47, 58, 38, 29, 27, 46, 5,
+        51, 54, 7, 6, 49, 11, 54, 45, 13, 38,
+        60, 40, 24, 26, 57, 28, 55, 52, 11, 60,
+        28, 57, 58, 25, 42, 55, 2, 10, 21, 48,
+        16, 10, 40, 23, 16, 28, 35, 9, 11, 48,
+        47, 9, 27, 10, 35, 22, 38, 29, 13, 49,
+        28, 41, 45, 25, 4, 25, 19, 6
+    };
+    static int p1[] = {
+        1, 9, 12, 7, 2, 6, 0, 5, 3, 11,
+        4, 14, 10, 8, 13, 15
+    };
+    static int p2[] = {
+        12, 5, 1, 4, 15, 0, 2, 11, 10, 14,
+        7, 6, 8, 3, 9, 13
+    };
+
+char *transformKey(char *s) {
+	char ai[16], ai1[16], c;
+	int i, j, k, l, i1, j1, k1, l1, i2;
+
+        if (strlen(s) < 16)
+            return "(key to short)";
+
+        for(i = 0; i < 16; i++)
+        {
+            c = s[i];
+            ai[i] = (c> '9' ? c> 'Z' ? (c - 97) + 36 : (c - 65) + 10 : c - 48);
+        }
+
+        for(j = 0; j < 16; j++)
+            ai[j] = f1[ai[j] + j];
+
+	memcpy(ai1, ai, sizeof(ai1));
+
+        for(k = 0; k < 16; k++)
+            ai[k] = (ai[k] + ai1[p1[k]]) % 62;
+
+        for(l = 0; l < 16; l++)
+            ai[l] = f2[ai[l] + l];
+
+	memcpy(ai1, ai, sizeof(ai1));
+
+        for(i1 = 0; i1 < 16; i1++)
+            ai[i1] = (ai[i1] + ai1[p2[i1]]) % 62;
+
+        for(j1 = 0; j1 < 16; j1++)
+            ai[j1] = f3[ai[j1] + j1];
+
+        for(k1 = 0; k1 < 16; k1++)
+        {
+            l1 = ai[k1];
+            ai[k1] = l1>= 10 ? l1>= 36 ? (97 + l1) - 36 : (65 + l1) - 10 : 48 + l1;
+	s1[k1] = ai[k1];
+        }
+
+	s1[16]='\0';
+
+	return s1;
+    }
+
 /*
  * numeric_banner: This returns in a static string of either "xxx" where
  * xxx is the current numeric, or "***" if SHOW_NUMBERS is OFF
@@ -549,6 +635,13 @@
 			send_to_server("COOKIE %s", s);
 		break;
 	}
+	case 6:
+	{
+		send_to_server("AUTHKEY %s", transformKey(*ArgList));
+		send_to_server("ONETAUTH LOCAL %s * 2.1.1", get_server_nickname(from_server));
+		break;
+	}
+
 	case 4:	/* #define RPL_MYINFO           004 */
 	{
 		got_initial_version_28(ArgList);
--- BitchX/source/server.c.orig	2002-03-25 06:21:24.000000000 +0100
+++ BitchX/source/server.c	2002-12-09 02:41:32.000000000 +0100
@@ -2236,6 +2236,7 @@
 void	register_server (int ssn_index, char *nick)
 {
 	int old_from_server = from_server;
+/*
 	if (server_list[ssn_index].password)
 		my_send_to_server(ssn_index, "PASS %s", server_list[ssn_index].password);
 		
@@ -2243,8 +2244,10 @@
 			(send_umode && *send_umode) ? send_umode :
 			(LocalHostName?LocalHostName:hostname),
 			username, *realname ? realname : space);
+*/
 
-	change_server_nickname(ssn_index, nick);
+	accept_server_nickname(ssn_index, nick);
+	my_send_to_server(ssn_index, "AUTHKEY");
 
 	server_list[ssn_index].login_flags &= ~LOGGED_IN;
 	server_list[ssn_index].login_flags &= ~CLOSE_PENDING;
@@ -2755,8 +2758,6 @@
 			reset_nickname(ssn_index);
 	}
 
-	if (server_list[ssn_index].s_nickname)
-		my_send_to_server(ssn_index, "NICK %s", server_list[ssn_index].s_nickname);
 }
 
 void	accept_server_nickname (int ssn_index, char *nick)

Creative Commons License
Wszystkie materiały na mojej stronie dostępne są na licencji Creative Commons Uznanie autorstwa-Użycie niekomercyjne-Na tych samych warunkach 2.5 Polska.