-- Basic Dethrace Wireshark dissector -- -- Installation instructions: -- 1. Open Wireshark GUI -- 2. Navigate to "Help -> About Wireshark" menu -- 3. Click on "Folder" tab and click in the list of paths on "Personal Lua Plugins". -- If the folder does not yet exist, a dialog will ask you to create it. -- 4. Paste this lua script in the "Personal Lua Plugins" directory, or create a symlink. -- 5. When modifying this script, reload all plug-ins with CTRL+SHIFT+L dethrace_protocol = Proto("dethrace", "Dethrace Protocol") DETHRACE_PORT = 12286 broadcast_types = { [0x31] = "PING Request", [0x32] = "PONG Response", } content_types = { [0x00] = "SendMeDetails", [0x01] = "Details", [0x02] = "Join", [0x03] = "NewPlayerList", [0x04] = "GuaranteeReply", [0x05] = "CarDetailsReq", [0x06] = "CarDetails", [0x07] = "Leave", [0x08] = "Hosticide", [0x09] = "RaceOver", [0x0a] = "StatusReport", [0x0b] = "StartRace", [0x0c] = "HeadUp", [0x0d] = "HostQuery", [0x0e] = "HostReply", [0x0f] = "Mechanics", [0x10] = "NonCar_Info", [0x11] = "TimeSync", [0x12] = "Confirm", [0x13] = "DisableCar", [0x14] = "EnableCar", [0x15] = "PowerUp", [0x16] = "Recover", [0x17] = "Scores", [0x18] = "Wasted", [0x19] = "Pedestrian", [0x1a] = "Gameplay", [0x1b] = "NonCarPosition", [0x1c] = "CopInfo", [0x1d] = "GameScores", [0x1e] = "OilSpill", [0x1f] = "CrushPoint", [0x20] = "None", } -- FIXME: missing table: index -> car name f_checksum = ProtoField.uint32("dethrace.checksum", "Checksum") f_broadcast_magic = ProtoField.string("dethrace.broadcast_magic", "Broadcast Magic", base.ASCII) f_broadcast_type = ProtoField.char("dethrace.broadcast_type", "Broadcast Type", base.NONE, broadcast_types) f_terminator = ProtoField.char("dethrace.terminator", "Terminator") f_message_magic = ProtoField.uint32("dethrace.magic", "Magic", base.HEX) f_message_guarantee = ProtoField.uint32("dethrace.guarantee", "Guarantee Number") f_message_sender = ProtoField.uint32("dethrace.sender", "Sender Player ID") f_message_version = ProtoField.int32("dethrace.version", "Version") f_message_timestamp = ProtoField.uint32("dethrace.time", "Sender Time Stamp") f_message_count = ProtoField.uint16("dethrace.count", "Count Contents") f_message_size = ProtoField.uint16("dethrace.size", "Overall Size") f_contents_size = ProtoField.uint8("dethrace.content_size", "Content Size") f_contents_type = ProtoField.uint8("dethrace.content_type", "Content Type", base.DEC, content_types) player_statuses = { [0] = "Unknown", [1] = "Ready", [2] = "Loading", [3] = "Wrecks Gallery", [4] = "Summary", [5] = "Not Responding", [6] = "Racing", [7] = "Main Menu", [8] = "Recovering", [9] = "Action Replay", } f_player_status = ProtoField.uint32("dethrace.player_status", "Player Status", base.DEC, player_statuses) function create_message_size_type(parent, message, name) local message_type = message(1, 1):uint() local tree = parent:add(dethrace_protocol, message(), string.format("%s (%d)", name, message_type)) tree:add_le(f_contents_size, message(0, 1)) tree:add_le(f_contents_type, message(1, 1)) result = {} result["tree"] = tree result["size"] = message(0, 1):le_uint() result["type"] = message(1, 1):le_uint() return result end f_pd_player_info = ProtoField.bytes("dethrace.pd_info", "PD Net Info") function dissect_pd_player_info(tree, data) tree:add_le(f_pd_player_info, data(0, 16)) return 16 end f_float = ProtoField.float("dethrace.float", "Floating value") function dissect_br_matrix34(parent, data, name) local matrix_tree = parent:add(name, data(0, 48)) local offset = 0 for row_i = 0, 3 do local row_tree = matrix_tree:add(string.format("m[%d]", row_i), data(offset, 12)) for col_i = 0, 2 do local f = data(offset, 4):le_float() row_tree:add_le(f_float, data(offset, 4)):set_text(string.format("m[%d][%d] = %f", row_i, col_i, f)) offset = offset + 4 end end return offset end function dissect_reduced_matrix(parent, data, name) local matrix_tree = parent:add(name, data(0, 36)) local offset = 0 for row_i = 0, 2 do local row_tree = matrix_tree:add(string.format("m[%d]", row_i), data(offset, 12)) for col_i = 0, 2 do local f = data(offset, 4):le_float() row_tree:add_le(f_float, data(offset, 4)):set_text(string.format("m[%d][%d] = %f", row_i, col_i, f)) offset = offset + 4 end end return offset end function dissect_br_vector3(parent, data, name) local tree = parent:add(name, data(0, 12)) local offset = 0 for i = 0, 2 do local f = data(offset, 4):le_float() tree:add_le(f_float, data(offset, 4)):set_text(string.format("v[%d] = %f", i, f)) offset = offset + 4 end return offset end function dissect_message_sendmedetails(tree, message) -- tNet_message_send_me_details (0) local t = create_message_size_type(tree, message, "Send Me Details")["tree"] -- finished end race_tracks = { [0] = "Sumo", [1] = "Coliseum", [2] = "Maim Street", [3] = "Coastal Carnage", [4] = "Fridge Racer", [5] = "Death Valley", [6] = "Off Quay", [7] = "Industrial Injury", [8] = "Avenue of Atrocities", [9] = "Terror on the Trails", [10] = "MagnaChem Mayhem", [11] = "Downtown Devastation", } game_stages = { [0] = "Starting", [1] = "Ready", [2] = "Playing", } game_types = { [0] = "Driven To Destruction", [1] = "Car Crusher", [2] = "Carnage", [3] = "Checkpoint Stampede", [4] = "Sudden Death", [5] = "Terminal Tag", [6] = "Fox 'n' Hounds", } details_options_start_types = { [0] = "Random Start", [1] = "Grid Start", } details_options_starting_money = { [0] = "$0", [1] = "$2,000", [2] = "$5,000", [3] = "$10,000", [4] = "$20,000", } details_options_car_choices = { [0] = "Eagle", [1] = "Hawk", [2] = "Both", [3] = "All", } f_details_host_name = ProtoField.stringz("dethrace.details.host_name", "Host Name") f_details_host_id = ProtoField.uint32("dethrace.details.host_id", "Host ID") f_details_num_players = ProtoField.uint32("dethrace.details.num_players", "Number Players") f_details_start_race = ProtoField.uint32("dethrace.details.start_race", "Start Race", base.DEC, race_tracks) f_details_no_races_yet = ProtoField.bool("dethrace.details.no_races_yet", "No Races Yet") f_details_status = ProtoField.uint32("dethrace.details.game_status", "Status", base.DEC, game_stages) f_details_type = ProtoField.uint32("dethrace.details.type", "Type", base.DEC, game_types) f_details_option_show_players_on_map = ProtoField.bool("dethrace.details.options.players_on_map", "Show Players On Map") f_details_option_show_peds_on_map = ProtoField.bool("dethrace.details.options.peds_on_map", "Show Peds On Map") f_details_option_enable_text_messages = ProtoField.bool("dethrace.details.options.text_messages", "Enable Text Messages") f_details_option_show_powerups_on_map = ProtoField.bool("dethrace.details.options.powerups_on_map", "Show Powerups On Map") f_details_option_powerup_respawn = ProtoField.bool("dethrace.details.options.powerup_respawn", "Powerup Respawn") f_details_option_open_game = ProtoField.bool("dethrace.details.options.open_game", "Open Game") f_details_option_starting_money_index = ProtoField.uint32("dethrace.details.options.starting_money", "Starting Money", base.DEC, details_options_starting_money) f_details_option_start_type = ProtoField.uint32("dethrace.details.options.start_Type", "Start Type", base.DEC, details_options_start_types) f_details_option_race_end_target = ProtoField.uint32("dethrace.details.options.end_target", "Race End Target") f_details_option_random_car_choice = ProtoField.bool("dethrace.details.options.random_car", "Random Cars") f_details_option_race_sequence_type = ProtoField.uint32("dethrace.details.options.race_sequence_type", "Race Sequence Type") f_details_option_car_choice = ProtoField.uint32("dethrace.details.options.car_choice", "Car Choice", base.DEC, details_options_car_choices) function dissect_game_details(parent, data) -- tNet_game_options local tree = parent:add("Options", data(48)) tree:add_le(f_details_option_show_players_on_map, data(0, 4)) tree:add_le(f_details_option_show_peds_on_map, data(4, 4)) tree:add_le(f_details_option_enable_text_messages, data(8, 4)) tree:add_le(f_details_option_show_powerups_on_map, data(12, 4)) tree:add_le(f_details_option_powerup_respawn, data(16, 4)) tree:add_le(f_details_option_open_game, data(20, 4)) tree:add_le(f_details_option_starting_money_index, data(24, 4)) tree:add_le(f_details_option_start_type, data(28, 4)) tree:add_le(f_details_option_race_end_target, data(32, 4)) tree:add_le(f_details_option_random_car_choice, data(36, 4)) tree:add_le(f_details_option_race_sequence_type, data(40, 4)) tree:add_le(f_details_option_car_choice, data(44, 4)) return 48 end function dissect_message_details(tree, message) -- tNet_message_my_details (1) local t = create_message_size_type(tree, message, "Details")["tree"] local offset = 2 offset = offset + dissect_pd_player_info(t, message(offset)) t:add(f_details_host_name, message(offset, 32)) offset = offset + 4 t:add_le(f_details_host_id, message(offset, 4)) offset = offset + 4 t:add_le(f_details_num_players, message(offset, 4)) offset = offset + 4 t:add_le(f_details_start_race, message(offset, 4)) offset = offset + 4 t:add_le(f_details_no_races_yet, message(offset, 4)) offset = offset + 4 t:add_le(f_details_status, message(offset, 4)) offset = offset + 4 offset = offset + dissect_game_details(t, message(offset)) t:add_le(f_details_type, message(offset, 4)) offset = offset + 4 end f_join_timestamp = ProtoField.uint32("dethrace.join.timestamp", "Timestamp") f_join_last_heard = ProtoField.uint32("dethrace.join.time_last_heard", "Last heard timestamp") f_join_reposition_time = ProtoField.uint32("dethrace.join.time_reposition", "Reposition timestamp") f_join_last_waste_time = ProtoField.uint32("dethrace.join.time_waste", "Last waste message timestamp") f_join_host = ProtoField.bool("dethrace.join.host", "Host") f_join_playerid = ProtoField.uint32("dethrace.join.playerid", "Player ID") f_join_player_name = ProtoField.stringz("dethrace.join.player_name", "Player Name") f_join_car = ProtoField.uint32("dethrace.join.car", "Car") -- FIXME: use table index -> car name f_join_grid = ProtoField.uint32("dethrace.join.grid", "Grid Index") f_join_grid_set = ProtoField.bool("dethrace.join.grid_set", "Grid Position Set") f_join_opponent_index = ProtoField.uint32("dethrace.join.opponent_index", "Opponent List Index") f_join_wait_confirm = ProtoField.uint32("dethrace.join.wait_confirm", "Awaiting Confirmation") -- FIXME: find out values f_join_score = ProtoField.uint32("dethrace.join.score", "Score") f_join_credits = ProtoField.uint32("dethrace.join.credits", "Credits") f_join_wasted = ProtoField.bool("dethrace.join.wasted", "Wasted") f_join_wasteage_attributed = ProtoField.bool("dethrace.join.wasteage_attributed", "Wasteage Attributed") -- FIXME: find out what this is f_join_name_not_clipped = ProtoField.bool("dethrace.join.name_not_clipped", "Name Not Clipped") -- FIXME: find out what this is f_join_race_stuff_initialized = ProtoField.bool("dethrace.join.race_stuff_initialized", "Race Stuff Initialized") f_join_played = ProtoField.bool("dethrace.join.played", "Played") f_join_won = ProtoField.bool("dethrace.join.won", "Won") f_join_next_car_index = ProtoField.uint32("dethrace.join.next_car_index", "Next Car Index") -- FIXME: find out what this is f_join_games_score = ProtoField.uint32("dethrace.join.games_score", "Games Score") f_join_last_score_index = ProtoField.uint32("dethrace.join.last_score_index", "Last Score Index") f_join_last_score_index = ProtoField.uint32("dethrace.join.last_score_index", "Last Score Index") f_join_car_ptr = ProtoField.uint64("dethrace.join.car_ptr", "Car Spec (pointer)") function dissect_game_player_info(tree, data) -- tNet_game_player_info local offset = dissect_pd_player_info(tree, data) tree:add_le(f_join_timestamp, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_last_heard, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_reposition_time, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_last_waste_time, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_host, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_playerid, data(offset, 4)) offset = offset + 4 tree:add(f_join_player_name, data(offset, 32)) offset = offset + 32 tree:add_le(f_player_status, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_car, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_grid, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_grid_set, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_opponent_index, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_wait_confirm, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_score, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_credits, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_wasted, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_wasteage_attributed, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_name_not_clipped, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_race_stuff_initialized, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_played, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_won, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_next_car_index, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_games_score, data(offset, 4)) offset = offset + 4 tree:add_le(f_join_last_score_index, data(offset, 4)) offset = offset + 4 offset = offset + dissect_br_matrix34(tree, data(offset), "Initial Position") assert(offset == 0xbc, "offset of car spec ptr") tree:add_le(f_join_car_ptr, data(offset, 4)) tree:set_len(offset) end function dissect_message_join(tree, message) -- tNet_message_join (2) local t = create_message_size_type(tree, message, "Join")["tree"] local offset = 4 dissect_game_player_info(t:add("Player Info", message(offset)), message(offset)) end newerplayerlist_count_magics = { [-1] = "-1 (Selected Car Unavailable)", [0] = "0 (Game Already Started)", } f_newplayerlist_count = ProtoField.int32("dethrace.newplayerlist.count", "Number Of Players")--, base.DEC, newerplayerlist_count_magics) f_newplayerlist_index = ProtoField.int32("dethrace.newplayerlist.index", "This Index") f_newplayerlist_batch_number = ProtoField.int32("dethrace.newplayerlist.batch_number", "Batch Number") function dissect_message_newplayerlist(tree, message) -- tNet_message_new_player_list (3) local t = create_message_size_type(tree, message, "New Player List")["tree"] local offset = 4 local t_count = t:add_le(f_newplayerlist_count, message(offset, 4)) if message(offset, 4):int() == 0 then t_count:set_text("Number Of Players: Game Already Started (0)") elseif message(offset, 4):int() == -1 then t_count:set_text("Number Of Players: Car Unavailable (-1)") end offset = offset + 4 t:add_le(f_newplayerlist_index, message(offset, 4)) offset = offset + 4 t:add_le(f_newplayerlist_batch_number, message(offset, 4)) offset = offset + 4 dissect_game_player_info(t:add("Player", message(offset)), message(offset)) -- finished end f_guaranteereply_number = ProtoField.uint32("dethrace.guaranteereply.number", "Guarantee Number") function dissect_message_guaranteereply(tree, message) -- tNet_message_guarantee_reply (4) local t = create_message_size_type(tree, message, "Guarantee Reply")["tree"] t:add_le(f_guaranteereply_number, message(4, 4)) -- FIXME: add reference to packet with this reply number -- finished end function dissect_message_cardetailsreq(tree, message) -- tNet_message_car_details_req (5) local t = create_message_size_type(tree, message, "Car Details Req")["tree"] -- finished end f_cardetails_count = ProtoField.uint32("dethrace.cardetails.count", "Car Count") f_cardetails_car = ProtoField.uint32("dethrace.cardetails.car_index", "Car") -- FIXME: use table index -> car name f_cardetails_owner = ProtoField.stringz("dethrace.cardetails.owner", "Owner") function dissect_message_cardetails(tree, message) -- tNet_message_car_details (6) local t = create_message_size_type(tree, message, "Car Details")["tree"] local offset = 4 t:add_le(f_cardetails_count, message(4, 4)) offset = offset + 4 for i = 0, 5 do local car_detail = message(offset, 20) local subtree = t:add(car_detail, string.format("Car[%d]", i)) subtree:add_le(f_cardetails_car, car_detail(0, 4)) subtree:add(f_cardetails_owner, car_detail(4, 16)) offset = offset + 20 end -- finished end function dissect_message_leave(tree, message) -- tNet_message_leave (7) local t = create_message_size_type(tree, message, "Leave")["tree"] -- finished end function dissect_message_hosticide(tree, message) -- tNet_message_host_pissing_off (8) local t = create_message_size_type(tree, message, "Hosticide")["tree"] -- finished end raceover_reasons = { [-1] = eRace_not_over_yet, [ 0] = eRace_over_laps, [ 1] = eRace_over_peds, [ 2] = eRace_over_opponents, [ 3] = eRace_over_abandoned, [ 4] = eRace_over_out_of_time, [ 5] = eRace_over_demo, [ 6] = eRace_over_network_victory, [ 7] = eRace_over_network_loss, } f_raceover_reason = ProtoField.uint32("dethrace.raceover.reason", "Reason", base.DEC, raceover_reasons) function dissect_message_raceover(tree, message) -- tNet_message_race_over (9) local t = create_message_size_type(tree, message, "Race Over (UNTESTED)")["tree"] t:add_le(f_raceover_reason, message(4, 4)) -- finished end function dissect_message_statusreport(tree, message) -- tNet_message_status_report (10) local t = create_message_size_type(tree, message, "Status Report")["tree"] t:add_le(f_player_status, message(4, 4)) -- finished end f_startrace_count = ProtoField.uint32("dethrace.startrace.count", "Car Count", base.DEC) f_startrace_racing = ProtoField.bool("dethrace.startrace.racing", "Racing") f_startrace_next = ProtoField.uint32("dethrace.startrace.next", "Next Race", base.DEC, race_tracks) f_startrace_grid_index = ProtoField.uint32("dethrace.startrace.grid.index", "Index", base.DEC) f_startrace_grid_next_car_index = ProtoField.uint32("dethrace.startrace.grid.next_car_index", "Next Car Index", base.DEC) function dissect_message_startrace(tree, message) -- tNet_message_start_race (11) local t = create_message_size_type(tree, message, "Start Race")["tree"] local offset = 4 t:add_le(f_startrace_count, message(offset, 4)) offset = offset + 4 t:add_le(f_startrace_racing, message(offset, 4)) offset = offset + 4 t:add_le(f_startrace_next, message(offset, 4)) offset = offset + 4 for i = 0, 5 do local start = offset local subtree = t:add(message(offset), string.format("Grid Car[%d]", i)) subtree:add_le(f_startrace_grid_index, message(offset, 4)) offset = offset + 4 subtree:add_le(f_startrace_grid_next_car_index, message(offset, 4)) offset = offset + 4 offset = offset + dissect_br_matrix34(subtree, message(offset), "Matrix") subtree:set_len(offset - start) end -- FIXME end f_headup_text = ProtoField.stringz("dethrace.headup.message", "Message") function dissect_message_headup(tree, message) -- tNet_message_headup (12) local t = create_message_size_type(tree, message, "Head-Up (UNTESTED)")["tree"] t:add(f_headup_text, message(4, 128)) -- finished end function dissect_message_hostquery(tree, message) -- tNet_message_host_query (13) local t = create_message_size_type(tree, message, "Host Query")["tree"] -- finished end f_hostreply_started = ProtoField.bool("dethrace.hostreply.started", "Race Started") f_hostreply_race = ProtoField.uint32("dethrace.hostreply.race", "Race", base.DEC, race_tracks) f_hostreply_pending = ProtoField.uint32("dethrace.hostreply.pending", "Pending Race", base.DEC, race_tracks) function dissect_message_hostreply(tree, message) -- tNet_message_host_reply (14) local t = create_message_size_type(tree, message, "Host Reply (UNTESTED)")["tree"] t:add(f_hostreply_started, message(4, 4)) t:add(f_hostreply_race, message(8, 4)) t:add(f_hostreply_pending, message(12, 4)) -- finished end f_mechanics_id = ProtoField.uint32("dethrace.mechanics.id", "ID") f_mechanics_time = ProtoField.uint32("dethrace.mechanics.time", "Time") f_mechanics_d = ProtoField.uint8("dethrace.mechanics.d", "D") f_mechanics_carcontrols = ProtoField.uint32("dethrace.mechanics.carcontrols", "Car Controls") -- FIXME: bitmask f_mechanics_coll_time = ProtoField.uint32("dethrace.mechanics.coll_time", "Car Control Collision Time") f_mechanics_curvature = ProtoField.int16("dethrace.mechanics.curvature", "Curvature") f_mechanics_revs = ProtoField.uint16("dethrace.mechanics.revs", "Revs") f_mechanics_front = ProtoField.float("dethrace.mechanics.front", "Front") f_mechanics_back = ProtoField.float("dethrace.mechanics.back", "Back") f_mechanics_repair_time = ProtoField.uint32("dethrace.mechanics.repair_time", "Repair Time") f_mechanics_powerups = ProtoField.int16("dethrace.mechanics.powerups", "Powerups") f_mechanics_damage = ProtoField.uint8("dethrace.mechanics.damage", "Damage") function dissect_message_mechanics(tree, message) -- tNet_message_mechanics_info (15) local t = create_message_size_type(tree, message, "Mechanics")["tree"] local offset = 4 t:add_le(f_mechanics_id, message(offset, 4)) offset = offset + 4 t:add_le(f_mechanics_time, message(offset, 4)) offset = offset + 4 offset = offset + dissect_reduced_matrix(t, message(offset), "Matrix") offset = offset + dissect_br_vector3(t, message(offset), "Velocity") offset = offset + dissect_br_vector3(t, message(offset), "Omega") do local sub = t:add("d", message(offset, 4)) for i = 0, 3 do local item = sub:add_le(f_mechanics_d, message(offset, 1)) item:set_text(string.format("d[%d] = %d", i, message(offset, 1):le_uint())) offset = offset + 1 end end t:add_le(f_mechanics_carcontrols, message(offset, 4)) offset = offset + 4 t:add_le(f_mechanics_coll_time, message(offset, 4)) offset = offset + 4 t:add_le(f_mechanics_curvature, message(offset, 2)) offset = offset + 2 t:add_le(f_mechanics_revs, message(offset, 2)) offset = offset + 2 t:add_le(f_mechanics_front, message(offset, 4)) offset = offset + 4 t:add_le(f_mechanics_back, message(offset, 4)) offset = offset + 4 t:add_le(f_mechanics_repair_time, message(offset, 4)) offset = offset + 4 t:add_le(f_mechanics_powerups, message(offset, 2)) offset = offset + 2 do local sub = t:add("Damage", message(offset, 4)) -- FIXME: use names of damage system (in addition to index) for i = 0, 11 do local item = sub:add_le(f_mechanics_damage, message(offset, 1)) item:set_text(string.format("damage[%d] = %d", i, message(offset, 1):le_uint())) offset = offset + 1 end end t:add_le(f_mechanics_powerups, message(offset, 2)) offset = offset + 2 -- omit 2 bytes of padding offset = offset + 2 if offset < message:len() then do local sub = t:add("Wheel Damage Offset", message(offset, 4)) for i = 0, 3 do local item = sub:add_le(f_float, message(offset, 4)) item:set_text(string.format("offset[%d] = %d", i, message(offset, 4):le_float())) offset = offset + 4 end end end end function dissect_message_noncar_info(tree, message) -- tNet_message_non_car_info (16) local t = create_message_size_type(tree, message, "Non-Car Info (INCOMPLETE)")["tree"] -- FIXME end f_timesync_time = ProtoField.uint32("dethrace.timesync.start_time", "Race Start Time") function dissect_message_timesync(tree, message) -- tNet_message_time_sync (17) local t = create_message_size_type(tree, message, "Time Sync")["tree"] local offset = 4 t:add_le(f_timesync_time, message(offset, 4)) offset = offset + 4 end function dissect_message_confirm(tree, message) -- tNet_message_players_confirm (18) local t = create_message_size_type(tree, message, "Confirm (INCOMPLETE)")["tree"] -- FIXME end function dissect_message_disablecar(tree, message) -- tNet_message_disable_car (19) local t = create_message_size_type(tree, message, "Disable Car (INCOMPLETE)")["tree"] -- FIXME end function dissect_message_enablecar(tree, message) -- tNet_message_enable_car (20) local t = create_message_size_type(tree, message, "Enable Car (INCOMPLETE)")["tree"] -- FIXME end function dissect_message_powerup(tree, message) -- tNet_message_powerup (21) local t = create_message_size_type(tree, message, "Power-Up (INCOMPLETE)")["tree"] -- FIXME end function dissect_message_recover(tree, message) -- tNet_message_recover (22) local t = create_message_size_type(tree, message, "Recover (INCOMPLETE)")["tree"] -- FIXME end f_scores_played = ProtoField.bool("dethrace.score.played", "Played") f_scores_won = ProtoField.bool("dethrace.scored.won", "Won") f_scores_score = ProtoField.int32("dethrace.scored.score", "Score") function dissect_message_scores(tree, message) -- tNet_message_game_scores (23) local header = create_message_size_type(tree, message, "Scores") local t = header["tree"] local offset = 4 for i = 0, 5 do local subtree = t:add(string.format("Scores[%d]", i), message(offset, 12)) subtree:add_le(f_scores_played, message(offset, 4)) offset = offset + 4 subtree:add_le(f_scores_won, message(offset, 4)) offset = offset + 4 subtree:add_le(f_scores_score, message(offset, 4)) offset = offset + 4 end end function dissect_message_wasted(tree, message) -- tNet_message_wasted (24) local t = create_message_size_type(tree, message, "Wasted (INCOMPLETE)")["tree"] -- FIXME end f_ped_instruction = ProtoField.int8("dethrace.ped.instruction", "Action Instruction") f_ped_flags = ProtoField.int8("dethrace.ped.flags", "Flags") f_ped_index = ProtoField.int16("dethrace.ped.index", "Index") function dissect_message_pedestrian(tree, message) -- tNet_message_pedestrian (25) local t = create_message_size_type(tree, message, "Pedestrian (INCOMPLETE)")["tree"] local offset = 2 t:add_le(f_ped_instruction, message(offset, 1)) offset = offset + 1 t:add_le(f_ped_flags, message(offset, 1)) offset = offset + 1 t:add_le(f_ped_index, message(offset, 2)) offset = offset + 2 -- padding offset = offset + 2 offset = offset + dissect_br_vector3(t, message(offset, 12), "Position") t:add_le(f_float, message(offset, 4)):set_text(string.format("Speed: %f", message(offset, 4):le_float())) offset = offset + 4 --offset = offset + dissect_br_vector3(t, message(offset, 12), "to_pos") -- FIXME end function dissect_message_gameplay(tree, message) -- tNet_message_gameplay (26) local t = create_message_size_type(tree, message, "Gameplay (INCOMPLETE)")["tree"] -- FIXME end function dissect_message_noncarposition(tree, message) -- tNet_message_non_car_position (27) local t = create_message_size_type(tree, message, "Non-Car Position (INCOMPLETE)")["tree"] -- FIXME end function dissect_message_copinfo(tree, message) -- tNet_message_cop_info (28) local t = create_message_size_type(tree, message, "Cop Info (INCOMPLETE)")["tree"] -- FIXME end function dissect_message_gamescores(tree, message) -- tNet_message_oil_spill (29) local t = create_message_size_type(tree, message, "Game Scores (INCOMPLETE)")["tree"] -- FIXME end function dissect_message_oilspill(tree, message) -- tNet_message_oil_spill (30) local t = create_message_size_type(tree, message, "Oil Spill (INCOMPLETE)")["tree"] -- FIXME end function dissect_message_crushpoint(tree, message) -- tNet_message_crush_point (31) local t = create_message_size_type(tree, message, "Crush Point (INCOMPLETE)")["tree"] -- FIXME end function dissect_message_none(tree, message) -- none (32) local t = create_message_size_type(tree, message, "None")["tree"] -- finished end dethrace_protocol.fields = { f_checksum, f_broadcast_magic, f_broadcast_type, f_terminator, f_float, f_pd_player_info, f_message_magic, f_message_guarantee, f_message_sender, f_message_version, f_message_timestamp, f_message_count, f_message_size, f_contents_size, f_contents_type, -- 2 (join) f_join_timestamp, f_join_last_heard, f_join_reposition_time, f_join_last_waste_time, f_join_host, f_join_playerid, f_join_player_name, f_join_car, f_join_grid, f_join_grid_set, f_join_opponent_index, f_join_wait_confirm, f_join_score, f_join_credits, f_join_wasted, f_join_wasteage_attributed, f_join_name_not_clipped, f_join_race_stuff_initialized, f_join_played, f_join_won, f_join_next_car_index, f_join_games_score, f_join_last_score_index, f_join_car_ptr, -- 3 (newplayerlist) f_newplayerlist_count, f_newplayerlist_index, f_newplayerlist_batch_number, f_details_host_name, f_details_host_id, f_details_num_players, f_details_start_race, f_details_no_races_yet, f_details_status, f_details_option_show_players_on_map, f_details_option_show_peds_on_map, f_details_option_enable_text_messages, f_details_option_show_powerups_on_map, f_details_option_powerup_respawn, f_details_option_open_game, f_details_option_starting_money_index, f_details_option_start_type, f_details_option_race_end_target, f_details_option_random_car_choice, f_details_option_race_sequence_type, f_details_option_car_choice, f_details_type, f_guaranteereply_number, f_cardetails_count, f_cardetails_car, f_cardetails_owner, f_raceover_reason, f_statusreport_status, f_startrace_count, f_startrace_racing, f_startrace_next, f_startrace_grid_index, f_startrace_grid_next_car_index, f_headup_text, f_hostreply_started, f_hostreply_race, f_hostreply_pending, f_player_status, f_mechanics_id, f_mechanics_time, f_mechanics_d, f_mechanics_carcontrols, f_mechanics_coll_time, f_mechanics_curvature, f_mechanics_revs, f_mechanics_front, f_mechanics_back, f_mechanics_repair_time, f_mechanics_powerups, f_mechanics_damage, f_timesync_time, f_scores_played, f_scores_won, f_scores_score, f_ped_instruction, f_ped_flags, f_ped_index, } dethrace_message_dissectors = { [ 0] = dissect_message_sendmedetails, [ 1] = dissect_message_details, [ 2] = dissect_message_join, [ 3] = dissect_message_newplayerlist, [ 4] = dissect_message_guaranteereply, [ 5] = dissect_message_cardetailsreq, [ 6] = dissect_message_cardetails, [ 7] = dissect_message_leave, [ 8] = dissect_message_hosticide, [ 9] = dissect_message_raceover, [10] = dissect_message_statusreport, [11] = dissect_message_startrace, [12] = dissect_message_headup, [13] = dissect_message_hostquery, [14] = dissect_message_hostreply, [15] = dissect_message_mechanics, [16] = dissect_message_noncar_info, [17] = dissect_message_timesync, [18] = dissect_message_confirm, [19] = dissect_message_disablecar, [20] = dissect_message_enablecar, [21] = dissect_message_powerup, [22] = dissect_message_recover, [23] = dissect_message_scores, [24] = dissect_message_wasted, [25] = dissect_message_pedestrian, [26] = dissect_message_gameplay, [27] = dissect_message_noncarposition, [28] = dissect_message_copinfo, [29] = dissect_message_gamescores, [30] = dissect_message_oilspill, [31] = dissect_message_crushpoint, [32] = dissect_message_none, } function dethrace_protocol.dissector(buffer, pinfo, tree) if buffer:bytes(4, 7) == ByteArray.new("43 57 39 35 4d 53 47") then -- "CW95MSG" local t = tree:add(dethrace_protocol, buffer(), "DethRace Broadcast Protocol") pinfo.cols.protocol = "DRBC" pinfo.cols.info = string.format("Broadcast: %s", broadcast_types[buffer(11, 1):uint()]) t:add(f_checksum, buffer(0, 4)) t:add(f_broadcast_magic, buffer(4, 7)) t:add(f_broadcast_type, buffer(11, 1)) t:add(f_terminator, buffer(12, 1)) elseif buffer(4, 4):le_uint() == 0x763a5058 then -- "XP:v" pinfo.cols.protocol = "DR"; do local t = tree:add(dethrace_protocol, buffer(0, 28), "DethRace Message Protocol") t:add(f_checksum, buffer(0, 4)) t:add_le(f_message_magic, buffer(4, 4)) t:add_le(f_message_guarantee, buffer(8, 4)) t:add_le(f_message_sender, buffer(12, 4)) t:add_le(f_message_version, buffer(16, 4)) t:add_le(f_message_timestamp, buffer(20, 4)) t:add_le(f_message_count, buffer(24, 2)) t:add_le(f_message_size, buffer(26, 2)) end local sender_type_str if (pinfo.src_port == DETHRACE_PORT) then sender_type_str = "HOST" else sender_type_str = "CLIENT" end local message = buffer(28) local message_type = message(1, 1):uint() pinfo.cols.info = string.format("[%s] %s (%d)", sender_type_str, content_types[message_type], message_type) dethrace_message_dissectors[message_type](tree, message) end end -- load the udp.port table udp_table = DissectorTable.get("udp.port") -- register our protocol to handle udp port 39456 udp_table:add(DETHRACE_PORT, dethrace_protocol)