WOWW: Add working decryption

This commit is contained in:
Gtker 2021-07-15 18:38:27 +02:00 committed by Wireshark GitLab Utility
parent e1b3796419
commit 92e62a74fd
1 changed files with 103 additions and 5 deletions

View File

@ -34,6 +34,8 @@
#include <config.h>
#include <epan/packet.h> /* Should be first Wireshark include (other than config.h) */
#include <epan/conversation.h> /* Should be first Wireshark include (other than config.h) */
#include <epan/wmem/wmem.h> /* Should be first Wireshark include (other than config.h) */
/* Prototypes */
void proto_reg_handoff_woww(void);
@ -51,11 +53,27 @@ static int hf_woww_opcode_field = -1;
#define WOWW_CLIENT_TO_SERVER pinfo->destport == WOWW_TCP_PORT
#define WOWW_SERVER_TO_CLIENT pinfo->srcport == WOWW_TCP_PORT
#define WOWW_HEADER_ARRAY_ALLOC_SIZE 8
#define WOWW_SESSION_KEY_LENGTH 40
static gint ett_woww = -1;
/* Packets that do not have at least a u16 size field and a u16 opcode field are not valid. */
#define WOWW_MIN_LENGTH 4
typedef struct WowwParticipant {
guint8 last_encrypted_value;
guint8 index;
gboolean unencrypted_packet_encountered;
} WowwParticipant_t;
typedef struct WowwConversation {
guint8 session_key[WOWW_SESSION_KEY_LENGTH];
wmem_tree_t* decrypted_headers;
WowwParticipant_t client;
WowwParticipant_t server;
} WowwConversation_t;
typedef enum {
SMSG_AUTH_CHALLENGE = 0x1EC,
CMSG_AUTH_SESSION = 0x1ED,
@ -67,6 +85,21 @@ static const value_string world_packet_strings[] = {
{ 0, NULL }
};
static guint8*
get_decrypted_header(const guint8 session_key[WOWW_SESSION_KEY_LENGTH], WowwParticipant_t* participant, const guint8* header, guint8 header_size) {
guint8* decrypted_header = wmem_alloc0(wmem_file_scope(), 8);
for (guint8 i = 0; i < header_size; i++) {
decrypted_header[i] = (header[i] - participant->last_encrypted_value) ^ session_key[participant->index];
participant->last_encrypted_value = header[i];
participant->index = (participant->index + 1) % WOWW_SESSION_KEY_LENGTH;
}
return decrypted_header;
}
static int
dissect_woww(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void *data _U_)
@ -95,7 +128,6 @@ dissect_woww(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
if (tvb_captured_length(tvb) < 1)
return 0;
/*** COLUMN DATA ***/
/* Set the Protocol column to the constant string of WOWW */
@ -114,28 +146,93 @@ dissect_woww(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
woww_tree = proto_item_add_subtree(ti, ett_woww);
// Get conversation data
conversation_t* conv = find_or_create_conversation(pinfo);
WowwConversation_t* wowwConversation = (WowwConversation_t *)conversation_get_proto_data(conv,
proto_woww);
if (wowwConversation == NULL) {
// Assume that file scope means for the lifetime of the dissection
wowwConversation = (WowwConversation_t*) wmem_new0(wmem_file_scope(), WowwConversation_t);
conversation_add_proto_data(conv, proto_woww, wowwConversation);
wowwConversation->decrypted_headers = wmem_tree_new(wmem_file_scope());
guint8 session_key[WOWW_SESSION_KEY_LENGTH] = {0xfc, 0x97, 0x81, 0xd9, 0xde, 0xbb, 0xc5, 0x8b, 0x84, 0x57, 0x9a, 0xe3, 0xe3, 0xaa, 0x8c, 0x0c, 0x6b, 0xdc, 0x9c, 0xbc, 0x9f, 0x4b, 0x09, 0x35, 0xc5, 0xa9, 0x67, 0xb6, 0x1d, 0x0c, 0x8a, 0xca, 0x56, 0x73, 0xdc, 0xe5, 0x14, 0xcf, 0xd9, 0xdd};
memcpy(wowwConversation->session_key, session_key, WOWW_SESSION_KEY_LENGTH);
}
// Isolate session key for packet
WowwParticipant_t* participant;
guint8 headerSize = 4;
if (WOWW_SERVER_TO_CLIENT) {
participant = &wowwConversation->server;
headerSize = 4;
} else {
participant = &wowwConversation->client;
headerSize = 6;
}
guint8* decrypted_header = NULL;
// First time we see this header, we need to decrypt it
if (!pinfo->fd->visited) {
guint8* header = wmem_alloc0(wmem_packet_scope(), WOWW_HEADER_ARRAY_ALLOC_SIZE);
// Read directly
for (int i = 0; i < headerSize; i++) {
header[i] = tvb_get_guint8(tvb, offset);
offset += 1;
}
// Only first packet is unencrypted, all the rest are encrypted
if (participant->unencrypted_packet_encountered) {
// Do analysis
decrypted_header = get_decrypted_header(wowwConversation->session_key, participant, header, headerSize);
} else {
participant->unencrypted_packet_encountered = true;
decrypted_header = wmem_alloc0(wmem_file_scope(), WOWW_HEADER_ARRAY_ALLOC_SIZE);
memcpy(decrypted_header, header, headerSize);
}
// Add to hashmap
wmem_tree_insert32(wowwConversation->decrypted_headers, pinfo->num, decrypted_header);
}
// Seen header before, no need to analyze again
else {
decrypted_header = wmem_tree_lookup32(wowwConversation->decrypted_headers, pinfo->num);
}
// Add to tree
tvbuff_t *next_tvb = tvb_new_child_real_data(tvb, decrypted_header, headerSize, headerSize);
add_new_data_source(pinfo, next_tvb, "Decrypted Header");
/* Add an item to the subtree, see section 1.5 of README.dissector for more
* information. */
// We're indexing into another tvb
offset = 0;
len = 2;
proto_tree_add_item(woww_tree, hf_woww_size_field, tvb,
proto_tree_add_item(woww_tree, hf_woww_size_field, next_tvb,
offset, len, ENC_BIG_ENDIAN);
offset += len;
if (WOWW_SERVER_TO_CLIENT) {
len = 2;
opcode = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
opcode = tvb_get_guint16(next_tvb, offset, ENC_LITTLE_ENDIAN);
} else if (WOWW_CLIENT_TO_SERVER) {
len = 4;
opcode = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN);
opcode = tvb_get_guint32(next_tvb, offset, ENC_LITTLE_ENDIAN);
}
proto_tree_add_item(woww_tree, hf_woww_opcode_field, tvb,
proto_tree_add_item(woww_tree, hf_woww_opcode_field, next_tvb,
offset, len, ENC_LITTLE_ENDIAN);
col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(opcode,
world_packet_strings,
"Encrypted Header"));
// Remember to go back to original tvb
return tvb_captured_length(tvb);
}
@ -177,6 +274,7 @@ proto_register_woww(void)
prefs_register_protocol(proto_woww,
NULL);
}