MCPC_dissect/mcpc.c

427 lines
12 KiB
C

//If you don't have config.h just comment 4 lines
// #include <config.h>
//
// |
// v
//
// /*
// #include <config.h>
// */
// Known to be problem for arch users
#include <config.h>
#ifndef HAVE_ZLIB
#error Wireshark compiled without zlib support
#endif
#include <ws_version.h>
#include <stdint.h>
#include <stdio.h>
#include <epan/prefs.h>
#include <epan/packet.h>
#include <epan/proto_data.h>
#include <epan/column-info.h>
#include <epan/dissectors/packet-tcp.h>
#if (WIRESHARK_VERSION_MAJOR==3 && WIRESHARK_VERSION_MINOR>=6) || WIRESHARK_VERSION_MAJOR>3
#include <wsutil/wmem/wmem.h>
#else
#include <epan/wmem/wmem.h>
#endif
#ifndef ENABLE_STATIC
WS_DLL_PUBLIC_DEF const gchar plugin_version[] = "0.0.3";
WS_DLL_PUBLIC_DEF const int plugin_want_major = WIRESHARK_VERSION_MAJOR;
WS_DLL_PUBLIC_DEF const int plugin_want_minor = WIRESHARK_VERSION_MINOR;
WS_DLL_PUBLIC_DEF void plugin_register();
#endif
#define PROTO_PORT 25565
#define PROTO_TAG "MCPC"
#include "mcpc.h"
#include "protocol.h"
#include "protocol_tree.h"
#include "protocol_constants.h"
static int
ett_mcpc=-1,
ett_proto=-1;
static dissector_handle_t mcpc_handle, conv_handle, ignore_handle;
static int
hf_packet_length=-1,
hf_packet_data_length=-1;
int proto_mcpc=-1;
int
hf_protocol_packetid_sb=-1,
hf_protocol_packetid_cb=-1,
hf_protocol_packetid_sb_hs=-1,
hf_protocol_packetid_sb_login=-1,
hf_protocol_packetid_cb_login=-1,
hf_protocol_packetid_sb_slp=-1,
hf_protocol_packetid_cb_slp=-1;
int8_t VarIntToUint(const guint8 *varint, uint32_t *result, guint maxlen){
uint8_t i=0;
*result=0;
do{
if(i>5)
return -1;
if((guint)i>maxlen)
return -1;
*result |= (varint[i]&0x7F) << (i*7);
}while((varint[i++]&0x80) != 0);
return (int8_t)i;
}
static guint getlen(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data _U_){
int ret;
uint32_t len;
guint packet_length;
packet_length=tvb_reported_length(tvb);//To read
if(packet_length==0)
return 0;
// return tvb_captured_length(tvb);
const guint8 *dt;
dt=tvb_get_ptr(tvb, offset, packet_length-offset);
ret=VarIntToUint(dt, &len, packet_length-offset);
if(ret==-1){//Invalidate
conversation_t *conv;
mcpc_protocol_context *ctx;
col_add_str(pinfo->cinfo, COL_INFO, "[INVALID] Failed to parse payload length");
conv=find_or_create_conversation(pinfo);
ctx=conversation_get_proto_data(conv, proto_mcpc);
ctx->state=STATE_INVALID;
conversation_set_dissector(conv, ignore_handle);
return 0;
}else
return len+ret;
}
static void subdissect_mcpc_proto(guint length, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, mcpc_protocol_context *ctx, gboolean visited){
const guint8 *dat;
dat=tvb_get_ptr(tvb, pinfo->desegment_offset, length);
if(visited){
if(tree){
if(pinfo->destport==ctx->serverPort){
switch (ctx->state) {
case STATE_PLAY:
tree_server_play(tree, tvb, pinfo, dat, length);
break;
case STATE_LOGIN:
tree_server_login(tree, tvb, pinfo, dat, length);
break;
case STATE_HANDSHAKE:
tree_server_handshake(tree, tvb, pinfo, dat, length);
break;
case STATE_SLP:
tree_server_slp(tree, tvb, pinfo, dat, length);
break;
}
}else{
switch (ctx->state) {
case STATE_PLAY:
tree_client_play(tree, tvb, pinfo, dat, length);
break;
case STATE_LOGIN:
tree_client_login(tree, tvb, pinfo, dat, length);
break;
case STATE_SLP:
tree_client_slp(tree, tvb, pinfo, dat, length);
break;
}
}
}
return;
}else{
if(pinfo->destport==ctx->serverPort){
switch (ctx->state) {
case STATE_PLAY:
return;
case STATE_LOGIN:
if(tree)
tree_server_login(tree, tvb, pinfo, dat, length);
// proto_tree_add_uint(packet_item, hf_protocol_packetid_sb_login, tvb, base_offset, varlen, varint);
return;
case STATE_HANDSHAKE:
if(parse_server_handshake(dat, length, ctx)==-1)
break;
if(tree)
tree_server_handshake(tree, tvb, pinfo, dat, length);
return;
case STATE_SLP:
col_add_str(pinfo->cinfo, COL_INFO, "[SLP]");
return;
}
}else{
switch (ctx->state) {
case STATE_PLAY:
return;
case STATE_LOGIN:
if(parse_client_login(dat, length, ctx)==-1)
break;
if(tree)
tree_client_login(tree, tvb, pinfo, dat, length);
return;
case STATE_SLP:
col_add_str(pinfo->cinfo, COL_INFO, "[SLP]");
return;
}
}
}
col_add_str(pinfo->cinfo, COL_INFO, "[INVALID]");
if(!visited){
ctx->state=STATE_INVALID;
conversation_t *conv;
conv=find_or_create_conversation(pinfo);
conversation_set_dissector(conv, ignore_handle);
}
}
static int subdissect_mcpc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_){
mcpc_protocol_context *ctx;
proto_item *packet_item;
proto_tree *mcpc_tree, *sub_mcpc_tree;
const guint8 *dt;
guint packet_length;
gint readed;
uint32_t protocol_length, varint;
guint8 protocol_length_length;
gint8 varlen;
col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_TAG);//Set MCPC protocol tag
pinfo->fd->subnum++;//NOTE: Is it ok?
if(pinfo->fd->visited){
ctx=p_get_proto_data(wmem_file_scope(), pinfo, proto_mcpc, pinfo->fd->subnum);
}else{
conversation_t *conv;
conv=find_or_create_conversation(pinfo);
ctx=conversation_get_proto_data(conv, proto_mcpc);
mcpc_protocol_context *save;
//Here I AFAIR trying to save state, but save full contex instead with ports
save=wmem_alloc(wmem_file_scope(), sizeof(mcpc_protocol_context));
*save=*ctx;
p_add_proto_data(wmem_file_scope(), pinfo, proto_mcpc, pinfo->fd->subnum, save);
}
if(pinfo->destport == ctx->serverPort)
col_set_str(pinfo->cinfo, COL_INFO, "[C => S]");
else
col_set_str(pinfo->cinfo, COL_INFO, "[S => C]");
packet_length=tvb_reported_length(tvb);//To read
dt=tvb_get_ptr(tvb, pinfo->desegment_offset, packet_length);
protocol_length_length=readed=VarIntToUint(dt, &protocol_length, packet_length);
col_append_fstr(pinfo->cinfo, COL_INFO, " %u bytes", protocol_length+protocol_length_length);
if(tree){
proto_item *ti;
ti = proto_tree_add_item(tree, proto_mcpc, tvb, 0, -1, FALSE);
mcpc_tree = proto_item_add_subtree(ti, ett_mcpc);
proto_tree_add_uint(mcpc_tree, hf_packet_length, tvb, 0, protocol_length_length, packet_length);
proto_item_append_text(ti, ", state: %d, len: %u", ctx->state, protocol_length+protocol_length_length);
}
tvbuff_t *new_tvb;
if(ctx->compressTrxld<0){
new_tvb=tvb_new_subset_remaining(tvb, protocol_length_length);
if(tree){
packet_item=proto_tree_add_item(mcpc_tree, proto_mcpc, new_tvb, 0, -1, FALSE);
proto_item_set_text(packet_item, "MC:JE packet");
sub_mcpc_tree=proto_item_add_subtree(packet_item, ett_proto);
}else
sub_mcpc_tree=NULL;
subdissect_mcpc_proto(protocol_length, new_tvb, pinfo, sub_mcpc_tree, ctx, pinfo->fd->visited);
}else{
varlen=VarIntToUint(dt+protocol_length_length, &varint, packet_length-readed);
if(varlen<0)
return 0;
if(tree)
proto_tree_add_uint(mcpc_tree, hf_packet_data_length, tvb, readed, varlen, varint);
readed+=varlen;
if((int32_t)varint>0){
new_tvb=tvb_uncompress(tvb, readed, packet_length-readed);//Decompress
if(new_tvb==NULL)
return 0;
add_new_data_source(pinfo, new_tvb, "Uncompressed packet");
}else{
new_tvb=tvb_new_subset_remaining(tvb, readed);
}
if(tree){
packet_item=proto_tree_add_item(mcpc_tree, proto_mcpc, new_tvb, 0, -1, FALSE);
proto_item_set_text(packet_item, "MC:JE packet");
sub_mcpc_tree=proto_item_add_subtree(packet_item, ett_proto);
}else
sub_mcpc_tree=NULL;
subdissect_mcpc_proto(tvb_captured_length(new_tvb), new_tvb, pinfo, sub_mcpc_tree, ctx, pinfo->fd->visited);
}
return tvb_captured_length(tvb);
// return protocol_length;
}
static int conv_dissect_mcpc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data){
pinfo->fd->subnum=0;
tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 0,
getlen, subdissect_mcpc, data);
return tvb_captured_length(tvb);
}
static int dissect_mcpc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data){
conversation_t *conv;
mcpc_protocol_context *ctx;
conv=find_or_create_conversation(pinfo);
ctx=conversation_get_proto_data(conv, proto_mcpc);
if(!ctx) {
ctx=wmem_alloc(wmem_file_scope(), sizeof(mcpc_protocol_context));
ctx->compressTrxld=-1;
ctx->serverPort=pinfo->destport;
ctx->state=STATE_HANDSHAKE;
conversation_add_proto_data(conv, proto_mcpc, ctx);
conversation_set_dissector(conv, conv_handle);
}
tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 0,
getlen, subdissect_mcpc, data);
return tvb_captured_length(tvb);
}
static int dissect_ignore(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data){
conversation_t *conv;
mcpc_protocol_context *ctx;
pinfo->fd->subnum=0;
conv=find_or_create_conversation(pinfo);
if(!(ctx=p_get_proto_data(wmem_file_scope(), pinfo, proto_mcpc, pinfo->fd->subnum)))
ctx=conversation_get_proto_data(conv, proto_mcpc);
if(ctx->state==STATE_INVALID){
col_add_str(pinfo->cinfo, COL_INFO, "[INVALID] before");
}else{
tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 0,
getlen, subdissect_mcpc, data);
}
return tvb_captured_length(tvb);
}
//Protocol register functions
static void proto_reg_handoff_mcpc(void) {//Register dissector
mcpc_handle = create_dissector_handle(dissect_mcpc, proto_mcpc);
conv_handle = create_dissector_handle(conv_dissect_mcpc, proto_mcpc);
ignore_handle = create_dissector_handle(dissect_ignore, proto_mcpc);
dissector_add_uint("tcp.port", PROTO_PORT, mcpc_handle);
}
static void proto_register_mcpc() {
static gint *ett[] = { &ett_mcpc, &ett_proto };
static hf_register_info hf[] = {
{ &hf_packet_length,
{
"Payload length", "mcpc.length",
FT_UINT32, BASE_DEC,
NULL, 0x0,
NULL, HFILL
}
},
{ &hf_packet_data_length,
{
"Uncompressed data length", "mcpc.data_length",
FT_UINT32, BASE_DEC,
NULL, 0x0,
NULL, HFILL
}
},
{ &hf_protocol_packetid_sb,
{
"Packet ID", "mcpc.packetid.serverbound",
FT_UINT8, BASE_HEX,
VALS(sbpackettypes), 0x0,
NULL, HFILL
}
},
{ &hf_protocol_packetid_cb,
{
"Packet ID", "mcpc.packetid.clientbound",
FT_UINT8, BASE_HEX,
VALS(cbpackettypes), 0x0,
NULL, HFILL
}
},
{ &hf_protocol_packetid_sb_hs,
{
"Packet ID(handshake)", "mcpc.packetid.serverbound.handshake",
FT_UINT8, BASE_HEX,
VALS(sbpackettypes_handshake), 0x0,
NULL, HFILL
}
},
{ &hf_protocol_packetid_sb_login,
{
"Packet ID(login)", "mcpc.packetid.serverbound.login",
FT_UINT8, BASE_HEX,
VALS(sbpackettypes_login), 0x0,
NULL, HFILL
}
},
{ &hf_protocol_packetid_cb_login,
{
"Packet ID(login)", "mcpc.packetid.clientbound.login",
FT_UINT8, BASE_HEX,
VALS(cbpackettypes_login), 0x0,
NULL, HFILL
}
},
{ &hf_protocol_packetid_sb_slp,
{
"Packet ID(SLP)", "mcpc.packetid.serverbound.status",
FT_UINT8, BASE_HEX,
VALS(sbpackettypes_slp), 0x0,
NULL, HFILL
}
},
{ &hf_protocol_packetid_cb_slp,
{
"Packet ID(SLP)", "mcpc.packetid.clientbound.status",
FT_UINT8, BASE_HEX,
VALS(cbpackettypes_slp), 0x0,
NULL, HFILL
}
}
};
//Register protocol
proto_mcpc = proto_register_protocol ("Minecraft: Java Edition", "Minecraft", "mcpc");
//Register protocol fields
proto_register_field_array(proto_mcpc, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
tree_register_fields();
fill_table();
}
//Plugin register function
#ifndef ENABLE_STATIC
void plugin_register(){
// register the new protocol, protocol fields, and subtrees
if (proto_mcpc == -1) { // execute protocol initialization only once
static proto_plugin plug;
plug.register_handoff=proto_reg_handoff_mcpc;
plug.register_protoinfo=proto_register_mcpc;
proto_register_plugin(&plug);
}
}
#endif