/README.md Secret
Last active
May 6, 2017 19:00
Revisions
-
schierlm created this gist
May 6, 2017 .There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,37 @@ Eulora Protocol dissector for Wireshark ======================================= Usage ----- Start Wireshark like this: wireshark -X lua_script:/path/to/eulora.lua Then capture or load a capture file. Eulora packets will be automatically dissected if they are on UDP port 13331, else use the "Decode as..." option in context menu Status ------ Single packets and multipackets are supported; fragmented packets are not (yet) supported. Detail dissectors are available for the following message types: [1] = "PING", [2] = "AUTHENTICATE", [3] = "PREAUTHENTICATE", [4] = "PREAUTHAPPROVED", [5] = "AUTHAPPROVED", [7] = "DISCONNECT", [9] = "CHANNEL_JOIN", [10] = "CHANNEL_JOINED", [13] = "USERCMD", [14] = "SYSTEM", [20] = "USERACTION", For other message types, only the message type and the raw hex data are shown (so far). See below for a sanitized .pcap file - all AUTHENTICATE messages have been removed, but not their replies. If you'd like certain message types to be added, feel free to comment here :) This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,350 @@ ---------------------------------------- -- Wireshark Lua dissector for Eulora protocol -- -- author: Michael Schierl <schierlm at gmx dot de>, or <mihi> @ freenode -- -- Based on the Lua example dissector script by Hadriel Kaplan <hadrielk at yahoo dot com> which is -- Copyright (c) 2014, Hadriel Kaplan -- This code is in the Public Domain, or the BSD (3 clause) license if Public Domain does not apply -- in your country. -- ---------------------------------------- local eulora = Proto("eulora","Eulora Protocol") local msgtypes = { [1] = "PING", [2] = "AUTHENTICATE", [3] = "PREAUTHENTICATE", [4] = "PREAUTHAPPROVED", [5] = "AUTHAPPROVED", [6] = "AUTHREJECTED", [7] = "DISCONNECT", [8] = "CHAT", [9] = "CHANNEL_JOIN", [10] = "CHANNEL_JOINED", [11] = "CHANNEL_LEAVE", [12] = "GUILDCMD", [13] = "USERCMD", [14] = "SYSTEM", [15] = "CHARREJECT", [16] = "DEAD_RECKONING", [17] = "FORCE_POSITION", [18] = "CELPERSIST", [19] = "CONFIRMQUESTION", [20] = "USERACTION", [21] = "ADMINCMD", [22] = "GUIINTERACT", [23] = "GUIINVENTORY", [24] = "VIEW_ITEM", [25] = "VIEW_CONTAINER", [26] = "VIEW_SKETCH", [27] = "VIEW_ACTION_LOCATION", [28] = "READ_BOOK", [29] = "WRITE_BOOK", [30] = "UPDATE_ITEM", [31] = "MODE", [32] = "WEATHER", [33] = "NEWSECTOR", [34] = "GUIGUILD", [35] = "EQUIPMENT", [36] = "GUIEXCHANGE", [37] = "EXCHANGE_REQUEST", [38] = "EXCHANGE_ADD_ITEM", [39] = "EXCHANGE_REMOVE_ITEM", [40] = "EXCHANGE_ACCEPT", [41] = "EXCHANGE_STATUS", [42] = "EXCHANGE_END", [43] = "EXCHANGE_AUTOGIVE", [44] = "EXCHANGE_MONEY", [45] = "GUIMERCHANT", [46] = "GUISTORAGE", [47] = "GROUPCMD", [48] = "GUIGROUP", [49] = "STATDRUPDATE", [50] = "SPELL_BOOK", [51] = "GLYPH_REQUEST", [52] = "GLYPH_ASSEMBLE", [53] = "PURIFY_GLYPH", [54] = "SPELL_CAST", [55] = "SPELL_CANCEL", [56] = "EFFECT", [57] = "EFFECT_STOP", [58] = "NPCAUTHENT", [59] = "NPCLIST", [60] = "GUITARGETUPDATE", [61] = "MAPLIST", [62] = "NPCCOMMANDLIST", [63] = "NPCREADY", [64] = "ALLENTITYPOS", [65] = "PERSIST_ALL_ENTITIES", [66] = "NEW_NPC", [67] = "PETITION", [68] = "MSGSTRINGS", [69] = "CHARACTERDATA", [70] = "AUTHCHARACTER", [71] = "AUTHCHARACTERAPPROVED", [72] = "CHAR_CREATE_CP", [73] = "COMBATEVENT", [74] = "LOOT", [75] = "LOOTITEM", [76] = "LOOTREMOVE", [77] = "GUISKILL", [78] = "OVERRIDEACTION", [79] = "QUESTLIST", [80] = "QUESTINFO", [81] = "GMGUI", [82] = "WORKCMD", [83] = "BUDDY_LIST", [84] = "BUDDY_STATUS", [85] = "MOTD", [86] = "MOTDREQUEST", [87] = "QUESTION", [88] = "QUESTIONRESPONSE", [89] = "SLOT_MOVEMENT", [90] = "QUESTIONCANCEL", [91] = "GUILDMOTDSET", [92] = "PLAYSOUND", [93] = "PLAYVOICE", [94] = "CHARACTERDETAILS", [95] = "CHARDETAILSREQUEST", [96] = "CHARDESCUPDATE", [97] = "FACTION_INFO", [98] = "QUESTREWARD", [99] = "NAMECHANGE", [100] = "GUILDCHANGE", [101] = "LOCKPICK", [102] = "GMSPAWNITEMS", [103] = "GMSPAWNTYPES", [104] = "GMSPAWNITEM", [105] = "ADVICE", [106] = "ACTIVEMAGIC", [107] = "GROUPCHANGE", [108] = "MAPACTION", [109] = "CLIENTSTATUS", [110] = "TUTORIAL", [111] = "BANKING", [112] = "CMDDROP", [113] = "REQUESTMOVEMENTS", [114] = "MOVEINFO", [115] = "MOVEMOD", [116] = "MOVELOCK", [117] = "CHAR_DELETE", [118] = "CHAR_CREATE_PARENTS", [119] = "CHAR_CREATE_CHILDHOOD", [120] = "CHAR_CREATE_LIFEEVENTS", [121] = "CHAR_CREATE_UPLOAD", [122] = "CHAR_CREATE_VERIFY", [123] = "CHAR_CREATE_NAME", [124] = "PERSIST_WORLD_REQUEST", [125] = "PERSIST_WORLD", [126] = "PERSIST_ACTOR_REQUEST", [127] = "PERSIST_ACTOR", [128] = "PERSIST_ITEM", [129] = "PERSIST_ACTIONLOCATION", [130] = "PERSIST_ALL", [131] = "REMOVE_OBJECT", [132] = "CHANGE_TRAIT", [133] = "DAMAGE_EVENT", [134] = "DEATH_EVENT", [135] = "TARGET_EVENT", [136] = "ZPOINT_EVENT", [137] = "BUY_EVENT", [138] = "SELL_EVENT", [139] = "PICKUP_EVENT", [140] = "DROP_EVENT", [141] = "LOOT_EVENT", [142] = "CONNECT_EVENT", [143] = "MOVEMENT_EVENT", [144] = "GENERIC_EVENT", [145] = "SOUND_EVENT", [146] = "CHAR_CREATE_TRAITS", [147] = "STATS", [148] = "PET_COMMAND", [149] = "PET_SKILL", [150] = "CRAFT_INFO", [151] = "PETITION_REQUEST", [152] = "HEART_BEAT", [153] = "NPC_COMMAND", [154] = "MINIGAME_STARTSTOP", [155] = "MINIGAME_BOARD", [156] = "MINIGAME_UPDATE", [157] = "ENTRANCE", [158] = "GMEVENT_LIST", [159] = "GMEVENT_INFO", [160] = "SEQUENCE", [161] = "NPCRACELIST", [162] = "INTRODUCTION", [163] = "CACHEFILE", [164] = "DIALOG_MENU", [165] = "SIMPLE_STRING", [166] = "ORDEREDTEST", [167] = "GENERICCMD", [168] = "CRAFT_CANCEL", [169] = "MUSICAL_SHEET", [170] = "PLAY_SONG", [171] = "STOP_SONG", [172] = "SIMPLE_RENDER_MESH", [173] = "NPC_WORKDONE", [174] = "PATH_NETWORK", [175] = "LOCATION", [176] = "MECS_ACTIVATE", [177] = "NPC_DELETED" } local pf_msgtype = ProtoField.uint8("eulora.msgtype", "Message type", base.DEC, msgtypes) local pf_ping_id = ProtoField.uint32("eulora.ping.id", "ID", base.HEX) local pf_ping_flags = ProtoField.uint8("eulora.ping.flags", "Flags", base.HEX) local pf_auth_version = ProtoField.uint32("eulora.auth.version", "Version", base.DEC) local pf_auth_user = ProtoField.stringz("eulora.auth.user", "User name", base.ASCII) eulora.fields = { pf_msgtype, pf_ping_id, pf_ping_flags, pf_auth_version, pf_auth_user } local msgtype_field = Field.new("eulora.msgtype") local ping_id_field = Field.new("eulora.ping.id") local ping_flags_field = Field.new("eulora.ping.flags") local function getStringLength(buffer, offset) return buffer(offset):stringz():len() end local function parseString(ctx, label) local val = ctx.tvbuf:range(ctx.offs):stringz() ctx.parent:add(ctx.tvbuf:range(ctx.offs, val:len()), label, val) ctx.offs = ctx.offs + val:len() + 1 end local function dissectPacket(tvbuf,pktinfo,tree) local pktlen = tvbuf:len() if pktlen < 15 then tree:add("Packet too short"); return end local msgsize = tvbuf:range(8,4):le_uint() local pktsize = tvbuf:range(12,2):le_uint() local msgflags = tvbuf:range(14,1):uint() tree:add(tvbuf:range(0,4), string.format("Packet ID: 0x%08x", tvbuf:range(0,4):le_uint())) tree:add(tvbuf:range(4,4), "Offset:", tvbuf:range(4,4):le_uint()) tree:add(tvbuf:range(8,4), "Message Size:", msgsize) tree:add(tvbuf:range(12,2), "Packet Size:", pktsize) tree:add(tvbuf:range(14,1), string.format("Flags: 0x%02x", msgflags)) if pktlen == 15 and pktsize == 0 then pktinfo.cols.info:set("ACK packet") return end if msgsize ~= pktsize then pktinfo.cols.info:set("Fragmented packet [not supported yet]") return end if pktlen < 18 or pktlen ~= msgsize + 15 then return end if msgflags == 2 or msgflags == 3 then local offs=15 while pktlen > offs + 14 do local mpsize = tvbuf:range(offs+12,2):le_uint() local subpacket = tree:add("Subpacket") dissectPacket(tvbuf:range(offs,mpsize+15):tvb(),pktinfo,subpacket) offs = offs + 15 + mpsize end pktinfo.cols.info:set("Multipacket") return end tree:add(pf_msgtype, tvbuf:range(15,1)) local msgtype_value = tvbuf:range(15,1):uint() pktinfo.cols.info:set(msgtype_field().display) local payloadsize_value = tvbuf:range(16,2):le_uint() tree:add(tvbuf:range(16,2), "Payload size:", payloadsize_value) if payloadsize_value ~= msgsize-3 then return end if msgtype_value == 1 and payloadsize_value == 5 then local payload_tree = tree:add(tvbuf:range(18, 5), "PING Payload") payload_tree:add_le(pf_ping_id, tvbuf:range(18,4)) payload_tree:add(pf_ping_flags, tvbuf:range(22,1)) pktinfo.cols.info:append(": "..ping_id_field().display.." ["..ping_flags_field().display.."]") elseif msgtype_value == 2 then local payload_tree = tree:add(tvbuf:range(18, payloadsize_value), "AUTH Payload") payload_tree:add_le(pf_auth_version, tvbuf:range(18,4)) local l = getStringLength(tvbuf, 22) payload_tree:add(pf_auth_user, tvbuf:range(22, l)) local ctx = {offs = 22 + l + 1, tvbuf = tvbuf, parent = payload_tree} parseString(ctx, "Password:") parseString(ctx, "Operating System:") parseString(ctx, "Graphics Card:") parseString(ctx, "Graphics Version:") parseString(ctx, "Password256:") pktinfo.cols.info:append(": "..tvbuf:range(22):stringz()) elseif msgtype_value == 3 and payloadsize_value == 4 then local payload_tree = tree:add(tvbuf:range(18, 4), "PREAUTH Payload") payload_tree:add(tvbuf:range(18,4), "Net version: ", tvbuf:range(18,4):le_uint()) elseif msgtype_value == 4 and payloadsize_value == 4 then local payload_tree = tree:add(tvbuf:range(18, 4), "PREAUTH_APPROVED Payload") payload_tree:add(tvbuf:range(18,4), "Client number: ", tvbuf:range(18,4):le_uint()) elseif msgtype_value == 5 and payloadsize_value > 8 then local payload_tree = tree:add(tvbuf:range(18, payloadsize_value), "AUTH_APPROVED Payload") payload_tree:add(tvbuf:range(18,4), "ClientValidToken: ", tvbuf:range(18,4):le_uint()) payload_tree:add(tvbuf:range(22,4), "Player ID: ", tvbuf:range(22,4):le_uint()) payload_tree:add(tvbuf:range(26,1), "Number of Characters: ", tvbuf:range(26,1):uint()) local ctx = {offs = 27, tvbuf = tvbuf, parent = payload_tree} while ctx.offs < 18 + payloadsize_value do parseString(ctx, "Value:") end elseif msgtype_value == 7 and payloadsize_value > 4 then local payload_tree = tree:add(tvbuf:range(18, payloadsize_value), "DISCONECT Payload") payload_tree:add(tvbuf:range(18,4), "Actor: ", tvbuf:range(18,4):le_uint()) parseString({offs = 22, tvbuf = tvbuf, parent = payload_tree}, "Reason:") pktinfo.cols.info:append(": "..tvbuf:range(22):stringz()) elseif msgtype_value == 9 then local payload_tree = tree:add(tvbuf:range(18, payloadsize_value), "CHANNEL_JOIN Payload") parseString({offs = 18, tvbuf = tvbuf, parent = payload_tree}, "Channel:") pktinfo.cols.info:append(": "..tvbuf:range(18):stringz()) elseif msgtype_value == 10 then local payload_tree = tree:add(tvbuf:range(18, payloadsize_value), "CHANNEL_JOINED Payload") local ctx = {offs = 18, tvbuf = tvbuf, parent = payload_tree} parseString(ctx, "Channel:") payload_tree:add(tvbuf:range(ctx.offs, 2), "Channel ID:", tvbuf:range(ctx.offs, 2):le_uint()) pktinfo.cols.info:append(": "..tvbuf:range(18):stringz()) elseif msgtype_value == 13 then local payload_tree = tree:add(tvbuf:range(18, payloadsize_value), "USERCMD Payload") parseString({offs = 18, tvbuf = tvbuf, parent = payload_tree}, "Command:") pktinfo.cols.info:append(": "..tvbuf:range(18):stringz()) elseif msgtype_value == 14 and payloadsize_value > 4 then local payload_tree = tree:add(tvbuf:range(18, payloadsize_value), "SYSTEM Payload") payload_tree:add(tvbuf:range(18,4), "Type: ", tvbuf:range(18,4):le_uint()) parseString({offs = 22, tvbuf = tvbuf, parent = payload_tree}, "Message:") pktinfo.cols.info:append(": "..tvbuf:range(22):stringz()) elseif msgtype_value == 20 and payloadsize_value > 5 then local payload_tree = tree:add(tvbuf:range(18, payloadsize_value), "USERACTION Payload") payload_tree:add(tvbuf:range(18,4), "Target: ", tvbuf:range(18,4):le_uint()) local ctx = {offs = 22, tvbuf = tvbuf, parent = payload_tree} parseString(ctx, "Action:") parseString(ctx, "Default behaviors:") pktinfo.cols.info:append(": "..tvbuf:range(22):stringz()) elseif payloadsize_value == 0 then tree:add(tvbuf:range(18, 0), "Empty Payload") else tree:add(tvbuf:range(18, payloadsize_value), "Payload") pktinfo.cols.info:append(": [details not supported yet]") end end function eulora.dissector(tvbuf,pktinfo,root) pktinfo.cols.protocol:set("Eulora") local pktlen = tvbuf:reported_length_remaining() local tree = root:add(eulora, tvbuf:range(0,pktlen)) dissectPacket(tvbuf:range(0,pktlen):tvb(),pktinfo,tree) return pktlen end DissectorTable.get("udp.port"):add(13331, eulora) Binary file not shown.