USB流量

USB流量

https://blog.csdn.net/qq_43625917/article/details/107723635

键盘映射

https://wenku.baidu.com/view/9050c3c3af45b307e971971e.html?wkts=1745125616138

USB流量指的是USB设备接口的流量,攻击者能够通过监听usb接口流量获取键盘敲击键、鼠标移动与点击、存储设备的铭文传输通信、USB无线网卡网络传输内容等等。在CTF中,USB流量分析主要以键盘和鼠标流量为主。

键盘流量

USB协议数据部分在Leftover Capture Data域中,数据长度为八个字节。其中键盘击键信息集中在第三个字节中。
1745125914065-7696e142-1d88-40b9-9bd5-f6436fc22e06.png

如图,发现击键信息为0x06,即对应的按键为C

1.解题思路:

1.使用kali linux中的tshark 命令把cap data提取出来:

1
2
tshark -r usb.pcap -T fields -e usb.capdata > usbdata.txt
tshark -r usb.pcap -T fields -e usb.capdata | sed '/^\s*$/d' > usbdata.txt #提取并去除空行

2.提取出来的数据可能会带冒号,也可能不带(有可能和wireshark的版本相关),但是一般的脚本都会按照有冒号的数据来识别

有冒号时提取数据的[6:8]
无冒号时数据在[4:6]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
f=open('usbdata.txt','r')
fi=open('out.txt','w')
while 1:
a=f.readline().strip()
if a:
if len(a)==16: # 鼠标流量的话len改为8
out=''
for i in range(0,len(a),2):
if i+2 != len(a):
out+=a[i]+a[i+1]+":"
else:
out+=a[i]+a[i+1]
fi.write(out)
fi.write('\n')
else:
break

fi.close()

1745126815922-1187ebe3-1002-47e9-9c55-c806b5269ebd.png

3.最后用脚本提取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#最后用脚本提取
# print((line[6:8])) #输出6到8之间的值
#取出68之间的值
mappings = { 0x04:"A", 0x05:"B", 0x06:"C", 0x07:"D", 0x08:"E", 0x09:"F", 0x0A:"G", 0x0B:"H", 0x0C:"I", 0x0D:"J", 0x0E:"K", 0x0F:"L", 0x10:"M", 0x11:"N",0x12:"O", 0x13:"P", 0x14:"Q", 0x15:"R", 0x16:"S", 0x17:"T", 0x18:"U",0x19:"V", 0x1A:"W", 0x1B:"X", 0x1C:"Y", 0x1D:"Z", 0x1E:"1", 0x1F:"2", 0x20:"3", 0x21:"4", 0x22:"5", 0x23:"6", 0x24:"7", 0x25:"8", 0x26:"9", 0x27:"0", 0x28:"\n", 0x2a:"[DEL]", 0X2B:" ", 0x2C:" ", 0x2D:"-", 0x2E:"=", 0x2F:"[", 0x30:"]", 0x31:"\\", 0x32:"~", 0x33:";", 0x34:"'", 0x36:",", 0x37:"." }
nums = []
keys = open('out.txt')
for line in keys:
if line[0]!='0' or line[1]!='0' 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':
continue
nums.append(int(line[6:8],16))
keys.close()
output = ""
for n in nums:
if n == 0 :
continue
if n in mappings:
output += mappings[n]
else:
output += '[unknown]'
print 'output :\n' + output

1745126850433-cf7bcd28-784c-4b6a-b2d0-0951be648226.png

flag{7200[DEL]53[DEL]93}

flag{720593}

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#!/usr/bin/env python
# coding:utf-8
import argparse
import os
from tempfile import NamedTemporaryFile

BOOT_KEYBOARD_MAP = {
0x00: (None, None), # Reserved (no event indicated)
0x01: ('', ''), # ErrorRollOver
0x02: ('', ''), # POSTFail
0x03: ('', ''), # ErrorUndefined
0x04: ('a', 'A'), # a
0x05: ('b', 'B'), # b
0x06: ('c', 'C'), # c
0x07: ('d', 'D'), # d
0x08: ('e', 'E'), # e
0x09: ('f', 'F'), # f
0x0a: ('g', 'G'), # g
0x0b: ('h', 'H'), # h
0x0c: ('i', 'I'), # i
0x0d: ('j', 'J'), # j
0x0e: ('k', 'K'), # k
0x0f: ('l', 'L'), # l
0x10: ('m', 'M'), # m
0x11: ('n', 'N'), # n
0x12: ('o', 'O'), # o
0x13: ('p', 'P'), # p
0x14: ('q', 'Q'), # q
0x15: ('r', 'R'), # r
0x16: ('s', 'S'), # s
0x17: ('t', 'T'), # t
0x18: ('u', 'U'), # u
0x19: ('v', 'V'), # v
0x1a: ('w', 'W'), # w
0x1b: ('x', 'X'), # x
0x1c: ('y', 'Y'), # y
0x1d: ('z', 'Z'), # z
0x1e: ('1', '!'), # 1
0x1f: ('2', '@'), # 2
0x20: ('3', '#'), # 3
0x21: ('4', '$'), # 4
0x22: ('5', '%'), # 5
0x23: ('6', '^'), # 6
0x24: ('7', '&'), # 7
0x25: ('8', '*'), # 8
0x26: ('9', '('), # 9
0x27: ('0', ')'), # 0
0x28: ('\n', '\n'), # Return (ENTER)
0x29: ('[ESC]', '[ESC]'), # Escape
0x2a: ('\b', '\b'), # Backspace
0x2b: ('\t', '\t'), # Tab
0x2c: (' ', ' '), # Spacebar
0x2d: ('-', '_'), # -
0x2e: ('=', '+'), # =
0x2f: ('[', '{'), # [
0x30: (']', '}'), # ]
0x31: ('\\', '|'), # \
0x32: ('', ''), # Non-US # and ~
0x33: (';', ':'), # ;
0x34: ('\'', '"'), # '
0x35: ('`', '~'), # `
0x36: (',', '<'), # ,
0x37: ('.', '>'), # .
0x38: ('/', '?'), # /
0x39: ('[CAPSLOCK]', '[CAPSLOCK]'), # Caps Lock
0x3a: ('[F1]', '[F1]'), # F1
0x3b: ('[F2]', '[F2]'), # F2
0x3c: ('[F3]', '[F3]'), # F3
0x3d: ('[F4]', '[F4]'), # F4
0x3e: ('[F5]', '[F5]'), # F5
0x3f: ('[F6]', '[F6]'), # F6
0x40: ('[F7]', '[F7]'), # F7
0x41: ('[F8]', '[F8]'), # F8
0x42: ('[F9]', '[F9]'), # F9
0x43: ('[F10]', '[F10]'), # F10
0x44: ('[F11]', '[F11]'), # F11
0x45: ('[F12]', '[F12]'), # F12
0x46: ('[PRINTSCREEN]', '[PRINTSCREEN]'), # Print Screen
0x47: ('[SCROLLLOCK]', '[SCROLLLOCK]'), # Scroll Lock
0x48: ('[PAUSE]', '[PAUSE]'), # Pause
0x49: ('[INSERT]', '[INSERT]'), # Insert
0x4a: ('[HOME]', '[HOME]'), # Home
0x4b: ('[PAGEUP]', '[PAGEUP]'), # Page Up
0x4c: ('[DELETE]', '[DELETE]'), # Delete Forward
0x4d: ('[END]', '[END]'), # End
0x4e: ('[PAGEDOWN]', '[PAGEDOWN]'), # Page Down
0x4f: ('[RIGHTARROW]', '[RIGHTARROW]'), # Right Arrow
0x50: ('[LEFTARROW]', '[LEFTARROW]'), # Left Arrow
0x51: ('[DOWNARROW]', '[DOWNARROW]'), # Down Arrow
0x52: ('[UPARROW]', '[UPARROW]'), # Up Arrow
0x53: ('[NUMLOCK]', '[NUMLOCK]'), # Num Lock
0x54: ('[KEYPADSLASH]', '/'), # Keypad /
0x55: ('[KEYPADASTERISK]', '*'), # Keypad *
0x56: ('[KEYPADMINUS]', '-'), # Keypad -
0x57: ('[KEYPADPLUS]', '+'), # Keypad +
0x58: ('[KEYPADENTER]', '[KEYPADENTER]'), # Keypad ENTER
0x59: ('[KEYPAD1]', '1'), # Keypad 1 and End
0x5a: ('[KEYPAD2]', '2'), # Keypad 2 and Down Arrow
0x5b: ('[KEYPAD3]', '3'), # Keypad 3 and PageDn
0x5c: ('[KEYPAD4]', '4'), # Keypad 4 and Left Arrow
0x5d: ('[KEYPAD5]', '5'), # Keypad 5
0x5e: ('[KEYPAD6]', '6'), # Keypad 6 and Right Arrow
0x5f: ('[KEYPAD7]', '7'), # Keypad 7 and Home
0x60: ('[KEYPAD8]', '8'), # Keypad 8 and Up Arrow
0x61: ('[KEYPAD9]', '9'), # Keypad 9 and Page Up
0x62: ('[KEYPAD0]', '0'), # Keypad 0 and Insert
0x63: ('[KEYPADPERIOD]', '.'), # Keypad . and Delete
0x64: ('', ''), # Non-US \ and |
0x65: ('', ''), # Application
0x66: ('', ''), # Power
0x67: ('[KEYPADEQUALS]', '='), # Keypad =
0x68: ('[F13]', '[F13]'), # F13
0x69: ('[F14]', '[F14]'), # F14
0x6a: ('[F15]', '[F15]'), # F15
0x6b: ('[F16]', '[F16]'), # F16
0x6c: ('[F17]', '[F17]'), # F17
0x6d: ('[F18]', '[F18]'), # F18
0x6e: ('[F19]', '[F19]'), # F19
0x6f: ('[F20]', '[F20]'), # F20
0x70: ('[F21]', '[F21]'), # F21
0x71: ('[F22]', '[F22]'), # F22
0x72: ('[F23]', '[F23]'), # F23
0x73: ('[F24]', '[F24]'), # F24
}


def parse_boot_keyboard_report(data: bytearray):
# 数据解析
modifiers = data[0] # 修改键字节
keys = data[2:8] # 键码字节

# 将修改键字节中的位解码为按键修饰符
ctrl = (modifiers & 0x11) != 0
shift = (modifiers & 0x22) != 0
alt = (modifiers & 0x44) != 0
gui = (modifiers & 0x88) != 0

# 解析键码字节并将其映射为字符
characters = []
for key in keys:
if key != 0:
# 键码不为0则查询映射表
if key in BOOT_KEYBOARD_MAP:
characters.append(BOOT_KEYBOARD_MAP[key][shift])
else:
characters.append(None)
return (ctrl, shift, alt, gui, characters)


def help_formatter(prog):
return argparse.HelpFormatter(prog, max_help_position=40)


def main():
# 解析命令行参数
parser = argparse.ArgumentParser(
description='Parse keyboard report data and output as text', formatter_class=help_formatter)
parser.add_argument('pcapng_file', help='path to the pcapng file')
args = parser.parse_args()

# 通过tshark解析pcapng文件,获取键盘数据包
tmpfile = NamedTemporaryFile(delete=False)
tmpfile.close()

command = "tshark -r %s -T fields -e usbhid.data -e usb.capdata > %s" % (
args.pcapng_file, tmpfile.name)
os.system(command)

with open(tmpfile.name, 'r') as f:
lines = f.readlines()

os.unlink(tmpfile.name)

# 解析键盘数据包,获取输入字符
text = ""
for line in lines:
capdata = line.strip().replace(':', '')
if capdata:
data = bytearray.fromhex(capdata)
characters = parse_boot_keyboard_report(data)[-1]
for character in characters:
if character:
text += character
else:
pass

raw_text = repr(text)
print(f'Raw output:\n{raw_text}')
print(f'Text output:\n{text}')


if __name__ == "__main__":
main()

鼠标流量

data.txt

USB协议鼠标数据部分在Leftover Capture Data域中,数据长度为四个字节。

其中,

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

第二个字节可以成是一个signed byte类型,其最高位为符号位,当这个值为正时,代表鼠标水平右移动多少像素,为负时,代表水平左移多少像素;

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

1745126908123-e49f79aa-7f3d-4bcd-aa32-876b815868f8.png

如图0x00002000,表示鼠标垂直向上移动20

1.题目背景

flag藏于usb流量中,通过USB协议数据中的鼠标移动轨迹转换成flag

tshark -r usb2.pcap -T fields -e usb.capdata |sed‘/^\s*$/d’> usbdata.txt

1745127142305-5044a87f-cc5f-4c31-ab1b-d113e03c2cdc.png

2.将文件用冒号进行分隔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#将上面的文件用脚本分隔,加上冒号;
f=open('usbdata.txt','r')
fi=open('out.txt','w')
while 1:
a=f.readline().strip()
if a:
if len(a)==8:#键盘流量的话len为16鼠标为8
out=''
for i in range(0,len(a),2):
if i+2 != len(a):
out+=a[i]+a[i+1]+":"
else:
out+=a[i]+a[i+1]
fi.write(out)
fi.write('\n')
else:
break
fi.close()

1745127165772-4d56af6d-1efc-48f4-a1f8-5f0a274fb104.png

3.使用脚本提取坐标数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
nums = []
keys = open('Python\\USBread\\mouse\\out.txt','r',encoding="utf_16")
f = open('Python\\USBread\\mouse\\xy.txt','w',encoding="utf_16")
posx = 0
posy = 0
for line in keys:
# if len(line) != 11:
# continue
x=int(line[3:5],16)
y=int(line[6:8],16)
if x>127:
print ("aaa")
x-=256
if y>127:
print ("bbb")
y-=256
posx += x
posy += y
btn_flag = int(line[0:2],16) #1 for left,2 for right , 0 for nothing
if btn_flag == 0:#此处的0可以修改为0(未按按键)、1(左键)、2(右键),将单独输出相应坐标文件。
print (posx,posy)
f.write(str(posx))
f.write(' ')
f.write(str(posy))
f.write('\n')
f.close()

1745127261717-f90c2ec5-ea70-4b55-9190-03997ba36f29.png

4.将坐标转换成图片

1745127296507-e2fb9199-df5a-42fe-a478-ed9f378575cf.png

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python
# coding:utf-8
import argparse
import os
from tempfile import NamedTemporaryFile
import struct
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D


def unpack_mouse_data(data: bytearray):
# 解包鼠标数据包
button_state = data[0]
if len(data) == 4:
(x, y) = struct.unpack_from("<bb", data, offset=1)
elif len(data) == 8:
(x, y) = struct.unpack_from("<bb", data, offset=1)
elif len(data) == 13:
(x, y) = struct.unpack_from("<hh", data, offset=2)

# 返回解包的数据
return button_state, x, y


def state2text(button_state: int) -> str:
button_map = {
0x0: 'No Button',
0x1: 'Left Click',
0x2: 'Right Click',
0x4: 'Middle Click',
}
states = []
if button_state == 0:
states.append(button_map[0])
else:
for key in button_map:
if button_state & key:
states.append(button_map[key])

return " And ".join(states)


def main():
# 解析命令行参数
parser = argparse.ArgumentParser(
description='Read mouse data from pcapng file and plot mouse trajectory.')
parser.add_argument('pcapng_file', help='path to the pcapng file')
parser.add_argument('button_mask', metavar='button_mask', default=15, type=int, choices=range(16), nargs='?',
help='a mask of mouse button states to be included in the trace to display, default is 15')
parser.add_argument('-o', '--output', default='output.png',
help='output file path, default is "output.png"')
args = parser.parse_args()

# 通过tshark解析pcapng文件,获取鼠标数据包
tmpfile = NamedTemporaryFile(delete=False)
tmpfile.close()

command = "tshark -r %s -T fields -e usbhid.data -e usb.capdata > %s" % (
args.pcapng_file, tmpfile.name)
os.system(command)

with open(tmpfile.name, 'r') as f:
lines = f.readlines()

os.unlink(tmpfile.name)

x_position = y_position = 0
last_button_state = -1

# 绘制鼠标轨迹图
fig, ax = plt.subplots()
colormap = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f',
'#bcbd22', '#17becf', '#aec7e8', '#ffbb78', '#98df8a', '#ff9896', '#c5b0d5', '#c49c94']

x_values = [x_position]
y_values = [y_position]

states = []

# 解析鼠标数据包,获取鼠标轨迹坐标
for line in lines:
capdata = line.strip().replace(':', '')
if capdata:
data = bytearray.fromhex(capdata)
button_state, x_offset, y_offset = unpack_mouse_data(data)
x_position += x_offset
y_position -= y_offset

if button_state != last_button_state:
if len(x_values) > 1:
color = colormap[last_button_state]
ax.plot(x_values, y_values, color=color)
x_values = [x_values[-1]]
y_values = [y_values[-1]]
last_button_state = button_state

# 筛选符合条件的按钮状态
if button_state & args.button_mask or (args.button_mask & 0b1000 and not button_state):
if button_state not in states:
states.append(button_state)
else:
x_values = []
y_values = []

x_values.append(x_position)
y_values.append(y_position)
else:
pass

if len(x_values) > 1:
color = colormap[last_button_state]
ax.plot(x_values, y_values, color=color)

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_title('Mouse Trajectory')

handles = [Line2D([], [], color=colormap[i], label=state2text(i))
for i in states]
plt.legend(handles=handles)
plt.savefig(args.output)
plt.show()


if __name__ == "__main__":
main()

更新: 2025-08-20 16:17:11
原文: https://www.yuque.com/chaye-apqbl/vsc85q/tzeg4079z6g5xnt8


USB流量
http://example.com/2025/01/19/MISC/流量分析/USB流量/
Author
chaye
Posted on
January 19, 2025
Licensed under