From 3500481775357529a385cd520bb41d6fbca46609 Mon Sep 17 00:00:00 2001 From: nickid2018 Date: Sun, 14 Jul 2024 12:17:07 +0800 Subject: [PATCH] Try to fix encryption channel error --- protocol/protocol_data.h | 35 +++-- protocol_je/je_dissect.c | 314 +++++++++++++++++--------------------- protocol_je/je_dissect.h | 5 - protocol_je/je_protocol.c | 30 +--- 4 files changed, 157 insertions(+), 227 deletions(-) diff --git a/protocol/protocol_data.h b/protocol/protocol_data.h index eeebb6f..de1f2da 100644 --- a/protocol/protocol_data.h +++ b/protocol/protocol_data.h @@ -16,34 +16,37 @@ typedef enum { HANDSHAKE, PLAY, PING, LOGIN, TRANSFER, CONFIGURATION, INVALID } je_state; -typedef struct { - guint8 *server_decrypt; - guint8 *client_decrypt; - gint server_decrypt_length; - gint client_decrypt_length; - - gcry_cipher_hd_t server_cipher; - gcry_cipher_hd_t client_cipher; - - gint server_last_decrypt_available; - gint client_last_decrypt_available; - gint server_required_length; - gint client_required_length; -} mcje_decryption_context; - typedef struct { je_state client_state; je_state server_state; + guint32 server_port; address server_address; + guint32 protocol_version; guint32 data_version; protocol_je_set protocol_set; + gint32 compression_threshold; + bool encrypted; + + gcry_cipher_hd_t server_cipher; + gcry_cipher_hd_t client_cipher; + gint server_last_segment_remaining; + gint client_last_segment_remaining; + void *extra; - mcje_decryption_context *decryption_context; } mcje_protocol_context; +typedef struct { + je_state client_state; + je_state server_state; + + bool encrypted; + guint8 *decrypted_data; + gint32 compression_threshold; +} mcje_frame_data; + typedef struct { gint record_total; gint record_latest; diff --git a/protocol_je/je_dissect.c b/protocol_je/je_dissect.c index 5e3cb12..c75e045 100644 --- a/protocol_je/je_dissect.c +++ b/protocol_je/je_dissect.c @@ -3,9 +3,7 @@ // #include -#include #include -#include #include "mc_dissector.h" #include "je_dissect.h" #include "je_protocol.h" @@ -20,9 +18,11 @@ void proto_reg_handoff_mcje() { dissector_add_uint_range_with_preference("tcp.port", MCJE_PORT, mcje_handle); } -void sub_dissect_je(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, mcje_protocol_context *ctx, bool is_server, bool visited) { +void sub_dissect_je(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, mcje_frame_data *frame_data, + mcje_protocol_context *ctx, bool is_server, + bool visited) { if (is_server) { - switch (ctx->server_state) { + switch (frame_data->server_state) { case HANDSHAKE: if (!visited && is_invalid(handle_server_handshake_switch(tvb, ctx))) return; @@ -57,7 +57,7 @@ void sub_dissect_je(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, mcje_pr return; } } else { - switch (ctx->client_state) { + switch (frame_data->client_state) { case PING: if (tree) handle_client_slp(tree, pinfo, tvb); @@ -88,25 +88,6 @@ void sub_dissect_je(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, mcje_pr } } -mcje_protocol_context *get_context(packet_info *pinfo) { - mcje_protocol_context *ctx; - if (pinfo->fd->visited) { - ctx = p_get_proto_data(wmem_file_scope(), pinfo, proto_mcje, pinfo->fd->subnum); - ((extra_data *) ctx->extra)->visited = true; - } else { - conversation_t *conv; - conv = find_or_create_conversation(pinfo); - ctx = conversation_get_proto_data(conv, proto_mcje); - mcje_protocol_context *save; - save = wmem_alloc(wmem_file_scope(), sizeof(mcje_protocol_context)); - *save = *ctx; - p_add_proto_data(wmem_file_scope(), pinfo, proto_mcje, pinfo->fd->subnum, save); - ((extra_data *) ctx->extra)->visited = false; - } - pinfo->fd->subnum++; - return ctx; -} - void mark_invalid(packet_info *pinfo) { conversation_t *conv = find_or_create_conversation(pinfo); mcje_protocol_context *ctx = conversation_get_proto_data(conv, proto_mcje); @@ -114,128 +95,68 @@ void mark_invalid(packet_info *pinfo) { ctx->server_state = INVALID; } -int dissect_je_core(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { - mcje_protocol_context *ctx = get_context(pinfo); - gint captured_length = (gint) tvb_captured_length(tvb); - - if (ctx->client_state == INVALID || ctx->server_state == INVALID) { - col_set_str(pinfo->cinfo, COL_INFO, "[Invalid] Data may be corrupted or meet a capturing failure."); - return captured_length; - } - - bool is_server = addresses_equal(&pinfo->dst, &ctx->server_address) && pinfo->destport == ctx->server_port; - gint read_pointer = 0; - gint packet_length = (gint) tvb_reported_length(tvb); - gint packet_length_vari; - gint packet_length_length = read_var_int(tvb, 0, &packet_length_vari); - read_pointer += packet_length_length; - col_append_fstr(pinfo->cinfo, COL_INFO, " (%d bytes)", packet_length_vari); +void dissect_je_core(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset, gint packet_len_len, gint len) { + conversation_t *conv = find_or_create_conversation(pinfo); + mcje_protocol_context *ctx = conversation_get_proto_data(conv, proto_mcje); + mcje_frame_data *frame_data = p_get_proto_data(wmem_file_scope(), pinfo, proto_mcje, 0); proto_tree *mcje_tree; if (tree) { proto_item *ti = proto_tree_add_item(tree, proto_mcje, tvb, 0, -1, FALSE); mcje_tree = proto_item_add_subtree(ti, ett_mcje); - proto_tree_add_uint(mcje_tree, hf_packet_length_je, tvb, 0, packet_length_length, packet_length_vari); + proto_tree_add_uint(mcje_tree, hf_packet_length_je, tvb, offset - packet_len_len, packet_len_len, len); proto_item_append_text( - ti, ", Client State: %s, Server State: %s", STATE_NAME[ctx->client_state], - STATE_NAME[ctx->server_state] + ti, ", Client State: %s, Server State: %s", + STATE_NAME[frame_data->client_state], STATE_NAME[frame_data->server_state] ); } tvbuff_t *new_tvb; - if (ctx->compression_threshold < 0) { - new_tvb = tvb_new_subset_length(tvb, read_pointer, packet_length_vari); - if (tree) { - proto_item *packet_item = proto_tree_add_item(mcje_tree, proto_mcje, new_tvb, 0, -1, FALSE); - proto_item_set_text(packet_item, "Minecraft JE Packet"); - proto_tree *sub_mcpc_tree = proto_item_add_subtree(packet_item, ett_je_proto); - sub_dissect_je(new_tvb, pinfo, sub_mcpc_tree, ctx, is_server, pinfo->fd->visited); - } else - sub_dissect_je(new_tvb, pinfo, NULL, ctx, is_server, pinfo->fd->visited); - } else { + if (frame_data->compression_threshold > 0) { gint uncompressed_length; - int var_len = read_var_int(tvb, read_pointer, &uncompressed_length); + int var_len = read_var_int(tvb, offset, &uncompressed_length); if (is_invalid(var_len)) { col_set_str(pinfo->cinfo, COL_INFO, "[Invalid] Invalid Compression VarInt"); mark_invalid(pinfo); - return captured_length; + return; } - read_pointer += var_len; - - if ((int32_t) uncompressed_length > 0) { - if (tree) { - proto_tree_add_uint( - mcje_tree, hf_packet_data_length_je, tvb, - read_pointer - var_len, var_len, uncompressed_length + offset += var_len; + if (uncompressed_length > 0) { + if (uncompressed_length < frame_data->compression_threshold) { + col_set_str(pinfo->cinfo, COL_INFO, "[Invalid] Badly compressed packet"); + col_append_fstr( + pinfo->cinfo, COL_INFO, " - size of %d is below server threshold of %d", + uncompressed_length, frame_data->compression_threshold ); - if (uncompressed_length < ctx->compression_threshold) { - col_set_str(pinfo->cinfo, COL_INFO, "[Invalid] Badly compressed packet"); - col_append_fstr( - pinfo->cinfo, COL_INFO, " - size of %d is below server threshold of %d", - uncompressed_length, ctx->compression_threshold - ); - mark_invalid(pinfo); - return captured_length; - } + mark_invalid(pinfo); + return; } - new_tvb = tvb_uncompress(tvb, read_pointer, packet_length - read_pointer); + + new_tvb = tvb_uncompress(tvb, offset, len - var_len); if (new_tvb == NULL) - return captured_length; + return; + add_new_data_source(pinfo, new_tvb, "Uncompressed packet"); } else { - if (tree) - proto_tree_add_uint( - mcje_tree, hf_packet_data_length_je, tvb, - read_pointer - 1, 1, packet_length_vari - 1 - ); - new_tvb = tvb_new_subset_remaining(tvb, read_pointer); + new_tvb = tvb_new_subset_length(tvb, offset, len - var_len); } - - if (tree) { - proto_item *packet_item = proto_tree_add_item(mcje_tree, proto_mcje, new_tvb, 0, -1, FALSE); - proto_item_set_text(packet_item, "Minecraft JE Packet"); - proto_tree *sub_mcpc_tree = proto_item_add_subtree(packet_item, ett_je_proto); - sub_dissect_je(new_tvb, pinfo, sub_mcpc_tree, ctx, is_server, pinfo->fd->visited); - } else - sub_dissect_je(new_tvb, pinfo, NULL, ctx, is_server, pinfo->fd->visited); + } else { + new_tvb = tvb_new_subset_length(tvb, offset, len); } - return captured_length; -} - -guint get_packet_length(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data) { - gint len; - gint packet_length = (gint) tvb_reported_length(tvb); - if (packet_length == 0) - return 0; - - reassemble_offset *reassemble_data = data; - gint remaining = packet_length - offset; - int ret = read_var_int_with_limit(tvb, offset, remaining > 3 ? 3 : remaining, &len); - if (is_invalid(ret)) { - if (remaining < 3) { - reassemble_data->record_latest = 0; - return 0; - } - col_set_str(pinfo->cinfo, COL_INFO, "[Invalid] Failed to parse payload length"); - mark_invalid(pinfo); - return 0; + bool is_server = addresses_equal(&pinfo->dst, &ctx->server_address) && pinfo->destport == ctx->server_port; + if (tree) { + proto_item *packet_item = proto_tree_add_item(mcje_tree, proto_mcje, new_tvb, 0, -1, FALSE); + proto_item_set_text(packet_item, "Minecraft JE Packet"); + proto_tree *sub_mcje_tree = proto_item_add_subtree(packet_item, ett_je_proto); + sub_dissect_je(new_tvb, pinfo, sub_mcje_tree, frame_data, ctx, is_server, pinfo->fd->visited); } else { - reassemble_data->record_latest = len + ret; - reassemble_data->record_total += len + ret; - return len + ret; + sub_dissect_je(new_tvb, pinfo, NULL, frame_data, ctx, is_server, pinfo->fd->visited); } } -// 0xFFFFFFFF: Decrypted Data for first -// 0xFFFFFFFE: Decrypted Data for second (if contains) -// 0xFFFFFFFD: Sub Number for second int dissect_je_conv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_) { - pinfo->fd->subnum = 0; - if (pinfo->curr_layer_num == 7) - pinfo->fd->subnum = GPOINTER_TO_UINT(p_get_proto_data(wmem_file_scope(), pinfo, proto_mcje, 0xFFFFFFFD)); - conversation_t *conv = find_or_create_conversation(pinfo); mcje_protocol_context *ctx = conversation_get_proto_data(conv, proto_mcje); if (!ctx) { @@ -244,87 +165,124 @@ int dissect_je_conv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, voi ctx->server_state = HANDSHAKE; ctx->compression_threshold = -1; ctx->server_port = pinfo->destport; + ctx->server_cipher = NULL; + ctx->client_cipher = NULL; + ctx->server_last_segment_remaining = 0; + ctx->client_last_segment_remaining = 0; copy_address(&ctx->server_address, &pinfo->dst); ctx->extra = wmem_alloc(wmem_file_scope(), sizeof(extra_data)); ((extra_data *) ctx->extra)->data = wmem_map_new(wmem_file_scope(), g_str_hash, g_str_equal); - ctx->decryption_context = NULL; conversation_add_proto_data(conv, proto_mcje, ctx); } + mcje_frame_data *frame_data = p_get_proto_data(wmem_file_scope(), pinfo, proto_mcje, 0); + if (!frame_data) { + frame_data = wmem_alloc(wmem_file_scope(), sizeof(mcje_frame_data)); + frame_data->client_state = ctx->client_state; + frame_data->server_state = ctx->server_state; + frame_data->encrypted = ctx->encrypted; + frame_data->decrypted_data = NULL; + frame_data->compression_threshold = ctx->compression_threshold; + p_add_proto_data(wmem_file_scope(), pinfo, proto_mcje, 0, frame_data); + } + col_set_str(pinfo->cinfo, COL_PROTOCOL, MCJE_SHORT_NAME); - gint length = tvb_reported_length_remaining(tvb, 0); - bool is_visited = pinfo->fd->visited; bool is_server = addresses_equal(&pinfo->dst, &ctx->server_address) && pinfo->destport == ctx->server_port; - col_clear(pinfo->cinfo, COL_INFO); - col_add_str(pinfo->cinfo, COL_INFO, is_server ? "[C => S] " : "[S => C] "); - mcje_decryption_context *decryption_ctx = ctx->decryption_context; - bool is_encrypted = decryption_ctx != NULL; - if (is_encrypted) { - guint8 *decrypt; - if (!is_visited) { - gcry_cipher_hd_t cipher = is_server ? decryption_ctx->server_cipher : decryption_ctx->client_cipher; - gint last_decrypt_available = is_server ? decryption_ctx->server_last_decrypt_available - : decryption_ctx->client_last_decrypt_available; - gint to_decrypt = length - last_decrypt_available; - guint8 **write_to = is_server ? &decryption_ctx->server_decrypt : &decryption_ctx->client_decrypt; - gint *old_length = is_server ? &decryption_ctx->server_decrypt_length - : &decryption_ctx->client_decrypt_length; - guint8 *old = *write_to; - *write_to = decrypt = wmem_alloc(wmem_file_scope(), length); - memcpy(*write_to, old + *old_length - last_decrypt_available, last_decrypt_available); + col_set_str(pinfo->cinfo, COL_INFO, is_server ? "[C => S] " : "[S => C] "); + if (frame_data->encrypted) + col_append_str(pinfo->cinfo, COL_INFO, "(Encrypted) "); + if (frame_data->client_state == INVALID || frame_data->server_state == INVALID) { + col_set_str(pinfo->cinfo, COL_INFO, "[Invalid] Data may be corrupted or meet a capturing failure."); + return (gint) tvb_captured_length(tvb); + } + + if (frame_data->encrypted) { + guint length = tvb_captured_length_remaining(tvb, 0); + gint length_remaining = is_server ? ctx->server_last_segment_remaining : ctx->client_last_segment_remaining; + gcry_cipher_hd_t *cipher = is_server ? &ctx->server_cipher : &ctx->client_cipher; + + if (*cipher == NULL) { + gchar *secret_key_str = pref_secret_key; + if (strlen(secret_key_str) != 32) { + col_set_str(pinfo->cinfo, COL_INFO, "[Invalid] Decryption Error: Secret key is not set"); + mark_invalid(pinfo); + return (gint) tvb_captured_length(tvb); + } + guint8 secret_key[16]; + for (int i = 0; i < 16; i++) { + gchar hex[3] = {secret_key_str[i * 2], secret_key_str[i * 2 + 1], '\0'}; + secret_key[i] = (guint8) strtol(hex, NULL, 16); + } + gcry_cipher_open(cipher, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB8, 0); + gcry_cipher_setkey(*cipher, secret_key, sizeof(secret_key)); + gcry_cipher_setiv(*cipher, secret_key, sizeof(secret_key)); + } + + if (!frame_data->decrypted_data) { + guint8 *decrypt = wmem_alloc(pinfo->pool, length - length_remaining); gcry_error_t err = gcry_cipher_decrypt( - cipher, *write_to + last_decrypt_available, to_decrypt, - tvb_memdup(pinfo->pool, tvb, last_decrypt_available, to_decrypt), - to_decrypt + *cipher, decrypt, + length - length_remaining, + tvb_memdup(pinfo->pool, tvb, length_remaining, length - length_remaining), + length - length_remaining ); - if (err != 0) { - col_append_str(pinfo->cinfo, COL_INFO, "[Invalid] Decryption Error: Decryption failed"); + if (err) { + col_set_str(pinfo->cinfo, COL_INFO, "[Invalid] Decryption Error: Decryption failed with code "); + col_append_fstr(pinfo->cinfo, COL_INFO, "%d", err); mark_invalid(pinfo); return (gint) tvb_captured_length(tvb); } - p_add_proto_data( - wmem_file_scope(), pinfo, proto_mcje, - pinfo->curr_layer_num == 6 ? 0xFFFFFFFF : 0xFFFFFFFE, *write_to - ); - *old_length = length; - } else { - if (pinfo->curr_layer_num == 6) - decrypt = p_get_proto_data(wmem_file_scope(), pinfo, proto_mcje, 0xFFFFFFFF); + + guint8 *merged = wmem_alloc(wmem_file_scope(), length); + tvb_memcpy(tvb, merged, 0, length_remaining); + memcpy(merged + length_remaining, decrypt, length - length_remaining); + frame_data->decrypted_data = merged; + } + + tvb = tvb_new_real_data(frame_data->decrypted_data, length, length); + } + + gint offset = 0; + gint packet_count = 0; + while (offset < tvb_reported_length(tvb)) { + gint available = tvb_reported_length_remaining(tvb, offset); + gint len = 0; + gint packet_len_len = read_var_int_with_limit(tvb, offset, available, &len); + + if (packet_len_len == INVALID_DATA) { + col_append_fstr(pinfo->cinfo, COL_INFO, "%d packet(s)", packet_count); + pinfo->desegment_offset = offset; + pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT; + if (is_server) + ctx->server_last_segment_remaining = available; else - decrypt = p_get_proto_data(wmem_file_scope(), pinfo, proto_mcje, 0xFFFFFFFE); + ctx->client_last_segment_remaining = available; + return offset + available; } - if (decrypt != NULL) { - col_append_str(pinfo->cinfo, COL_INFO, "(Encrypted) "); - tvb = tvb_new_child_real_data(tvb, decrypt, length, length); - add_new_data_source(pinfo, tvb, "Decrypted Data"); + + if (len + packet_len_len > available) { + col_append_fstr(pinfo->cinfo, COL_INFO, "%d packet(s)", packet_count); + pinfo->desegment_offset = offset; + pinfo->desegment_len = len + packet_len_len - available; + if (is_server) + ctx->server_last_segment_remaining = available; + else + ctx->client_last_segment_remaining = available; + return offset + available; } - } - reassemble_offset *reassemble_data = wmem_new(pinfo->pool, reassemble_offset); - reassemble_data->record_total = 0; - reassemble_data->record_latest = 0; - tcp_dissect_pdus( - tvb, pinfo, tree, TRUE, 0, get_packet_length, - dissect_je_core, reassemble_data - ); - if (!is_visited && pinfo->curr_layer_num == 6) - p_add_proto_data(wmem_file_scope(), pinfo, proto_mcje, 0xFFFFFFFD, GUINT_TO_POINTER(pinfo->fd->subnum)); - if (!is_visited && is_encrypted) { - gint *last_decrypt_available = is_server ? &decryption_ctx->server_last_decrypt_available - : &decryption_ctx->client_last_decrypt_available; - gint *required_length = is_server ? &decryption_ctx->server_required_length - : &decryption_ctx->client_required_length; - gint read = reassemble_data->record_total; - if (read > length) { - read -= reassemble_data->record_latest; - *required_length = reassemble_data->record_latest; - } else - *required_length = 0; - *last_decrypt_available = length - read; + offset += packet_len_len; + dissect_je_core(tvb, pinfo, tree, offset, packet_len_len, len); + offset += len; + packet_count++; } - wmem_free(pinfo->pool, reassemble_data); + if (is_server) + ctx->server_last_segment_remaining = 0; + else + ctx->client_last_segment_remaining = 0; + col_append_fstr(pinfo->cinfo, COL_INFO, "%d packet(s)", packet_count); return (gint) tvb_captured_length(tvb); } \ No newline at end of file diff --git a/protocol_je/je_dissect.h b/protocol_je/je_dissect.h index 685e053..fa582b7 100644 --- a/protocol_je/je_dissect.h +++ b/protocol_je/je_dissect.h @@ -18,11 +18,6 @@ void proto_register_mcje(); void proto_reg_handoff_mcje(); -void sub_dissect_je(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, mcje_protocol_context *ctx, - bool is_client, bool visited); - -int dissect_je_core(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data); - int dissect_je_conv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_); #endif //MC_DISSECTOR_JE_DISSECT_H diff --git a/protocol_je/je_protocol.c b/protocol_je/je_protocol.c index 26e61c3..1d67d22 100644 --- a/protocol_je/je_protocol.c +++ b/protocol_je/je_protocol.c @@ -203,34 +203,8 @@ int handle_server_login_switch(tvbuff_t *tvb, mcje_protocol_context *ctx) { return INVALID_DATA; if (packet_id == get_packet_id(ctx->protocol_set->login, "login_acknowledgement", false)) ctx->server_state = CONFIGURATION; - if (packet_id == PACKET_ID_SERVER_ENCRYPTION_BEGIN) { - gchar *secret_key_str = pref_secret_key; - if (strlen(secret_key_str) != 32) { - ctx->client_state = ctx->server_state = INVALID; - return INVALID_DATA; - } - guint8 secret_key[16]; - for (int i = 0; i < 16; i++) { - gchar hex[3] = {secret_key_str[i * 2], secret_key_str[i * 2 + 1], '\0'}; - secret_key[i] = (guint8) strtol(hex, NULL, 16); - } - mcje_decryption_context *decryption_context = wmem_new(wmem_file_scope(), mcje_decryption_context); - decryption_context->client_last_decrypt_available = 0; - decryption_context->server_last_decrypt_available = 0; - decryption_context->client_decrypt_length = 0; - decryption_context->server_decrypt_length = 0; - decryption_context->client_required_length = 0; - decryption_context->server_required_length = 0; - decryption_context->client_decrypt = wmem_alloc(wmem_file_scope(), 0); - decryption_context->server_decrypt = wmem_alloc(wmem_file_scope(), 0); - gcry_cipher_open(&decryption_context->server_cipher, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB8, 0); - gcry_cipher_setkey(decryption_context->server_cipher, secret_key, sizeof(secret_key)); - gcry_cipher_setiv(decryption_context->server_cipher, secret_key, sizeof(secret_key)); - gcry_cipher_open(&decryption_context->client_cipher, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB8, 0); - gcry_cipher_setkey(decryption_context->client_cipher, secret_key, sizeof(secret_key)); - gcry_cipher_setiv(decryption_context->client_cipher, secret_key, sizeof(secret_key)); - ctx->decryption_context = decryption_context; - } + if (packet_id == PACKET_ID_SERVER_ENCRYPTION_BEGIN) + ctx->encrypted = true; return 0; }