USB协议 键盘流量 鼠标流量
捋一下知识点
一、USB 协议基础概念
| 概念 | 说明 |
|---|---|
| Host | 主机(PC),发起请求的一方 |
| USB Device | 客户端设备(如键盘、鼠标、U盘) |
| Endpoint | 设备上的通信端点,分为 IN(设备→主机)和 OUT(主机→设备) |
| Interface Class | 设备类型,常见有: HID:Human Interface Device(键盘/鼠标) Mass Storage:U盘/移动硬盘 Printer:打印机 |
二、USB HID报文结构
一个典型的HID键盘报告是8字节的数据包
1 | Byte 0: Modifier Keys (Ctrl, Shift,Alt...) |
1 | 00 00 04 00 00 00 00 00 |
Byte 0: 0x00 → 无修饰键
Byte 1: 0x00 → 保留位
Byte 2: 0x04 → 按下 ‘A’ 键(键码)
Bytes 3~7: 全 0 → 表示只按了一个键
只有当字节不为 0 时才表示有效按键
三、关键字段
1. Modifier Keys 映射表(第 0 字节)
| Hex | Meaning |
|---|---|
| 0x01 | Ctrl |
| 0x02 | Shift |
| 0x04 | Alt |
| 0x08 | Win |
| 0x10 | Menu |
| 0x20 | Num Lock |
如果某位被置位,说明该修饰键正在使用(例如 0x02 表示 Shift 被按住)
2. Key Code 映射表(第 2~7 字节)
这是最核心的部分,用于还原用户输入。
| Key Code (Hex) | Character / Action |
|---|---|
| 0x04 | A |
| 0x05 | B |
| 0x06 | C |
| … | … |
| 0x1F | 2 |
| 0x20 | 3 |
| 0x21 | 4 |
| 0x28 | n |
| 0x2a | [DEL] |
| 0x2B | 空格 |
| 0x32 | ~ |
| 0x33 | ; |
| 0x34 | ‘ |
| 0x36 | , |
| 0x37 | . |
注意:不是所有键都有映射,有些是特殊功能键(如 F1-F12)
四、Wireshark流量分析
1.定位USB HID
protocol列可以找USB``USBHID
过滤器可以用usbhid.data
2.根据设备地址分离流量
每个usb设备有一个唯一地址 格式是x.x.x
例如2.8.1``2.4.1``2.10.1
过滤器:usb.addr=="2.8.1"
或者usb.data_len == 8 and usb.src =="1.6.1"
有一个很奇怪的点 我在导出的时候必须导出到一个文件夹里 否则还是原始数据 试了好几次都这样
3.导出原始数据 tshark
注意要在官网下载wireshark然后要配一下PATH (之前不知道在哪下的,,没有tshark
"D:\Wireshark\tshark.exe" -r 流量包地址 -Y "usb.capdata" -T fields -e usb.capdata > 导出文件地址
如:"D:\Wireshark\tshark.exe" -r D:\luxluusb\2.4.1.pcapng -Y "usb.capdata" -T fields -e usb.capdata > D:\luxluusb\2.4.1.txt
-r是读取pcap文件
-Y是过滤 只取用usb.capdata
-T fields是输出字段形式
-e usb.capdata是提取原始数据
>导出到文件里
五、py脚本
通用脚本
每行是标准USB HID报告描述符格式 共6字节(16hex字符)
1 | # -*- coding: utf-8 -*- |
[CISCN 2022 初赛]ez_usb
下载附件获得ez_usb.pcapng


Protocol列看到大量的USB和USBHID协议

随便看一条 看到HID Data:0400000000000000八字节
使用过滤器:usbhid.data
发现存在三种设备地址:2.8.1``2.4.1``2.10.1
每个地址对应不同的USB地址,分别分析它们的数据
分别用过滤器:usb.addr=="2.8.1" usb.addr=="2.4.1"``usb.addr=="2.10.1"分别导出对应分组 获得三个流量包
在D盘开一个叫luxluusb的文件夹 把三个流量包放进去
在cmd逐行粘贴并回车执行
1 | "D:\Wireshark\tshark.exe" -r D:\luxluusb\2.4.1.pcapng -Y "usb.capdata" -T fields -e usb.capdata > D:\luxluusb\2.4.1.txt |
1 | "D:\Wireshark\tshark.exe" -r D:\luxluusb\2.8.1.pcapng -Y "usb.capdata" -T fields -e usb.capdata > D:\luxluusb\2.8.1.txt |
1 | "D:\Wireshark\tshark.exe" -r D:\luxluusb\2.10.1.pcapng -Y "usb.capdata" -T fields -e usb.capdata > D:\luxluusb\2.10.1.txt |

2.4.1.txt中的数据只有7个字节 不是键盘的数据包 放弃
剩下2.8.1.txt``2.10.1.txt观察后发现 这两个文件包含有效的HID数据包 我们需要每行的第三个字节
第三个字节是实际按键码 例如0x04对应'A'
用脚本提取这些键码 并映射为字符
1 | # -*- coding: utf-8 -*- |
1 | output : 35C535765E50074A |
1 | output:526172211A0700[unknown]C[unknown]F907300000D00000000000000C4527424943500300000002[unknown]A000000[unknown]02B9F9B0530778B5541D33080020000000666C61672[unknown]E[unknown]747874[unknown]B9B[unknown]A013242F3A[unknown]FC[unknown]000B092C229D6E994167C05[unknown]A7[unknown]8708B271F[unknown]FC[unknown]042AE3D251E65536[unknown]F9A[unknown]DA87C77406B67D0[unknown]E6316684766[unknown]A86E844D[unknown]C81AA2[unknown]C72C71348D10C4[unknown]C[DEL]3D7B[unknown]00400700 |
删除[unknown]和C[DEL]

是一个rar文件 用winrar打开 密码就是另一个output 但是改成小写 因为实际上没有按cap lock键 仍然是小写 嗯嗯!
NSSCTF{20de17cc-d2c1-4b61-bebd-41159ed7172d}
[NISACTF 2022]破损的flag
flag真的是flag吗?**
Tips:flag格式为NSSCTF{}
**ps:记得补全单词哦,单词和单词之间记得加_哦
确认文件类型是流量包 在后缀加.pcapng

tshark抓取原始数据
1 | "D:\Wireshark\tshark.exe" -r "D:\usb\usbdata.pcapng" -T fields -e usb.capdata > "D:\usb\usbdata.txt" |
运行脚本还原字符串

1 | UJKONJK,TFVBHYHJIPOKRDCVGRDCVGPOKQWSZTFVBHUJKOWAZXDQASEWSDRPOKXDFVIKLPNJKWSDRRFGYRDCVGUHNMKBHJMYHJI |
以3-5个字母为单位,键盘上被围起来的字符有
i m g u l f f l a g i s w e l c o m e t f j n u
im gulf flag is welcome t fjnu
NSSCTF{welcome_to_fjnu}
[MoeCTF 2022]usb
使用过滤器usbhid.data
1 | "D:\Wireshark\tshark.exe" -r "D:\usb\usbhid.pcapng" -T fields -e usb.capdata > "D:\usb\usb.txt" |

MOECTF{Learned-a6ou7-USB-tr2ffic}
但是不是flag 我回头才发现那个脚本忽略了modifier

脚本我又改了改 会自动完成del 增加了modifier key 还有shift按住等操作
<font style="background-color:rgb(249, 242, 244);">NSSCTF{Learned_a6ou7_USB_tr@ffic}</font>
在一个wp看到了一把梭软件 CTF-NetA-V0.3.0

[GFCTF 2021]双击开始冒险
据说是生日 还是四位数字 爆破


应该是出题人的QQ号捏 搜了一下 啥也没有 看了一眼wp 出题人之前有一个个性签名WW91IGxvdmUgbWUsIEkgbG92ZSB5b3U=现在换掉了嗯嗯
From Base64之后获得You love me, I love you是第二层的密码
打开流量包 可以看到只有地址为1.2.1的Info是URB_INTERUPT in其他都比较杂乱 不符合标准
过滤器:usb.data_len == 8 and usb.src =="1.2.1"
导出特定分组
1 | "D:\Wireshark\tshark.exe" -r "D:\usb\usbnew.pcapng" -T fields -e usb.capdata > "D:\usb\usbnew.txt" |
1 | # mouse_parser_for_your_data.py |
提取后得到鼠标位移状态 进行绘图
1 | import matplotlib.pyplot as plt |

7724774CTF是F1111AG.jpg*的密码
010末尾获得flag

NSSCTF{this_is_rea1_fllllll11ag}
[SWPU 2020]来猜谜了
图片经过LSB 压缩包形式 save bin

过滤器usb.data_len == 8 and usb.src =="1.1.1"
导出
1 | "D:\Wireshark\tshark.exe" -r "D:\usb\usben.pcap" -T fields -e usb.capdata > "D:\usb\usben.txt" |
跑脚本

搜了一下
adfgx密码
二战时期德国使用的密码体系 是棋盘密码和换位加密的组合
先把明文变成坐标 再把坐标打乱顺序 最终得到看似无规律的密文
1 | A D F G X |
比如说我们要加密luxlu
看表对照我们就可以得到XX XD FF XX XD(先读行再读列)
也就是X X X D F F X X X D
假设密钥是misc
长度是4 四列 密钥长度是4 总字符数是10 于是行数是10/4=3行 最后一行不完整
创建了一个3行 四列的表格
| X | X | X | D |
|---|---|---|---|
| F | F | X | X |
| X | D |
确定列的读取顺序 根据密钥字母顺序 密钥是misc
按照字母表顺序misc 1234的顺序是c i m s 4213
读取DXXFDXFXXX是最后密文
这个题不要那么复杂 只需要读表就行了 go go go
题名来猜迷了还有jpg隐写联想到outguess
kali运行outguess
outguess -k gogogo -r /home/kali/Desktop/mi.jpg /home/kali/Desktop/mi.txt
NSSCTF{Out9uEsS_1s_V4rY_e4sy}
[MoeCTF 2021]诺亚的日记
很常规的操作

moectf{D@m3daNe_D4me_yoooooo}
[LitCTF 2025]消失的文字
鼠标流量 同样方法绘制出轨迹

水平翻折获得868F-83BD-FF是文本的密码
搜一下名字hidden-word

LitCTF{39553317-df30-4951-8aad-fcaf3028ca9d}
[NewStarCTF2025w2]Misc 城邦:NewKeyboard
过滤器:usb.addr=="1.1.2"
分别导出字母表和flag的特定分组
1 | "D:\Wireshark\tshark.exe" -r D:\usb\1.pcapng -Y "usb.capdata" -T fields -e usb.capdata > D:\usb\1.txt |
名为abcdefghijklmnopqrstuvwxyz1234567890-_!{}.pcapng的文件的输出结果如下 研究了一段时间 我才发现 这是一个已知输入内容的USB键盘流量包 也就是字符对照表
有一个傻瓜方法就是把上面这个做成映射表然后运行python脚本 不去考虑编码逻辑
观察字母表的txt文件 解释一下字符映射规律
比如说“**_**”
1 | [Modifier][保留][Key1][Key2][Key3]...(共 21 字节,42 个 hex 字符) |
**Modifier四字节 **0100**是没有按**shift** ****0102**是按住了**shift**
从第五字节开始 如果全是**00**表示没有按键按下
1 | 01020000000000200000000000000000000000000000 → shift+"-"对应"_" |
1 | 01020000000000000000000000000000000000000000 → shift仍然按着 |
1 | 01000000000000000000000000000000000000000000 → shift也松开了 |
1 | 01001000000000000000000000000000000000000000 |
1 | normalKeys = { |

事实证明 如果不把alphabet.txt看懂是无法写出脚本的
flag{th1s_is_newkeyboard_y0u_get_it!}


另存为jpg图片
