CTF实战:如何用Python脚本快速解析USB键盘流量中的隐藏flag(附完整代码)

发布时间:2026/6/27 21:39:04

CTF实战:如何用Python脚本快速解析USB键盘流量中的隐藏flag(附完整代码) CTF实战Python脚本解析USB键盘流量的高阶技巧与深度优化在CTF竞赛中USB键盘流量分析题往往成为选手们的拦路虎——看似简单的键位转换背后隐藏着数据清洗、协议解析、异常处理等多重技术挑战。本文将分享一套经过实战检验的Python解析方案不仅涵盖基础键位映射更深入探讨如何应对真实比赛中常见的复杂场景。1. USB键盘流量解析的核心原理USB键盘流量通常存储在pcap文件的Leftover Capture Data字段中每个数据包包含8字节数据。关键信息集中在第3字节十六进制表示这个字节对应HID Usage ID也就是具体的键位编码。典型数据结构示例00:00:12:00:00:00:00:00 00:00:00:00:00:00:00:00 00:00:0f:00:00:00:00:00其中第3字节12表示字母O0f对应字母L全零数据包通常表示按键释放注意实际比赛中数据可能包含冒号分隔或连续十六进制两种格式需要预处理统一格式。2. 构建健壮的键位映射字典基础映射表仅包含字母和数字远远不够完整解决方案需要考虑以下要素keymap { # 字母区 0x04: a, 0x05: b, 0x06: c, 0x07: d, 0x08: e, 0x09: f, 0x0A: g, 0x0B: h, # 数字区 0x1E: 1, 0x1F: 2, 0x20: 3, # 功能键 0x28: [ENTER], 0x29: [ESC], 0x2A: [DEL], 0x2B: [TAB], 0x2C: , 0x39: [CAPS], # 符号区 0x2D: -, 0x2E: , 0x2F: [, 0x30: ], # 扩展键 0x4F: →, 0x50: ←, 0x51: ↓, 0x52: ↑ } shift_keymap { # 大写字母映射 0x04: A, 0x05: B, 0x06: C, # 符号映射 0x1E: !, 0x1F: , 0x20: #, 0x2D: _, 0x2E: , 0x2F: {, 0x30: } }特殊场景处理技巧大小写切换需要跟踪Caps Lock和Shift键状态组合键识别0xE0前缀的扩展键码非标准映射某些CTF题目会自定义键位编码3. 实战型数据预处理流程原始流量数据往往包含噪声需要多步清洗提取有效数据tshark -r keyboard.pcap -T fields -e usb.capdata | grep -v 00:00:00:00:00:00:00:00 raw.txt格式标准化处理def normalize_data(line): line line.strip().replace(:, ) if len(line) 16: # 键盘流量标准长度 return line[4:6] # 提取关键字节 return None异常数据过滤valid_codes [] with open(raw.txt) as f: for line in f: code normalize_data(line) if code and code ! 00: # 过滤空击键 valid_codes.append(int(code, 16))4. 高级解析算法实现基础解析器升级方案class KeyboardParser: def __init__(self): self.shift_down False self.caps_lock False self.output [] def parse_code(self, code): if code 0x02: # Left Shift self.shift_down True return elif code 0x80: # Shift release self.shift_down False return elif code 0x39: # Caps Lock self.caps_lock not self.caps_lock return char self._map_code(code) if char in ([DEL], [BACKSPACE]): if self.output: self.output.pop() return return char def _map_code(self, code): upper_case self.shift_down ^ self.caps_lock if upper_case and code in shift_keymap: return shift_keymap[code] return keymap.get(code, f[0x{code:02X}]) def reconstruct_keystrokes(codes): parser KeyboardParser() result [] for code in codes: char parser.parse_code(code) if char: result.append(char) return .join(result)关键改进点状态跟踪准确处理Shift和Caps Lock删除键处理实时修正输出结果未知键保留用十六进制格式标记而非丢弃5. 典型CTF题型解题框架针对不同题目类型可套用以下解题模板类型1直接键位转换def solve_type1(pcap_file): # 步骤1提取数据 os.system(ftshark -r {pcap_file} -T fields -e usb.capdata raw.txt) # 步骤2清洗数据 codes [] with open(raw.txt) as f: for line in f: if len(line.strip()) 23: # 带冒号的格式 code line[6:8] if code ! 00: codes.append(int(code, 16)) # 步骤3转换键位 flag reconstruct_keystrokes(codes) return flag.split([ENTER])[0] # 截取第一个回车前的内容类型2隐藏数字密码def solve_type2(pcap_file): codes extract_keycodes(pcap_file) digits [] for code in codes: if 0x1E code 0x27: # 数字区 digit code - 0x1E 1 digits.append(str(digit % 10)) # 0x27对应0 return .join(digits[-6:]) # 取最后6位数字类型3需要删除键修正def solve_with_deletion(codes): result [] for code in codes: if code 0x2A: # DEL键 if result: result.pop() else: char keymap.get(code, ) if char: result.append(char) return .join(result)6. 性能优化与批量处理面对大型流量文件时可采用以下优化策略内存映射处理大文件def process_large_file(filename): with open(filename, r) as f: mm mmap.mmap(f.fileno(), 0) for line in iter(mm.readline, b): process_line(line.decode()) mm.close()多线程并行处理from concurrent.futures import ThreadPoolExecutor def parallel_parse(pcap_files): with ThreadPoolExecutor(max_workers4) as executor: results list(executor.map(solve_type1, pcap_files)) return results预处理过滤器配置def create_bpf_filter(): return ( usb.transfer_type 0x01 # 控制传输 usb.device_address 1.2.3 # 指定设备 frame.len 72 # 标准键盘数据包长度 )7. 实战案例金融CTF赛题解析以某次金融安全竞赛中的题目为例题目背景流量文件包含多个USB设备的混合数据需要先过滤出键盘流量flag格式为flag{6位数字}解题步骤首先分离键盘流量tshark -r mixed.pcap -Y usb.device_address1.2 -T fields -e usb.capdata keyboard.txt使用增强版解析脚本codes [] with open(keyboard.txt) as f: for line in f: if len(line.strip()) 23: byte line[6:8] if byte ! 00: codes.append(int(byte, 16)) digits [] num_map {0x1E:1, 0x1F:2, 0x20:3, 0x21:4, 0x22:5, 0x23:6, 0x24:7, 0x25:8, 0x26:9, 0x27:0} for code in codes: if code in num_map: digits.append(str(num_map[code])) elif code 0x2A: # 处理删除键 if digits: digits.pop() flag flag{ .join(digits[-6:]) }最终输出flag{428759}技术要点使用BPF过滤器精准定位键盘设备处理数字键和删除键的特殊逻辑只保留最后6位有效数字8. 常见问题排查指南问题现象可能原因解决方案输出全是乱码键位映射表不匹配检查HID Usage ID版本缺失部分字符未处理Shift组合键添加Shift状态跟踪数字转换错误数字区映射偏移错误验证0x1E-0x27对应关系解析速度慢重复处理空数据包增加前置过滤条件出现未知键码特殊功能键未定义扩展映射字典调试技巧# 在解析循环中添加调试输出 print(fProcessing code: 0x{code:02X} - {char} (Shift: {self.shift_down}, Caps: {self.caps_lock}))对于更复杂的异常排查建议分阶段验证首先确认原始数据提取是否完整检查键位映射字典是否覆盖所有出现码值验证状态机逻辑特别是Shift/Caps Lock检查删除键处理边界条件9. 扩展应用自动化解题系统将上述技术整合成自动化工具class CTFKeyboardSolver: def __init__(self): self.parser KeyboardParser() def solve(self, pcap_path, flag_formatflag{6d}): codes self._extract_codes(pcap_path) text self.parser.reconstruct(codes) if 6d in flag_format: return self._extract_digits(text, 6) elif del in flag_format: return self._handle_deletions(text) else: return text.split(\n)[0] def _extract_codes(self, path): # 实现数据提取逻辑 pass def _extract_digits(self, text, length): # 提取指定长度数字 pass def _handle_deletions(self, text): # 处理含删除键的场景 pass使用示例solver CTFKeyboardSolver() flag solver.solve(challenge.pcap, flag{6d}) print(fFound flag: {flag})这套系统可以灵活应对不同格式的题目要求通过参数配置适应多种变体题型。

相关新闻