1 /* packet-tibia.c
2 * Routines for Tibia/OTServ login and game protocol dissection
3 *
4 * Copyright 2017, Ahmad Fatoum <ahmad[AT]a3f.at>
5 *
6 * A dissector for:
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
10 *
11 * SPDX-License-Identifier: GPL-2.0-or-later
12 */
13
14
15 /* Tibia (https://tibia.com) is a Massively Multiplayer Online Role-Playing
16 * Game (MMORPG) by Cipsoft GmbH.
17 *
18 * Three official clients exist: The current Qt-based 11.0+ client,
19 * the old C++ client used from Tibia 7.0 till 10.99 and the Flash client.
20 * The latter two are being phased out. They use the same protocol,
21 * except that the session key for the Flash client is transported alongside
22 * the character list over HTTPS. It's possible this is done in the same manner
23 * as in the native client from 10.74 up. We don't support the Flash client.
24 *
25 * The dissector supports Tibia versions from 7.0 (2001) till
26 * 11.00 (2016-10-12). Tibia has an active open source server emulator
27 * community (OTServ) that still makes use of older versions and surpasses
28 * the official servers in popularity, therefore compatibility with older
29 * protocol iterations should be maintained.
30 *
31 * Transport is over TCP, with recent versions encrypting player interaction
32 * with XTEA. Authentication and key exchange is done with a hard-coded
33 * RSA public key in the client.
34 *
35 * Two protocols are dissected: The Tibia login protocol and the Tibia game
36 * protocol. Traditionally, login servers were stateless and only responsible
37 * for providing the addresses of the game servers alongside the character
38 * list upon successful authentication. Then a new authentication request
39 * (this time with character selection) is sent to the game server.
40 * That way, a client who knows the game server address can very well skip
41 * the login server entirely. Starting with 10.61, this is no longer possible,
42 * as the login server provides a session key that needs to be sent to the
43 * game server.
44 *
45 * Starting with Tibia 7.61, login server requests can't be reliably
46 * differentiated from game server requests. Therefore we apply some heuristics
47 * to classify packets.
48 *
49 * Starting with Tibia 11.01, a web service takes the role of the login server.
50 * Starting with Tibia 11.11, the Adler32 checksum was replaced by a 32-bit
51 * sequence number. The most significant bit indicates whether the packet was
52 * DEFLATE-compressed. These features are not yet supported.
53 *
54 * Packets from and to the game server contain commands. Commands are
55 * identified by the first octet and are variable in length. The dissector has
56 * most command names hard-coded. However, a complete implementation of the
57 * game protocol is unlikely.
58 *
59 * The RSA private key usually used by OTServ is hard-coded in. Server
60 * administrators may add their own private key in PEM or PKCS#12 format over
61 * an UAT. For servers where the private key is indeed private (like
62 * for official servers), the symmetric XTEA key (retrievable by memory
63 * peeking or MitM) may be provided to the dissector via UAT.
64 *
65 * Unsurprisingly, no official specification of the protocol exist, following
66 * resources have been written by the community:
67 *
68 * - OTServ: Community effort to replicate a Tibia Server.
69 * - Outcast: A Tibia client implementation of the game protocol as of 2006.
70 * Comes with a PDF spec written by Khaos
71 * - TibiaAPI: Bot framework, containing a listing of commands as of 2009
72 * - TFS: OTServ-Fork which is kept up-to-date with most of the official protocol
73 * - otclient: Open Source implementation of an up-to-date Tibia client
74 *
75 * An official slide set by Cipsoft detailing the architecture of Tibia
76 * from Game Developers Conference Europe 2011 is also available:
77 * http://www.gdcvault.com/play/1014908/Inside-Tibia-The-Technical-Infrastructure
78 *
79 * The login protocol, as implemented here, has been inferred from network
80 * footage and game client execution traces and was written from scratch.
81 * The listing of game protocol commands were taken from TibiaAPI and Khaos' spec
82 * No code of Cipsoft GmbH was used.
83 *
84 * Tibia is a registered trademark of Cipsoft GmbH.
85 */
86
87 #include "config.h"
88 #include <epan/packet.h>
89 #include "packet-tcp.h"
90 #include <wsutil/adler32.h>
91 #include <epan/address.h>
92 #include <epan/to_str.h>
93 #include <epan/prefs.h>
94 #include <epan/uat.h>
95 #include <epan/conversation.h>
96 #include <epan/value_string.h>
97 #include <epan/expert.h>
98 #include <epan/address.h>
99 #include <wsutil/filesystem.h>
100 #include <wsutil/file_util.h>
101 #include <wsutil/wsgcrypt.h>
102 #include <wsutil/report_message.h>
103 #include <wsutil/xtea.h>
104 #include <wsutil/strtoi.h>
105 #include <wsutil/rsa.h>
106 #include <errno.h>
107 #include <epan/ws_printf.h>
108 #include <epan/ptvcursor.h>
109
110 void proto_register_tibia(void);
111 void proto_reg_handoff_tibia(void);
112
113 /* preferences */
114 static gboolean try_otserv_key = TRUE,
115 show_char_name = TRUE,
116 show_acc_info = TRUE,
117 show_xtea_key = FALSE,
118 dissect_game_commands = FALSE,
119 reassemble_tcp_segments = TRUE;
120
121 /* User Access Tables */
122 #if HAVE_LIBGNUTLS
123 struct rsakey {
124 address addr;
125 guint16 port;
126
127 gcry_sexp_t privkey;
128 };
129 GHashTable *rsakeys;
130
131 struct rsakeys_assoc {
132 char *ipaddr;
133 char *port;
134
135 char *keyfile;
136 char *password;
137 };
138
139 UAT_CSTRING_CB_DEF(rsakeylist_uats, ipaddr, struct rsakeys_assoc)
140 UAT_CSTRING_CB_DEF(rsakeylist_uats, port, struct rsakeys_assoc)
141 UAT_FILENAME_CB_DEF(rsakeylist_uats, keyfile, struct rsakeys_assoc)
142 UAT_CSTRING_CB_DEF(rsakeylist_uats, password, struct rsakeys_assoc)
143
144 static void rsakey_free(void *_rsakey);
145
146 static uat_t *rsakeys_uat = NULL;
147 static struct rsakeys_assoc *rsakeylist_uats = NULL;
148 static guint nrsakeys = 0;
149 #endif
150
151 #define XTEA_KEY_LEN 16
152
153 struct xteakeys_assoc {
154 guint32 framenum;
155
156 char *key;
157 };
158 GHashTable *xteakeys;
159
160 static void *xteakeys_copy_cb(void *, const void *, size_t);
161 static void xteakeys_free_cb(void *);
162 static void xtea_parse_uat(void);
163 static gboolean xteakeys_uat_fld_key_chk_cb(void *, const char *, guint, const void *, const void *, char **);
164
165 UAT_DEC_CB_DEF(xteakeylist_uats, framenum, struct xteakeys_assoc)
166 UAT_CSTRING_CB_DEF(xteakeylist_uats, key, struct xteakeys_assoc)
167
168 static uat_t *xteakeys_uat = NULL;
169 static struct xteakeys_assoc *xteakeylist_uats = NULL;
170 static guint nxteakeys = 0;
171
172 #define COND_POISONED 0x1
173 #define COND_BURNING 0x2
174 #define COND_ELECTROCUTED 0x4
175 #define COND_DRUNK 0x8
176 #define COND_MANASHIELD 0x10
177 #define COND_PARALYZED 0x20
178 #define COND_HASTE 0x40
179 #define COND_BATTLE 0x80
180 #define COND_DROWNING 0x100
181 #define COND_FREEZING 0x200
182 #define COND_DAZZLED 0x400
183 #define COND_CURSED 0x800
184 #define COND_BUFF 0x1000
185 #define COND_PZBLOCK 0x2000
186 #define COND_PZ 0x4000
187 #define COND_BLEEDING 0x8000
188 #define COND_HUNGRY 0x10000
189
190 /* The login server has been traditionally on 7171,
191 * For OTServ, the game server often listens on the same IP/port,
192 * but occasionally on 7172. Official Tibia doesn't host login and
193 * game servers on the same IP address
194 */
195
196 #define TIBIA_DEFAULT_TCP_PORT_RANGE "7171,7172"
197
198 static gint proto_tibia = -1;
199
200 static gint hf_tibia_len = -1;
201 static gint hf_tibia_nonce = -1;
202 static gint hf_tibia_adler32 = -1;
203 static gint hf_tibia_adler32_status = -1;
204 static gint hf_tibia_os = -1;
205 static gint hf_tibia_proto_version = -1;
206 static gint hf_tibia_client_version = -1;
207 static gint hf_tibia_file_versions = -1;
208 static gint hf_tibia_file_version_spr = -1;
209 static gint hf_tibia_file_version_dat = -1;
210 static gint hf_tibia_file_version_pic = -1;
211 static gint hf_tibia_game_preview_state = -1;
212 static gint hf_tibia_content_revision = -1;
213 static gint hf_tibia_undecoded_rsa_data = -1;
214 static gint hf_tibia_undecoded_xtea_data = -1;
215 static gint hf_tibia_unknown = -1;
216 static gint hf_tibia_xtea_key = -1;
217 static gint hf_tibia_loginflags_gm = -1;
218 static gint hf_tibia_acc_name = -1;
219 static gint hf_tibia_acc_number = -1;
220 static gint hf_tibia_session_key = -1;
221 static gint hf_tibia_char_name = -1;
222 static gint hf_tibia_acc_pass = -1;
223 static gint hf_tibia_char_name_convo = -1;
224 static gint hf_tibia_acc_name_convo = -1;
225 static gint hf_tibia_acc_pass_convo = -1;
226 static gint hf_tibia_session_key_convo = -1;
227
228 static gint hf_tibia_client_info = -1;
229 static gint hf_tibia_client_locale = -1;
230 static gint hf_tibia_client_locale_id = -1;
231 static gint hf_tibia_client_locale_name = -1;
232 static gint hf_tibia_client_ram = -1;
233 static gint hf_tibia_client_cpu = -1;
234 static gint hf_tibia_client_cpu_name = -1;
235 static gint hf_tibia_client_clock = -1;
236 static gint hf_tibia_client_clock2 = -1;
237 static gint hf_tibia_client_gpu = -1;
238 static gint hf_tibia_client_vram = -1;
239 static gint hf_tibia_client_resolution = -1;
240 static gint hf_tibia_client_resolution_x = -1;
241 static gint hf_tibia_client_resolution_y = -1;
242 static gint hf_tibia_client_resolution_hz = -1;
243
244 static gint hf_tibia_payload_len = -1;
245 static gint hf_tibia_loginserv_command = -1;
246 static gint hf_tibia_gameserv_command = -1;
247 static gint hf_tibia_client_command = -1;
248
249 static gint hf_tibia_motd = -1;
250 static gint hf_tibia_dlg_error = -1;
251 static gint hf_tibia_dlg_info = -1;
252
253 static gint hf_tibia_charlist = -1;
254 static gint hf_tibia_charlist_length = -1;
255 static gint hf_tibia_charlist_entry_name = -1;
256 static gint hf_tibia_charlist_entry_world = -1;
257 static gint hf_tibia_charlist_entry_ip = -1;
258 static gint hf_tibia_charlist_entry_port = -1;
259
260 static gint hf_tibia_worldlist = -1;
261 static gint hf_tibia_worldlist_length = -1;
262 static gint hf_tibia_worldlist_entry_name = -1;
263 static gint hf_tibia_worldlist_entry_ip = -1;
264 static gint hf_tibia_worldlist_entry_port = -1;
265 static gint hf_tibia_worldlist_entry_preview = -1;
266 static gint hf_tibia_worldlist_entry_id = -1;
267 static gint hf_tibia_pacc_days = -1;
268
269 static gint hf_tibia_channel_id = -1;
270 static gint hf_tibia_channel_name = -1;
271
272 static gint hf_tibia_char_cond = -1;
273 static gint hf_tibia_char_cond_poisoned = -1;
274 static gint hf_tibia_char_cond_burning = -1;
275 static gint hf_tibia_char_cond_electrocuted = -1;
276 static gint hf_tibia_char_cond_drunk = -1;
277 static gint hf_tibia_char_cond_manashield = -1;
278 static gint hf_tibia_char_cond_paralyzed = -1;
279 static gint hf_tibia_char_cond_haste = -1;
280 static gint hf_tibia_char_cond_battle = -1;
281 static gint hf_tibia_char_cond_drowning = -1;
282 static gint hf_tibia_char_cond_freezing = -1;
283 static gint hf_tibia_char_cond_dazzled = -1;
284 static gint hf_tibia_char_cond_cursed = -1;
285 static gint hf_tibia_char_cond_buff = -1;
286 static gint hf_tibia_char_cond_pzblock = -1;
287 static gint hf_tibia_char_cond_pz = -1;
288 static gint hf_tibia_char_cond_bleeding = -1;
289 static gint hf_tibia_char_cond_hungry = -1;
290
291 static int * const char_conds[] = {
292 &hf_tibia_char_cond_poisoned,
293 &hf_tibia_char_cond_burning,
294 &hf_tibia_char_cond_electrocuted,
295 &hf_tibia_char_cond_drunk,
296 &hf_tibia_char_cond_manashield,
297 &hf_tibia_char_cond_paralyzed,
298 &hf_tibia_char_cond_haste,
299 &hf_tibia_char_cond_battle,
300 &hf_tibia_char_cond_drowning,
301 &hf_tibia_char_cond_freezing,
302 &hf_tibia_char_cond_dazzled,
303 &hf_tibia_char_cond_cursed,
304 &hf_tibia_char_cond_buff,
305 &hf_tibia_char_cond_pzblock,
306 &hf_tibia_char_cond_pz,
307 &hf_tibia_char_cond_bleeding,
308 &hf_tibia_char_cond_hungry,
309 NULL
310 };
311
312 static gint hf_tibia_chat_msg = -1;
313 static gint hf_tibia_speech_type = -1;
314
315 static gint hf_tibia_coords_x = -1;
316 static gint hf_tibia_coords_y = -1;
317 static gint hf_tibia_coords_z = -1;
318 static gint hf_tibia_coords = -1;
319 static gint hf_tibia_stackpos = -1;
320
321 #if 0
322 static gint hf_tibia_item = -1;
323 #endif
324 static gint hf_tibia_container = -1;
325 static gint hf_tibia_container_icon = -1;
326 static gint hf_tibia_container_slot = -1;
327 static gint hf_tibia_container_slots = -1;
328 static gint hf_tibia_inventory = -1;
329 static gint hf_tibia_vip = -1;
330 static gint hf_tibia_vip_online = -1;
331 static gint hf_tibia_player = -1;
332 static gint hf_tibia_creature = -1;
333 static gint hf_tibia_creature_health = -1;
334 static gint hf_tibia_window = -1;
335 static gint hf_tibia_window_icon = -1;
336 static gint hf_tibia_window_textlen = -1;
337 static gint hf_tibia_window_text = -1;
338
339 static gint hf_tibia_light_level = -1;
340 static gint hf_tibia_light_color = -1;
341 static gint hf_tibia_magic_effect_id = -1;
342 static gint hf_tibia_animated_text_color = -1;
343 static gint hf_tibia_animated_text = -1;
344 static gint hf_tibia_projectile = -1;
345 static gint hf_tibia_squarecolor = -1;
346 static gint hf_tibia_textmsg_class = -1;
347 static gint hf_tibia_textmsg = -1;
348 static gint hf_tibia_walk_dir = -1;
349
350
351 static gint ett_tibia = -1;
352 static gint ett_command = -1;
353 static gint ett_file_versions = -1;
354 static gint ett_client_info = -1;
355 static gint ett_locale = -1;
356 static gint ett_cpu = -1;
357 static gint ett_resolution = -1;
358 static gint ett_charlist = -1;
359 static gint ett_worldlist = -1;
360 static gint ett_char = -1;
361 static gint ett_world = -1;
362 static gint ett_coords = -1;
363 static gint ett_char_cond = -1;
364
365
366 static expert_field ei_xtea_len_toobig = EI_INIT;
367 static expert_field ei_adler32_checksum_bad = EI_INIT;
368 static expert_field ei_rsa_plaintext_no_leading_zero = EI_INIT;
369 static expert_field ei_rsa_ciphertext_too_short = EI_INIT;
370 static expert_field ei_rsa_decrypt_failed = EI_INIT;
371
372
373 struct proto_traits {
374 guint32 adler32:1, rsa:1, compression:1, xtea:1, login_webservice:1, acc_name:1, nonce:1,
375 extra_gpu_info:1, gmbyte:1, hwinfo:1;
376 guint32 outfit_addons:1, stamina:1, lvl_on_msg:1;
377 guint32 ping:1, client_version:1, game_preview:1, auth_token:1, session_key:1;
378 guint32 game_content_revision:1, worldlist_in_charlist:1;
379 guint string_enc;
380 };
381
382 struct tibia_convo {
383 guint32 xtea_key[XTEA_KEY_LEN / sizeof (guint32)];
384 guint32 xtea_framenum;
385 const guint8 *acc, *pass, *char_name, *session_key;
386 struct proto_traits has;
387
388 guint16 proto_version;
389 guint8 loginserv_is_peer :1;
390 guint16 clientport;
391 guint16 servport;
392
393 gcry_sexp_t privkey;
394 };
395
396 static struct proto_traits
get_version_traits(guint16 version)397 get_version_traits(guint16 version)
398 {
399 struct proto_traits has;
400 memset(&has, 0, sizeof has);
401 has.gmbyte = TRUE; /* Not sure when the GM byte first appeared */
402 has.string_enc = ENC_ISO_8859_1;
403
404 if (version >= 761) /* 761 was a test client. 770 was the first release */
405 has.xtea = has.rsa = TRUE;
406 if (version >= 780)
407 has.outfit_addons = has.stamina = has.lvl_on_msg = TRUE;
408 if (version >= 830)
409 has.adler32 = has.acc_name = TRUE;
410 if (version >= 841)
411 has.hwinfo = has.nonce = TRUE;
412 if (version >= 953)
413 has.ping = TRUE;
414 if (version >= 980)
415 has.client_version = has.game_preview = TRUE;
416 if (version >= 1010)
417 has.worldlist_in_charlist = TRUE;
418 if (version >= 1061)
419 has.extra_gpu_info = TRUE;
420 if (version >= 1071)
421 has.game_content_revision = TRUE;
422 if (version >= 1072)
423 has.auth_token = TRUE;
424 if (version >= 1074)
425 has.session_key = TRUE;
426 if (version >= 1101)
427 has.login_webservice = TRUE;
428 if (version >= 1111) {
429 has.compression = TRUE; /* with DEFLATE */
430 has.adler32 = FALSE;
431 }
432 #if 0 /* With the legacy client being phased out, maybe Unicode support incoming? */
433 if (version >= 11xy)
434 has.string_enc = ENC_UTF_8;
435 #endif
436
437 return has;
438 }
439
440 static guint16
get_version_get_charlist_packet_size(struct proto_traits * has)441 get_version_get_charlist_packet_size(struct proto_traits *has)
442 {
443 guint16 size = 2;
444 if (has->adler32 || has->compression)
445 size += 4;
446 size += 17;
447 if (has->extra_gpu_info)
448 size += 222;
449 if (has->rsa)
450 size += 128;
451
452 return size;
453 }
454 static guint16
get_version_char_login_packet_size(struct proto_traits * has)455 get_version_char_login_packet_size(struct proto_traits *has)
456 {
457 guint16 size = 2;
458 if (has->adler32 || has->compression)
459 size += 4;
460 size += 5;
461 if (has->client_version)
462 size += 4;
463 if (has->game_content_revision)
464 size += 2;
465 if (has->game_preview)
466 size += 1;
467 if (has->rsa)
468 size += 128;
469
470 return size;
471 }
472
473
474 #define XTEA_FROM_UAT 0
475 #define XTEA_UNKNOWN 0xFFFFFFFF
476
477 static struct tibia_convo *
tibia_get_convo(packet_info * pinfo)478 tibia_get_convo(packet_info *pinfo)
479 {
480 conversation_t *epan_conversation = find_or_create_conversation(pinfo);
481
482 struct tibia_convo *convo = (struct tibia_convo*)conversation_get_proto_data(epan_conversation, proto_tibia);
483
484 if (!convo) {
485 address *servaddr;
486 convo = wmem_new0(wmem_file_scope(), struct tibia_convo);
487
488 /* FIXME there gotta be a cleaner way... */
489 if (pinfo->srcport >= 0xC000) {
490 convo->clientport = pinfo->srcport;
491
492 convo->servport = pinfo->destport;
493 servaddr = &pinfo->dst;
494 } else {
495 convo->clientport = pinfo->destport;
496
497 convo->servport = pinfo->srcport;
498 servaddr = &pinfo->src;
499 }
500 (void)servaddr;
501 #ifdef HAVE_LIBGNUTLS
502 struct rsakey rsa_key;
503 rsa_key.port = convo->servport;
504 rsa_key.addr = *servaddr;
505 convo->privkey = (gcry_sexp_t)g_hash_table_lookup(rsakeys, &rsa_key);
506 #endif
507 convo->xtea_framenum = XTEA_UNKNOWN;
508
509 conversation_add_proto_data(epan_conversation, proto_tibia, (void *)convo);
510 }
511
512 if (convo->xtea_framenum == XTEA_UNKNOWN) {
513 guint8 *xtea_key = (guint8*)g_hash_table_lookup(xteakeys, GUINT_TO_POINTER(pinfo->num));
514 if (xtea_key) {
515 memcpy(convo->xtea_key, xtea_key, XTEA_KEY_LEN);
516 convo->xtea_framenum = XTEA_FROM_UAT;
517 }
518 }
519
520 return convo;
521 }
522
523 static guint32
ipv4tonl(const char * str)524 ipv4tonl(const char *str)
525 {
526 guint32 ipaddr = 0;
527 for (int octet = 0; octet < 4; octet++) {
528 ws_strtou8(str, &str, &((guint8*)&ipaddr)[octet]);
529 str++;
530 }
531 return ipaddr;
532 }
533
534 static void
register_gameserv_addr(struct tibia_convo * convo,guint32 ipaddr,guint16 port)535 register_gameserv_addr(struct tibia_convo *convo, guint32 ipaddr, guint16 port)
536 {
537 (void)convo; (void)ipaddr; (void)port;
538 #if HAVE_LIBGNUTLS
539 /* Game servers in the list inherit the same RSA key as the login server */
540 if (convo->has.rsa) {
541 struct rsakey *entry = g_new(struct rsakey, 1);
542 alloc_address_wmem(NULL, &entry->addr, AT_IPv4, sizeof ipaddr, &ipaddr);
543 entry->port = port;
544 entry->privkey = NULL;
545 if (g_hash_table_lookup(rsakeys, entry) == NULL) {
546 entry->privkey = convo->privkey;
547 g_hash_table_insert(rsakeys, entry, entry->privkey);
548 } else {
549 rsakey_free(entry);
550 }
551 }
552
553 /* TODO Mark all communication with the IP/Port pair above
554 * as Tibia communication. How?
555 */
556 #endif
557 }
558
559 static gcry_sexp_t otserv_key;
560 static gcry_sexp_t
convo_get_privkey(struct tibia_convo * convo)561 convo_get_privkey(struct tibia_convo *convo)
562 {
563 return convo->privkey ? convo->privkey
564 : try_otserv_key ? otserv_key
565 : NULL;
566 }
567
568 enum client_cmd {
569 /* from TibiaAPI */
570 C_GET_CHARLIST = 0x01,
571 C_LOGIN_CHAR = 0x0A,
572 C_LOGOUT = 0x14, /* I think this is a 7.7+ thing */
573 C_PONG = 0x1E,
574
575 C_AUTO_WALK = 0x64,
576 C_GO_NORTH = 0x65,
577 C_GO_EAST = 0x66,
578 C_GO_SOUTH = 0x67,
579 C_GO_WEST = 0x68,
580 C_AUTO_WALK_CANCEL = 0x69,
581 C_GO_NE = 0x6A,
582 C_GO_SE = 0x6B,
583 C_GO_SW = 0x6C,
584 C_GO_NW = 0x6D,
585 C_TURN_NORTH = 0x6F,
586 C_TURN_EAST = 0x70,
587 C_TURN_SOUTH = 0x71,
588 C_TURN_WEST = 0x72,
589 C_MOVE_ITEM = 0x78,
590 C_SHOP_BUY = 0x7A,
591 C_SHOP_SELL = 0x7B,
592 C_SHOP_CLOSE = 0x7C,
593 C_ITEM_USE = 0x82,
594 C_ITEM_USE_ON = 0x83,
595 C_ITEM_USE_BATTLELIST = 0x84,
596 C_ITEM_ROTATE = 0x85,
597 C_CONTAINER_CLOSE = 0x87,
598 C_CONTAINER_OPEN_PARENT = 0x88,
599 C_LOOK_AT = 0x8C,
600 C_PLAYER_SPEECH = 0x96,
601 C_CHANNEL_LIST = 0x97,
602 C_CHANNEL_OPEN = 0x98,
603 C_CHANNEL_CLOSE = 0x99,
604 C_PRIVATE_CHANNEL_OPEN = 0x9A,
605 C_NPC_CHANNEL_CLOSE = 0x9E,
606 C_FIGHT_MODES = 0xA0,
607 C_ATTACK = 0xA1,
608 C_FOLLOW = 0xA2,
609 C_CANCEL_GO = 0xBE,
610 C_TILE_UPDATE = 0xC9,
611 C_CONTAINER_UPDATE = 0xCA,
612 C_SET_OUTFIT = 0xD3,
613 C_VIP_ADD = 0xDC,
614 C_VIP_REMOVE = 0xDD
615 };
616
617 static const value_string from_client_packet_types[] = {
618 { C_GET_CHARLIST, "Charlist request" },
619 { C_LOGIN_CHAR, "Character login" },
620
621 { C_LOGOUT, "Logout" },
622 { C_PONG, "Pong" },
623
624 { C_AUTO_WALK, "Map walk" },
625 { C_GO_NORTH, "Go north"},
626 { C_GO_EAST, "Go east"},
627 { C_GO_SOUTH, "Go south"},
628 { C_GO_WEST, "Go west"},
629 { C_AUTO_WALK_CANCEL, "Map walk cancel" },
630 { C_GO_NE, "Go north-east"},
631 { C_GO_SE, "Go south-east"},
632 { C_GO_SW, "Go south-west"},
633 { C_GO_NW, "Go north-west"},
634
635 { C_TURN_NORTH, "Turn north" },
636 { C_TURN_EAST, "Turn east" },
637 { C_TURN_SOUTH, "Turn south" },
638 { C_TURN_WEST, "Turn west" },
639 { C_MOVE_ITEM, "Move item" },
640 { C_SHOP_BUY, "Buy in shop" },
641 { C_SHOP_SELL, "Sell in shop" },
642 { C_SHOP_CLOSE, "Close shop" },
643 { C_ITEM_USE, "Use item" },
644 { C_ITEM_USE_ON, "Use item on" },
645 { C_ITEM_USE_BATTLELIST, "Use item on battle list" },
646 { C_ITEM_ROTATE, "Rotate item" },
647
648 { C_CONTAINER_CLOSE, "Close container" },
649 { C_CONTAINER_OPEN_PARENT, "Open parent container" },
650 { C_LOOK_AT, "Look at" },
651 { C_PLAYER_SPEECH, "Speech" },
652 { C_CHANNEL_LIST, "List channels" },
653 { C_CHANNEL_OPEN, "Open public channel" },
654 { C_CHANNEL_CLOSE, "close channel" },
655 { C_PRIVATE_CHANNEL_OPEN, "Open private channel" },
656 { C_NPC_CHANNEL_CLOSE, "Open NPC channel" },
657 { C_FIGHT_MODES, "Set fight modes" },
658 { C_ATTACK, "Attack" },
659 { C_FOLLOW, "Follow" },
660 { C_CANCEL_GO, "Cancel go" },
661 { C_TILE_UPDATE, "Update tile" },
662 { C_CONTAINER_UPDATE, "Update container" },
663 { C_SET_OUTFIT, "Set outfit" },
664 { C_VIP_ADD, "Add VIP" },
665 { C_VIP_REMOVE, "Remove VIP" },
666
667 { 0, NULL }
668 };
669
670 static value_string_ext from_client_packet_types_ext = VALUE_STRING_EXT_INIT(from_client_packet_types);
671
672 enum loginserv_cmd {
673 LOGINSERV_DLG_ERROR = 0x0A,
674 LOGINSERV_DLG_ERROR2 = 0x0B,
675 LOGINSERV_DLG_MOTD = 0x14,
676 LOGINSERV_SESSION_KEY = 0x28,
677 LOGINSERV_DLG_CHARLIST = 0x64
678 };
679
680 static const value_string from_loginserv_packet_types[] = {
681 { LOGINSERV_DLG_ERROR, "Error" },
682 { LOGINSERV_DLG_ERROR2, "Error" },
683 { LOGINSERV_DLG_MOTD, "MOTD" },
684 { LOGINSERV_SESSION_KEY, "Session key" },
685 { LOGINSERV_DLG_CHARLIST, "Charlist" },
686
687 { 0, NULL }
688 };
689
690 enum gameserv_cmd {
691 /* Credit to Khaos (OBJECT Networks). Values and comments extracted from PDF table */
692 S_MAPINIT = 0x0A, /* Long playerCreatureId Int unknownU16 (Byte reportBugs?) */
693 S_GMACTIONS = 0x0B, /* Used to be 32 unknown bytes, but with GMs removed it might not be in use anymore */
694 S_DLG_ERROR = 0x14, /* String errorMessage */
695 S_DLG_INFO = 0x15,
696 S_DLG_TOOMANYPLAYERS = 0x16,
697 S_PING = 0x1E,
698 S_NONCE = 0x1F,
699 S_PLAYERLOC = 0x64, /* Coord pos */
700 S_GO_NORTH = 0x65, /* MapDescription (18,1) */
701 S_GO_EAST = 0x66, /* MapDescription (1,14) */
702 S_GO_SOUTH = 0x67, /* MapDescription (18,1) */
703 S_GO_WEST = 0x68, /* MapDescription (1,14) */
704 S_TILEUPDATE = 0x69, /* Coord pos TileDescription td */
705 S_ADDITEM = 0x6a, /* Coord pos ThingDescription thing */
706 S_REPLACEITEM = 0x6b, /* Coord pos Byte stackpos ThingDescription thing */
707 S_REMOVEITEM = 0x6c, /* Coord pos Byte stackpos */
708 S_MOVE_THING = 0x6d,
709 S_CONTAINER = 0x6e, /* Byte index Short containerIcon Byte slotCount ThingDescription item */
710 S_CONTAINERCLOSE = 0x6f, /* Byte index */
711 S_ADDITEMCONTAINER = 0x70, /* Byte index ThingDescription itm */
712 S_TRANSFORMITEMCONTAINER = 0x71, /* Byte index Byte slot */
713 S_REMOVEITEMCONTAINER = 0x72, /* Byte index Byte slot */
714 S_INVENTORYEMPTY = 0x78, /* Byte invSlot */
715 S_INVENTORYITEM = 0x79, /* Byte invSlot ThingDescription itm */
716 S_TRADEREQ = 0x7d, /* String otherperson Byte slotCount ThingDescription itm */
717 S_TRADEACK = 0x7e, /* String otherperson Byte slotCount ThingDescription itm */
718 S_TRADECLOSE = 0x7f,
719 S_LIGHTLEVEL = 0x82, /* Byte lightlevel Byte lightcolor */
720 S_MAGIC_EFFECT = 0x83,
721 S_ANIMATEDTEXT = 0x84, /* Coord pos Byte color String message */
722 S_DISTANCESHOT = 0x85, /* Coord pos1 Byte stackposition Coord pos2 */
723 S_CREATURESQUARE = 0x86, /* Long creatureid Byte squarecolor */
724 S_CREATURE_HEALTH = 0x8C,
725 S_CREATURELIGHT = 0x8d, /* Long creatureid Byte ? Byte ? */
726 S_SETOUTFIT = 0x8e, /* Long creatureid Byte lookType Byte headType Byte bodyType Byte legsType Byte feetType // can extended look go here too? */
727 S_CREATURESPEED = 0x8f, /* YIKES! I didnt handle this! */
728 S_TEXTWINDOW = 0x96, /* Long windowId Byte icon Byte maxlength String message */
729 S_STATUSMSG = 0xA0, /* Status status */
730 S_SKILLS = 0xA1, /* Skills skills */
731 S_PLAYER_CONDITION = 0xA2,
732 S_CANCELATTACK = 0xA3,
733 S_SPEAK = 0xAA,
734 S_CHANNELSDIALOG = 0xAB, /* Byte channelCount (Int channelId String channelName) */
735 S_CHANNEL_OPEN = 0xAC,
736 S_OPENPRIV = 0xAD, /* String playerName */
737 S_TEXTMESSAGE = 0xB4, /* Byte msgClass String string */
738 S_CANCELWALK = 0xB5, /* Byte direction */
739 S_FLOORUP = 0xBE, /* Advanced topic; read separate text */
740 S_FLOORDOWN = 0xBF, /* Advanced topic; read separate text */
741 S_OUTFITLIST = 0xC8, /* Byte lookType Byte headType Byte bodyType Byte legsType Byte feetType Byte firstModel Byte lastModel */
742 S_VIPADD = 0xD2, /* long guid string name byte isonline */
743 S_VIPLOGIN = 0xD3, /* long guid */
744 S_VIPLOGOUT = 0xD4 /* long guid*/
745 };
746 static const value_string from_gameserv_packet_types[] = {
747
748 { S_MAPINIT, "Initialize map" },
749 { S_GMACTIONS, "GM actions" },
750 { S_DLG_ERROR, "Error" },
751 { S_DLG_INFO, "Info" },
752 { S_DLG_TOOMANYPLAYERS, "Too many players" },
753 { S_PING, "Ping" },
754 { S_NONCE, "Nonce" },
755 { S_PLAYERLOC, "Set player location" },
756 { S_GO_NORTH, "Go north" },
757 { S_GO_EAST, "Go east" },
758 { S_GO_SOUTH, "Go south" },
759 { S_GO_WEST, "Go west" },
760 { S_TILEUPDATE, "Update tile" },
761 { S_ADDITEM, "Add item" },
762 { S_REPLACEITEM, "Replace item" },
763 { S_REMOVEITEM, "Remove item" },
764 { S_MOVE_THING, "Move thing" },
765 { S_CONTAINER, "Open container" },
766 { S_CONTAINERCLOSE, "Close container" },
767
768 { S_ADDITEMCONTAINER, "Add item in container" },
769 { S_TRANSFORMITEMCONTAINER, "Transform item in container" },
770 { S_REMOVEITEMCONTAINER, "Remove item in container" },
771
772 { S_INVENTORYEMPTY, "Inventory empty" },
773 { S_INVENTORYITEM, "Inventory item" },
774 { S_TRADEREQ, "Trade request" },
775 { S_TRADEACK, "Trade acknowledge" },
776 { S_TRADECLOSE, "Trade over" },
777 { S_LIGHTLEVEL, "Light level" },
778 { S_MAGIC_EFFECT, "Magic effect" },
779 { S_ANIMATEDTEXT, "Animated text" },
780 { S_DISTANCESHOT, "Distance shot" },
781 { S_CREATURESQUARE, "Creature square" },
782 { S_CREATURE_HEALTH, "Creature health" },
783 { S_CREATURELIGHT, "Creature light" },
784 { S_SETOUTFIT, "Set outfit" },
785 { S_CREATURESPEED, "Set creature speed" },
786 { S_TEXTWINDOW, "Text window" },
787 { S_STATUSMSG, "Status message" },
788 { S_SKILLS, "Skills" },
789 { S_PLAYER_CONDITION, "Player condition" },
790 { S_CANCELATTACK, "Cancel attack" },
791 { S_SPEAK, "Creature speech" },
792 { S_CHANNELSDIALOG, "Channels dialog" },
793 { S_CHANNEL_OPEN, "Channel open" },
794 { S_OPENPRIV, "Private channel open" },
795 { S_TEXTMESSAGE, "Text message" },
796 { S_CANCELWALK, "Cancel walk" },
797 { S_FLOORUP, "Floor +1" },
798 { S_FLOORDOWN, "Floor -1" },
799 { S_OUTFITLIST, "Outfit list" },
800 { S_VIPADD, "Add VIP" },
801 { S_VIPLOGIN, "VIP login" },
802 { S_VIPLOGOUT, "VIP logout" },
803
804 { 0, NULL }
805 };
806
807 static value_string_ext from_gameserv_packet_types_ext = VALUE_STRING_EXT_INIT(from_gameserv_packet_types);
808
809 static const unit_name_string mb_unit = {"MB", NULL};
810
811 static int
dissect_loginserv_packet(struct tibia_convo * convo,tvbuff_t * tvb,int offset,int len,packet_info * pinfo,proto_tree * tree,gboolean first_fragment)812 dissect_loginserv_packet(struct tibia_convo *convo, tvbuff_t *tvb, int offset, int len, packet_info *pinfo, proto_tree *tree, gboolean first_fragment )
813 {
814 ptvcursor_t *ptvc = ptvcursor_new(pinfo->pool, tree, tvb, offset);
815
816 col_append_str(pinfo->cinfo, COL_INFO, first_fragment ? " commands:" : ",");
817 len += offset;
818
819 if (ptvcursor_current_offset(ptvc) < len) {
820 for (;;) {
821 int cmd = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
822 ptvcursor_add_with_subtree(ptvc, hf_tibia_loginserv_command, 1, convo->has.string_enc, ett_command);
823 ptvcursor_advance(ptvc, 1);
824
825 switch ((enum loginserv_cmd)cmd) {
826 case LOGINSERV_DLG_ERROR:
827 case LOGINSERV_DLG_ERROR2:
828 ptvcursor_add(ptvc, hf_tibia_dlg_error, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
829 break;
830 case LOGINSERV_DLG_MOTD:
831 ptvcursor_add(ptvc, hf_tibia_motd, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
832 break;
833 case LOGINSERV_SESSION_KEY:
834 ptvcursor_add(ptvc, hf_tibia_session_key, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
835 break;
836 case LOGINSERV_DLG_CHARLIST:
837 if (convo->has.worldlist_in_charlist) {
838 guint8 world_count = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
839 ptvcursor_add(ptvc, hf_tibia_worldlist_length, 1, ENC_NA);
840 /* Empty character list? */
841 if (world_count) {
842 ptvcursor_add_with_subtree(ptvc, hf_tibia_worldlist, SUBTREE_UNDEFINED_LENGTH, ENC_NA, ett_worldlist);
843 while (world_count--) {
844 proto_item *it = ptvcursor_add(ptvc, hf_tibia_worldlist_entry_id, 1, ENC_NA);
845 ptvcursor_push_subtree(ptvc, it, ett_world);
846
847 ptvcursor_add(ptvc, hf_tibia_worldlist_entry_name, 2, convo->has.string_enc | ENC_LITTLE_ENDIAN);
848 guint ipv4addr_len = tvb_get_letohs(tvb, ptvcursor_current_offset(ptvc));
849 char *ipv4addr_str = (char*)tvb_get_string_enc(pinfo->pool, tvb, ptvcursor_current_offset(ptvc) + 2, ipv4addr_len, ENC_LITTLE_ENDIAN | convo->has.string_enc);
850 guint32 ipv4addr = ipv4tonl(ipv4addr_str);
851 ptvcursor_add(ptvc, hf_tibia_worldlist_entry_ip, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
852 guint16 port = tvb_get_letohs(tvb, ptvcursor_current_offset(ptvc));
853 ptvcursor_add(ptvc, hf_tibia_worldlist_entry_port, 2, ENC_LITTLE_ENDIAN);
854 ptvcursor_add(ptvc, hf_tibia_worldlist_entry_preview, 1, ENC_NA);
855
856 ptvcursor_pop_subtree(ptvc);
857
858 register_gameserv_addr(convo, ipv4addr, port);
859 }
860 ptvcursor_pop_subtree(ptvc);
861 }
862
863 guint8 char_count = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
864 ptvcursor_add(ptvc, hf_tibia_charlist_length, 1, ENC_NA);
865 if (char_count) {
866 ptvcursor_add_with_subtree(ptvc, hf_tibia_charlist, SUBTREE_UNDEFINED_LENGTH, ENC_NA, ett_charlist);
867 while (char_count--) {
868 proto_item *it = ptvcursor_add(ptvc, hf_tibia_worldlist_entry_id, 1, ENC_NA);
869 ptvcursor_push_subtree(ptvc, it, ett_char);
870 ptvcursor_add(ptvc, hf_tibia_charlist_entry_name, 2, convo->has.string_enc | ENC_LITTLE_ENDIAN);
871
872
873 ptvcursor_pop_subtree(ptvc);
874 }
875 ptvcursor_pop_subtree(ptvc);
876 }
877 } else {
878 guint8 char_count = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
879 ptvcursor_add(ptvc, hf_tibia_charlist_length, 1, ENC_NA);
880 if (char_count) {
881 ptvcursor_add_with_subtree(ptvc, hf_tibia_charlist, SUBTREE_UNDEFINED_LENGTH, ENC_NA, ett_charlist);
882
883 while (char_count--) {
884 proto_item *it = ptvcursor_add(ptvc, hf_tibia_charlist_entry_name, 2, convo->has.string_enc | ENC_LITTLE_ENDIAN);
885 ptvcursor_push_subtree(ptvc, it, ett_char);
886
887 ptvcursor_add(ptvc, hf_tibia_charlist_entry_world, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
888
889 guint32 ipv4addr = tvb_get_ipv4(tvb, ptvcursor_current_offset(ptvc));
890 ptvcursor_add(ptvc, hf_tibia_charlist_entry_ip, 4, ENC_BIG_ENDIAN);
891
892 guint16 port = tvb_get_letohs(tvb, ptvcursor_current_offset(ptvc));
893 ptvcursor_add(ptvc, hf_tibia_charlist_entry_port, 2, ENC_BIG_ENDIAN);
894
895
896 ptvcursor_pop_subtree(ptvc);
897
898 register_gameserv_addr(convo, ipv4addr, port);
899 }
900
901 ptvcursor_pop_subtree(ptvc);
902 }
903
904 ptvcursor_add(ptvc, hf_tibia_pacc_days, 2, ENC_LITTLE_ENDIAN);
905 }
906 break;
907 default:
908 offset = ptvcursor_current_offset(ptvc);
909 call_data_dissector(tvb_new_subset_length(tvb, offset, len - offset), pinfo, ptvcursor_tree(ptvc));
910 ptvcursor_advance(ptvc, len - offset);
911 }
912
913 ptvcursor_pop_subtree(ptvc);
914
915 col_append_fstr(pinfo->cinfo, COL_INFO, " %s (0x%x)",
916 val_to_str(cmd, from_loginserv_packet_types, "Unknown"), cmd);
917
918 if (ptvcursor_current_offset(ptvc) >= len)
919 break;
920
921 col_append_str(pinfo->cinfo, COL_INFO, ",");
922 }
923 }
924
925 offset = ptvcursor_current_offset(ptvc);
926 ptvcursor_free(ptvc);
927
928 return offset;
929 }
930
931 static void
dissect_coord(ptvcursor_t * ptvc,gboolean with_stackpos)932 dissect_coord(ptvcursor_t *ptvc, gboolean with_stackpos)
933 {
934 tvbuff_t *tvb;
935 proto_tree *tree;
936 int offset;
937
938 guint32 x, y, z, stackpos;
939 proto_item *coords_tuple = ptvcursor_add_with_subtree(ptvc, hf_tibia_coords, SUBTREE_UNDEFINED_LENGTH, ENC_NA, ett_coords);
940 {
941 tvb = ptvcursor_tvbuff(ptvc);
942 tree = ptvcursor_tree(ptvc);
943 offset = ptvcursor_current_offset(ptvc);
944
945 proto_tree_add_item_ret_uint(tree, hf_tibia_coords_x, tvb, offset, 2, ENC_LITTLE_ENDIAN, &x);
946 offset += 2;
947 proto_tree_add_item_ret_uint(tree, hf_tibia_coords_y, tvb, offset, 2, ENC_LITTLE_ENDIAN, &y);
948 offset += 2;
949 proto_tree_add_item_ret_uint(tree, hf_tibia_coords_z, tvb, offset, 1, ENC_NA, &z);
950 offset += 1;
951
952 ptvcursor_advance(ptvc, 5);
953 }
954 if (with_stackpos) {
955 proto_tree_add_item_ret_uint(tree, hf_tibia_stackpos, tvb, offset, 1, ENC_NA, &stackpos);
956 proto_item_set_text(coords_tuple, "Coordinates: (%u, %u, %u)[%u]", x, y, z, stackpos);
957 ptvcursor_advance(ptvc, 1);
958 } else {
959 proto_item_set_text(coords_tuple, "Coordinates: (%u, %u, %u)", x, y, z);
960 }
961
962 ptvcursor_pop_subtree(ptvc);
963 }
964
965
966 static int
dissect_gameserv_packet(struct tibia_convo * convo,tvbuff_t * tvb,int offset,int len,packet_info * pinfo,proto_tree * tree,gboolean first_fragment)967 dissect_gameserv_packet(struct tibia_convo *convo, tvbuff_t *tvb, int offset, int len, packet_info *pinfo, proto_tree *tree, gboolean first_fragment)
968 {
969 ptvcursor_t *ptvc = ptvcursor_new(pinfo->pool, tree, tvb, offset);
970
971 col_append_str(pinfo->cinfo, COL_INFO, first_fragment ? " commands:" : ",");
972 len += offset;
973
974 if (ptvcursor_current_offset(ptvc) < len) {
975 for (;;) {
976 int cmd = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
977 ptvcursor_add_with_subtree(ptvc, hf_tibia_gameserv_command, 1, convo->has.string_enc, ett_command);
978 ptvcursor_advance(ptvc, 1);
979
980 switch ((enum gameserv_cmd)cmd) {
981 case S_DLG_INFO:
982 case S_DLG_ERROR:
983 case S_DLG_TOOMANYPLAYERS:
984 ptvcursor_add(ptvc, cmd == S_DLG_ERROR ? hf_tibia_dlg_error : hf_tibia_dlg_info, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
985 break;
986 case S_GMACTIONS: /* 0x0B, Used to be 32 unknown bytes, but with GMs removed it might not be in use anymore */
987 ptvcursor_add(ptvc, hf_tibia_unknown, 32, ENC_NA);
988 break;
989 case S_PLAYERLOC: /* 0x64,Coord pos */
990 dissect_coord(ptvc, FALSE);
991 break;
992 case S_TILEUPDATE: /* 0x69,Coord pos TileDescription td */
993 dissect_coord(ptvc, FALSE);
994 ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
995 break;
996 case S_ADDITEM: /* 0x6a,Coord pos ThingDescription thing */
997 dissect_coord(ptvc, FALSE);
998 ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
999 break;
1000 case S_REPLACEITEM: /* 0x6b,Coord pos Byte stackpos ThingDescription thing */
1001 dissect_coord(ptvc, TRUE);
1002 ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1003 break;
1004 case S_REMOVEITEM: /* 0x6c,Coord pos Byte stackpos */
1005 dissect_coord(ptvc, TRUE);
1006 break;
1007 case S_MOVE_THING: /* 0x6d, */
1008 dissect_coord(ptvc, TRUE);
1009 dissect_coord(ptvc, FALSE);
1010 break;
1011 case S_CONTAINER: /* 0x6e,Byte index Short containerIcon Byte slotCount ThingDescription item */
1012 ptvcursor_add(ptvc, hf_tibia_container, 1, ENC_NA);
1013 ptvcursor_add(ptvc, hf_tibia_container_icon, 2, ENC_LITTLE_ENDIAN);
1014 ptvcursor_add(ptvc, hf_tibia_container_slots, 2, ENC_LITTLE_ENDIAN);
1015 ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1016 break;
1017 case S_CONTAINERCLOSE: /* 0x6f,Byte index */
1018 ptvcursor_add(ptvc, hf_tibia_container, 1, ENC_NA);
1019 break;
1020 case S_ADDITEMCONTAINER: /* 0x70,Byte index ThingDescription itm */
1021 ptvcursor_add(ptvc, hf_tibia_container, 1, ENC_NA);
1022 ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1023 break;
1024 case S_TRANSFORMITEMCONTAINER:/* 0x71,Byte index Byte slot */
1025 ptvcursor_add(ptvc, hf_tibia_container, 1, ENC_NA);
1026 ptvcursor_add(ptvc, hf_tibia_container_slot, 1, ENC_NA);
1027 break;
1028 case S_REMOVEITEMCONTAINER: /* 0x72,Byte index Byte slot */
1029 ptvcursor_add(ptvc, hf_tibia_container, 1, ENC_NA);
1030 ptvcursor_add(ptvc, hf_tibia_container_slot, 1, ENC_NA);
1031 break;
1032 case S_INVENTORYEMPTY: /* 0x78,Byte invSlot */
1033 ptvcursor_add(ptvc, hf_tibia_inventory, 1, ENC_NA);
1034 break;
1035 case S_INVENTORYITEM: /* 0x79,Byte invSlot ThingDescription itm */
1036 ptvcursor_add(ptvc, hf_tibia_inventory, 1, ENC_NA);
1037 ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1038 break;
1039 case S_TRADEREQ: /* 0x7d,String otherperson Byte slotCount ThingDescription itm */
1040 ptvcursor_add(ptvc, hf_tibia_player, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1041 ptvcursor_add(ptvc, hf_tibia_inventory, 1, ENC_NA);
1042 ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1043 break;
1044 case S_TRADEACK: /* 0x7e,String otherperson Byte slotCount ThingDescription itm */
1045 ptvcursor_add(ptvc, hf_tibia_player, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1046 ptvcursor_add(ptvc, hf_tibia_inventory, 1, ENC_NA);
1047 ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1048 break;
1049
1050 case S_TRADECLOSE: /* 0x7f, */
1051 break;
1052 case S_LIGHTLEVEL: /* 0x82,Byte lightlevel Byte lightcolor */
1053 ptvcursor_add(ptvc, hf_tibia_light_level, 1, ENC_NA);
1054 ptvcursor_add(ptvc, hf_tibia_light_color, 1, ENC_NA);
1055 break;
1056 case S_MAGIC_EFFECT: /* 0x83, */
1057 dissect_coord(ptvc, FALSE);
1058 ptvcursor_add(ptvc, hf_tibia_magic_effect_id, 1, ENC_NA);
1059 break;
1060 case S_ANIMATEDTEXT: /* 0x84,Coord pos Byte color String message */
1061 dissect_coord(ptvc, FALSE);
1062 ptvcursor_add(ptvc, hf_tibia_animated_text_color, 1, ENC_NA);
1063 ptvcursor_add(ptvc, hf_tibia_animated_text, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1064 break;
1065 case S_DISTANCESHOT: /* 0x85,Coord pos1 Byte stackposition Coord pos2 */
1066 dissect_coord(ptvc, FALSE);
1067 ptvcursor_add(ptvc, hf_tibia_projectile, 4, ENC_LITTLE_ENDIAN);
1068 dissect_coord(ptvc, FALSE);
1069 break;
1070 case S_CREATURESQUARE: /* 0x86,Long creatureid Byte squarecolor */
1071 ptvcursor_add(ptvc, hf_tibia_creature, 4, ENC_LITTLE_ENDIAN);
1072 ptvcursor_add(ptvc, hf_tibia_squarecolor, 1, ENC_NA);
1073 break;
1074 case S_CREATURE_HEALTH: /* 0x8C, */
1075 ptvcursor_add(ptvc, hf_tibia_creature, 1, ENC_LITTLE_ENDIAN);
1076 ptvcursor_add(ptvc, hf_tibia_creature_health, 1, ENC_NA);
1077 break;
1078 case S_CREATURELIGHT: /* 0x8d,Long creatureid Byte ? Byte ? */
1079 ptvcursor_add(ptvc, hf_tibia_creature, 1, ENC_LITTLE_ENDIAN);
1080 ptvcursor_add(ptvc, hf_tibia_unknown, 2, ENC_NA);
1081 break;
1082 case S_SETOUTFIT: /* 0x8e,Long creatureid Byte lookType Byte headType Byte bodyType Byte legsType Byte feetType // can extended look go here too? */
1083 ptvcursor_add(ptvc, hf_tibia_creature, 1, ENC_LITTLE_ENDIAN);
1084 ptvcursor_add(ptvc, hf_tibia_unknown, len - ptvcursor_current_offset(ptvc), ENC_NA);
1085 break;
1086 case S_TEXTWINDOW: /* 0x96,Long windowId Byte icon Byte maxlength String message */
1087 ptvcursor_add(ptvc, hf_tibia_window, 4, ENC_LITTLE_ENDIAN);
1088 ptvcursor_add(ptvc, hf_tibia_window_icon, 1, ENC_NA);
1089 ptvcursor_add(ptvc, hf_tibia_window_textlen, 1, ENC_NA);
1090 ptvcursor_add(ptvc, hf_tibia_window_text, 1, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1091 break;
1092 case S_PLAYER_CONDITION: /* 0xA2, */
1093 proto_tree_add_bitmask(ptvcursor_tree(ptvc), ptvcursor_tvbuff(ptvc), ptvcursor_current_offset(ptvc), hf_tibia_char_cond, ett_char_cond, char_conds, ENC_LITTLE_ENDIAN);
1094 ptvcursor_advance(ptvc, 4);
1095 break;
1096 case S_CANCELATTACK: /* 0xA3, */
1097 break;
1098 case S_CHANNEL_OPEN:
1099 ptvcursor_add(ptvc, hf_tibia_channel_id, 2, ENC_LITTLE_ENDIAN);
1100 ptvcursor_add(ptvc, hf_tibia_channel_name, 2, ENC_LITTLE_ENDIAN|convo->has.string_enc);
1101 ptvcursor_add(ptvc, hf_tibia_unknown, 4, ENC_NA);
1102 break;
1103 case S_OPENPRIV: /* 0xAD,String playerName */
1104 ptvcursor_add(ptvc, hf_tibia_player, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1105 break;
1106 case S_TEXTMESSAGE: /* 0xB4,Byte msgClass String string */
1107 ptvcursor_add(ptvc, hf_tibia_textmsg_class, 1, ENC_NA);
1108 ptvcursor_add(ptvc, hf_tibia_textmsg, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1109 break;
1110 case S_CANCELWALK: /* 0xB5,Byte direction */
1111 ptvcursor_add(ptvc, hf_tibia_walk_dir, 1, ENC_NA);
1112 break;
1113 case S_VIPADD: /* 0xd2,long guid string name byte isonline */
1114 ptvcursor_add(ptvc, hf_tibia_vip, 4, ENC_LITTLE_ENDIAN);
1115 ptvcursor_add(ptvc, hf_tibia_player, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc);
1116 ptvcursor_add(ptvc, hf_tibia_vip_online, 1, ENC_NA);
1117 break;
1118 case S_VIPLOGIN: /* 0xd3,long guid */
1119 ptvcursor_add(ptvc, hf_tibia_vip, 4, ENC_LITTLE_ENDIAN);
1120 break;
1121 case S_VIPLOGOUT: /* 0xd4long guid*/
1122 ptvcursor_add(ptvc, hf_tibia_vip, 4, ENC_LITTLE_ENDIAN);
1123 break;
1124 case S_PING:
1125 break;
1126 case S_NONCE: /* 0x1F, */
1127 ptvcursor_add(ptvc, hf_tibia_nonce, 5, ENC_NA);
1128 break;
1129
1130 case S_MAPINIT: /* 0x0A, Long playerCreatureId Int unknownU16 (Byte reportBugs?) */
1131 case S_OUTFITLIST: /* 0xC8,Byte lookType Byte headType Byte bodyType Byte legsType Byte feetType Byte firstModel Byte lastModel */
1132 /* TODO This changed with mounts and outfit */
1133 case S_FLOORUP: /* 0xBE,Advanced topic; read separate text */
1134 case S_FLOORDOWN: /* 0xBF,Advanced topic; read separate text */
1135 case S_SPEAK: /* 0xAA, */
1136 case S_CHANNELSDIALOG: /* 0xAB,Byte channelCount (Int channelId String channelName) */
1137 case S_STATUSMSG: /* 0xA0,Status status */
1138 case S_SKILLS: /* 0xA1,Skills skills */
1139 case S_CREATURESPEED: /* 0x8f,YIKES! I didnt handle this! */
1140 case S_GO_NORTH: /* 0x65,MapDescription (18,1) */
1141 case S_GO_EAST: /* 0x66,MapDescription (1,14) */
1142 case S_GO_SOUTH: /* 0x67,MapDescription (18,1) */
1143 case S_GO_WEST: /* 0x68,MapDescription (1,14) */
1144 default:
1145 offset = ptvcursor_current_offset(ptvc);
1146 call_data_dissector(tvb_new_subset_length(tvb, offset, len - offset), pinfo, ptvcursor_tree(ptvc));
1147 ptvcursor_advance(ptvc, len - offset);
1148 }
1149
1150
1151 ptvcursor_pop_subtree(ptvc);
1152
1153 col_append_fstr(pinfo->cinfo, COL_INFO, " %s (0x%x)",
1154 val_to_str(cmd, from_gameserv_packet_types, "Unknown"), cmd);
1155
1156 if (ptvcursor_current_offset(ptvc) >= len)
1157 break;
1158
1159 col_append_str(pinfo->cinfo, COL_INFO, ",");
1160 }
1161 }
1162
1163 offset = ptvcursor_current_offset(ptvc);
1164 ptvcursor_free(ptvc);
1165
1166 return offset;
1167 }
1168
1169 static int
dissect_client_packet(struct tibia_convo * convo,tvbuff_t * tvb,int offset,int len,packet_info * pinfo,proto_tree * tree,gboolean first_fragment)1170 dissect_client_packet(struct tibia_convo *convo, tvbuff_t *tvb, int offset, int len, packet_info *pinfo, proto_tree *tree, gboolean first_fragment)
1171 {
1172 ptvcursor_t *ptvc = ptvcursor_new(pinfo->pool, tree, tvb, offset);
1173
1174 col_append_str(pinfo->cinfo, COL_INFO, first_fragment ? " commands:" : ",");
1175 len += offset;
1176
1177 if (ptvcursor_current_offset(ptvc) < len) {
1178 for (;;) {
1179 int cmd = tvb_get_guint8(tvb, ptvcursor_current_offset(ptvc));
1180 ptvcursor_add_with_subtree(ptvc, hf_tibia_client_command, 1, convo->has.string_enc, ett_command);
1181 ptvcursor_advance(ptvc, 1);
1182
1183 switch ((enum client_cmd)cmd) {
1184 case C_PLAYER_SPEECH: {
1185 guint8 type = tvb_get_guint8(ptvcursor_tvbuff(ptvc), ptvcursor_current_offset(ptvc));
1186
1187 ptvcursor_add(ptvc, hf_tibia_speech_type, 1, ENC_NA);
1188 if (type == 0x7)
1189 ptvcursor_add(ptvc, hf_tibia_channel_id, 2, ENC_LITTLE_ENDIAN);
1190 ptvcursor_add(ptvc, hf_tibia_chat_msg, 2, ENC_LITTLE_ENDIAN|convo->has.string_enc);
1191 }
1192 break;
1193 case C_PONG:
1194 break;
1195 default:
1196 offset = ptvcursor_current_offset(ptvc);
1197 call_data_dissector(tvb_new_subset_length(tvb, offset, len - offset), pinfo, ptvcursor_tree(ptvc));
1198 ptvcursor_advance(ptvc, len - offset);
1199 }
1200
1201 ptvcursor_pop_subtree(ptvc);
1202
1203 col_append_fstr(pinfo->cinfo, COL_INFO, " %s (0x%x)",
1204 val_to_str(cmd, from_client_packet_types, "Unknown"), cmd);
1205
1206 if (ptvcursor_current_offset(ptvc) >= len)
1207 break;
1208
1209 col_append_str(pinfo->cinfo, COL_INFO, ",");
1210 }
1211 }
1212
1213 offset = ptvcursor_current_offset(ptvc);
1214 ptvcursor_free(ptvc);
1215
1216 return offset;
1217 }
1218
1219 static int
dissect_game_packet(struct tibia_convo * convo,tvbuff_t * tvb,int offset,packet_info * pinfo,proto_tree * tree,gboolean is_xtea_encrypted,gboolean first_fragment)1220 dissect_game_packet(struct tibia_convo *convo, tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, gboolean is_xtea_encrypted, gboolean first_fragment)
1221 {
1222 proto_item *ti = NULL;
1223 int len = tvb_captured_length_remaining(tvb, offset);
1224
1225 if (show_acc_info) {
1226 if (convo->has.session_key) {
1227 if (convo->session_key) {
1228 ti = proto_tree_add_string(tree, hf_tibia_session_key_convo, tvb, offset, 0, (const char*)convo->session_key);
1229 proto_item_set_generated(ti);
1230 }
1231 } else {
1232 if (convo->acc) {
1233 ti = proto_tree_add_string(tree, hf_tibia_acc_name_convo, tvb, offset, 0, (const char*)convo->acc);
1234 proto_item_set_generated(ti);
1235 }
1236
1237 if (convo->pass) {
1238 ti = proto_tree_add_string(tree, hf_tibia_acc_pass_convo, tvb, offset, 0, (const char*)convo->pass);
1239 proto_item_set_generated(ti);
1240 }
1241 }
1242 }
1243
1244 if (show_char_name && convo->char_name) {
1245 ti = proto_tree_add_string(tree, hf_tibia_char_name_convo, tvb, offset, 0, (const char*)convo->char_name);
1246 proto_item_set_generated(ti);
1247 }
1248
1249 if (is_xtea_encrypted) {
1250 if (pinfo->num > convo->xtea_framenum) {
1251 if (show_xtea_key && convo->has.xtea) {
1252 ti = proto_tree_add_bytes_with_length(tree, hf_tibia_xtea_key, tvb, 0, 0, (guint8*)convo->xtea_key, XTEA_KEY_LEN);
1253 proto_item_set_generated(ti);
1254 }
1255
1256 int end = offset + len;
1257
1258 if (len % 8 != 0)
1259 return -1;
1260
1261 guint8 *decrypted_buffer = (guint8*)wmem_alloc(pinfo->pool, len);
1262
1263 for (guint8 *dstblock = decrypted_buffer; offset < end; offset += 8) {
1264 decrypt_xtea_le_ecb(dstblock, tvb_get_ptr(tvb, offset, 8), convo->xtea_key, 32);
1265 dstblock += 8;
1266 }
1267
1268 tvb = tvb_new_child_real_data(tvb, decrypted_buffer, len, len);
1269 add_new_data_source(pinfo, tvb, "Decrypted Game Data");
1270
1271 offset = 0;
1272 } else {
1273 proto_tree_add_item(tree, hf_tibia_undecoded_xtea_data, tvb, offset, len, ENC_NA);
1274 return offset;
1275 }
1276 }
1277 if (convo->has.xtea) {
1278 len = tvb_get_letohs(tvb, offset);
1279 ti = proto_tree_add_item(tree, hf_tibia_payload_len, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1280 offset += 2;
1281 if (len > tvb_captured_length_remaining(tvb, offset)) {
1282 expert_add_info(pinfo, ti, &ei_xtea_len_toobig);
1283 return offset;
1284 }
1285 }
1286
1287
1288 if (pinfo->srcport == convo->servport && convo->loginserv_is_peer)
1289 return dissect_loginserv_packet(convo, tvb, offset, len, pinfo, tree, first_fragment);
1290
1291 if (!dissect_game_commands) {
1292 call_data_dissector(tvb_new_subset_length(tvb, offset, len), pinfo, tree);
1293 return offset + len;
1294 }
1295
1296 if (pinfo->srcport == convo->servport)
1297 return dissect_gameserv_packet(convo, tvb, offset, len, pinfo, tree, first_fragment);
1298 else
1299 return dissect_client_packet(convo, tvb, offset, len, pinfo, tree, first_fragment);
1300 }
1301
1302 static int
dissect_tibia(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * fragment_num)1303 dissect_tibia(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *fragment_num)
1304 {
1305 tvbuff_t *tvb_decrypted = tvb;
1306 gboolean is_xtea_encrypted = FALSE;
1307 enum { TIBIA_GAMESERV, TIBIA_LOGINSERV } serv = TIBIA_GAMESERV;
1308 guint16 plen = tvb_get_letohs(tvb, 0) + 2;
1309
1310 /* if announced length != real length it's not a tibia packet */
1311 if (tvb_reported_length_remaining(tvb, 0) != plen)
1312 return 0;
1313
1314 struct tibia_convo *convo = tibia_get_convo(pinfo);
1315
1316 int offset = 2;
1317 int a32len = tvb_reported_length_remaining(tvb, offset + 4);
1318 guint32 packet_cksum = tvb_get_letohl(tvb, offset);
1319 guint32 computed_cksum = GUINT32_TO_LE(adler32_bytes(tvb_get_ptr(tvb, offset + 4, a32len), a32len));
1320 convo->has.adler32 = packet_cksum == computed_cksum;
1321 if (convo->has.adler32)
1322 offset += 4;
1323
1324 /* FIXME Tibia >=11.11 has a sequence number instead, this is yet unhandled */
1325
1326 /* Is it a nonce? */
1327 if (tvb_get_letohs(tvb, offset) == plen - offset - 2
1328 && tvb_get_guint8(tvb, offset+2) == S_NONCE) {
1329 /* Don't do anything. We'll handle it as unencrypted game command later */
1330 } else {
1331 guint8 cmd;
1332 guint16 version;
1333 struct proto_traits version_has;
1334 cmd = tvb_get_guint8(tvb, offset);
1335 offset += 1;
1336 offset += 2; /* OS */
1337 version = tvb_get_letohs(tvb, offset);
1338 version_has = get_version_traits(version);
1339
1340 switch(cmd) {
1341 case C_GET_CHARLIST:
1342 if ((700 <= version && version <= 760 && !convo->has.adler32 && 25 <= plen && plen <= 54)
1343 || get_version_get_charlist_packet_size(&version_has) == plen) {
1344 serv = TIBIA_LOGINSERV;
1345 convo->loginserv_is_peer = TRUE;
1346 }
1347 break;
1348 case C_LOGIN_CHAR:
1349 /* The outcast client I tried, zero-pads the 760 login request.
1350 * I don't think the Cipsoft client ever did this.
1351 */
1352 if ((700 <= version && version <= 760 && !convo->has.adler32 && 25 <= plen && plen <= 54)
1353 || get_version_char_login_packet_size(&version_has) == plen)
1354 serv = TIBIA_LOGINSERV;
1355 break;
1356 default:
1357 is_xtea_encrypted = convo->has.xtea;
1358 }
1359 }
1360
1361
1362 offset = 0; /* With the version extracted, let's build the tree */
1363
1364 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Tibia");
1365 if (GPOINTER_TO_UINT(fragment_num) == 1) {
1366 /* We don't want to repeat ourselves in the info column if there are fragments */
1367 if (serv == TIBIA_LOGINSERV)
1368 col_set_str(pinfo->cinfo, COL_INFO, "Login");
1369 else if (pinfo->srcport == convo->servport)
1370 col_set_str(pinfo->cinfo, COL_INFO, "Server");
1371 else
1372 col_set_str(pinfo->cinfo, COL_INFO, "Client");
1373
1374 }
1375
1376 proto_item *ti = proto_tree_add_item(tree, proto_tibia, tvb, 0, -1, ENC_NA);
1377 proto_tree *tibia_tree = proto_item_add_subtree(ti, ett_tibia);
1378
1379 proto_tree_add_item(tibia_tree, hf_tibia_len, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1380 offset += 2;
1381 if (convo->has.adler32) {
1382 proto_tree_add_checksum(tibia_tree, tvb, offset, hf_tibia_adler32, hf_tibia_adler32_status, &ei_adler32_checksum_bad, pinfo, computed_cksum, ENC_LITTLE_ENDIAN, PROTO_CHECKSUM_VERIFY);
1383 offset += 4;
1384 } else if (convo->has.compression) {
1385 offset += 4;
1386 }
1387
1388 if (serv == TIBIA_GAMESERV)
1389 return dissect_game_packet(convo, tvb, offset, pinfo, tibia_tree, is_xtea_encrypted, GPOINTER_TO_UINT(fragment_num) == 1);
1390
1391 proto_tree_add_item(tibia_tree, hf_tibia_client_command, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1392 offset += 1;
1393 proto_tree_add_item(tibia_tree, hf_tibia_os, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1394 offset += 2;
1395
1396 convo->proto_version = tvb_get_letohs(tvb, offset);
1397 convo->has = get_version_traits(convo->proto_version);
1398 proto_tree_add_item(tibia_tree, hf_tibia_proto_version, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1399 offset += 2;
1400 if (convo->has.client_version) {
1401 proto_tree_add_item(tibia_tree, hf_tibia_client_version, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1402 offset += 4;
1403 }
1404 if (convo->loginserv_is_peer) {
1405 proto_tree *vertree;
1406 /* The first 4 bytes of the client's tibia.pic, tibia.dat and tibia.spr files */
1407 proto_item *subti = proto_tree_add_item(tibia_tree, hf_tibia_file_versions, tvb, offset, 12, ENC_NA);
1408 vertree = proto_item_add_subtree(subti, ett_file_versions);
1409 proto_tree_add_item(vertree, hf_tibia_file_version_spr, tvb, offset, 4, ENC_BIG_ENDIAN);
1410 offset += 4;
1411 proto_tree_add_item(vertree, hf_tibia_file_version_dat, tvb, offset, 4, ENC_BIG_ENDIAN);
1412 offset += 4;
1413 proto_tree_add_item(vertree, hf_tibia_file_version_pic, tvb, offset, 4, ENC_BIG_ENDIAN);
1414 offset += 4;
1415 } else if (convo->has.game_content_revision) {
1416 proto_tree_add_item(tibia_tree, hf_tibia_content_revision, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1417 offset += 2;
1418 }
1419
1420 if (convo->has.game_preview) {
1421 proto_tree_add_item(tibia_tree, hf_tibia_game_preview_state, tvb, offset, 1, ENC_NA);
1422 offset += 1;
1423 }
1424
1425 int rsa1_end = 0; /* End of first RSA block */
1426 if (convo->has.rsa) {
1427 gcry_sexp_t privkey;
1428 if (!(privkey = convo_get_privkey(convo))) {
1429 proto_tree_add_item(tibia_tree, hf_tibia_undecoded_rsa_data, tvb, offset, plen - offset, ENC_NA);
1430 return offset;
1431 }
1432
1433 guint ciphertext_len = tvb_captured_length_remaining(tvb, offset);
1434 if (ciphertext_len < 128) {
1435 expert_add_info(pinfo, ti, &ei_rsa_ciphertext_too_short);
1436 return offset;
1437 }
1438 rsa1_end = offset + 128;
1439 guint8 *payload = (guint8*)tvb_memdup(pinfo->pool, tvb, offset, 128);
1440
1441 char *err = NULL;
1442 size_t payload_len;
1443 if (!(payload_len = rsa_decrypt_inplace(128, payload, privkey, FALSE, &err))) {
1444 expert_add_info_format(pinfo, ti, &ei_rsa_decrypt_failed, "Decrypting RSA block failed: %s", err);
1445 g_free(err);
1446 return offset;
1447 }
1448 size_t leading_zeroes = 128 - payload_len;
1449 memmove(payload + leading_zeroes, payload, payload_len);
1450 memset(payload, 0x00, leading_zeroes);
1451
1452 tvb_decrypted = tvb_new_child_real_data(tvb, payload, 128, 128);
1453 add_new_data_source(pinfo, tvb_decrypted, "Decrypted Login Data");
1454
1455 if (tvb_get_guint8(tvb_decrypted, 0) != 0x00) {
1456 expert_add_info(pinfo, ti, &ei_rsa_plaintext_no_leading_zero);
1457 return offset;
1458 }
1459
1460 offset = 1;
1461
1462 tvb_memcpy(tvb_decrypted, convo->xtea_key, 1, XTEA_KEY_LEN);
1463 proto_tree_add_item(tibia_tree, hf_tibia_xtea_key, tvb_decrypted, 1, XTEA_KEY_LEN, ENC_NA);
1464 offset += XTEA_KEY_LEN;
1465 convo->xtea_framenum = pinfo->num;
1466 }
1467
1468 if (!convo->loginserv_is_peer && convo->has.gmbyte) {
1469 proto_tree_add_item(tibia_tree, hf_tibia_loginflags_gm, tvb_decrypted, offset, 1, ENC_NA);
1470 offset += 1;
1471 }
1472
1473 int len;
1474 if (convo->has.session_key && !convo->loginserv_is_peer) {
1475 /* OTServs I tested against use "$acc\n$pacc" as session key */
1476 if (convo->session_key) {
1477 proto_tree_add_item_ret_length(tibia_tree, hf_tibia_session_key, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, &len);
1478 } else {
1479 proto_tree_add_item_ret_string_and_length(tibia_tree, hf_tibia_session_key, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, wmem_file_scope(), &convo->session_key, &len);
1480 }
1481 offset += len;
1482 } else if (convo->has.acc_name) {
1483 if (convo->acc) {
1484 proto_tree_add_item_ret_length(tibia_tree, hf_tibia_acc_name, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, &len);
1485 } else {
1486 proto_tree_add_item_ret_string_and_length(tibia_tree, hf_tibia_acc_name, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, wmem_file_scope(), &convo->acc, &len);
1487 }
1488 offset += len;
1489 } else /* account number */ {
1490 char *accnum = wmem_strdup_printf(pinfo->pool, "%" G_GUINT32_FORMAT, tvb_get_letohl(tvb_decrypted, offset));
1491 proto_tree_add_string(tibia_tree, hf_tibia_acc_number, tvb_decrypted, offset, 4, accnum);
1492 if (!convo->acc)
1493 convo->acc = (guint8*)wmem_strdup(wmem_file_scope(), accnum);
1494 offset += 4;
1495 }
1496
1497 if (!convo->loginserv_is_peer) {
1498 if (convo->char_name) {
1499 proto_tree_add_item_ret_length(tibia_tree, hf_tibia_char_name, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, &len);
1500 } else {
1501 proto_tree_add_item_ret_string_and_length(tibia_tree, hf_tibia_char_name, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, wmem_file_scope(), &convo->char_name, &len);
1502 }
1503 offset += len;
1504 }
1505
1506 if (!convo->has.session_key || convo->loginserv_is_peer) {
1507 if (convo->pass) {
1508 proto_tree_add_item_ret_length(tibia_tree, hf_tibia_acc_pass, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, &len);
1509 } else {
1510 proto_tree_add_item_ret_string_and_length(tibia_tree, hf_tibia_acc_pass, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN | convo->has.string_enc, wmem_file_scope(), &convo->pass, &len);
1511 }
1512 offset += len;
1513 }
1514
1515 if (convo->loginserv_is_peer && convo->has.hwinfo) {
1516 proto_item *item;
1517 proto_tree *infotree, *subtree;
1518
1519 item = proto_tree_add_item(tibia_tree, hf_tibia_client_info, tvb_decrypted, offset, 47, ENC_NA);
1520 infotree = proto_item_add_subtree(item, ett_client_info);
1521
1522 /* Subtree { */
1523 guint locale_id;
1524 const guint8 *locale_name;
1525
1526 item = proto_tree_add_item(infotree, hf_tibia_client_locale, tvb_decrypted, offset, 4, ENC_NA);
1527 subtree = proto_item_add_subtree(item, ett_locale);
1528
1529 proto_tree_add_item_ret_uint(subtree, hf_tibia_client_locale_id, tvb_decrypted, offset, 1, ENC_NA, &locale_id);
1530 offset += 1;
1531
1532 proto_tree_add_item_ret_string(subtree, hf_tibia_client_locale_name, tvb_decrypted, offset, 3, convo->has.string_enc|ENC_NA, pinfo->pool, &locale_name);
1533 offset += 3;
1534 proto_item_set_text(item, "Locale: %s (0x%X)", locale_name, locale_id);
1535 /* } */
1536
1537 proto_tree_add_item(infotree, hf_tibia_client_ram, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN);
1538 offset += 2;
1539
1540 proto_tree_add_item(infotree, hf_tibia_unknown, tvb_decrypted, offset, 6, ENC_NA);
1541 offset += 6;
1542
1543 /* Subtree { */
1544 guint clock1, clock2;
1545 const guint8 *cpu;
1546
1547 item = proto_tree_add_item(infotree, hf_tibia_client_cpu, tvb_decrypted, offset, 15, ENC_NA);
1548 subtree = proto_item_add_subtree(item, ett_cpu);
1549
1550 proto_tree_add_item_ret_string(subtree, hf_tibia_client_cpu_name, tvb_decrypted, offset, 9, convo->has.string_enc|ENC_NA, pinfo->pool, &cpu);
1551 offset += 9;
1552
1553 proto_tree_add_item(subtree, hf_tibia_unknown, tvb_decrypted, offset, 2, ENC_NA);
1554 offset += 2;
1555
1556 proto_tree_add_item_ret_uint(subtree, hf_tibia_client_clock, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN, &clock1);
1557 offset += 2;
1558
1559 proto_tree_add_item_ret_uint(subtree, hf_tibia_client_clock2, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN, &clock2);
1560 offset += 2;
1561
1562 proto_item_set_text(item, "CPU: %s (%uMhz/%uMhz)", cpu, clock2, clock1);
1563 /* } */
1564
1565
1566 proto_tree_add_item(infotree, hf_tibia_unknown, tvb_decrypted, offset, 4, ENC_NA);
1567 offset += 4;
1568
1569 proto_tree_add_item(infotree, hf_tibia_client_gpu, tvb_decrypted, offset, 9, convo->has.string_enc|ENC_NA);
1570 offset += 9;
1571
1572 proto_tree_add_item(infotree, hf_tibia_client_vram, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN);
1573 offset += 2;
1574
1575 /* Subtree { */
1576 guint x, y, hz;
1577
1578 item = proto_tree_add_item(infotree, hf_tibia_client_resolution, tvb_decrypted, offset, 5, ENC_NA);
1579 subtree = proto_item_add_subtree(item, ett_resolution);
1580
1581 proto_tree_add_item_ret_uint(subtree, hf_tibia_client_resolution_x, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN, &x);
1582 offset += 2;
1583 proto_tree_add_item_ret_uint(subtree, hf_tibia_client_resolution_y, tvb_decrypted, offset, 2, ENC_LITTLE_ENDIAN, &y);
1584 offset += 2;
1585 proto_tree_add_item_ret_uint(subtree, hf_tibia_client_resolution_hz, tvb_decrypted, offset, 1, ENC_LITTLE_ENDIAN, &hz);
1586 offset += 1;
1587
1588 proto_item_set_text(item, "Resolution: %ux%u @ %uHz", x, y, hz);
1589 /* } */
1590
1591 } else if (!convo->loginserv_is_peer && convo->has.nonce) {
1592 proto_tree_add_item(tibia_tree, hf_tibia_nonce, tvb_decrypted, offset, 5, ENC_NA);
1593 offset += 5;
1594 }
1595
1596 if (convo->has.rsa) {
1597 /* Undecoded hardware info maybe */
1598 call_data_dissector(tvb_new_subset_length(tvb_decrypted, offset, 128 - offset), pinfo, tibia_tree);
1599 }
1600
1601 if (rsa1_end)
1602 offset = rsa1_end;
1603
1604 if (offset != plen) {
1605 /* TODO Extended GPU info and authentication token (RSA-encrypted again) */
1606 call_data_dissector(tvb_new_subset_length(tvb, offset, plen - offset), pinfo, tibia_tree);
1607 }
1608 return plen;
1609 }
1610
1611 static const value_string operating_systems[] = {
1612 { 2, "Windows" },
1613 { 0, NULL }
1614 };
1615
1616 static const value_string speech_types[] = {
1617 { 0x1, "Say" },
1618 { 0x2, "Whisper" },
1619 { 0x3, "Yell" },
1620 { 0x7, "Public Channel" },
1621 { 0, NULL }
1622 };
1623
1624 #if defined(HAVE_LIBGNUTLS)
1625 static guint
rsakey_hash(gconstpointer _rsakey)1626 rsakey_hash(gconstpointer _rsakey)
1627 {
1628 const struct rsakey *rsakey = (const struct rsakey *)_rsakey;
1629 return add_address_to_hash(rsakey->port, &rsakey->addr);
1630 }
1631
1632 static gboolean
rsakey_equal(gconstpointer _a,gconstpointer _b)1633 rsakey_equal(gconstpointer _a, gconstpointer _b)
1634 {
1635 const struct rsakey *a = (const struct rsakey *)_a,
1636 *b = (const struct rsakey *)_b;
1637 return a->port == b->port && addresses_equal(&a->addr, &b->addr);
1638 }
1639 static void
rsakey_free(void * _rsakey)1640 rsakey_free(void *_rsakey)
1641 {
1642 struct rsakey *rsakey = (struct rsakey *)_rsakey;
1643
1644 /* gcry_sexp_release(rsakey->privkey); */ /* private key may be shared. */
1645 free_address_wmem(NULL, &rsakey->addr);
1646 g_free(rsakey);
1647 }
1648
1649 static void
rsa_parse_uat(void)1650 rsa_parse_uat(void)
1651 {
1652 g_hash_table_remove_all(rsakeys);
1653
1654 for (guint i = 0; i < nrsakeys; i++) {
1655 struct rsakeys_assoc *uats = &rsakeylist_uats[i];
1656
1657 /* try to load keys file first */
1658 FILE *fp = ws_fopen(uats->keyfile, "rb");
1659 if (!fp) {
1660 report_open_failure(uats->keyfile, errno, FALSE);
1661 return;
1662 }
1663
1664 gnutls_x509_privkey_t priv_key;
1665 char *err = NULL;
1666 if (*uats->password) {
1667 priv_key = rsa_load_pkcs12(fp, uats->password, &err);
1668 if (err) {
1669 report_failure("%s\n", err);
1670 g_free(err);
1671 }
1672 } else {
1673 priv_key = rsa_load_pem_key(fp, &err);
1674 if (err) {
1675 report_failure("%s\n", err);
1676 g_free(err);
1677 }
1678 }
1679 fclose(fp);
1680
1681 if (!priv_key) {
1682 report_failure("Can't load private key from %s\n", uats->keyfile);
1683 return;
1684 }
1685
1686 struct rsakey *entry;
1687 guint32 ipaddr;
1688 gcry_sexp_t private_key = rsa_privkey_to_sexp(priv_key, &err);
1689 if (!private_key) {
1690 g_free(err);
1691 report_failure("Can't extract private key parameters for %s", uats->keyfile);
1692 goto end;
1693 }
1694
1695 entry = g_new(struct rsakey, 1);
1696 ws_strtou16(uats->port, NULL, &entry->port);
1697 ipaddr = ipv4tonl(uats->ipaddr);
1698 alloc_address_wmem(NULL, &entry->addr, AT_IPv4, sizeof ipaddr, &ipaddr);
1699 entry->privkey = private_key;
1700
1701
1702 g_hash_table_insert(rsakeys, entry, entry->privkey);
1703
1704 end:
1705 gnutls_x509_privkey_deinit(priv_key);
1706 }
1707 }
1708
1709 static void
rsakeys_free_cb(void * r)1710 rsakeys_free_cb(void *r)
1711 {
1712 struct rsakeys_assoc *h = (struct rsakeys_assoc *)r;
1713
1714 g_free(h->ipaddr);
1715 g_free(h->port);
1716 g_free(h->keyfile);
1717 g_free(h->password);
1718 }
1719
1720 static void*
rsakeys_copy_cb(void * dst_,const void * src_,size_t len _U_)1721 rsakeys_copy_cb(void *dst_, const void *src_, size_t len _U_)
1722 {
1723 const struct rsakeys_assoc *src = (const struct rsakeys_assoc *)src_;
1724 struct rsakeys_assoc *dst = (struct rsakeys_assoc *)dst_;
1725
1726 dst->ipaddr = g_strdup(src->ipaddr);
1727 dst->port = g_strdup(src->port);
1728 dst->keyfile = g_strdup(src->keyfile);
1729 dst->password = g_strdup(src->password);
1730
1731 return dst;
1732 }
1733
1734 static gboolean
rsakeys_uat_fld_ip_chk_cb(void * r _U_,const char * ipaddr,guint len _U_,const void * u1 _U_,const void * u2 _U_,char ** err)1735 rsakeys_uat_fld_ip_chk_cb(void* r _U_, const char* ipaddr, guint len _U_, const void* u1 _U_, const void* u2 _U_, char** err)
1736 {
1737 /* There are no Tibia IPv6 servers, although Tibia 11.0+'s Protocol in theory supports it */
1738 if (ipaddr && g_hostname_is_ip_address(ipaddr) && strchr(ipaddr, '.')) {
1739 *err = NULL;
1740 return TRUE;
1741 }
1742
1743 *err = g_strdup_printf("No IPv4 address given.");
1744 return FALSE;
1745 }
1746
1747 static gboolean
rsakeys_uat_fld_port_chk_cb(void * _record _U_,const char * str,guint len _U_,const void * chk_data _U_,const void * fld_data _U_,char ** err)1748 rsakeys_uat_fld_port_chk_cb(void *_record _U_, const char *str, guint len _U_, const void *chk_data _U_, const void *fld_data _U_, char **err)
1749 {
1750 guint16 val;
1751 if (!ws_strtou16(str, NULL, &val)) {
1752 *err = g_strdup("Invalid argument. Expected a decimal between [0-65535]");
1753 return FALSE;
1754 }
1755 *err = NULL;
1756 return TRUE;
1757 }
1758
1759 static gboolean
rsakeys_uat_fld_fileopen_chk_cb(void * r _U_,const char * p,guint len _U_,const void * u1 _U_,const void * u2 _U_,char ** err)1760 rsakeys_uat_fld_fileopen_chk_cb(void* r _U_, const char* p, guint len _U_, const void* u1 _U_, const void* u2 _U_, char** err)
1761 {
1762 if (p && *p) {
1763 ws_statb64 st;
1764 if (ws_stat64(p, &st) != 0) {
1765 *err = g_strdup_printf("File '%s' does not exist or access is denied.", p);
1766 return FALSE;
1767 }
1768 } else {
1769 *err = g_strdup("No filename given.");
1770 return FALSE;
1771 }
1772
1773 *err = NULL;
1774 return TRUE;
1775 }
1776
1777 static gboolean
rsakeys_uat_fld_password_chk_cb(void * r,const char * p,guint len _U_,const void * u1 _U_,const void * u2 _U_,char ** err)1778 rsakeys_uat_fld_password_chk_cb(void *r, const char *p, guint len _U_, const void *u1 _U_, const void *u2 _U_, char **err)
1779 {
1780 if (p && *p) {
1781 struct rsakeys_assoc *f = (struct rsakeys_assoc *)r;
1782 FILE *fp = ws_fopen(f->keyfile, "rb");
1783 if (fp) {
1784 char *msg = NULL;
1785 gnutls_x509_privkey_t priv_key = rsa_load_pkcs12(fp, p, &msg);
1786 if (!priv_key) {
1787 fclose(fp);
1788 *err = g_strdup_printf("Could not load PKCS#12 key file: %s", msg);
1789 g_free(msg);
1790 return FALSE;
1791 }
1792 g_free(msg);
1793 gnutls_x509_privkey_deinit(priv_key);
1794 fclose(fp);
1795 } else {
1796 *err = g_strdup_printf("Leave this field blank if the keyfile is not PKCS#12.");
1797 return FALSE;
1798 }
1799 }
1800
1801 *err = NULL;
1802 return TRUE;
1803 }
1804 #endif
1805
1806 static void
xtea_parse_uat(void)1807 xtea_parse_uat(void)
1808 {
1809 g_hash_table_remove_all(xteakeys);
1810
1811 for (guint i = 0; i < nxteakeys; i++) {
1812 guint key_idx = 0;
1813 guint8 *key = (guint8*)g_malloc(XTEA_KEY_LEN);
1814
1815 for (const char *str = xteakeylist_uats[i].key; str[0] && str[1] && key_idx < XTEA_KEY_LEN; str++) {
1816 if (g_ascii_ispunct(*str))
1817 continue;
1818
1819 key[key_idx++] = (g_ascii_xdigit_value(str[0]) << 4)
1820 + g_ascii_xdigit_value(str[1]);
1821 str++;
1822 }
1823
1824 g_hash_table_insert(xteakeys, GUINT_TO_POINTER(xteakeylist_uats[i].framenum), key);
1825 }
1826 }
1827
1828 static void
xteakeys_free_cb(void * r)1829 xteakeys_free_cb(void *r)
1830 {
1831 struct xteakeys_assoc *h = (struct xteakeys_assoc *)r;
1832
1833 g_free(h->key);
1834 }
1835
1836 static void*
xteakeys_copy_cb(void * dst_,const void * src_,size_t len _U_)1837 xteakeys_copy_cb(void *dst_, const void *src_, size_t len _U_)
1838 {
1839 const struct xteakeys_assoc *src = (const struct xteakeys_assoc *)src_;
1840 struct xteakeys_assoc *dst = (struct xteakeys_assoc *)dst_;
1841
1842 dst->framenum = src->framenum;
1843 dst->key = g_strdup(src->key);
1844
1845 return dst;
1846 }
1847
1848 static gboolean
xteakeys_uat_fld_key_chk_cb(void * r _U_,const char * key,guint len,const void * u1 _U_,const void * u2 _U_,char ** err)1849 xteakeys_uat_fld_key_chk_cb(void *r _U_, const char *key, guint len, const void *u1 _U_, const void *u2 _U_, char **err)
1850 {
1851 if (len >= XTEA_KEY_LEN*2) {
1852 gsize i = 0;
1853
1854 do {
1855 if (g_ascii_ispunct(*key))
1856 continue;
1857 if (!g_ascii_isxdigit(*key))
1858 break;
1859 i++;
1860 } while (*++key);
1861
1862 if (*key == '\0' && i == 2*XTEA_KEY_LEN) {
1863 *err = NULL;
1864 return TRUE;
1865 }
1866 }
1867
1868 *err = g_strdup_printf("XTEA keys are 32 character long hex strings.");
1869 return FALSE;
1870 }
1871
1872
1873 void
proto_register_tibia(void)1874 proto_register_tibia(void)
1875 {
1876 static hf_register_info hf[] = {
1877 { &hf_tibia_len,
1878 { "Packet length", "tibia.len",
1879 FT_UINT16, BASE_DEC,
1880 NULL, 0x0,
1881 NULL, HFILL }
1882 },
1883 { &hf_tibia_adler32,
1884 { "Adler32 checksum", "tibia.checksum",
1885 FT_UINT32, BASE_HEX,
1886 NULL, 0x0,
1887 NULL, HFILL }
1888 },
1889 { &hf_tibia_adler32_status,
1890 { "Checksum status", "tibia.checksum.status",
1891 FT_UINT8, BASE_NONE,
1892 VALS(proto_checksum_vals), 0x0,
1893 NULL, HFILL }
1894 },
1895 { &hf_tibia_nonce,
1896 { "Game server nonce", "tibia.nonce",
1897 FT_BYTES, BASE_NONE,
1898 NULL, 0x0,
1899 NULL, HFILL }
1900 },
1901 { &hf_tibia_os,
1902 { "Operating system", "tibia.os",
1903 FT_UINT16, BASE_HEX,
1904 VALS(operating_systems), 0x0,
1905 NULL, HFILL }
1906 },
1907 { &hf_tibia_proto_version,
1908 { "Protocol version", "tibia.version",
1909 FT_UINT16, BASE_DEC,
1910 NULL, 0x0,
1911 NULL, HFILL }
1912 },
1913 { &hf_tibia_client_version,
1914 { "Client version", "tibia.client_version",
1915 FT_UINT32, BASE_DEC,
1916 NULL, 0x0,
1917 NULL, HFILL }
1918 },
1919 { &hf_tibia_file_versions,
1920 { "File versions", "tibia.version.files",
1921 FT_NONE, BASE_NONE, NULL, 0x0,
1922 NULL, HFILL }
1923 },
1924 { &hf_tibia_file_version_spr,
1925 { "Tibia.spr version", "tibia.version.spr",
1926 FT_UINT32, BASE_HEX,
1927 NULL, 0x0,
1928 NULL, HFILL }
1929 },
1930 { &hf_tibia_file_version_dat,
1931 { "Tibia.dat version", "tibia.version.dat",
1932 FT_UINT32, BASE_HEX,
1933 NULL, 0x0,
1934 NULL, HFILL }
1935 },
1936 { &hf_tibia_file_version_pic,
1937 { "Tibia.pic version", "tibia.version.pic",
1938 FT_UINT32, BASE_HEX,
1939 NULL, 0x0,
1940 NULL, HFILL }
1941 },
1942 { &hf_tibia_content_revision,
1943 { "Content revision", "tibia.version.content",
1944 FT_UINT16, BASE_HEX,
1945 NULL, 0x0,
1946 NULL, HFILL }
1947 },
1948 { &hf_tibia_undecoded_rsa_data,
1949 { "RSA-encrypted login data", "tibia.rsa_data",
1950 FT_BYTES, BASE_NONE,
1951 NULL, 0x0,
1952 NULL, HFILL }
1953 },
1954 { &hf_tibia_undecoded_xtea_data,
1955 { "XTEA-encrypted game data", "tibia.xtea_data",
1956 FT_BYTES, BASE_NONE,
1957 NULL, 0x0,
1958 NULL, HFILL }
1959 },
1960 { &hf_tibia_unknown,
1961 { "Unknown Data", "tibia.unknown",
1962 FT_BYTES, BASE_NONE,
1963 NULL, 0x0,
1964 NULL, HFILL }
1965 },
1966 { &hf_tibia_xtea_key,
1967 { "Symmetric key (XTEA)", "tibia.xtea",
1968 FT_BYTES, BASE_NONE,
1969 NULL, 0x0,
1970 NULL, HFILL }
1971 },
1972 { &hf_tibia_loginflags_gm,
1973 { "Gamemaster", "tibia.login.flags.gm",
1974 FT_BOOLEAN, 8,
1975 NULL, 0x1,
1976 NULL, HFILL }
1977 },
1978 { &hf_tibia_game_preview_state,
1979 { "Game Preview State", "tibia.login.flags.preview",
1980 FT_BOOLEAN, 8,
1981 NULL, 0x1,
1982 NULL, HFILL }
1983 },
1984 { &hf_tibia_char_cond,
1985 {"Character Condition", "tibia.cond",
1986 FT_UINT32, BASE_HEX,
1987 NULL, 0,
1988 NULL, HFILL}
1989 },
1990 { &hf_tibia_char_cond_poisoned,
1991 { "Poisoned", "tibia.cond.poisoned",
1992 FT_BOOLEAN, 32,
1993 TFS(&tfs_yes_no), COND_POISONED,
1994 NULL, HFILL }
1995 },
1996 { &hf_tibia_char_cond_burning,
1997 { "Burning", "tibia.cond.burning",
1998 FT_BOOLEAN, 32,
1999 TFS(&tfs_yes_no), COND_BURNING,
2000 NULL, HFILL }
2001 },
2002 { &hf_tibia_char_cond_electrocuted,
2003 { "Electrocuted", "tibia.cond.electrocuted",
2004 FT_BOOLEAN, 32,
2005 TFS(&tfs_yes_no), COND_ELECTROCUTED,
2006 NULL, HFILL }
2007 },
2008 { &hf_tibia_char_cond_drunk,
2009 { "Drunk", "tibia.cond.drunk",
2010 FT_BOOLEAN, 32,
2011 TFS(&tfs_yes_no), COND_DRUNK,
2012 NULL, HFILL }
2013 },
2014 { &hf_tibia_char_cond_manashield, /* Utamo Vita */
2015 { "Mana Shield", "tibia.cond.manashield",
2016 FT_BOOLEAN, 32,
2017 TFS(&tfs_yes_no), COND_MANASHIELD,
2018 NULL, HFILL }
2019 },
2020 { &hf_tibia_char_cond_paralyzed,
2021 { "Paralyzed", "tibia.cond.paralyzed",
2022 FT_BOOLEAN, 32,
2023 TFS(&tfs_yes_no), COND_PARALYZED,
2024 NULL, HFILL }
2025 },
2026 { &hf_tibia_char_cond_haste,
2027 { "Haste", "tibia.cond.haste",
2028 FT_BOOLEAN, 32,
2029 TFS(&tfs_yes_no), COND_HASTE,
2030 NULL, HFILL }
2031 },
2032 { &hf_tibia_char_cond_battle,
2033 { "Battle lock", "tibia.cond.battle",
2034 FT_BOOLEAN, 32,
2035 TFS(&tfs_yes_no), COND_BATTLE,
2036 NULL, HFILL }
2037 },
2038 { &hf_tibia_char_cond_drowning,
2039 { "Drowning", "tibia.cond.drowning",
2040 FT_BOOLEAN, 32,
2041 TFS(&tfs_yes_no), COND_DROWNING,
2042 NULL, HFILL }
2043 },
2044 { &hf_tibia_char_cond_freezing,
2045 { "Freezing", "tibia.cond.freezing",
2046 FT_BOOLEAN, 32,
2047 TFS(&tfs_yes_no), COND_FREEZING,
2048 NULL, HFILL }
2049 },
2050 { &hf_tibia_char_cond_dazzled,
2051 { "Dazzled", "tibia.cond.dazzled",
2052 FT_BOOLEAN, 32,
2053 TFS(&tfs_yes_no), COND_DAZZLED,
2054 NULL, HFILL }
2055 },
2056 { &hf_tibia_char_cond_cursed,
2057 { "Cursed", "tibia.cond.cursed",
2058 FT_BOOLEAN, 32,
2059 TFS(&tfs_yes_no), COND_CURSED,
2060 NULL, HFILL }
2061 },
2062 { &hf_tibia_char_cond_buff, /* e.g. after casting Utura */
2063 { "Buff", "tibia.cond.buff",
2064 FT_BOOLEAN, 32,
2065 TFS(&tfs_yes_no), COND_BUFF,
2066 NULL, HFILL }
2067 },
2068 { &hf_tibia_char_cond_pzblock, /* Blocked from entering PZ */
2069 { "Protection Zone Block", "tibia.cond.pzblock",
2070 FT_BOOLEAN, 32,
2071 TFS(&tfs_yes_no), COND_PZBLOCK,
2072 NULL, HFILL }
2073 },
2074 { &hf_tibia_char_cond_pz,
2075 { "Protection Zone", "tibia.cond.pz",
2076 FT_BOOLEAN, 32,
2077 TFS(&tfs_yes_no), COND_PZ,
2078 NULL, HFILL }
2079 },
2080 { &hf_tibia_char_cond_bleeding,
2081 { "Bleeding", "tibia.cond.bleeding",
2082 FT_BOOLEAN, 32,
2083 TFS(&tfs_yes_no), COND_BLEEDING,
2084 NULL, HFILL }
2085 },
2086 { &hf_tibia_char_cond_hungry,
2087 { "Hungry", "tibia.cond.hungry",
2088 FT_BOOLEAN, 32,
2089 TFS(&tfs_yes_no), COND_HUNGRY,
2090 NULL, HFILL }
2091 },
2092 { &hf_tibia_acc_name,
2093 { "Account", "tibia.acc",
2094 FT_UINT_STRING, BASE_NONE,
2095 NULL, 0x0,
2096 NULL, HFILL }
2097 },
2098 { &hf_tibia_acc_number,
2099 { "Account", "tibia.acc",
2100 FT_STRING, BASE_NONE,
2101 NULL, 0x0,
2102 NULL, HFILL }
2103 },
2104 { &hf_tibia_session_key,
2105 { "Session key", "tibia.session_key",
2106 FT_UINT_STRING, BASE_NONE,
2107 NULL, 0x0,
2108 NULL, HFILL }
2109 },
2110 { &hf_tibia_char_name,
2111 { "Character name", "tibia.char",
2112 FT_UINT_STRING, BASE_NONE,
2113 NULL, 0x0,
2114 NULL, HFILL }
2115 },
2116 { &hf_tibia_acc_pass,
2117 { "Password", "tibia.pass",
2118 FT_UINT_STRING, BASE_NONE,
2119 NULL, 0x0,
2120 NULL, HFILL }
2121 },
2122 { &hf_tibia_char_name_convo,
2123 { "Character name", "tibia.char",
2124 FT_STRING, BASE_NONE,
2125 NULL, 0x0,
2126 NULL, HFILL }
2127 },
2128 { &hf_tibia_acc_name_convo,
2129 { "Account", "tibia.acc",
2130 FT_STRING, BASE_NONE,
2131 NULL, 0x0,
2132 NULL, HFILL }
2133 },
2134 { &hf_tibia_acc_pass_convo,
2135 { "Password", "tibia.pass",
2136 FT_STRING, BASE_NONE,
2137 NULL, 0x0,
2138 NULL, HFILL }
2139 },
2140 { &hf_tibia_session_key_convo,
2141 { "Session key", "tibia.session_key",
2142 FT_STRING, BASE_NONE,
2143 NULL, 0x0,
2144 NULL, HFILL }
2145 },
2146 { &hf_tibia_client_info,
2147 { "Client information", "tibia.client.info",
2148 FT_NONE, BASE_NONE,
2149 NULL, 0x0,
2150 NULL, HFILL }
2151 },
2152 { &hf_tibia_client_locale,
2153 { "Locale", "tibia.client.locale",
2154 FT_NONE, BASE_NONE,
2155 NULL, 0x0,
2156 NULL, HFILL }
2157 },
2158 { &hf_tibia_client_locale_id,
2159 { "Locale ID", "tibia.client.locale.id",
2160 FT_UINT8, BASE_DEC,
2161 NULL, 0x0,
2162 NULL, HFILL }
2163 },
2164 { &hf_tibia_client_locale_name,
2165 { "Locale", "tibia.client.locale.name",
2166 FT_STRING, BASE_NONE,
2167 NULL, 0x0,
2168 NULL, HFILL }
2169 },
2170 { &hf_tibia_client_ram,
2171 { "Total RAM", "tibia.client.ram",
2172 FT_UINT16, BASE_DEC,
2173 NULL, 0x0,
2174 NULL, HFILL }
2175 },
2176 { &hf_tibia_client_cpu,
2177 { "CPU", "tibia.client.cpu",
2178 FT_NONE, BASE_NONE,
2179 NULL, 0x0,
2180 NULL, HFILL }
2181 },
2182 { &hf_tibia_client_cpu_name,
2183 { "CPU", "tibia.client.cpu.name",
2184 FT_STRINGZ, BASE_NONE,
2185 NULL, 0x0,
2186 NULL, HFILL }
2187 },
2188 { &hf_tibia_client_clock,
2189 { "CPU clock", "tibia.client.cpu.clock",
2190 FT_UINT8, BASE_DEC,
2191 NULL, 0x0,
2192 NULL, HFILL }
2193 },
2194 { &hf_tibia_client_clock2,
2195 { "CPU clock2", "tibia.client.cpu.clock2",
2196 FT_UINT8, BASE_DEC,
2197 NULL, 0x0,
2198 NULL, HFILL }
2199 },
2200 { &hf_tibia_client_gpu,
2201 { "GPU", "tibia.client.gpu",
2202 FT_STRINGZ, BASE_NONE,
2203 NULL, 0x0,
2204 NULL, HFILL }
2205 },
2206 { &hf_tibia_client_vram,
2207 { "Video RAM", "tibia.client.vram",
2208 FT_UINT8, BASE_DEC|BASE_UNIT_STRING,
2209 &mb_unit, 0x0,
2210 NULL, HFILL }
2211 },
2212 { &hf_tibia_client_resolution,
2213 { "Screen resolution", "tibia.client.resolution",
2214 FT_NONE, BASE_NONE,
2215 NULL, 0x0,
2216 NULL, HFILL }
2217 },
2218 { &hf_tibia_client_resolution_x,
2219 { "Horizontal resolution", "tibia.client.resolution.x",
2220 FT_UINT16, BASE_DEC,
2221 NULL, 0x0,
2222 NULL, HFILL }
2223 },
2224 { &hf_tibia_client_resolution_y,
2225 { "Vertical resolution", "tibia.client.resolution.y",
2226 FT_UINT16, BASE_DEC,
2227 NULL, 0x0,
2228 NULL, HFILL }
2229 },
2230 { &hf_tibia_client_resolution_hz,
2231 { "Refresh rate", "tibia.client.resolution.hz",
2232 FT_UINT8, BASE_DEC,
2233 NULL, 0x0,
2234 NULL, HFILL }
2235 },
2236 { &hf_tibia_payload_len,
2237 { "Payload length", "tibia.payload.len",
2238 FT_UINT16, BASE_DEC,
2239 NULL, 0x0,
2240 NULL, HFILL }
2241 },
2242 { &hf_tibia_loginserv_command,
2243 { "Command", "tibia.cmd",
2244 FT_UINT8, BASE_HEX,
2245 VALS(from_loginserv_packet_types), 0x0,
2246 NULL, HFILL }
2247 },
2248 { &hf_tibia_gameserv_command,
2249 { "Command", "tibia.cmd",
2250 FT_UINT8, BASE_HEX|BASE_EXT_STRING,
2251 &from_gameserv_packet_types_ext, 0x0,
2252 NULL, HFILL }
2253 },
2254 { &hf_tibia_client_command,
2255 { "Command", "tibia.cmd",
2256 FT_UINT8, BASE_HEX|BASE_EXT_STRING,
2257 &from_client_packet_types_ext, 0x0,
2258 NULL, HFILL }
2259 },
2260 { &hf_tibia_motd,
2261 { "Message of the day", "tibia.motd",
2262 FT_UINT_STRING, BASE_NONE, NULL, 0x0,
2263 NULL, HFILL }
2264 },
2265 { &hf_tibia_dlg_error,
2266 { "Error message", "tibia.login.err",
2267 FT_UINT_STRING, BASE_NONE, NULL, 0x0,
2268 NULL, HFILL }
2269 },
2270 { &hf_tibia_dlg_info,
2271 { "Info message", "tibia.login.info",
2272 FT_UINT_STRING, BASE_NONE, NULL, 0x0,
2273 NULL, HFILL }
2274 },
2275 { &hf_tibia_charlist,
2276 { "Character list", "tibia.charlist",
2277 FT_NONE, BASE_NONE, NULL, 0x0,
2278 NULL, HFILL }
2279 },
2280 { &hf_tibia_charlist_length,
2281 { "Character count", "tibia.charlist.count",
2282 FT_UINT8, BASE_DEC,
2283 NULL, 0x0,
2284 NULL, HFILL }
2285 },
2286 { &hf_tibia_charlist_entry_name,
2287 { "Character name", "tibia.charlist.name",
2288 FT_UINT_STRING, BASE_NONE,
2289 NULL, 0x0,
2290 NULL, HFILL }
2291 },
2292 { &hf_tibia_charlist_entry_world,
2293 { "World", "tibia.charlist.world",
2294 FT_UINT_STRING, BASE_NONE,
2295 NULL, 0x0,
2296 NULL, HFILL }
2297 },
2298 { &hf_tibia_charlist_entry_ip,
2299 { "IP", "tibia.charlist.ip",
2300 FT_IPv4, BASE_NONE,
2301 NULL, 0x0,
2302 NULL, HFILL }
2303 },
2304 { &hf_tibia_charlist_entry_port,
2305 { "Port", "tibia.charlist.port",
2306 FT_UINT16, BASE_DEC,
2307 NULL, 0x0,
2308 NULL, HFILL }
2309 },
2310 { &hf_tibia_worldlist,
2311 { "World list", "tibia.worldlist",
2312 FT_NONE, BASE_NONE, NULL, 0x0,
2313 NULL, HFILL }
2314 },
2315 { &hf_tibia_worldlist_entry_name,
2316 { "World", "tibia.worldlist.name",
2317 FT_UINT_STRING, BASE_NONE,
2318 NULL, 0x0,
2319 NULL, HFILL }
2320 },
2321 { &hf_tibia_worldlist_length,
2322 { "World count", "tibia.worldlist.count",
2323 FT_UINT16, BASE_DEC,
2324 NULL, 0x0,
2325 NULL, HFILL }
2326 },
2327 { &hf_tibia_worldlist_entry_id,
2328 { "World ID", "tibia.worldlist.id",
2329 FT_UINT8, BASE_DEC,
2330 NULL, 0x0,
2331 NULL, HFILL }
2332 },
2333 { &hf_tibia_worldlist_entry_ip,
2334 { "IP", "tibia.worldlist.ip",
2335 FT_UINT_STRING, BASE_NONE,
2336 NULL, 0x0,
2337 NULL, HFILL }
2338 },
2339 { &hf_tibia_worldlist_entry_port,
2340 { "Port", "tibia.worldlist.port",
2341 FT_UINT16, BASE_DEC,
2342 NULL, 0x0,
2343 NULL, HFILL }
2344 },
2345 { &hf_tibia_worldlist_entry_preview,
2346 { "Preview State", "tibia.worldlist.preview",
2347 FT_BOOLEAN, 8,
2348 NULL, 0x1,
2349 NULL, HFILL }
2350 },
2351 { &hf_tibia_pacc_days,
2352 { "Premium days left", "tibia.pacc",
2353 FT_UINT16, BASE_DEC,
2354 NULL, 0x0,
2355 NULL, HFILL }
2356 },
2357 { &hf_tibia_channel_id,
2358 { "Channel id", "tibia.channel.id",
2359 FT_UINT16, BASE_HEX,
2360 NULL, 0x0,
2361 NULL, HFILL }
2362 },
2363 { &hf_tibia_channel_name,
2364 { "Channel name", "tibia.channel",
2365 FT_UINT_STRING, BASE_NONE,
2366 NULL, 0x0,
2367 NULL, HFILL }
2368 },
2369 { &hf_tibia_speech_type,
2370 { "Type", "tibia.speechtype",
2371 FT_UINT8, BASE_HEX,
2372 VALS(speech_types), 0x0,
2373 NULL, HFILL }
2374 },
2375 { &hf_tibia_chat_msg,
2376 { "Message", "tibia.msg",
2377 FT_UINT_STRING, BASE_NONE, NULL, 0x0,
2378 NULL, HFILL }
2379 },
2380 { &hf_tibia_coords_x,
2381 { "X-Coordinate", "tibia.coord.x",
2382 FT_UINT16, BASE_DEC, NULL, 0x0,
2383 NULL, HFILL }
2384 },
2385 { &hf_tibia_coords_y,
2386 { "Y-Coordinate", "tibia.coords.y",
2387 FT_UINT16, BASE_DEC, NULL, 0x0,
2388 NULL, HFILL }
2389 },
2390 { &hf_tibia_coords_z,
2391 { "Z-Coordinate", "tibia.coords.z",
2392 FT_UINT8, BASE_DEC, NULL, 0x0,
2393 NULL, HFILL }
2394 },
2395 { &hf_tibia_coords,
2396 { "Coordinates", "tibia.coords",
2397 FT_STRING, BASE_NONE,
2398 NULL, 0x0,
2399 NULL, HFILL }
2400 },
2401 { &hf_tibia_stackpos,
2402 { "Stack position", "tibia.coords.stackpos",
2403 FT_UINT8, BASE_DEC,
2404 NULL, 0x0,
2405 NULL, HFILL }
2406 },
2407 #if 0
2408 { &hf_tibia_item,
2409 { "Item ID", "tibia.item",
2410 FT_UINT16, BASE_DEC,
2411 NULL, 0x0,
2412 NULL, HFILL }
2413 },
2414 #endif
2415 { &hf_tibia_container,
2416 { "Container index", "tibia.container",
2417 FT_UINT8, BASE_DEC,
2418 NULL, 0x0,
2419 NULL, HFILL }
2420 },
2421 { &hf_tibia_container_icon,
2422 { "Container icon", "tibia.container.icon",
2423 FT_UINT8, BASE_DEC,
2424 NULL, 0x0,
2425 NULL, HFILL }
2426 },
2427 { &hf_tibia_container_slot,
2428 { "Container slot", "tibia.container.slot",
2429 FT_UINT8, BASE_DEC,
2430 NULL, 0x0,
2431 NULL, HFILL }
2432 },
2433 { &hf_tibia_container_slots,
2434 { "Container slots", "tibia.container.slots",
2435 FT_UINT8, BASE_DEC,
2436 NULL, 0x0,
2437 NULL, HFILL }
2438 },
2439 { &hf_tibia_inventory,
2440 { "Inventory slot", "tibia.inventory",
2441 FT_UINT16, BASE_DEC,
2442 NULL, 0x0,
2443 NULL, HFILL }
2444 },
2445 { &hf_tibia_vip,
2446 { "VIP GUID", "tibia.vip",
2447 FT_UINT32, BASE_HEX, NULL, 0x0,
2448 NULL, HFILL }
2449 },
2450 { &hf_tibia_vip_online,
2451 { "Online", "tibia.vip.online",
2452 FT_BOOLEAN, 8,
2453 NULL, 0x1,
2454 NULL, HFILL }
2455 },
2456 { &hf_tibia_player,
2457 { "Player name", "tibia.player",
2458 FT_UINT_STRING, BASE_NONE,
2459 NULL, 0x0,
2460 NULL, HFILL }
2461 },
2462 { &hf_tibia_creature,
2463 { "Creature", "tibia.creature",
2464 FT_UINT32, BASE_HEX,
2465 NULL, 0x0,
2466 NULL, HFILL }
2467 },
2468 { &hf_tibia_creature_health,
2469 { "Creature", "tibia.creature.health",
2470 FT_UINT8, BASE_DEC|BASE_UNIT_STRING,
2471 &units_percent, 0x0,
2472 NULL, HFILL }
2473 },
2474 { &hf_tibia_window,
2475 { "Window", "tibia.window",
2476 FT_UINT32, BASE_HEX,
2477 NULL, 0x0,
2478 NULL, HFILL }
2479 },
2480 { &hf_tibia_window_icon,
2481 { "Window Icon", "tibia.window.icon",
2482 FT_UINT8, BASE_HEX,
2483 NULL, 0x0,
2484 NULL, HFILL }
2485 },
2486 { &hf_tibia_window_textlen,
2487 { "Window Text Length", "tibia.window.text.len",
2488 FT_UINT8, BASE_DEC,
2489 NULL, 0x0,
2490 NULL, HFILL }
2491 },
2492 { &hf_tibia_window_text,
2493 { "Window Text", "tibia.window.text",
2494 FT_UINT_STRING, BASE_NONE,
2495 NULL, 0x0,
2496 NULL, HFILL }
2497 },
2498 { &hf_tibia_squarecolor,
2499 { "Square Color", "tibia.creature.square",
2500 FT_UINT8, BASE_HEX,
2501 NULL, 0x0,
2502 NULL, HFILL }
2503 },
2504 { &hf_tibia_light_color,
2505 { "Light Color", "tibia.light.color",
2506 FT_UINT8, BASE_HEX,
2507 NULL, 0x0,
2508 NULL, HFILL }
2509 },
2510 { &hf_tibia_light_level,
2511 { "Light Level", "tibia.light.level",
2512 FT_UINT8, BASE_DEC,
2513 NULL, 0x0,
2514 NULL, HFILL }
2515 },
2516 { &hf_tibia_magic_effect_id,
2517 { "Magic Effect", "tibia.magic_effect",
2518 FT_UINT8, BASE_HEX,
2519 NULL, 0x0,
2520 NULL, HFILL }
2521 },
2522 { &hf_tibia_animated_text_color,
2523 { "Text Color", "tibia.animated_text.color",
2524 FT_UINT8, BASE_HEX,
2525 NULL, 0x0,
2526 NULL, HFILL }
2527 },
2528 { &hf_tibia_animated_text,
2529 { "Text", "tibia.animated_text",
2530 FT_UINT16, BASE_HEX,
2531 NULL, 0x0,
2532 NULL, HFILL }
2533 },
2534 { &hf_tibia_textmsg_class,
2535 { "Text Message Class", "tibia.textmsg.class",
2536 FT_UINT8, BASE_HEX,
2537 NULL, 0x0,
2538 NULL, HFILL }
2539 },
2540 { &hf_tibia_textmsg,
2541 { "Text", "tibia.textmsg",
2542 FT_UINT_STRING, BASE_NONE,
2543 NULL, 0x0,
2544 NULL, HFILL }
2545 },
2546 { &hf_tibia_projectile,
2547 { "Projectile", "tibia.projectile",
2548 FT_UINT32, BASE_HEX,
2549 NULL, 0x0,
2550 NULL, HFILL }
2551 },
2552 { &hf_tibia_walk_dir,
2553 { "Walk Direction", "tibia.walk_dir",
2554 FT_UINT8, BASE_HEX,
2555 NULL, 0x0,
2556 NULL, HFILL }
2557 },
2558 };
2559
2560 /* Setup protocol subtree array */
2561 static gint *ett[] = {
2562 &ett_tibia,
2563 &ett_command,
2564 &ett_file_versions,
2565 &ett_client_info,
2566 &ett_locale,
2567 &ett_cpu,
2568 &ett_resolution,
2569 &ett_charlist,
2570 &ett_char,
2571 &ett_worldlist,
2572 &ett_world,
2573 &ett_coords,
2574 &ett_char_cond,
2575 };
2576
2577 static ei_register_info ei[] = {
2578 { &ei_xtea_len_toobig,
2579 { "tibia.error.xtea.length.toobig", PI_DECRYPTION, PI_ERROR,
2580 "XTEA-encrypted length exceeds packet", EXPFILL }
2581 },
2582 { &ei_adler32_checksum_bad, { "tibia.error.checksum_bad", PI_CHECKSUM, PI_ERROR,
2583 "Bad checksum", EXPFILL }
2584 },
2585 { &ei_rsa_plaintext_no_leading_zero,
2586 { "tibia.error.rsa", PI_DECRYPTION, PI_ERROR,
2587 "First byte after RSA decryption must be zero", EXPFILL }
2588 },
2589 { &ei_rsa_ciphertext_too_short,
2590 { "tibia.error.rsa.length.tooshort", PI_DECRYPTION, PI_ERROR,
2591 "RSA-encrypted data is at least 128 byte long", EXPFILL }
2592 },
2593 { &ei_rsa_decrypt_failed,
2594 { "tibia.error.rsa.failed", PI_DECRYPTION, PI_ERROR,
2595 "Decrypting RSA block failed", EXPFILL }
2596 },
2597 };
2598
2599 proto_tibia = proto_register_protocol (
2600 "Tibia Protocol", /* name */
2601 "Tibia", /* short name */
2602 "tibia" /* abbrev */
2603 );
2604 proto_register_field_array(proto_tibia, hf, array_length(hf));
2605 proto_register_subtree_array(ett, array_length(ett));
2606
2607 expert_module_t *expert_tibia = expert_register_protocol(proto_tibia);
2608 expert_register_field_array (expert_tibia, ei, array_length (ei));
2609
2610 module_t *tibia_module = prefs_register_protocol(proto_tibia, proto_reg_handoff_tibia);
2611
2612 prefs_register_bool_preference(tibia_module, "try_otserv_key", "Try OTServ's RSA key",
2613 "Try the default RSA key in use by nearly all Open Tibia servers", &try_otserv_key);
2614
2615 prefs_register_bool_preference(tibia_module, "show_char_name", "Show character name for each packet",
2616 "Shows active character for every packet", &show_char_name);
2617 prefs_register_bool_preference(tibia_module, "show_acc_info", "Show account info for each packet",
2618 "Shows account name/password or session key for every packet", &show_acc_info);
2619 prefs_register_bool_preference(tibia_module, "show_xtea_key", "Show symmetric key used for each packet",
2620 "Shows which XTEA key was applied for a packet", &show_xtea_key);
2621 prefs_register_bool_preference(tibia_module, "dissect_game_commands", "Attempt dissection of game packet commands",
2622 "Only decrypt packets and dissect login packets. Pass game commands to the data dissector", &dissect_game_commands);
2623 prefs_register_bool_preference(tibia_module, "reassemble_tcp_segments",
2624 "Reassemble Tibia packets spanning multiple TCP segments",
2625 "Whether the Tibia dissector should reassemble packets spanning multiple TCP segments."
2626 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
2627 &reassemble_tcp_segments);
2628
2629
2630 #ifdef HAVE_LIBGNUTLS
2631 static uat_field_t rsakeylist_uats_flds[] = {
2632 UAT_FLD_CSTRING_OTHER(rsakeylist_uats, ipaddr, "IP address", rsakeys_uat_fld_ip_chk_cb, "IPv4 address"),
2633 UAT_FLD_CSTRING_OTHER(rsakeylist_uats, port, "Port", rsakeys_uat_fld_port_chk_cb, "Port Number"),
2634 UAT_FLD_FILENAME_OTHER(rsakeylist_uats, keyfile, "Key File", rsakeys_uat_fld_fileopen_chk_cb, "Private keyfile."),
2635 UAT_FLD_CSTRING_OTHER(rsakeylist_uats, password,"Password", rsakeys_uat_fld_password_chk_cb, "Password (for keyfile)"),
2636 UAT_END_FIELDS
2637 };
2638
2639 rsakeys_uat = uat_new("RSA Keys",
2640 sizeof(struct rsakeys_assoc),
2641 "tibia_rsa_keys", /* filename */
2642 TRUE, /* from_profile */
2643 &rsakeylist_uats, /* data_ptr */
2644 &nrsakeys, /* numitems_ptr */
2645 UAT_AFFECTS_DISSECTION,
2646 NULL,
2647 rsakeys_copy_cb,
2648 NULL,
2649 rsakeys_free_cb,
2650 rsa_parse_uat,
2651 NULL,
2652 rsakeylist_uats_flds);
2653 prefs_register_uat_preference(tibia_module, "rsakey_table",
2654 "RSA keys list",
2655 "A table of RSA keys for decrypting protocols newer than 7.61",
2656 rsakeys_uat
2657 );
2658
2659 rsakeys = g_hash_table_new_full(rsakey_hash, rsakey_equal, rsakey_free, NULL);
2660 #endif
2661
2662 static uat_field_t xteakeylist_uats_flds[] = {
2663 UAT_FLD_DEC(xteakeylist_uats, framenum, "Frame Number", "XTEA key"),
2664 UAT_FLD_CSTRING_OTHER(xteakeylist_uats, key, "XTEA Key", xteakeys_uat_fld_key_chk_cb, "Symmetric (XTEA) key"),
2665 UAT_END_FIELDS
2666 };
2667
2668 xteakeys_uat = uat_new("XTEA Keys",
2669 sizeof(struct xteakeys_assoc),
2670 "tibia_xtea_keys", /* filename */
2671 TRUE, /* from_profile */
2672 &xteakeylist_uats, /* data_ptr */
2673 &nxteakeys, /* numitems_ptr */
2674 UAT_AFFECTS_DISSECTION,
2675 NULL,
2676 xteakeys_copy_cb,
2677 NULL,
2678 xteakeys_free_cb,
2679 xtea_parse_uat,
2680 NULL,
2681 xteakeylist_uats_flds);
2682 prefs_register_uat_preference(tibia_module, "xteakey_table",
2683 "XTEA keys list",
2684 "A table of XTEA keys for decrypting protocols newer than 7.61",
2685 xteakeys_uat
2686 );
2687
2688 xteakeys = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
2689
2690 /* TODO best way to store this in source? */
2691 const char sexp[] =
2692 "(private-key (rsa"
2693 "(n #9b646903b45b07ac956568d87353bd7165139dd7940703b03e6dd079399661b4a837aa60561d7ccb9452fa0080594909882ab5bca58a1a1b35f8b1059b72b1212611c6152ad3dbb3cfbee7adc142a75d3d75971509c321c5c24a5bd51fd460f01b4e15beb0de1930528a5d3f15c1e3cbf5c401d6777e10acaab33dbe8d5b7ff5#)"
2694 "(e #010001#)"
2695 "(d #428bd3b5346daf71a761106f71a43102f8c857d6549c54660bb6378b52b0261399de8ce648bac410e2ea4e0a1ced1fac2756331220ca6db7ad7b5d440b7828865856e7aa6d8f45837feee9b4a3a0aa21322a1e2ab75b1825e786cf81a28a8a09a1e28519db64ff9baf311e850c2bfa1fb7b08a056cc337f7df443761aefe8d81#)"
2696 "(p #91b37307abe12c05a1b78754746cda444177a784b035cbb96c945affdc022d21da4bd25a4eae259638153e9d73c97c89092096a459e5d16bcadd07fa9d504885#)"
2697 "(q #0111071b206bafb9c7a2287d7c8d17a42e32abee88dfe9520692b5439d9675817ff4f8c94a4abcd4b5f88e220f3a8658e39247a46c6983d85618fd891001a0acb1#)"
2698 "(u #6b21cd5e373fe462a22061b44a41fd01738a3892e0bd8728dbb5b5d86e7675235a469fea3266412fe9a659f486144c1e593d56eb3f6cfc7b2edb83ba8e95403a#)"
2699 "))";
2700
2701 gcry_error_t err = gcry_sexp_new(&otserv_key, sexp, 0, 1);
2702 if (err)
2703 report_failure("Loading OTServ RSA key failed: %s/%s\n", gcry_strerror(err), gcry_strsource(err));
2704 }
2705
2706 static guint
get_dissect_tibia_len(packet_info * pinfo _U_,tvbuff_t * tvb,int offset,void * data _U_)2707 get_dissect_tibia_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
2708 {
2709 return tvb_get_letohs(tvb, offset) + 2;
2710 }
2711
2712 static int
dissect_tibia_tcp(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)2713 dissect_tibia_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
2714 {
2715 static guint32 packet_num, fragment_num;
2716
2717 if (!packet_num) packet_num = pinfo->num;
2718 if (packet_num != pinfo->num) {
2719 fragment_num = 0;
2720 packet_num = pinfo->num;
2721 }
2722
2723 fragment_num++;
2724
2725
2726 tcp_dissect_pdus(tvb, pinfo, tree, reassemble_tcp_segments, 2,
2727 get_dissect_tibia_len, dissect_tibia, GUINT_TO_POINTER(fragment_num));
2728 return tvb_reported_length(tvb);
2729 }
2730
2731 void
proto_reg_handoff_tibia(void)2732 proto_reg_handoff_tibia(void)
2733 {
2734 dissector_handle_t tibia_handle = create_dissector_handle(dissect_tibia_tcp, proto_tibia);
2735
2736 dissector_add_uint_range_with_preference("tcp.port", TIBIA_DEFAULT_TCP_PORT_RANGE, tibia_handle);
2737 }
2738
2739
2740 /*
2741 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2742 *
2743 * Local variables:
2744 * c-basic-offset: 4
2745 * tab-width: 8
2746 * indent-tabs-mode: nil
2747 * End:
2748 *
2749 * vi: set shiftwidth=4 tabstop=8 expandtab:
2750 * :indentSize=4:tabSize=8:noTabs=true:
2751 */
2752