|
local debug_level = { |
|
DISABLED = 0, |
|
LEVEL_1 = 1, |
|
LEVEL_2 = 2 |
|
} |
|
|
|
local DEBUG = debug_level.LEVEL_1 |
|
|
|
local default_settings = |
|
{ |
|
debug_level = debug_level.LEVEL_2, |
|
enabled = true, -- whether this dissector is enabled or not |
|
port = 9339, -- default TCP port number |
|
max_msg_len = 65535, -- max length of message |
|
-- subdissect = true, -- whether to call sub-dissector or not |
|
-- subdiss_type = wtap.NETLINK, -- the encap we get the subdissector for |
|
} |
|
|
|
SC_HDR_SIZE = 7 |
|
|
|
local dprint = function() end |
|
local dprint2 = function() end |
|
local function resetDebugLevel() |
|
if default_settings.debug_level > debug_level.DISABLED then |
|
dprint = function(...) |
|
print(...) |
|
end |
|
|
|
if default_settings.debug_level > debug_level.LEVEL_1 then |
|
dprint2 = dprint |
|
end |
|
else |
|
dprint = function() end |
|
dprint2 = dprint |
|
end |
|
end |
|
-- call it now |
|
resetDebugLevel() |
|
|
|
|
|
|
|
scdata_protocol = Proto("scdata", "Supercell Protocol - BrawlStars") |
|
|
|
-- original list from https://github.com/clugh/cocdp/wiki/Protocol-Messages |
|
local message_types = { |
|
[ 10100 ] = "ClientHello", |
|
[ 10101 ] = "Login", |
|
[ 10102 ] = "LoginUsingSession", |
|
[ 10103 ] = "CreateAccount", |
|
[ 10107 ] = "ClientCapabilities", |
|
[ 10108 ] = "Ping", |
|
[ 10112 ] = "AuthenticationCheck", |
|
[ 10113 ] = "SetDeviceToken", |
|
[ 10116 ] = "ResetAccount", |
|
[ 10117 ] = "ReportUser", |
|
[ 10118 ] = "AccountSwitched", |
|
[ 10150 ] = "AppleBillingRequest", |
|
[ 10151 ] = "GoogleBillingRequest", |
|
[ 10200 ] = "CreateAvatar", |
|
[ 10201 ] = "SelectAvatar", |
|
[ 10206 ] = "SendChatToAvatar", |
|
[ 10212 ] = "ChangeAvatarName", |
|
[ 10501 ] = "AcceptFriend", |
|
[ 10502 ] = "AddFriend", |
|
[ 10503 ] = "AskForAddableFriends", |
|
[ 10504 ] = "AskForFriendList", |
|
[ 10506 ] = "RemoveFriend", |
|
[ 10507 ] = "AddFriendByEmail", |
|
[ 10509 ] = "AddFriendByAvatarNameAndCode", |
|
[ 10512 ] = "AskForPlayingGamecenterFriends", |
|
[ 10513 ] = "AskForPlayingFacebookFriends", |
|
[ 10599 ] = "xx_GetSuggestedFriends?", |
|
[ 10901 ] = "AskForMailList", |
|
[ 10904 ] = "TakeMailAttachments", |
|
[ 14101 ] = "AttackResult", |
|
[ 14102 ] = "EndClientTurn", |
|
[ 14104 ] = "AskForTargetHomeList", |
|
[ 14106 ] = "AttackHome", |
|
[ 14108 ] = "ChangeHomeName", |
|
[ 14113 ] = "VisitHome", |
|
[ 14114 ] = "HomeBattleReplay", |
|
[ 14123 ] = "AttackMatchedHome", |
|
[ 14134 ] = "AttackNpc", |
|
[ 14201 ] = "BindFacebookAccount", |
|
[ 14211 ] = "UnbindFacebookAccount", |
|
[ 14212 ] = "BindGamecenterAccount", |
|
[ 14262 ] = "BindGoogleServiceAccount", |
|
[ 14301 ] = "CreateAlliance", |
|
[ 14302 ] = "AskForAllianceData", |
|
[ 14303 ] = "AskForJoinableAlliancesList", |
|
[ 14305 ] = "JoinAlliance", |
|
[ 14306 ] = "ChangeAllianceMemberRole", |
|
[ 14307 ] = "KickAllianceMember", |
|
[ 14308 ] = "LeaveAlliance", |
|
[ 14309 ] = "AskForAllianceUnitDonations", |
|
[ 14310 ] = "DonateAllianceUnit", |
|
[ 14315 ] = "ChatToAllianceStream", |
|
[ 14316 ] = "ChangeAllianceSettings", |
|
[ 14317 ] = "RequestJoinAlliance", |
|
[ 14321 ] = "RespondToAllianceJoinRequest", |
|
[ 14322 ] = "SendAllianceInvitation", |
|
[ 14323 ] = "JoinAllianceUsingInvitation", |
|
[ 14324 ] = "SearchAlliances", |
|
[ 14325 ] = "AskForAvatarProfile", |
|
[ 14330 ] = "SendAllianceMail", |
|
[ 14331 ] = "HomeShareReplay", |
|
[ 14401 ] = "AskForAllianceRankingList", |
|
[ 14403 ] = "AskForAvatarRankingList", |
|
[ 14404 ] = "AskForAvatarLocalRankingList", |
|
[ 14405 ] = "AskForAvatarStream", |
|
[ 14418 ] = "RemoveAvatarStreamEntry", |
|
[ 14503 ] = "AskForLeagueMemberList", |
|
[ 14715 ] = "SendGlobalChatLine", |
|
[ 16000 ] = "LogicDeviceLinkCodeRequest", |
|
[ 16001 ] = "LogicDeviceLinkMenuClosed", |
|
[ 16002 ] = "LogicDeviceLinkEnterCode", |
|
[ 16003 ] = "LogicDeviceLinkConfirmYes", |
|
[ 20000 ] = "Encryption", |
|
[ 20100 ] = "ServerHello", |
|
[ 20101 ] = "CreateAccountResult", |
|
[ 20103 ] = "LoginFailed", |
|
[ 20104 ] = "LoginOk", |
|
[ 20105 ] = "FriendList", |
|
[ 20106 ] = "FriendListUpdate", |
|
[ 20107 ] = "AddableFriends", |
|
[ 20108 ] = "Pong", |
|
[ 20109 ] = "FriendOnlineStatus", |
|
[ 20110 ] = "FriendLoggedIn", |
|
[ 20111 ] = "FriendLoggedOut", |
|
[ 20117 ] = "ReportUserStatus", |
|
[ 20118 ] = "ChatAccountBanStatus", |
|
[ 20121 ] = "BillingRequestFailed", |
|
[ 20151 ] = "AppleBillingProcessedByServer", |
|
[ 20152 ] = "GoogleBillingProcessedByServer", |
|
[ 20161 ] = "ShutdownStarted", |
|
[ 20171 ] = "PersonalBreakStarted", |
|
[ 20199 ] = "xx_FriendSuggestions?", |
|
[ 20201 ] = "AvatarData", |
|
[ 20202 ] = "CreateAvatarFailed", |
|
[ 20203 ] = "CreateAvatarOk", |
|
[ 20205 ] = "AvatarNameChangeFailed", |
|
[ 20801 ] = "Notification", |
|
[ 20903 ] = "MailList", |
|
[ 24101 ] = "OwnHomeData", |
|
[ 24103 ] = "AttackHomeFailed", |
|
[ 24104 ] = "OutOfSync", |
|
[ 24105 ] = "TargetHomeList", |
|
[ 24106 ] = "AttackReportList", |
|
[ 24107 ] = "EnemyHomeData", |
|
[ 24109 ] = "HomeStatusList", |
|
[ 24111 ] = "AvailableServerCommand", |
|
[ 24112 ] = "WaitingToGoHome", |
|
[ 24113 ] = "VisitedHomeData", |
|
[ 24114 ] = "HomeBattleReplayData", |
|
[ 24115 ] = "ServerError", |
|
[ 24116 ] = "HomeBattleReplayFailed", |
|
[ 24133 ] = "NpcData", |
|
[ 24201 ] = "FacebookAccountBound", |
|
[ 24202 ] = "FacebookAccountAlreadyBound", |
|
[ 24211 ] = "GamecenterAccountBound", |
|
[ 24212 ] = "GamecenterAccountAlreadyBound", |
|
[ 24214 ] = "FacebookAccountUnbound", |
|
[ 24261 ] = "GoogleServiceAccountBound", |
|
[ 24262 ] = "GoogleServiceAccountAlreadyBound", |
|
[ 24301 ] = "AllianceData", |
|
[ 24302 ] = "AllianceJoinFailed", |
|
[ 24303 ] = "AllianceJoinOk", |
|
[ 24304 ] = "JoinableAllianceList", |
|
[ 24305 ] = "AllianceLeaveOk", |
|
[ 24306 ] = "ChangeAllianceMemberRoleOk", |
|
[ 24307 ] = "KickAllianceMemberOk", |
|
[ 24308 ] = "AllianceMember", |
|
[ 24309 ] = "AllianceMemberRemoved", |
|
[ 24310 ] = "AllianceList", |
|
[ 24311 ] = "AllianceStream", |
|
[ 24312 ] = "AllianceStreamEntry", |
|
[ 24318 ] = "AllianceStreamEntryRemoved", |
|
[ 24319 ] = "AllianceJoinRequestOk", |
|
[ 24320 ] = "AllianceJoinRequestFailed", |
|
[ 24321 ] = "AllianceInvitationSendFailed", |
|
[ 24322 ] = "AllianceInvitationSentOk", |
|
-- [ 24324 ] = "", |
|
[ 24333 ] = "AllianceChangeFailed", |
|
[ 24334 ] = "AvatarProfile", |
|
-- [ 24335 ] = "", |
|
-- [ 24402 ] = "", |
|
[ 24404 ] = "AvatarLocalRankingList", |
|
[ 24411 ] = "AvatarStream", |
|
[ 24412 ] = "AvatarStreamEntry", |
|
[ 24418 ] = "AvatarStreamEntryRemoved", |
|
[ 24555 ] = "xx_FriendPresence?", |
|
[ 24503 ] = "LeagueMemberList", |
|
[ 24715 ] = "GlobalChatLine", |
|
[ 25892 ] = "Disconnected", |
|
[ 26002 ] = "LogicDeviceLinkCodeResponse", |
|
[ 26003 ] = "LogicDeviceLinkNewDeviceLinked", |
|
[ 26004 ] = "LogicDeviceLinkCodeDeactivated", |
|
[ 26005 ] = "LogicDeviceLinkResponse", |
|
[ 26007 ] = "LogicDeviceLinkDone", |
|
[ 26008 ] = "LogicDeviceLinkError" |
|
} |
|
|
|
-- Header fields |
|
-- packet_type = ProtoField.uint16("scdata.type", "Packet Type", base.DEC) |
|
packet_type = ProtoField.new("Packet Type", "scdata.type", ftypes.UINT16, message_types, base.DEC) |
|
packet_size = ProtoField.uint24("scdata.size", "Packet Size", base.DEC) |
|
packet_version = ProtoField.uint16("scdata.version", "Packet Version", base.DEC) |
|
|
|
-- Payload fields |
|
packet_data = ProtoField.bytes("scdata.data", "Packet Data") |
|
|
|
scdata_protocol.fields = { |
|
packet_type, |
|
packet_size, |
|
packet_version, |
|
packet_data |
|
} |
|
|
|
|
|
function scdata_protocol.dissector(tvbuf, pktinfo, root) |
|
local pktlen = tvbuf:len() |
|
local bytes_consumed = 0 |
|
|
|
while bytes_consumed < pktlen do |
|
|
|
local pdu_bytes_consumed = scdata_protocol_pdu_dissect(tvbuf, pktinfo, root, bytes_consumed) |
|
|
|
if pdu_bytes_consumed > 0 then |
|
-- a pdu was successfully parsed |
|
bytes_consumed = bytes_consumed + pdu_bytes_consumed |
|
elseif pdu_bytes_consumed == 0 then |
|
-- error parsing the pdu, ignore the rest of the packet |
|
return 0 |
|
else |
|
-- need more bytes to parse the pdu |
|
-- set the desgment_offset to what we consumed so far |
|
-- and desgment_len to how many bytes we need |
|
|
|
pktinfo.desegment_offset = bytes_consumed |
|
pdu_bytes_consumed = -pdu_bytes_consumed |
|
pktinfo.desegment_len = pdu_bytes_consumed |
|
|
|
return pktlen |
|
end |
|
end |
|
|
|
return bytes_consumed |
|
end |
|
|
|
|
|
|
|
function get_message_size(tvbuf) |
|
local size = tvbuf(2, 3):uint() |
|
return size |
|
end |
|
|
|
-- The following function is used to dissect a PDU. |
|
-- If the return is positive, it means that the PDU was dissected and consumed the return number of bytes |
|
-- If the return is negative, it means it needs the return number of bytes to dissect the PDU |
|
-- If the return is 0, it means it cannot dissect the PDU and the message should be ignored. |
|
|
|
function scdata_protocol_pdu_dissect(tvbuf, pktinfo, root, offset) |
|
|
|
local msglen = tvbuf:len() - offset |
|
|
|
if msglen ~= tvbuf:reported_length_remaining(offset) then |
|
dprint2("[*] SCDATA: Captured packet was shorter than original, can't reassmble") |
|
return 0 |
|
end |
|
|
|
if msglen < SC_HDR_SIZE then |
|
dprint2("[*] SCDATA: Need more bytes to parse the header") |
|
return -DESEGMENT_ONE_MORE_SEGMENT |
|
end |
|
|
|
local pdulen = tvbuf:range(offset + 2, 3):uint() |
|
|
|
if (pdulen + SC_HDR_SIZE) > msglen then |
|
dprint2("[*] SCDATA: Need more bytes to parse the packet") |
|
return msglen - (pdulen + SC_HDR_SIZE) |
|
end |
|
|
|
-- We have enough bytes to parse a pdu |
|
local subroot = root:add(scdata_protocol, tvbuf:range(offset, pdulen + SC_HDR_SIZE)) |
|
subroot:add(packet_type, tvbuf(offset, 2)) |
|
subroot:add(packet_size, tvbuf(offset + 2, 3)) |
|
subroot:add(packet_version, tvbuf(offset + 5, 2)) |
|
subroot:add(packet_data, tvbuf(offset + 7, pdulen)) |
|
|
|
return pdulen + SC_HDR_SIZE |
|
end |
|
|
|
|
|
|
|
local function enableDissector() |
|
-- using DissectorTable:set() removes existing dissector(s), whereas the |
|
-- DissectorTable:add() one adds ours before any existing ones, but |
|
-- leaves the other ones alone, which is better |
|
DissectorTable.get("tcp.port"):add(default_settings.port, scdata_protocol) |
|
end |
|
-- call it now, because we're enabled by default |
|
enableDissector() |
|
|
|
local function disableDissector() |
|
DissectorTable.get("tcp.port"):remove(default_settings.port, scdata_protocol) |
|
end |
|
|
|
local debug_pref_enum = { |
|
{ 1, "Disabled", debug_level.DISABLED }, |
|
{ 2, "Level 1", debug_level.LEVEL_1 }, |
|
{ 3, "Level 2", debug_level.LEVEL_2 }, |
|
} |
|
|
|
scdata_protocol.prefs.enabled = Pref.bool("Enabled", default_settings.enabled, "Enable the scdata protocol dissector") |
|
scdata_protocol.prefs.debug = Pref.enum("Debug", default_settings.debug, "Enable debug messages", debug_pref_enum) |
|
|
|
function scdata_protocol.prefs_changed() |
|
dprint2("scdata_protocol.prefs_changed") |
|
|
|
default_settings.debug_level = scdata_protocol.prefs.debug |
|
resetDebugLevel() |
|
|
|
if default_settings.enabled ~= scdata_protocol.prefs.enabled then |
|
default_settings.enabled = scdata_protocol.prefs.enabled |
|
if sdefault_settings.enabled then |
|
enableDissector() |
|
else |
|
disableDissector() |
|
end |
|
|
|
reload() |
|
end |
|
end |
|
|
|
dprint2("SCDATA: Loaded") |