signal_protobuf.lua 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. -- try to interpret the data field of all websockets as Signal WebSocketMessage protobufs
  2. do
  3. local websocket_dissector = Dissector.get("websocket")
  4. local protobuf_dissector = Dissector.get("protobuf")
  5. local proto = Proto("signal_protobuf", "Protobufs in Signal WebSockets")
  6. local f_length = ProtoField.uint32("signal_protobuf.length", "Length", base.DEC)
  7. wsField = Field.new("websocket")
  8. wsDataField = Field.new("data.data")
  9. proto.fields = { f_length }
  10. proto.dissector = function(tvb, pinfo, tree)
  11. if wsField() ~= nil then
  12. local dataField = wsDataField()
  13. if dataField ~= nil then
  14. local subtree = tree:add(proto, tvb())
  15. pinfo.private["pb_msg_type"] = "message,signalservice.WebSocketMessage"
  16. local protobufTvb = ByteArray.tvb(dataField.range:tvb():bytes(), "Reassembled protobuf data")
  17. protobuf_dissector:call(protobufTvb, pinfo, tree)
  18. pinfo.columns.protocol:set('signal_protobuf')
  19. end
  20. end
  21. end
  22. DissectorTable.get("tls.port"):add(0, proto)
  23. register_postdissector(proto)
  24. end
  25. -- now that there's a WebSocketMessage protobuf to examine, bind some additional dissectors to its fields and possibly subfields
  26. do
  27. local protobuf_field_table = DissectorTable.get("protobuf_field")
  28. local json_dissector = Dissector.get("json")
  29. local protobuf_dissector = Dissector.get("protobuf")
  30. local signal_body_proto = Proto("signal_body", "parse Signal body fields, as JSON or another protobuf")
  31. local signal_content_proto = Proto("signal_content", "parse Signal content protobuf fields")
  32. jsonMembers = Field.new("json.member_with_value")
  33. signal_content_proto.dissector = function(tvb, pinfo, tree)
  34. local protobufTvb = tvb:bytes(1, tvb:len()-1):tvb("content for protobuf")
  35. pinfo.private["pb_msg_type"] = "message,signal.proto.sealed_sender.UnidentifiedSenderMessage"
  36. protobuf_dissector:call(protobufTvb, pinfo, tree)
  37. pinfo.columns.protocol:set('signal_sealed_sender')
  38. end
  39. signal_body_proto.dissector = function(tvb, pinfo, tree)
  40. -- fixme: replace this with conditional dissectors based on whether there's a "content-type:application/json" header
  41. if tvb:range(0, 2):string() == "{\""
  42. then
  43. -- JSON with high probability
  44. json_dissector:call(tvb, pinfo, tree)
  45. local members = { jsonMembers() }
  46. for k, member in pairs(members) do
  47. if tostring(member):sub(1, 8) == "content:" then
  48. local b_b64 = ByteArray.new(tostring(member):sub(8), true)
  49. local b_decoded = ByteArray.base64_decode(b_b64)
  50. signal_content_proto.dissector(b_decoded:tvb("b64 decoded bytes"), pinfo, tree)
  51. end
  52. end
  53. else
  54. pinfo.private["pb_msg_type"] = "message,textsecure.Envelope"
  55. protobuf_dissector:call(tvb, pinfo, tree)
  56. pinfo.columns.protocol:set('signal_envelope')
  57. end
  58. end
  59. protobuf_field_table:add("signalservice.WebSocketRequestMessage.body", signal_body_proto)
  60. protobuf_field_table:add("signalservice.WebSocketResponseMessage.body", signal_body_proto)
  61. protobuf_field_table:add("textsecure.Envelope.content", signal_content_proto)
  62. end