CTF 中的USB流量分析常见为鼠标流量和键盘流量

键盘流量

一般为8字节16位,其中的第三字节(第5-6个数字)决定键盘输入的字符

对照表(P53-59)

部分数据解释

  • 字节下标(我还没发现这个在哪儿)

    • 0 : 修改键(组合键)
    • 1 : OEM 保留
    • 2~7 : 按键码
  • BYTE1

    • bit0: Left Control 是否按下,按下为 1
    • bit1: Left Shift 是否按下,按下为 1
    • bit2: Left Alt 是否按下,按下为 1
    • bit3: Left WIN/GUI 是否按下,按下为 1
    • bit4: Right Control 是否按下,按下为 1
    • bit5: Right Shift 是否按下,按下为 1
    • bit6: Right Alt 是否按下,按下为 1
    • bit7: Right WIN/GUI 是否按下,按下为 1
  • BYTE2 - 暂不清楚,有的地方说是保留位

  • BYTE3-BYTE8 - 这六个为普通按键

  • 0b10(0x02) 和 0b100000(0x20)都是按下了shift键

    键盘发送 02 00 0e 00 00 00 00 00,表示同时按下了 Left Shift + ‘k’,即大写 K。

导出键盘流量

.\tshark.exe -r usb.pcapng -T fields -e usbhid.data  > keyboard.txt

# 可选参数
# 筛选指定长度导出
# -Y "usb.device_address == 16"
#
# 去除空行
# | sed '/^\s*$/d'
#
# 参数讲解
# -r:设置tshark分析的输入文件
# -T:设置解码结果输出的格式,包括fileds,text,ps,psml和pdml,默认为text
# -e:导出协议字段
# -Y:对应过滤器的过滤条件

导出后应该是每行为16个字符的文本,使用脚本将他们转化为字符

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
def parse_usb_data(file_path):
normal_keys = {"04": "a", "05": "b", "06": "c", "07": "d", "08": "e", "09": "f", "0a": "g", "0b": "h", "0c": "i", "0d": "j", "0e": "k", "0f": "l", "10": "m", "11": "n", "12": "o", "13": "p", "14": "q", "15": "r", "16": "s", "17": "t", "18": "u", "19": "v", "1a": "w", "1b": "x", "1c": "y", "1d": "z", "1e": "1", "1f": "2", "20": "3", "21": "4", "22": "5", "23": "6", "24": "7", "25": "8", "26": "9", "27": "0", "28": "<RET>", "29": "<ESC>", "2a": "<DEL>", "2b": "\t", "2c": "<SPACE>", "2d": "-", "2e": "=", "2f": "[", "30": "]", "31": "\\", "32": "<NON>", "33": ";", "34": "'", "35": "<GA>", "36": ",", "37": ".", "38": "/", "39": "<CAP>", "3a": "<F1>", "3b": "<F2>", "3c": "<F3>", "3d": "<F4>", "3e": "<F5>", "3f": "<F6>", "40": "<F7>", "41": "<F8>", "42": "<F9>", "43": "<F10>", "44": "<F11>", "45": "<F12>"}
shift_keys = {"04": "A", "05": "B", "06": "C", "07": "D", "08": "E", "09": "F", "0a": "G", "0b": "H", "0c": "I", "0d": "J", "0e": "K", "0f": "L", "10": "M", "11": "N", "12": "O", "13": "P", "14": "Q", "15": "R", "16": "S", "17": "T", "18": "U", "19": "V", "1a": "W", "1b": "X", "1c": "Y", "1d": "Z", "1e": "!", "1f": "@", "20": "#", "21": "$", "22": "%", "23": "^", "24": "&", "25": "*", "26": "(", "27": ")", "28": "<RET>", "29": "<ESC>", "2a": "<DEL>", "2b": "\t", "2c": "<SPACE>", "2d": "_", "2e": "+", "2f": "{", "30": "}", "31": "|", "32": "<NON>", "33": "\"", "34": ":", "35": "<GA>", "36": "<", "37": ">", "38": "?", "39": "<CAP>", "3a": "<F1>", "3b": "<F2>", "3c": "<F3>", "3d": "<F4>", "3e": "<F5>", "3f": "<F6>", "40": "<F7>", "41": "<F8>", "42": "<F9>", "43": "<F10>", "44": "<F11>", "45": "<F12>"}
output = []

try:
with open(file_path, 'r') as keys_file:
lines = (line.strip() for line in keys_file) # 去除行末可能存在的换行符或空格
for line in lines:
if not line or len(line) < 23: # 忽略空行和不完整行
continue
try:
if line[0] != '0' or (line[1] != '0' and line[1] != '2') or line[3] != '0' or line[4] != '0' or line[9] != '0' or line[10] != '0' or line[12] != '0' or line[13] != '0' or line[15] != '0' or line[16] != '0' or line[18] != '0' or line[19] != '0' or line[21] != '0' or line[22] != '0' or line[6:8] == "00":
continue
if line[6:8] in normal_keys.keys():
output += [normal_keys[line[6:8]]] if line[1] == '0' else [shift_keys[line[6:8]]]
else:
output += ['[unknown]']
except:
pass
except FileNotFoundError:
print("File not found!")
return []

flag = 0
for i in range(len(output)):
try:
a = output.index('<DEL>')
del output[a]
del output[a - 1]
except:
pass
for i in range(len(output)):
try:
if output[i] == "<CAP>":
flag += 1
output.pop(i)
if flag == 2:
flag = 0
if flag != 0:
output[i] = output[i].upper()
except:
pass
return "".join(output)


def add_fenhao(file_path):
output = [] # 用于存储处理后的内容

# 读取文件内容,处理每一行数据
with open(file_path, 'r') as file:
for line in file:
line = line.strip() # 去除行首行尾的空格和换行符
if re.match('[a-fA-F0-9]{16}', line):
formatted_line = ':'.join(line[i:i+2] for i in range(0, len(line), 2))
output.append(formatted_line)
elif re.match('\S{2}:\S{2}:\S{2}:\S{2}:\S{2}:\S{2}:\S{2}:\S{2}', line):
output.append(line)
else:
pass
# 将处理后的内容写回原文件
with open(file_path, 'w') as file:
file.write('\n'.join(output))
return file_path



if __name__ == "__main__":
file_path = add_fenhao(r"C:\Users\17333\Downloads\keyboard.txt") # 输入8字节(16位)键盘数据文件的路径
result = parse_usb_data(file_path)
print("此文件的键盘映射如下\n", result)

# 使用脚本仅需修改72行文件路径即可
# 注意,此脚本不显示大写键,退格键等,而是直接展示最终的结果。

鼠标流量

鼠标流量和键盘类似,但应该为4字节8字符数据(我自己抓不知道怎么回事抓到的也是8字节的数据)

鼠标流量的四字节中

  • 第一个字节代表按键,当取0x00时,代表没有按键、为0x01时,代表按左键,为0x02时,代表当前按键为右键。

  • 第二个字节可以看成是一个 signed byte 类型,其最高位为符号位,当这个值为正(小于127)时,代表鼠标水平右移多少像素,为负(补码负数,大于127小于255)时,代表水平左移多少像素。

  • 第三个字节与第二字节类似,代表垂直上下移动的偏移。

  • 第四个是扩展字节,关于滚轮的操作记录

    • 0 - 没有滚轮运动
    • 1 - 垂直向上滚动一下
    • 0xFF - 垂直向下滚动一下
    • 2 - 水平滚动右键一下
    • 0xFE - 水平滚动左键单击一下

鼠标发送 00 01 fc 00,表示鼠标右移 01 像素,垂直向下移动 124 像素。

导出流量包

.\tshark.exe -r usb.pcapng -T fields -e usbhid.data > mouse.txt

# 可选参数
# 筛选指定长度导出
# -Y "usb.device_address == 8"
#
# 去除空行
# | sed '/^\s*$/d'
#
# 参数讲解
# -r:设置tshark分析的输入文件
# -T:设置解码结果输出的格式,包括fileds,text,ps,psml和pdml,默认为text
# -e:导出协议字段
# -Y:对应过滤器的过滤条件

解码脚本(还没试成功,因为找不到可用的流量包,我的流量包是8字节16位)

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
def parse_usb_data(file_path):
normal_keys = {"04": "a", "05": "b", "06": "c", "07": "d", "08": "e", "09": "f", "0a": "g", "0b": "h", "0c": "i", "0d": "j", "0e": "k", "0f": "l", "10": "m", "11": "n", "12": "o", "13": "p", "14": "q", "15": "r", "16": "s", "17": "t", "18": "u", "19": "v", "1a": "w", "1b": "x", "1c": "y", "1d": "z", "1e": "1", "1f": "2", "20": "3", "21": "4", "22": "5", "23": "6", "24": "7", "25": "8", "26": "9", "27": "0", "28": "<RET>", "29": "<ESC>", "2a": "<DEL>", "2b": "\t", "2c": "<SPACE>", "2d": "-", "2e": "=", "2f": "[", "30": "]", "31": "\\", "32": "<NON>", "33": ";", "34": "'", "35": "<GA>", "36": ",", "37": ".", "38": "/", "39": "<CAP>", "3a": "<F1>", "3b": "<F2>", "3c": "<F3>", "3d": "<F4>", "3e": "<F5>", "3f": "<F6>", "40": "<F7>", "41": "<F8>", "42": "<F9>", "43": "<F10>", "44": "<F11>", "45": "<F12>"}
shift_keys = {"04": "A", "05": "B", "06": "C", "07": "D", "08": "E", "09": "F", "0a": "G", "0b": "H", "0c": "I", "0d": "J", "0e": "K", "0f": "L", "10": "M", "11": "N", "12": "O", "13": "P", "14": "Q", "15": "R", "16": "S", "17": "T", "18": "U", "19": "V", "1a": "W", "1b": "X", "1c": "Y", "1d": "Z", "1e": "!", "1f": "@", "20": "#", "21": "$", "22": "%", "23": "^", "24": "&", "25": "*", "26": "(", "27": ")", "28": "<RET>", "29": "<ESC>", "2a": "<DEL>", "2b": "\t", "2c": "<SPACE>", "2d": "_", "2e": "+", "2f": "{", "30": "}", "31": "|", "32": "<NON>", "33": ":", "34": "\"", "35": "<GA>", "36": "<", "37": ">", "38": "?", "39": "<CAP>", "3a": "<F1>", "3b": "<F2>", "3c": "<F3>", "3d": "<F4>", "3e": "<F5>", "3f": "<F6>", "40": "<F7>", "41": "<F8>", "42": "<F9>", "43": "<F10>", "44": "<F11>", "45": "<F12>"}
output = []

try:
with open(file_path, 'r') as keys_file:
lines = (line.strip() for line in keys_file) # 去除行末可能存在的换行符或空格
for line in lines:
if not line or len(line) < 23: # 忽略空行和不完整行
continue
try:
if line[0] != '0' or (line[1] != '0' and line[1] != '2') or line[3] != '0' or line[4] != '0' or line[9] != '0' or line[10] != '0' or line[12] != '0' or line[13] != '0' or line[15] != '0' or line[16] != '0' or line[18] != '0' or line[19] != '0' or line[21] != '0' or line[22] != '0' or line[6:8] == "00":
continue
if line[6:8] in normal_keys.keys():
output += [normal_keys[line[6:8]]] if line[1] == '0' else [shift_keys[line[6:8]]]
else:
output += ['[unknown]']
except:
pass
except FileNotFoundError:
print("File not found!")
return []

flag = 0
for i in range(len(output)):
try:
a = output.index('<DEL>')
del output[a]
del output[a - 1]
except:
pass
for i in range(len(output)):
try:
if output[i] == "<CAP>":
flag += 1
output.pop(i)
if flag == 2:
flag = 0
if flag != 0:
output[i] = output[i].upper()
except:
pass
return "".join(output)


def add_fenhao(file_path):
output = [] # 用于存储处理后的内容

# 读取文件内容,处理每一行数据
with open(file_path, 'r') as file:
for line in file:
line = line.strip() # 去除行首行尾的空格和换行符
if re.match('[a-fA-F0-9]{16}', line):
formatted_line = ':'.join(line[i:i+2] for i in range(0, len(line), 2))
output.append(formatted_line)
elif re.match('\S{2}:\S{2}:\S{2}:\S{2}:\S{2}:\S{2}:\S{2}:\S{2}', line):
output.append(line)
else:
pass
# 将处理后的内容写回原文件
with open(file_path, 'w') as file:
file.write('\n'.join(output))
return file_path



if __name__ == "__main__":
file_path = add_fenhao(r"C:\Users\17333\Desktop\usb.txt") # 输入8字节(16位)键盘数据文件的路径
result = parse_usb_data(file_path)
print("此文件的键盘映射如下\n", result)