Skip to content

Instantly share code, notes, and snippets.

@sprout42
Last active February 27, 2020 17:48
Show Gist options
  • Save sprout42/64e942d6a4cc85b1af2a5c624420da1f to your computer and use it in GitHub Desktop.
Save sprout42/64e942d6a4cc85b1af2a5c624420da1f to your computer and use it in GitHub Desktop.
GreatFET USB messages dissector
-- GreatFET USB protocol decoder
-- Run this to dump the classes and verbs in your greatfet firmare:
--
-- print('greatfet_classes = {')
-- gf_classes = sorted(gf.apis.core.get_available_classes())
-- for c in gf_classes:
-- print(' [%#06x] = {' % (c))
-- class_name = gf.apis.core.get_class_name(c)
-- print(' ["name"] = "%s", ' % (class_name))
-- verbs = sorted(gf.apis.core.get_available_verbs(c))
-- print(' ["verbs"] = {')
-- for v in verbs:
-- verb_name = gf.apis.core.get_verb_name(c, v)
-- if len(verb_name):
-- # gf.apis.core.get_verb_in_signature(c, v)
-- # gf.apis.core.get_verb_in_param_names(c, v)
-- # gf.apis.core.get_verb_out_signature(c, v)
-- # gf.apis.core.get_verb_out_param_names(c, v)
-- print(' [%#06x] = "%s", ' % (v, verb_name))
-- print(' }, ')
-- print(' }, ')
-- print('}')
-- signatures:
-- https://github.com/greatscottgadgets/greatfet/wiki/LibGreat-Verb-Signatures
--
local greatfet_classes = {
[0x0000] = {
["name"] = "core",
["verbs"] = {
[0x0000] = "read_board_id",
[0x0001] = "read_version_string",
[0x0002] = "read_part_id",
[0x0003] = "read_serial_number",
[0x0004] = "get_available_classes",
[0x0005] = "get_avaiable_verbs",
[0x0006] = "get_verb_name",
[0x0007] = "get_verb_descriptor",
[0x0008] = "get_class_name",
[0x0009] = "get_class_docs",
}
},
[0x0001] = {
["name"] = "firmware",
["verbs"] = {
[0x0000] = "initialize",
[0x0001] = "full_erase",
[0x0002] = "page_erase",
[0x0003] = "write_page",
[0x0004] = "read_page",
}
},
[0x0010] = {
["name"] = "debug",
["verbs"] = {
[0x0000] = "read_dmesg",
[0x0001] = "clear_dmesg",
[0x0002] = "peek",
[0x0003] = "poke",
}
},
[0x0011] = {
["name"] = "selftest",
["verbs"] = {
[0x0000] = "measure_clock_frequencies",
[0x0001] = "measure_raw_clock",
}
},
[0x0100] = {
["name"] = "example",
["verbs"] = {
[0x0000] = "sum_and_difference",
[0x0001] = "capitalize",
}
},
[0x0101] = {
["name"] = "spi_flash",
["verbs"] = {
[0x0000] = "initialize",
[0x0001] = "full_erase",
[0x0002] = "write_page",
[0x0003] = "read_page",
[0x0004] = "query_device_id",
[0x0005] = "query_topology",
}
},
[0x0102] = {
["name"] = "heartbeat",
["verbs"] = {
[0x0000] = "stop",
[0x0001] = "start",
[0x0002] = "set_period",
[0x0003] = "get_period",
}
},
[0x0103] = {
["name"] = "gpio",
["verbs"] = {
[0x0000] = "set_up_pin",
[0x0001] = "release_pin",
[0x0002] = "get_pin_directions",
[0x0003] = "read_pins",
[0x0004] = "write_pins",
}
},
[0x0104] = {
["name"] = "greatdancer",
["verbs"] = {
[0x0000] = "connect",
[0x0001] = "disconnect",
[0x0002] = "bus_reset",
[0x0003] = "set_address",
[0x0004] = "set_up_endpoints",
[0x0005] = "get_status",
[0x0006] = "read_setup",
[0x0007] = "stall_endpoint",
[0x0008] = "send_on_endpoint",
[0x0009] = "clean_up_transfer",
[0x000a] = "start_nonblocking_read",
[0x000b] = "finish_nonblocking_read",
[0x000c] = "get_nonblocking_data_length",
}
},
[0x0105] = {
["name"] = "dac",
["verbs"] = {
[0x0000] = "initialize",
[0x0001] = "set_value",
[0x0002] = "set_voltage",
}
},
[0x0106] = {
["name"] = "glitchkit",
["verbs"] = {
[0x0000] = "set_synchronization_events",
[0x0001] = "set_trigger_events",
[0x0002] = "add_trigger_events",
[0x0003] = "provide_target_clock",
}
},
[0x0107] = {
["name"] = "glitchkit_usb",
["verbs"] = {
[0x0000] = "configure_requests",
[0x0001] = "control_in",
}
},
[0x0108] = {
["name"] = "i2c",
["verbs"] = {
[0x0000] = "start",
[0x0001] = "stop",
[0x0002] = "read",
[0x0003] = "write",
[0x0004] = "scan",
[0x0005] = "issue_start",
[0x0006] = "issue_stop",
[0x0007] = "issue_bytes",
[0x0008] = "read_bytes",
[0x0009] = "stream_periodic_read",
[0x000a] = "stop_periodic_read",
}
},
[0x0109] = {
["name"] = "spi",
["verbs"] = {
[0x0000] = "init",
[0x0001] = "set_clock_polarity_and_phase",
[0x0002] = "transmit",
[0x0003] = "clock_data",
[0x0004] = "enable_drive",
}
},
[0x010a] = {
["name"] = "leds",
["verbs"] = {
[0x0000] = "toggle",
[0x0001] = "on",
[0x0002] = "off",
}
},
[0x010b] = {
["name"] = "jtag",
["verbs"] = {
[0x0000] = "configure",
[0x0001] = "scan",
[0x0002] = "scan_out",
[0x0003] = "scan_in",
[0x0004] = "run_clock",
}
},
[0x010c] = {
["name"] = "jtag_msp430",
["verbs"] = {
[0x0000] = "start",
[0x0001] = "stop",
[0x0002] = "halt_cpu",
[0x0003] = "release_cpu",
[0x0004] = "set_instruction_fetch",
[0x0005] = "read_mem",
[0x0006] = "write_mem",
[0x0007] = "write_flash",
[0x0008] = "erase_flash",
[0x0009] = "erase_info",
[0x000a] = "set_pc",
[0x000b] = "set_reg",
}
},
[0x010d] = {
["name"] = "logic_analyzer",
["verbs"] = {
[0x0000] = "configure",
[0x0001] = "change_first_pin",
[0x0002] = "configure_alt_mappings",
[0x0003] = "start",
[0x0004] = "stop",
[0x0005] = "dump_sgpio_configuration",
}
},
[0x010e] = {
["name"] = "sdir",
["verbs"] = {
[0x0000] = "start_receive",
[0x0001] = "stop",
[0x0002] = "dac_register_read",
[0x0003] = "dac_register_write",
}
},
[0x0110] = {
["name"] = "pattern_generator",
["verbs"] = {
[0x0000] = "stop",
[0x0001] = "generate_simple_pattern",
[0x0002] = "upload_samples",
[0x0003] = "generate_pattern",
[0x0004] = "dump_sgpio_configuration",
}
},
[0x0111] = {
["name"] = "adc",
["verbs"] = {
[0x0000] = "read_samples",
[0x0001] = "stream_periodic_read",
[0x0002] = "stop_periodic_read",
}
},
[0x0112] = {
["name"] = "uart",
["verbs"] = {
[0x0000] = "initialize",
[0x0001] = "synchronous_transmit",
[0x0002] = "read",
}
},
[0x0114] = {
["name"] = "swra124",
["verbs"] = {
[0x0000] = "setup",
[0x0001] = "debug_init",
[0x0002] = "read_status",
[0x0003] = "get_chip_id",
[0x0004] = "halt",
[0x0005] = "resume",
[0x0006] = "debug_instr",
[0x0007] = "step_instr",
[0x0008] = "get_pc",
}
},
[0x0115] = {
["name"] = "loadables",
["verbs"] = {
[0x0000] = "load_m0_page",
[0x0001] = "start_m0",
[0x0002] = "halt_m0",
}
},
[0x0116] = {
["name"] = "clock_gen",
["verbs"] = {
[0x0000] = "output_clock",
}
}
}
-- Commands to the GreatFET have the values:
--
-- usb.src: host
-- usb.dst: *
-- usb.urb_type: URB_SUBMIT ('S' / 0x53)
-- usb.transfer_type: URB_CONTROL (0x02)
-- usb.endpoint_address: 0x00
-- usb.endpoint_address.direction: OUT (0)
-- usb.endpoint_address.number: 0 (Endpoint 0)
-- usb.setup_flag: relevant (0)
-- usb.data_flag: present (0)
-- usb.urb_status: -EINPROGRESS (-115)
-- usb.urb_len: 9
-- usb.data_len: 9
-- usb.bmRequestType: 0x42
-- usb.bmRequestType.direction: 0x0 (Host-to-device)
-- usb.bmRequestType.type: 0x2 (Vendor)
-- usb.bmRequestType.recipient: 0x02 (Endpoint 0x02)
-- usb.setup.bRequest: 0x65 (101 or LIBGREAT_REQUEST_NUMBER)
-- usb.setup.wValue: 0x0000
-- usb.setup.wIndex: 0x0000
-- usb.setup.wLength: 0x0009
-- usb.data_fragment: 04010000 05000000 00
--
-- This will be acked:
-- usb.src: *
-- usb.dst: host
-- usb.urb_type: URB_COMPLETE ('C' / 0x43)
-- usb.transfer_type: URB_CONTROL (0x02)
-- usb.endpoint_address: 0x00
-- usb.endpoint_address.direction: OUT (0)
-- usb.endpoint_address.number: 0 (Endpoint 0)
-- usb.setup_flag: not relevant ('-')
-- usb.data_flag: not present ('>')
-- usb.urb_status: Success (0)
-- usb.urb_len: 9
-- usb.data_len: 0
--
-- Then the host will check on the status of the command:
-- usb.src: host
-- usb.dst: *
-- usb.urb_type: URB_SUBMIT ('S' / 0x53)
-- usb.transfer_type: URB_CONTROL (0x02)
-- usb.endpoint_address: 0x80
-- usb.endpoint_address.direction: IN (1)
-- usb.endpoint_address.number: 0 (Endpoint 0)
-- usb.setup_flag: not relevant ('-')
-- usb.data_flag: not present ('<')
-- usb.urb_status: -EINPROGRESS (-115)
-- usb.urb_len: 4096 (max response length)
-- usb.data_len: 0
-- usb.bmRequestType: 0xc2
-- usb.bmRequestType.direction: 1 (Device-to-host)
-- usb.bmRequestType.type: 2 (Vendor)
-- usb.bmRequestType.recipient: 2 (Endpoint 0x02)
-- usb.setup.bRequest: 0x65 (101 or LIBGREAT_REQUEST_NUMBER)
-- usb.setup.wValue: 0x0000
-- usb.setup.wIndex: 0x0000
-- usb.setup.wLength: 0x0010 (4096)
--
-- The command response:
-- usb.src: *
-- usb.dst: host
-- usb.urb_type: URB_COMPLETE ('C' / 0x43)
-- usb.transfer_type: URB_CONTROL (0x02)
-- usb.endpoint_address: 0x80
-- usb.endpoint_address.direction: IN (1)
-- usb.endpoint_address.number: 0 (Endpoint 0)
-- usb.setup_flag: relevant (0)
-- usb.data_flag: present (0)
-- usb.urb_status: Success (0)
-- usb.urb_len: 4
-- usb.data_len: 4
-- usb.control.response 00000000
-- request/response flag
-- if wIndex == 0 this is a normal request/response command
-- if wIndex == 1 this indicates no response is needed
-- if wIndex == 2 this is a repeat of the previous command
greatfet_msg_type = {
NONE = 0,
REQUEST = 1,
REQUEST_ACK = 4,
GET_RESPONSE = 6,
RESPONSE = 7,
}
local greatfet_msg_type_desc = {
[0] = "NONE",
[1] = "REQUEST",
[2] = "REQUEST",
[3] = "REQUEST",
[4] = "REQUEST_ACK",
[5] = "ERROR",
[6] = "GET_RESPONSE",
[7] = "RESPONSE",
}
greatfet_proto = Proto("greatfet", "GreatFet USB")
greatfet_msg_type_F = ProtoField.uint8("greatfet.msg_type", "Message Type", base.DEC, greatfet_msg_type_desc)
greatfet_flags_F = ProtoField.uint8("greatfet.flags", "Flags", base.DEC)
greatfet_class_F = ProtoField.uint32("greatfet.class", "Class", base.HEX)
greatfet_verb_F = ProtoField.uint32("greatfet.verb", "Verb", base.HEX)
greatfet_data_F = ProtoField.bytes("greatfet.data", "Data")
greatfet_proto.fields = {
greatfet_msg_type_F,
greatfet_flags_F,
greatfet_class_F,
greatfet_verb_F,
greatfet_data_F,
}
-- Create all of the command-specific sub-protocols
-- for k, v in pairs(greatfet_classes) do
-- greatfet_classes[k]["proto"] = = Proto(v["name"], string.format("GreatFet %s Subcommand", v["name"]))
-- end
-- declare some Fields to be read
usb_src_f = Field.new("usb")
usb_urb_type_f = Field.new("usb.urb_type")
usb_txfr_type_f = Field.new("usb.transfer_type")
usb_addr_f = Field.new("usb.endpoint_address")
usb_urb_status_f = Field.new("usb.urb_status")
usb_data_len_f = Field.new("usb.data_len")
usb_setup_req_f = Field.new("usb.setup.bRequest")
usb_setup_idx_f = Field.new("usb.setup.wIndex")
usb_data_f = Field.new("usb.data_fragment")
usb_resp_f = Field.new("usb.control.Response")
--function get_greatfet_msg_type(buffer, pinfo, tree)
local function get_greatfet_msg_type(pinfo)
local urb_type = usb_urb_type_f()
local txfr_type = usb_txfr_type_f().value
local ep_addr = usb_addr_f().value
local data_len = usb_data_len_f().value
if usb_setup_req_f() then
local req_val = usb_setup_req_f().value
else
local req_val = 0x00
end
-- BUG IN WIRESHARK: can't use FT_CHAR in lua scripts, so we have to first
-- convert this to a string
if tostring(urb_type) == "'S'" and -- ('S') URB_SUBMIT
txfr_type == 0x02 and -- URB_CONTROL
ep_addr == 0x00 and -- OUT / ENDPOINT 0
req_val == 0x65 then -- LIBGREAT_REQUEST_NUMBER
return greatfet_msg_type.REQUEST
elseif tostring(urb_type) == "'C'" and -- ('C') URB_COMPLETE
txfr_type == 0x02 and -- URB_CONTROL
ep_addr == 0x00 then -- OUT / ENDPOINT 0
return greatfet_msg_type.REQUEST_ACK
elseif tostring(urb_type) == "'S'" and -- ('S') URB_SUBMIT
txfr_type == 0x02 and -- URB_CONTROL
ep_addr == 0x80 and -- IN / ENDPOINT 0
req_val == 0x65 then -- LIBGREAT_REQUEST_NUMBER
return greatfet_msg_type.GET_RESPONSE
elseif tostring(urb_type) == "'C'" and -- ('C') URB_COMPLETE
txfr_type == 0x02 and -- URB_CONTROL
ep_addr == 0x80 then -- IN / ENDPOINT 0
return greatfet_msg_type.RESPONSE
end
return greatfet_msg_type.NONE
end
local greatfet_decode_enable = false
function greatfet_proto.dissector(buffer, pinfo, tree)
local usb_src = usb_src_f()
if usb_src then
local gf_msg_type = get_greatfet_msg_type(pinfo)
-- Only enable the plugin after we see a valid REQUEST
if gf_msg_type == greatfet_msg_type.REQUEST then
greatfet_decode_enable = true
end
if greatfet_decode_enable and gf_msg_type ~= greatfet_msg_type.NONE then
pinfo.cols.protocol = "GreatFET"
local subtree = tree:add(greatfet_proto, buffer(), "GreatFET USB Command")
-- The bytes that make up the message type are the usb.urb_type,
-- control and endpoint address bytes
subtree:add(greatfet_msg_type_F, buffer(usb_urb_type_f().offset, 3), gf_msg_type)
-- The command specific information is added below
if gf_msg_type == greatfet_msg_type.REQUEST then
local flags = usb_setup_idx_f().value
subtree:add_le(greatfet_flags_F, buffer(usb_setup_idx_f().offset, 1), flags)
local cmd_data_len = usb_data_len_f().value
local cmd_data = usb_data_f()
local cmd_class = nil
local cmd_verb = nil
if cmd_data_len >= 4 then
cmd_class = cmd_data.range(0, 4):le_int()
subtree:add_le(greatfet_class_F, buffer(cmd_data.offset, 4), cmd_class)
class_str = greatfet_classes[cmd_class]["name"]
end
if cmd_data_len >= 4 then
cmd_verb = cmd_data.range(4, 4):le_int()
subtree:add_le(greatfet_verb_F, buffer(cmd_data.offset+4, 4), cmd_verb)
verb_str = greatfet_classes[cmd_class]["verbs"][cmd_verb]
end
if cmd_data_len > 8 then
pinfo.cols.packet_len = cmd_data_len-8
local cmd_params = cmd_data.range(8, cmd_data_len-8)
--subtree:add(greatfet_data_F, buffer(cmd_data.offset+8, cmd_data_len-8), cmd_params)
subtree:add(greatfet_data_F, cmd_params)
if flags == 1 then
pinfo.cols.info = string.format("Request (no resp): %s.%s(%s)", class_str, verb_str, cmd_params)
else
pinfo.cols.info = string.format("Request: %s.%s(%s)", class_str, verb_str, cmd_params)
end
else
pinfo.cols.packet_len = 0
if flags == 1 then
pinfo.cols.info = string.format("Request (no resp): %s.%s()", class_str, verb_str)
else
pinfo.cols.info = string.format("Request: %s.%s()", class_str, verb_str)
end
end
elseif gf_msg_type == greatfet_msg_type.REQUEST_ACK then
pinfo.cols.packet_len = 0
pinfo.cols.info = "ACK"
elseif gf_msg_type == greatfet_msg_type.GET_RESPONSE then
pinfo.cols.packet_len = 0
local flags = usb_setup_idx_f().value
subtree:add_le(greatfet_flags_F, buffer(usb_setup_idx_f().offset, 1), flags)
if flags == 2 then
pinfo.cols.info = "GetResponse (repeat)"
else
pinfo.cols.info = "GetResponse"
end
elseif gf_msg_type == greatfet_msg_type.RESPONSE then
local resp_data = usb_resp_f()
if resp_data then
--local data_len = usb_data_len_f().value
--subtree:add(greatfet_data_F, buffer(resp_data.offset, data_len), resp_data.range())
subtree:add(greatfet_data_F, resp_data.range())
pinfo.cols.packet_len = resp_data.range():len()
pinfo.cols.info = string.format("Response: %s", resp_data.range())
else
pinfo.cols.packet_len = 0
--pinfo.cols.info = string.format("Response: %s", usb_urb_status_f())
pinfo.cols.info = "Response: (empty)"
end
end
end
end
end
-- register our protocol as a postdissector
register_postdissector(greatfet_proto)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment