Last active
February 27, 2020 17:48
-
-
Save sprout42/64e942d6a4cc85b1af2a5c624420da1f to your computer and use it in GitHub Desktop.
GreatFET USB messages dissector
This file contains hidden or 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 characters
-- 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