[๐Ÿฅˆ Silver 3] ํ”„๋ฆฌ์  ํ„ฐ๋ฅผ 70๋ฒˆ ๋ˆ„๋ฅด๋ฉด ๋ชจ์Šค๋ถ€ํ˜ธ๊ฐ€ ๋œ๋‹ค โ€” DreamHack My Nervous PPT ํ’€์ด

2026-08-17ยท1๋ถ„ ์ฝ๊ธฐยท

[๐Ÿฅˆ Silver 3] ํ”„๋ฆฌ์  ํ„ฐ๋ฅผ 70๋ฒˆ ๋ˆ„๋ฅด๋ฉด ๋ชจ์Šค๋ถ€ํ˜ธ๊ฐ€ ๋œ๋‹ค โ€” DreamHack My Nervous PPT ํ’€์ด

๋ฐœํ‘œ ์ „ ๊ธด์žฅํ•ด์„œ ํ”„๋ฆฌ์  ํ„ฐ๋ฅผ ๋ˆŒ๋Ÿฌ๋Œ”๋‹ค๋Š” ์ด์•ผ๊ธฐ์™€ ํ•จ๊ป˜ USBPcap ์บก์ณ ํ•˜๋‚˜๋งŒ ์ฃผ์–ด์ง„๋‹ค. 310๊ฐœ ํŒจํ‚ท ์ค‘ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋Š” ๋ฌด์„  ํ”„๋ฆฌ์  ํ„ฐ๊ฐ€ ์˜ฌ๋ฆฐ 8๋ฐ”์ดํŠธ HID ํ‚ค๋ณด๋“œ ๋ฆฌํฌํŠธ 140๊ฐœ๋ฟ์ด๊ณ , ๊ฑฐ๊ธฐ ๋“ฑ์žฅํ•˜๋Š” ํ‚ค๋Š” UpArrow์™€ DownArrow ๋‘˜๋ฟ์ด๋‹ค. ๋‘ ์ข…๋ฅ˜ ์‹ ํ˜ธ์— ๋ˆŒ๋ฆผ ๊ฐ„๊ฒฉ์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ๊ธ€์ž ๊ฒฝ๊ณ„๊นŒ์ง€ ์žˆ์œผ๋‹ˆ ๋ชจ์Šค๋ถ€ํ˜ธ๋‹ค. ๊ฐ„๊ฒฉ ํžˆ์Šคํ† ๊ทธ๋žจ์œผ๋กœ 19๋ฉ์–ด๋ฆฌ๋ฅผ ์ž๋ฅด๊ณ  ๋ณตํ˜ธํ•˜๋ฉด ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋‚˜์˜จ๋‹ค.

๋ฌธ์ œ: DreamHack โ€” My Nervous PPT ๋ถ„๋ฅ˜: Misc / Forensics ๋‚œ์ด๋„: ๐Ÿฅˆ Silver 3 FLAG: KHK{LOOK_AT_MY_PPT}

๋ฌธ์ œ ์„ค๋ช…์ด ์งง๋‹ค.

Umm.. ๋‚œ PPT ๋ฐœํ‘œ๋ฅผ ํ•˜๊ธฐ ์ „, ๊ธด์žฅ๋œ ๋งˆ์Œ์„ ํ’€๊ณ ์ž ํ”„๋ฆฌ์  ํ„ฐ๋ฅผ ๋ง‰ ๋ˆ„๋ฅด๊ณค ํ•˜์ง€.. :( FLag ํ˜•์‹์€ KHK{} ์ž…๋‹ˆ๋‹ค. *์ˆ˜์ • โ€” ๋ชจ๋“  Flag๋Š” ๋Œ€๋ฌธ์ž์ž…๋‹ˆ๋‹ค.

์ฃผ์–ด์ง„ ํŒŒ์ผ์€ My_PPT.pcapng ํ•˜๋‚˜. 21KB์งœ๋ฆฌ ํŒจํ‚ท ์บก์ณ๋‹ค. ์„ค๋ช…์— ๋‚˜์˜จ "ํ”„๋ฆฌ์  ํ„ฐ"๊ฐ€ ๊ณง ํžŒํŠธ๋‹ค โ€” ๋ฐœํ‘œ์šฉ ๋ฌด์„  ๋ฆฌ๋ชจ์ปจ์€ ํ˜ธ์ŠคํŠธ ์ž…์žฅ์—์„œ ๊ทธ๋ƒฅ USB ํ‚ค๋ณด๋“œ๋กœ ๋ณด์ด๊ณ , ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ํ‚ค ์ž…๋ ฅ์ด ์˜ฌ๋ผ๊ฐ„๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ์ด ์บก์ณ๋Š” ๋„คํŠธ์›Œํฌ ํŠธ๋ž˜ํ”ฝ์ด ์•„๋‹ˆ๋ผ USB ํŠธ๋ž˜ํ”ฝ์ด๊ณ , ์šฐ๋ฆฌ๊ฐ€ ๋ณต์›ํ•ด์•ผ ํ•  ๊ฑด ๋ˆ„๊ตฐ๊ฐ€ 156์ดˆ ๋™์•ˆ ๋ˆ„๋ฅธ ๋ฒ„ํŠผ์˜ ์ˆœ์„œ๋‹ค.

"๋ง‰ ๋ˆ„๋ฅด๊ณค ํ•˜์ง€"๋ผ๋Š” ๋ง์ด ๋ฌด์˜๋ฏธํ•œ ์—ฐํƒ€๋ฅผ ๋œปํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ฝํžˆ์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ๊ทธ ๋ฐ˜๋Œ€๋‹ค. ๋ˆ„๋ฅธ ํŒจํ„ด ์ž์ฒด๊ฐ€ ๋ฉ”์‹œ์ง€๋‹ค.


๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…My Nervous PPT (id 1757)
๋‚œ์ด๋„๐Ÿฅˆ Silver 3
๋ถ„๋ฅ˜Misc / Forensics
์ œ๊ณต ํŒŒ์ผMy_PPT.pcapng (21,500 ๋ฐ”์ดํŠธ, ํŒจํ‚ท 310๊ฐœ)
์บก์ณ ํ™˜๊ฒฝWindows 11 23H2 + USBPcap2 + Dumpcap 4.4.3
์„œ๋ฒ„์—†์Œ (์™„์ „ ์˜คํ”„๋ผ์ธ ๋ฌธ์ œ)
ํ•ต์‹ฌ ๊ธฐ๋ฒ•USB HID ๋ถ€ํŠธ ํ‚ค๋ณด๋“œ ๋ฆฌํฌํŠธ ๋ณต์› โ†’ ๋ˆŒ๋ฆผ ๊ฐ„๊ฒฉ์œผ๋กœ ๊ธ€์ž ๋ถ„ํ•  โ†’ ๋ชจ์Šค๋ถ€ํ˜ธ ๋ณตํ˜ธ

ํ’€์ด ํ๋ฆ„์€ ๋„ค ๋‹จ๊ณ„๋‹ค. โ‘  pcapng์—์„œ USB ์ธํ„ฐ๋ŸฝํŠธ ์ „์†ก์˜ 8๋ฐ”์ดํŠธ HID ๋ฆฌํฌํŠธ๋งŒ ๋ฝ‘๊ณ  โ‘ก ๊ทธ์ค‘ keycode๊ฐ€ 0์ด ์•„๋‹Œ "๋ˆŒ๋ฆผ"๋งŒ ๋‚จ๊ธฐ๊ณ  โ‘ข ๋ˆŒ๋ฆผ ์‚ฌ์ด ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์œผ๋กœ ๊ธ€์ž๋ฅผ ์ž๋ฅธ ๋’ค โ‘ฃ ๋‘ ํ‚ค๋ฅผ ์ ๊ณผ ์„ ์— ๋Œ€์‘์‹œ์ผœ ๋ชจ์Šค๋ถ€ํ˜ธ๋กœ ์ฝ๋Š”๋‹ค.



๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” USBPcap ์ด ๋‚จ๊ธด pcapng

pcap์ด๋ผ๊ณ  ํ•˜๋ฉด ๋Œ€๊ฐœ ์ด๋”๋„ท ํ”„๋ ˆ์ž„์„ ๋– ์˜ฌ๋ฆฌ๋Š”๋ฐ, pcapng์˜ ๋งํฌ ํƒ€์ž…์—๋Š” USB๋„ ์žˆ๋‹ค. ์œˆ๋„์šฐ์—์„œ๋Š” USBPcap ๋“œ๋ผ์ด๋ฒ„๊ฐ€ USB ์Šคํƒ ์‚ฌ์ด์— ๋ผ์–ด ์•‰์•„ IRP(I/O Request Packet)๋ฅผ ๊ทธ๋Œ€๋กœ ๋–  ์ค€๋‹ค. Wireshark๊ฐ€ ๊ทธ๊ฑธ usb-usbpcap ์ธ์บก์А๋ ˆ์ด์…˜(152๋ฒˆ)์œผ๋กœ ์ €์žฅํ•œ๋‹ค.

๋จผ์ € ํŒŒ์ผ์ด ๋ฌด์—‡์ธ์ง€๋ถ€ํ„ฐ ํ™•์ธํ•œ๋‹ค.

file extracted/My_PPT.pcapng && capinfos extracted/My_PPT.pcapng

file ๊ณผ capinfos ๋กœ ๋ณธ My_PPT.pcapng โ€” ์ธ์บก์А๋ ˆ์ด์…˜์ด USB packets with USBPcap header ์ด๊ณ  ํŒจํ‚ท 310๊ฐœ, ์บก์ณ ๊ธธ์ด 156.5์ดˆ, Windows 11 23H2 ์—์„œ Dumpcap 4.4.3 ์œผ๋กœ ๋–ด๋‹ค๋Š” ๊ฒƒ๊นŒ์ง€ ํŒŒ์ผ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์— ๊ทธ๋Œ€๋กœ ๋‚จ์•„ ์žˆ๋‹ค
file ๊ณผ capinfos ๋กœ ๋ณธ My_PPT.pcapng โ€” ์ธ์บก์А๋ ˆ์ด์…˜์ด USB packets with USBPcap header ์ด๊ณ  ํŒจํ‚ท 310๊ฐœ, ์บก์ณ ๊ธธ์ด 156.5์ดˆ, Windows 11 23H2 ์—์„œ Dumpcap 4.4.3 ์œผ๋กœ ๋–ด๋‹ค๋Š” ๊ฒƒ๊นŒ์ง€ ํŒŒ์ผ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์— ๊ทธ๋Œ€๋กœ ๋‚จ์•„ ์žˆ๋‹ค

์ฝ์„ ๊ฐ’์ด ์—ฌ๋Ÿฟ ๋‚˜์˜จ๋‹ค. ์ธ์บก์А๋ ˆ์ด์…˜์ด USB packets with USBPcap header๋ผ USB ์บก์ณ๊ฐ€ ๋งž๊ณ , ์ธํ„ฐํŽ˜์ด์Šค ์ด๋ฆ„์ด \\.\USBPcap2๋ผ ํŠน์ • USB ๋ฃจํŠธ ํ—ˆ๋ธŒ ํ•˜๋‚˜๋งŒ ๋–ด๋‹ค. 156.498์ดˆ ๋™์•ˆ 310๊ฐœ๋‹ˆ๊นŒ ์ดˆ๋‹น ๋‘ ๊ฐœ๊ผด โ€” ๋Œ€์—ญํญ์„ ์“ฐ๋Š” ์žฅ์น˜๋Š” ์—†๊ณ  ๋ˆ„๊ตฐ๊ฐ€ ๋ฒ„ํŠผ์„ ๋„์—„๋„์—„ ๋ˆ„๋ฅธ ํ”์ ์— ๊ฐ€๊น๋‹ค.

Capture oper-sys์— 64-bit Windows 11 (23H2), build 22631๊นŒ์ง€ ๋ฐ•ํ˜€ ์žˆ๋‹ค. ์ถœ์ œ์ž๊ฐ€ ์ž๊ธฐ ๋…ธํŠธ๋ถ์—์„œ ๊ทธ๋Œ€๋กœ ๋– ์„œ ์˜ฌ๋ฆฐ ํŒŒ์ผ์ด๋‹ค.


๐Ÿ”ฌ ์ •์ฐฐ โ€” ์ด ๋ฒ„์Šค์— ๋ˆ„๊ฐ€ ๋ถ™์–ด ์žˆ๋‚˜

USBPcap์€ ๊ทธ ํ—ˆ๋ธŒ์— ๋ถ™์€ ์žฅ์น˜๋ฅผ ์ „๋ถ€ ๋‹ด๊ธฐ ๋•Œ๋ฌธ์—, ๋จผ์ € "์–ด๋А ์žฅ์น˜๊ฐ€ ๋งํ•˜๊ณ  ์žˆ๋‚˜"๋ฅผ ๊ฐˆ๋ผ์•ผ ํ•œ๋‹ค. tshark๋กœ ์•ž๋ถ€๋ถ„์„ ์ฝ์–ด ๋ณด๋ฉด ์—ด๊ฑฐ(enumeration) ๊ณผ์ •์ด ๊ทธ๋Œ€๋กœ ๋ณด์ธ๋‹ค.

cp extracted/My_PPT.pcapng /tmp/mnp.pcapng && tshark -r /tmp/mnp.pcapng -c 18

tshark ๋กœ ์ฝ์€ ์•ž 18๊ฐœ ํŒจํ‚ท โ€” host ์™€ 2.2.0, 2.31.0, 2.32.0 ์‚ฌ์ด์—์„œ GET DESCRIPTOR DEVICE, GET DESCRIPTOR CONFIGURATION, SET CONFIGURATION ์ด ์„ธ ์Œ์”ฉ ๋ฐ˜๋ณต๋˜๋Š” ์ „ํ˜•์ ์ธ USB ์žฅ์น˜ ์—ด๊ฑฐ ๊ณผ์ •์ด ์ฐํ˜€ ์žˆ๋‹ค
tshark ๋กœ ์ฝ์€ ์•ž 18๊ฐœ ํŒจํ‚ท โ€” host ์™€ 2.2.0, 2.31.0, 2.32.0 ์‚ฌ์ด์—์„œ GET DESCRIPTOR DEVICE, GET DESCRIPTOR CONFIGURATION, SET CONFIGURATION ์ด ์„ธ ์Œ์”ฉ ๋ฐ˜๋ณต๋˜๋Š” ์ „ํ˜•์ ์ธ USB ์žฅ์น˜ ์—ด๊ฑฐ ๊ณผ์ •์ด ์ฐํ˜€ ์žˆ๋‹ค

GET DESCRIPTOR DEVICE โ†’ GET DESCRIPTOR CONFIGURATION โ†’ SET CONFIGURATION ์ด ์žฅ์น˜๋ณ„๋กœ ํ•œ ์„ธํŠธ์”ฉ ๋ฐ˜๋ณต๋œ๋‹ค. ์บก์ณ๋ฅผ ์‹œ์ž‘ํ•œ ์‹œ์ ์— ์ด๋ฏธ ๊ฝ‚ํ˜€ ์žˆ๋˜ ์žฅ์น˜๋“ค์„ USBPcap์ด ๋’ค๋Šฆ๊ฒŒ ํ›‘์€ ๊ฒƒ์ด๋‹ค.

/tmp ๋กœ ๋ณต์‚ฌํ•˜๋Š” ์ด์œ ๋Š” ๋’ค์˜ ์‚ฝ์งˆ ํ† ๊ธ€์— ์ ์—ˆ๋‹ค. ํŒŒ์ผ์„ ๊ทธ ์ž๋ฆฌ์—์„œ ์ฝ์„ ์ˆ˜๊ฐ€ ์—†์—ˆ๋‹ค.

์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ๋Š” tshark ์ถœ๋ ฅ์„ ๋ˆˆ์œผ๋กœ ์ข‡๋Š” ๋Œ€์‹ , ์บก์ณ๋ฅผ ์ง์ ‘ ํŒŒ์‹ฑํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด ๋‘๊ณ  ๊ณ„์† ์“ฐ๊ธฐ๋กœ ํ–ˆ๋‹ค. ๋‚˜์ค‘์— ๋ณตํ˜ธ๊นŒ์ง€ ์ด ํŒŒ์„œ๋ฅผ ๊ทธ๋Œ€๋กœ ์žฌํ™œ์šฉํ•œ๋‹ค.

โ–ธ๐Ÿ“„ analyze.py ์ „๋ฌธ โ€” ์žฅ์น˜ ๋ชฉ๋ก ยท ๋””์Šคํฌ๋ฆฝํ„ฐ ยท ๊ฐ„๊ฒฉ ํžˆ์Šคํ† ๊ทธ๋žจ
#!/usr/bin/env python3
"""My Nervous PPT โ€” ์บก์ณ ์ „ ์ •์ฐฐ์šฉ ๋ถ„์„ ์Šคํฌ๋ฆฝํŠธ.
 
solve.py ์˜ ํŒŒ์„œ๋ฅผ ๊ทธ๋Œ€๋กœ ์žฌ์‚ฌ์šฉํ•ด์„œ ์บก์ณ์— ์“ธ ์„ธ ๊ฐ€์ง€๋ฅผ ๋ฝ‘๋Š”๋‹ค.
  [1] ์บก์ณ์— ์žกํžŒ USB ์žฅ์น˜ ๋ชฉ๋ก (๋””๋ฐ”์ด์Šค ๋””์Šคํฌ๋ฆฝํ„ฐ์˜ VID/PID)
  [2] 32๋ฒˆ ์žฅ์น˜์˜ ์„ค์ • ๋””์Šคํฌ๋ฆฝํ„ฐ ํŒŒ์‹ฑ โ€” ์™œ 8๋ฐ”์ดํŠธ ๋ถ€ํŠธ ํ‚ค๋ณด๋“œ ๋ฆฌํฌํŠธ์ธ๊ฐ€
  [3] HID ๋ฆฌํฌํŠธ ํ†ต๊ณ„ + ๋ˆŒ๋ฆผ ๊ฐ„๊ฒฉ ํžˆ์Šคํ† ๊ทธ๋žจ โ€” ๊ธ€์ž ๊ฒฝ๊ณ„๊ฐ€ ์–ด๋””์„œ ๊ฐˆ๋ฆฌ๋‚˜
 
์‚ฌ์šฉ๋ฒ•:  python3 analyze.py extracted/My_PPT.pcapng [devices|config|reports]
        (์„น์…˜ ์ด๋ฆ„์„ ๋นผ๋ฉด ์…‹ ๋‹ค ์ถœ๋ ฅํ•œ๋‹ค)
"""
import struct
import sys
from collections import Counter
 
from solve import parse_pcapng, hid_reports
 
CLASS = {0x00: "(์ธํ„ฐํŽ˜์ด์Šค๋ณ„)", 0x03: "HID", 0x09: "Hub", 0xE0: "Wireless", 0xFF: "Vendor"}
PROTO = {0: "None", 1: "Keyboard", 2: "Mouse"}
 
 
def usbpcap_split(pkt):
    """USBPcap ์˜์‚ฌํ—ค๋”์—์„œ (device, endpoint, transfer, ํŽ˜์ด๋กœ๋“œ) ๋ฅผ ๊บผ๋‚ธ๋‹ค."""
    hlen = struct.unpack_from("<H", pkt, 0)[0]
    device = struct.unpack_from("<H", pkt, 19)[0]
    endpoint, transfer = pkt[21], pkt[22]
    return device, endpoint, transfer, pkt[hlen:]
 
 
def show_devices(packets):
    print("[1] ์บก์ณ์— ์žกํžŒ USB ์žฅ์น˜ (๋””๋ฐ”์ด์Šค ๋””์Šคํฌ๋ฆฝํ„ฐ ์‘๋‹ต)")
    seen = {}
    for _, pkt in packets:
        dev, _, transfer, data = usbpcap_split(pkt)
        # bLength=18, bDescriptorType=0x01 ์ธ ๊ฒƒ์ด ๋””๋ฐ”์ด์Šค ๋””์Šคํฌ๋ฆฝํ„ฐ
        if transfer == 0x02 and len(data) == 18 and data[0] == 18 and data[1] == 0x01:
            vid, pid = struct.unpack_from("<HH", data, 8)
            seen.setdefault(dev, (vid, pid, data[4]))
    for dev, (vid, pid, cls) in sorted(seen.items()):
        print(f"    device {dev:>3}  VID:PID = {vid:04x}:{pid:04x}   "
              f"bDeviceClass=0x{cls:02x} {CLASS.get(cls, '')}")
    print()
 
 
def show_config(packets, want_dev):
    print(f"[2] device {want_dev} ์˜ ์„ค์ • ๋””์Šคํฌ๋ฆฝํ„ฐ")
    for _, pkt in packets:
        dev, _, transfer, data = usbpcap_split(pkt)
        if dev != want_dev or transfer != 0x02 or len(data) < 9 or data[1] != 0x02:
            continue
        off = 0
        while off + 2 <= len(data):
            blen, btype = data[off], data[off + 1]
            if blen == 0:
                break
            b = data[off:off + blen]
            if btype == 0x02:
                print(f"    CONFIGURATION  ์ด๊ธธ์ด={struct.unpack_from('<H', b, 2)[0]}  "
                      f"์ธํ„ฐํŽ˜์ด์Šค={b[4]}๊ฐœ  ์ตœ๋Œ€์ „๋ฅ˜={b[8] * 2}mA")
            elif btype == 0x04:
                print(f"    INTERFACE {b[2]}    class=0x{b[5]:02x} {CLASS.get(b[5], '')}  "
                      f"subclass=0x{b[6]:02x}{' (Boot)' if b[6] == 1 else ''}  "
                      f"protocol=0x{b[7]:02x} {PROTO.get(b[7], '')}  endpoint={b[4]}๊ฐœ")
            elif btype == 0x21:
                print(f"      HID          bcdHID=0x{struct.unpack_from('<H', b, 2)[0]:04x}  "
                      f"๋ฆฌํฌํŠธ ๋””์Šคํฌ๋ฆฝํ„ฐ {struct.unpack_from('<H', b, 7)[0]}๋ฐ”์ดํŠธ")
            elif btype == 0x05:
                # bmAttributes ํ•˜์œ„ 2๋น„ํŠธ: 00=Control 01=Isochronous 10=Bulk 11=Interrupt
                tt = {0: "Control", 1: "Isochronous", 2: "Bulk", 3: "Interrupt"}[b[3] & 3]
                print(f"      ENDPOINT     0x{b[2]:02x} {'IN' if b[2] & 0x80 else 'OUT'}  "
                      f"{tt}  wMaxPacketSize={struct.unpack_from('<H', b, 4)[0]}  "
                      f"bInterval={b[6]}ms")
            off += blen
        print()
        return
 
 
def show_reports(packets):
    reports = list(hid_reports(packets))
    print(f"[3] 8๋ฐ”์ดํŠธ HID ํ‚ค๋ณด๋“œ ๋ฆฌํฌํŠธ {len(reports)}๊ฐœ")
    print("    modifier ๋ถ„ํฌ:", dict(Counter(f"0x{r[0]:02x}" for _, r in reports)))
    print("    keycode  ๋ถ„ํฌ:", dict(Counter(f"0x{r[2]:02x}" for _, r in reports)))
    print("    2~7๋ฒˆ ๋ฐ”์ดํŠธ๊ฐ€ ํ•ญ์ƒ 0์ธ๊ฐ€:",
          all(r[1] == 0 and r[3:] == b"\x00" * 5 for _, r in reports))
    print()
 
    presses = [ts for ts, r in reports if r[2] != 0]
    gaps = sorted(round(b - a, 3) for a, b in zip(presses, presses[1:]))
    print(f"    ๋ˆŒ๋ฆผ {len(presses)}ํšŒ / ๋ˆŒ๋ฆผ ์‚ฌ์ด ๊ฐ„๊ฒฉ {len(gaps)}๊ฐœ")
    print(f"    ๊ธ€์ž ์•ˆ  : ์ตœ์†Œ {gaps[0]:.3f}s ~ ์ตœ๋Œ€ "
          f"{max(g for g in gaps if g < 2):.3f}s  ({sum(1 for g in gaps if g < 2)}๊ฐœ)")
    print(f"    ๊ธ€์ž ์‚ฌ์ด: ์ตœ์†Œ {min(g for g in gaps if g >= 2):.3f}s ~ ์ตœ๋Œ€ "
          f"{gaps[-1]:.3f}s  ({sum(1 for g in gaps if g >= 2)}๊ฐœ)")
    print("    โ†’ 0.9s ์™€ 3.1s ์‚ฌ์ด๊ฐ€ ํ…… ๋น„์–ด ์žˆ์–ด ์ž„๊ณ„๊ฐ’ 2s ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ž๋ฅธ๋‹ค")
 
 
def main():
    path = sys.argv[1] if len(sys.argv) > 1 else "extracted/My_PPT.pcapng"
    want = sys.argv[2] if len(sys.argv) > 2 else "all"
    packets = parse_pcapng(path)
    if want in ("all", "devices"):
        show_devices(packets)
    if want in ("all", "config"):
        show_config(packets, 32)
    if want in ("all", "reports"):
        show_reports(packets)
 
 
if __name__ == "__main__":
    main()

๋จผ์ € ์žฅ์น˜ ๋ชฉ๋ก๋ถ€ํ„ฐ.

python3 analyze.py extracted/My_PPT.pcapng devices

analyze.py ๊ฐ€ ๋””๋ฐ”์ด์Šค ๋””์Šคํฌ๋ฆฝํ„ฐ์—์„œ ๋ฝ‘์€ USB ์žฅ์น˜ ๋‹ค์„ฏ ๊ฐœ โ€” ์ธํ…” ๋ธ”๋ฃจํˆฌ์Šค, ๋งˆ์ดํฌ๋กœ์†Œํ”„ํŠธ ๋ณตํ•ฉ์žฅ์น˜, ๋ฆฌ์–ผํ…, ํ—ˆ๋ธŒ๊ฐ€ ํ•˜๋‚˜์”ฉ ์žˆ๊ณ  device 32 ๋งŒ VID PID ๊ฐ€ 3243 0131 ๋กœ ์ •์ฒด๊ฐ€ ๋ถˆ๋ถ„๋ช…ํ•œ ์žฅ์น˜๋‹ค
analyze.py ๊ฐ€ ๋””๋ฐ”์ด์Šค ๋””์Šคํฌ๋ฆฝํ„ฐ์—์„œ ๋ฝ‘์€ USB ์žฅ์น˜ ๋‹ค์„ฏ ๊ฐœ โ€” ์ธํ…” ๋ธ”๋ฃจํˆฌ์Šค, ๋งˆ์ดํฌ๋กœ์†Œํ”„ํŠธ ๋ณตํ•ฉ์žฅ์น˜, ๋ฆฌ์–ผํ…, ํ—ˆ๋ธŒ๊ฐ€ ํ•˜๋‚˜์”ฉ ์žˆ๊ณ  device 32 ๋งŒ VID PID ๊ฐ€ 3243 0131 ๋กœ ์ •์ฒด๊ฐ€ ๋ถˆ๋ถ„๋ช…ํ•œ ์žฅ์น˜๋‹ค

๋‹ค์„ฏ ๊ฐœ ์ค‘ ๋„ท์€ ๋ฒค๋” ID๋งŒ ๋ด๋„ ๋Œ€์ถฉ ๊ฐˆ๋ฆฐ๋‹ค. 8087์€ ์ธํ…”, 045e๋Š” ๋งˆ์ดํฌ๋กœ์†Œํ”„ํŠธ, 0bda๋Š” ๋ฆฌ์–ผํ…์ด๊ณ  214b:7250์€ bDeviceClass=0x09, ์ฆ‰ ํ—ˆ๋ธŒ๋‹ค. ๋‚จ๋Š” ๊ฑด 3243:0131 ํ•˜๋‚˜. bDeviceClass๊ฐ€ 0์ด๋ผ๋Š” ๊ฑด ํด๋ž˜์Šค๋ฅผ ์žฅ์น˜๊ฐ€ ์•„๋‹ˆ๋ผ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ •ํ•œ๋‹ค๋Š” ๋œป์ด๋ผ, ์‹ค์ œ๋กœ ๋ญ”์ง€ ๋ณด๋ ค๋ฉด ์„ค์ • ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ํŽผ์ณ์•ผ ํ•œ๋‹ค.

๊ทธ 32๋ฒˆ ์žฅ์น˜์˜ ์„ค์ • ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ํŽผ์ณ ๋ณธ๋‹ค.

python3 analyze.py extracted/My_PPT.pcapng config

device 32 ์˜ ์„ค์ • ๋””์Šคํฌ๋ฆฝํ„ฐ ํŒŒ์‹ฑ ๊ฒฐ๊ณผ โ€” HID ํด๋ž˜์Šค ์ธํ„ฐํŽ˜์ด์Šค ๋‘ ๊ฐœ๊ฐ€ ๋ชจ๋‘ subclass 0x01 Boot, protocol 0x01 Keyboard ๋กœ ์„ ์–ธ๋ผ ์žˆ๊ณ  ์—”๋“œํฌ์ธํŠธ 0x81 ๊ณผ 0x82 ๊ฐ€ ๊ฐ๊ฐ bInterval 10ms ์ธํ„ฐ๋ŸฝํŠธ IN ์ด๋‹ค
device 32 ์˜ ์„ค์ • ๋””์Šคํฌ๋ฆฝํ„ฐ ํŒŒ์‹ฑ ๊ฒฐ๊ณผ โ€” HID ํด๋ž˜์Šค ์ธํ„ฐํŽ˜์ด์Šค ๋‘ ๊ฐœ๊ฐ€ ๋ชจ๋‘ subclass 0x01 Boot, protocol 0x01 Keyboard ๋กœ ์„ ์–ธ๋ผ ์žˆ๊ณ  ์—”๋“œํฌ์ธํŠธ 0x81 ๊ณผ 0x82 ๊ฐ€ ๊ฐ๊ฐ bInterval 10ms ์ธํ„ฐ๋ŸฝํŠธ IN ์ด๋‹ค

์ด ํ™”๋ฉด์ด ์ด๋ฒˆ ํ’€์ด์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ทผ๊ฑฐ๋‹ค. ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋‘˜์ธ๋ฐ ๋‘˜ ๋‹ค subclass=0x01 (Boot) ยท protocol=0x01 Keyboard ๋กœ ์„ ์–ธ๋ผ ์žˆ๋‹ค. ๋ฌด์„  ํ”„๋ฆฌ์  ํ„ฐ๊ฐ€ ํ”ํžˆ ์“ฐ๋Š” ๊ตฌ์„ฑ์ด๋‹ค โ€” ํ•˜๋‚˜๋Š” ์ผ๋ฐ˜ ํ‚ค(๋ฐฉํ–ฅํ‚คยทF5), ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ๋ฉ€ํ‹ฐ๋ฏธ๋””์–ด ํ‚ค๋ฅผ ์˜ฌ๋ฆฐ๋‹ค.

Boot ํ”„๋กœํ† ์ฝœ์ด๋ผ๋Š” ๊ฒŒ ์™œ ์ค‘์š”ํ•˜๋ƒ๋ฉด, ๋ฆฌํฌํŠธ ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ์ฝ์ง€ ์•Š์•„๋„ ๋ฆฌํฌํŠธ ํ˜•์‹์ด ํ™•์ •๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. USB HID ๋ช…์„ธ ๋ถ€๋ก B๊ฐ€ ๋ถ€ํŠธ ํ‚ค๋ณด๋“œ ๋ฆฌํฌํŠธ๋ฅผ 8๋ฐ”์ดํŠธ๋กœ ๋ชป ๋ฐ•์•„ ๋’€๋‹ค. ์บก์ณ์—๋Š” ๋ฆฌํฌํŠธ ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ์‹ค์ œ๋กœ ๋ฐ›์•„ ์˜ค๋Š” ํŒจํ‚ท์ด ์—†๋Š”๋ฐ(206๋ฐ”์ดํŠธ์งœ๋ฆฌ๊ฐ€ ์žˆ๋‹ค๊ณ  ์„ ์–ธ๋งŒ ๋ผ ์žˆ๋‹ค), ๊ทธ๋ž˜๋„ ํ•ด์„์ด ๋ง‰ํžˆ์ง€ ์•Š๋Š” ์ด์œ ๊ฐ€ ์ด๊ฒƒ์ด๋‹ค.

์—”๋“œํฌ์ธํŠธ๋Š” 0x81 IN ์ธํ„ฐ๋ŸฝํŠธ, bInterval=10ms. ์‹ค์ œ ๋ฐ์ดํ„ฐ๋Š” ์ „๋ถ€ ์—ฌ๊ธฐ๋กœ ์˜ฌ๋ผ์˜จ๋‹ค.



๐Ÿงฑ ๋ฐฐ๊ฒฝ โ€” ๋ถ€ํŠธ ํ‚ค๋ณด๋“œ ๋ฆฌํฌํŠธ 8๋ฐ”์ดํŠธ

๋ถ€ํŠธ ํ‚ค๋ณด๋“œ ๋ฆฌํฌํŠธ์˜ ๊ตฌ์กฐ๋Š” ์ด๋ ‡๋‹ค.

๋ฐ”์ดํŠธ์ด๋ฆ„๋œป
0modifierCtrlยทShiftยทAltยทGUI ๋น„ํŠธ๋งต (์ขŒ์šฐ 8์ข…)
1reservedํ•ญ์ƒ 0
2~7keycode 1~6์ง€๊ธˆ ๋ˆŒ๋ ค ์žˆ๋Š” ํ‚ค์˜ usage ID, ์ตœ๋Œ€ 6๊ฐœ ๋™์‹œ

์—ฌ๊ธฐ์„œ ์ž์ฃผ ํ—ท๊ฐˆ๋ฆฌ๋Š” ์  ํ•˜๋‚˜. ์ด ๋ฆฌํฌํŠธ๋Š” "ํ‚ค๋ฅผ ๋ˆŒ๋ €๋‹ค"๋Š” ์ด๋ฒคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ "์ง€๊ธˆ ์ด ์ˆœ๊ฐ„ ๋ˆŒ๋ ค ์žˆ๋Š” ํ‚ค์˜ ๋ชฉ๋ก" ์ด๋‹ค. ๊ทธ๋ž˜์„œ ํ‚ค๋ฅผ ๋–ผ๋ฉด keycode ์ž๋ฆฌ๊ฐ€ 0์œผ๋กœ ์ฑ„์›Œ์ง„ ๋ฆฌํฌํŠธ๊ฐ€ ํ•œ ๋ฒˆ ๋” ์˜ฌ๋ผ์˜จ๋‹ค. ์บก์ณ์— ๋ฆฌํฌํŠธ๊ฐ€ 140๊ฐœ์ธ๋ฐ ์‹ค์ œ ๋ˆŒ๋ฆผ์ด 70ํšŒ์ธ ๊ฑด ๊ทธ๋ž˜์„œ๋‹ค โ€” ๋ˆ„๋ฅผ ๋•Œ ํ•˜๋‚˜, ๋—„ ๋•Œ ํ•˜๋‚˜.

๋ถ€ํŠธ ํ‚ค๋ณด๋“œ ๋ฆฌํฌํŠธ 8๋ฐ”์ดํŠธ์˜ ์ž๋ฆฌ๋ณ„ ์˜๋ฏธ์™€ ์ด๋ฒˆ ๋ฌธ์ œ์˜ ๋ณตํ˜ธ ํ๋ฆ„์„ ์ •๋ฆฌํ•œ ๋„์‹ โ€” modifier ๋Š” 140๊ฐœ ์ „๋ถ€ 0x02 ๋กœ ๊ณ ์ •์ด๊ณ  keycode ์ž๋ฆฌ๋งŒ 0x51 ๊ณผ 0x52 ๋กœ ๊ฐˆ๋ฆฌ๋ฉฐ, ๋ˆŒ๋ฆผ 70ํšŒ๋ฅผ ๊ฐ„๊ฒฉ์œผ๋กœ ์ž˜๋ผ 19๋ฉ์–ด๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  ๋ชจ์Šค๋กœ ์ฝ์œผ๋ฉด ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋œ๋‹ค
๋ถ€ํŠธ ํ‚ค๋ณด๋“œ ๋ฆฌํฌํŠธ 8๋ฐ”์ดํŠธ์˜ ์ž๋ฆฌ๋ณ„ ์˜๋ฏธ์™€ ์ด๋ฒˆ ๋ฌธ์ œ์˜ ๋ณตํ˜ธ ํ๋ฆ„์„ ์ •๋ฆฌํ•œ ๋„์‹ โ€” modifier ๋Š” 140๊ฐœ ์ „๋ถ€ 0x02 ๋กœ ๊ณ ์ •์ด๊ณ  keycode ์ž๋ฆฌ๋งŒ 0x51 ๊ณผ 0x52 ๋กœ ๊ฐˆ๋ฆฌ๋ฉฐ, ๋ˆŒ๋ฆผ 70ํšŒ๋ฅผ ๊ฐ„๊ฒฉ์œผ๋กœ ์ž˜๋ผ 19๋ฉ์–ด๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  ๋ชจ์Šค๋กœ ์ฝ์œผ๋ฉด ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋œ๋‹ค

์ด์ œ ์‹ค์ œ ๋ฆฌํฌํŠธ๋ฅผ ๋ณธ๋‹ค.

tshark -r /tmp/mnp.pcapng -c 60 -Y usbhid.data -T fields -e frame.time_relative -e usb.src -e usbhid.data

tshark ๋กœ ๋ฝ‘์€ usbhid.data ํ•„๋“œ โ€” 2.32.1 ์—์„œ ์˜ฌ๋ผ์˜จ 8๋ฐ”์ดํŠธ ๋ฆฌํฌํŠธ๊ฐ€ 0200520000000000 ๊ณผ 0200510000000000 ๊ทธ๋ฆฌ๊ณ  0200000000000000 ์„ธ ์ข…๋ฅ˜๋ฟ์ด๊ณ , ๋ˆ„๋ฅธ ๋ฆฌํฌํŠธ์™€ ๋—€ ๋ฆฌํฌํŠธ๊ฐ€ 40๋ฐ€๋ฆฌ์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ ์ง์„ ์ด๋ฃฌ๋‹ค
tshark ๋กœ ๋ฝ‘์€ usbhid.data ํ•„๋“œ โ€” 2.32.1 ์—์„œ ์˜ฌ๋ผ์˜จ 8๋ฐ”์ดํŠธ ๋ฆฌํฌํŠธ๊ฐ€ 0200520000000000 ๊ณผ 0200510000000000 ๊ทธ๋ฆฌ๊ณ  0200000000000000 ์„ธ ์ข…๋ฅ˜๋ฟ์ด๊ณ , ๋ˆ„๋ฅธ ๋ฆฌํฌํŠธ์™€ ๋—€ ๋ฆฌํฌํŠธ๊ฐ€ 40๋ฐ€๋ฆฌ์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ ์ง์„ ์ด๋ฃฌ๋‹ค

ํŒจํ„ด์ด ํ•œ๋ˆˆ์— ๋“ค์–ด์˜จ๋‹ค. ๊ฐ’์ด ๋”ฑ ์„ธ ์ข…๋ฅ˜๋‹ค.

  • 02 00 52 00 00 00 00 00 โ€” Left Shift + UpArrow ๋ฅผ ๋ˆ„๋ฆ„
  • 02 00 51 00 00 00 00 00 โ€” Left Shift + DownArrow ๋ฅผ ๋ˆ„๋ฆ„
  • 02 00 00 00 00 00 00 00 โ€” Shift ๋Š” ์žก์€ ์ฑ„ ํ‚ค๋ฅผ ๋—Œ

0x51๊ณผ 0x52๋Š” HID Usage Tables ํ‚ค๋ณด๋“œ ํŽ˜์ด์ง€์—์„œ ๊ฐ๊ฐ DownArrow์™€ UpArrow๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ˆ„๋ฆ„๊ณผ ๋—Œ์ด ์•ฝ 40๋ฐ€๋ฆฌ์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ ๋ถ™์–ด ๋‹ค๋‹Œ๋‹ค. ์‚ฌ๋žŒ์ด ์†์œผ๋กœ ๋ˆ„๋ฅธ ๊ฒƒ์น˜๊ณ ๋Š” ์งง์€๋ฐ, ๋ฆฌ๋ชจ์ปจ ํŽŒ์›จ์–ด๊ฐ€ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์— ๋ˆŒ๋ฆผ/๋—Œ์„ ๋ถ™์—ฌ ์˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

modifier๊ฐ€ ํ•ญ์ƒ 0x02(Left Shift)๋กœ ๊ณ ์ •์ด๋ผ๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜๋‹ค. ๊ฐ’์ด ๋ณ€ํ•˜์ง€ ์•Š์œผ๋‹ˆ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ์ง€ ์•Š๋‹ค. ์ฆ‰ ์˜๋ฏธ๋ฅผ ๋‚˜๋ฅด๋Š” ๊ฑด keycode ํ•œ ๋ฐ”์ดํŠธ๋ฟ์ด๊ณ , ๊ทธ ๊ฐ’์€ ๋‘ ๊ฐ€์ง€๋ฐ–์— ์—†๋‹ค.


๐Ÿ› ์‚ฝ์งˆ

์—ฌ๊ธฐ๊นŒ์ง€ ์˜ค๋Š” ๋™์•ˆ ์„ธ ๋ฒˆ ํ—›๋Œ์•˜๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 1 โ€” tshark ๊ฐ€ ๋‚ด ํŒŒ์ผ์„ ๋ชป ์ฝ๋Š”๋‹ค

์ฒ˜์Œ์— ๋ฌธ์ œ ํด๋”์—์„œ ๊ณง์žฅ tshark๋ฅผ ๋Œ๋ ธ๋”๋‹ˆ ์ด๋žฌ๋‹ค.

tshark: You don't have permission to read the file "My_PPT.pcapng".

ํŒŒ์ผ ์†Œ์œ ์ž๋„ ๋‚˜๊ณ  ๊ถŒํ•œ๋„ -rw-rw-r--์ธ๋ฐ ๋ชป ์ฝ๋Š”๋‹ค๊ณ  ํ•œ๋‹ค. ์†Œ์œ ๊ถŒ ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ AppArmor ๋ฌธ์ œ์˜€๋‹ค. ์šฐ๋ถ„ํˆฌ 24.04๋ถ€ํ„ฐ /etc/apparmor.d/tshark ํ”„๋กœํŒŒ์ผ์ด ๋ถ™๋Š”๋ฐ, ๋ฐ”๊นฅ์ชฝ tshark ํ”„๋กœํŒŒ์ผ์€ abstractions/user-tmp๋งŒ includeํ•˜๊ณ  ํ™ˆ ๋””๋ ‰ํ† ๋ฆฌ๋Š” ์•„์˜ˆ ์—ด์–ด ์ฃผ์ง€ ์•Š๋Š”๋‹ค. pcap ์ฝ๊ธฐ ๊ทœ์น™(owner rw /**.pcap{,ng}{,.gz})์€ ์•ˆ์ชฝ dumpcap ์„œ๋ธŒํ”„๋กœํŒŒ์ผ์—๋งŒ ๋“ค์–ด ์žˆ๋‹ค.

์ปค๋„ ๊ฐ์‚ฌ ๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด ๋ช…ํ™•ํ•˜๋‹ค.

apparmor="DENIED" operation="open" profile="tshark"
  name="/home/jinho/tsh_test/mp.pcapng" requested_mask="r" denied_mask="r"
  fsuid=1000 ouid=1000

์‹œ์Šคํ…œ ํ”„๋กœํŒŒ์ผ์„ ๊ณ ์น˜๋Š” ๋Œ€์‹  ํ—ˆ์šฉ๋œ /tmp๋กœ ๋ณต์‚ฌํ•ด ์“ฐ๊ธฐ๋กœ ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๊ธ€์˜ tshark ๋ช…๋ น์ด ์ „๋ถ€ /tmp/mnp.pcapng๋ฅผ ์ฝ๋Š”๋‹ค. capinfos๋Š” ํ”„๋กœํŒŒ์ผ์ด ์—†์–ด์„œ ์›๋ž˜ ์ž๋ฆฌ์—์„œ ๊ทธ๋ƒฅ ์ฝํžŒ๋‹ค.

ํŒŒ์ผ ๊ถŒํ•œ์„ ์•„๋ฌด๋ฆฌ ๋“ค์—ฌ๋‹ค๋ด๋„ ๋‹ต์ด ์•ˆ ๋‚˜์˜ค๋ฉด dmesg | grep -i apparmor ๋ฅผ ๋จผ์ € ๋ณผ ๊ฒƒ.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 2 โ€” usb.capdata ๋Š” ๋น„์–ด ์žˆ๋‹ค

๋ฆฌํฌํŠธ ํŽ˜์ด๋กœ๋“œ๋ฅผ ๊บผ๋‚ด๋ ค๊ณ  ์Šต๊ด€์ ์œผ๋กœ -e usb.capdata๋ฅผ ์ผ๋Š”๋ฐ ์ปฌ๋Ÿผ์ด ํ†ต์งธ๋กœ ๋น„์–ด์„œ ๋‚˜์™”๋‹ค. ํŒจํ‚ท์€ ๋ถ„๋ช…ํžˆ ์žˆ๋Š”๋ฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค๋Š” ๋œป์ด๋ผ ํ•œ์ฐธ ํ—ค๋งธ๋‹ค.

์›์ธ์€ ํ•„๋“œ ์ด๋ฆ„์ด์—ˆ๋‹ค. Wireshark๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค ํด๋ž˜์Šค๋ฅผ HID๋กœ ์ธ์‹ํ•˜๋ฉด ํŽ˜์ด๋กœ๋“œ๋ฅผ usb ๊ณ„์ธต์ด ์•„๋‹ˆ๋ผ usbhid ๊ณ„์ธต์— ๋ถ™์ธ๋‹ค. ๊ทธ๋ž˜์„œ ํ•„๋“œ๊ฐ€ usb.capdata๊ฐ€ ์•„๋‹ˆ๋ผ usbhid.data ๋‹ค. ํŒจํ‚ท ํ•˜๋‚˜๋ฅผ -T json์œผ๋กœ ํ†ต์งธ๋กœ ์ฐ์–ด ๋ณด๊ณ ์„œ์•ผ ์•Œ์•˜๋‹ค.

"usbhid.data": "02:00:52:00:00:00:00:00"

ํ•„๋“œ ์ด๋ฆ„์„ ๊ธฐ์–ต์— ์˜์กดํ•˜์ง€ ๋ง๊ณ , ๋ง‰ํžˆ๋ฉด ํŒจํ‚ท ํ•˜๋‚˜๋ฅผ ํ†ต์งธ๋กœ ๋คํ”„ํ•ด ์‹ค์ œ ํŠธ๋ฆฌ๋ฅผ ๋ณด๋Š” ๊ฒŒ ๋น ๋ฅด๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 3 โ€” USBPcap ํ—ค๋” ํ•„๋“œ ์œ„์น˜๋ฅผ ๋ˆˆ๋Œ€์ค‘์œผ๋กœ ์…Œ๋‹ค

์ž์ฒด ํŒŒ์„œ๋ฅผ ์งœ๋ฉด์„œ ์ „์†ก ํƒ€์ž… ํ•„๋“œ๋ฅผ pkt[26]์œผ๋กœ ์ฝ์—ˆ๋”๋‹ˆ ์ธํ„ฐ๋ŸฝํŠธ ์ „์†ก์ด ํ•˜๋‚˜๋„ ์•ˆ ๊ฑธ๋ ค์„œ ์ด๋ ‡๊ฒŒ ์ฃฝ์—ˆ๋‹ค.

HID ๋ˆŒ๋ฆผ ์ด๋ฒคํŠธ๊ฐ€ ์—†๋‹ค

USBPcap ์˜์‚ฌํ—ค๋”์˜ ์‹ค์ œ ๋ฐฐ์น˜๋Š” ์ด๋ ‡๋‹ค.

์˜คํ”„์…‹ํฌ๊ธฐํ•„๋“œ
02headerLen
28irpId
104status
142function
161info
172bus
192device
211endpoint
221transfer
234dataLength

transfer๋Š” 22๋ฒˆ์ด์ง€ 26๋ฒˆ์ด ์•„๋‹ˆ๋‹ค. 26๋ฒˆ์€ dataLength์˜ ์ค‘๊ฐ„์ด๋ผ ํ•ญ์ƒ 0์ด ๋‚˜์™”๊ณ , ๊ทธ๋ž˜์„œ ์กฐ๊ฑด์ด ํ•œ ๋ฒˆ๋„ ์ฐธ์ด ๋˜์ง€ ์•Š์•˜๋‹ค.

๊ฐ™์€ ์‹ค์ˆ˜๋ฅผ ํ•œ ๋ฒˆ ๋” ํ–ˆ๋‹ค. ์—”๋“œํฌ์ธํŠธ์˜ bmAttributes ํ•˜์œ„ 2๋น„ํŠธ๋ฅผ {0:Control, 1:Interrupt, 2:Bulk, 3:Isochronous}๋กœ ๋งคํ•‘ํ–ˆ๋Š”๋ฐ, ๋ช…์„ธ์˜ ์ˆœ์„œ๋Š” Control / Isochronous / Bulk / Interrupt ๋‹ค. ์ธํ„ฐ๋ŸฝํŠธ ์—”๋“œํฌ์ธํŠธ๊ฐ€ "Isochronous"๋กœ ์ถœ๋ ฅ๋ผ๋„ ์ฝ”๋“œ๋Š” ๋ฉ€์ฉกํžˆ ๋Œ๊ธฐ ๋•Œ๋ฌธ์— ์ถœ๋ ฅ์„ ๋ˆˆ์œผ๋กœ ์ฝ๋‹ค๊ฐ€ ์ด์ƒํ•ด์„œ ๋ฐœ๊ฒฌํ–ˆ๋‹ค.

๋ฐ”์ดํŠธ ์˜คํ”„์…‹๊ณผ enum ์ˆœ์„œ๋Š” ์™ธ์šด ๊ฑธ ์“ฐ์ง€ ๋ง๊ณ  ๋ช…์„ธ๋ฅผ ์—ด์–ด ์„ธ๋Š” ๊ฒŒ ๊ฒฐ๊ตญ ๋น ๋ฅด๋‹ค. ํŠนํžˆ ํ›„์ž์ฒ˜๋Ÿผ ํ‹€๋ ค๋„ ํฌ๋ž˜์‹œ๊ฐ€ ์•ˆ ๋‚˜๋Š” ์ข…๋ฅ˜๊ฐ€ ์œ„ํ—˜ํ•˜๋‹ค.



๐Ÿ’ฃ ํ•ต์‹ฌ โ€” ๊ฐ„๊ฒฉ์ด ๊ธ€์ž๋ฅผ ๊ฐ€๋ฅธ๋‹ค

์‹ ํ˜ธ๊ฐ€ ๋‘ ์ข…๋ฅ˜๋ผ๋Š” ๊ฑธ ์•Œ์•˜์œผ๋‹ˆ, ์ด์ œ ์ด๊ฑธ ์–ด๋–ป๊ฒŒ ๋ฌธ์ž๋กœ ๋ฐ”๊พธ๋А๋ƒ๊ฐ€ ๋‚จ๋Š”๋‹ค.

๊ฐ€์žฅ ๋จผ์ € ๋– ์˜ค๋ฅด๋Š” ๊ฑด ์ด์ง„๋ฒ•์ด๋‹ค. Down์„ 0, Up์„ 1๋กœ ๋†“๊ณ  70๋น„ํŠธ๋ฅผ 8๋น„ํŠธ์”ฉ ๋Š์œผ๋ฉด 8๊ธ€์ž ๋‚จ์ง“ ๋‚˜์˜จ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ 70์€ 8๋กœ ๋‚˜๋ˆ„์–ด๋–จ์–ด์ง€์ง€ ์•Š๊ณ , ๋ฌด์—‡๋ณด๋‹ค ๋ˆŒ๋ฆผ ์‹œ๊ฐ์„ ๋ณด๋ฉด ๋ฉ์–ด๋ฆฌ๊ฐ€ ์ด๋ฏธ ๋ˆˆ์— ๋ณด์ธ๋‹ค. 5.6์ดˆยท5.9์ดˆยท6.4์ดˆ์— ์„ธ ๋ฒˆ ๋ชฐ์•„์น˜๊ณ  ๋‚˜์„œ 9.6์ดˆ๊นŒ์ง€ ์กฐ์šฉํ•˜๋‹ค. ์ด์ง„๋ฒ•์ด๋ผ๋ฉด ๊ทธ๋Ÿด ์ด์œ ๊ฐ€ ์—†๋‹ค.

๊ฐ„๊ฒฉ์„ ์‹ค์ œ๋กœ ์„ธ์–ด ๋ณธ๋‹ค.

python3 analyze.py extracted/My_PPT.pcapng reports

analyze.py ์˜ ๋ฆฌํฌํŠธ ํ†ต๊ณ„ โ€” 8๋ฐ”์ดํŠธ ๋ฆฌํฌํŠธ 140๊ฐœ ์ค‘ modifier ๋Š” ์ „๋ถ€ 0x02 ์ด๊ณ  keycode ๋Š” 0x52 ๊ฐ€ 41ํšŒ, 0x51 ์ด 29ํšŒ, 0x00 ์ด 70ํšŒ๋‹ค. ๋ˆŒ๋ฆผ ์‚ฌ์ด ๊ฐ„๊ฒฉ์€ ๊ธ€์ž ์•ˆ์ด 0.328์ดˆ์—์„œ 0.880์ดˆ, ๊ธ€์ž ์‚ฌ์ด๊ฐ€ 3.184์ดˆ์—์„œ 7.808์ดˆ๋กœ ์™„์ „ํžˆ ๊ฐˆ๋ผ์ง„๋‹ค
analyze.py ์˜ ๋ฆฌํฌํŠธ ํ†ต๊ณ„ โ€” 8๋ฐ”์ดํŠธ ๋ฆฌํฌํŠธ 140๊ฐœ ์ค‘ modifier ๋Š” ์ „๋ถ€ 0x02 ์ด๊ณ  keycode ๋Š” 0x52 ๊ฐ€ 41ํšŒ, 0x51 ์ด 29ํšŒ, 0x00 ์ด 70ํšŒ๋‹ค. ๋ˆŒ๋ฆผ ์‚ฌ์ด ๊ฐ„๊ฒฉ์€ ๊ธ€์ž ์•ˆ์ด 0.328์ดˆ์—์„œ 0.880์ดˆ, ๊ธ€์ž ์‚ฌ์ด๊ฐ€ 3.184์ดˆ์—์„œ 7.808์ดˆ๋กœ ์™„์ „ํžˆ ๊ฐˆ๋ผ์ง„๋‹ค

๊น”๋”ํ•˜๊ฒŒ ๊ฐˆ๋ฆฐ๋‹ค.

  • ๊ธ€์ž ์•ˆ์˜ ๊ฐ„๊ฒฉ: 0.328s ~ 0.880s (51๊ฐœ)
  • ๊ธ€์ž ์‚ฌ์ด์˜ ๊ฐ„๊ฒฉ: 3.184s ~ 7.808s (18๊ฐœ)

0.9์ดˆ์™€ 3.1์ดˆ ์‚ฌ์ด์— ๊ฐ’์ด ํ•˜๋‚˜๋„ ์—†๋‹ค. ์ด๋ ‡๊ฒŒ ๋šœ๋ ทํ•˜๊ฒŒ ๋น„์–ด ์žˆ์œผ๋ฉด ์ž„๊ณ„๊ฐ’์„ ์–ด๋””๋กœ ์žก๋“  ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™๋‹ค๋Š” ๋œป์ด๋ผ, 2์ดˆ๋กœ ์žก์•˜๋‹ค. ๊ฐ„๊ฒฉ 18๊ฐœ๊ฐ€ ๊ฒฝ๊ณ„์ด๋ฏ€๋กœ ๊ธ€์ž๋Š” 19๊ฐœ๋‹ค.

2~7๋ฒˆ ๋ฐ”์ดํŠธ๊ฐ€ ํ•ญ์ƒ 0์ธ๊ฐ€: True ๋„ ๊ฐ™์ด ํ™•์ธํ•ด ๋’€๋‹ค. ๋™์‹œ ์ž…๋ ฅ์ด ํ•œ ๋ฒˆ๋„ ์—†์—ˆ๋‹ค๋Š” ๋œป์ด๋ผ, keycode ์ž๋ฆฌ ํ•˜๋‚˜๋งŒ ๋ด๋„ ์ •๋ณด๋ฅผ ๋น ๋œจ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.

๋‘ ์ข…๋ฅ˜ ์‹ ํ˜ธ + ๊ธ€์ž ๋‹จ์œ„ ๊ตฌ๋ถ„ + ๊ธ€์ž๋งˆ๋‹ค ๊ธธ์ด๊ฐ€ 1~6์œผ๋กœ ์ œ๊ฐ๊ฐ. ์ด ์„ธ ๊ฐ€์ง€๊ฐ€ ๊ฒน์น˜๋ฉด ๋‹ต์€ ๋ชจ์Šค๋ถ€ํ˜ธ๋‹ค. ๊ณ ์ • ๊ธธ์ด ๋ถ€ํ˜ธ์˜€๋‹ค๋ฉด ๋ฉ์–ด๋ฆฌ ํฌ๊ธฐ๊ฐ€ ์ „๋ถ€ ๊ฐ™์•„์•ผ ํ•œ๋‹ค.



๐Ÿงฑ ๋ฐฐ๊ฒฝ โ€” ๋ชจ์Šค๋ถ€ํ˜ธ

๋ชจ์Šค๋ถ€ํ˜ธ๋Š” ์ (dot)๊ณผ ์„ (dash) ๋‘ ๊ธฐํ˜ธ๋กœ ๊ธ€์ž๋ฅผ ๋งŒ๋“ ๋‹ค. ๊ทœ์น™์€ ๊ธธ์ด์˜ ๋น„์œจ๋กœ ์ •์˜๋ผ ์žˆ๋‹ค.

๊ตญ์ œ ๋ชจ์Šค๋ถ€ํ˜ธ ํ‘œ โ€” ์  ํ•˜๋‚˜๊ฐ€ 1๋‹จ์œ„, ์„ ์€ 3๋‹จ์œ„, ๊ฐ™์€ ๊ธ€์ž ์•ˆ์˜ ๊ฐ„๊ฒฉ์€ 1๋‹จ์œ„, ๊ธ€์ž ์‚ฌ์ด๋Š” 3๋‹จ์œ„, ๋‹จ์–ด ์‚ฌ์ด๋Š” 7๋‹จ์œ„๋ผ๋Š” ๊ทœ์น™๊ณผ ํ•จ๊ป˜ A๋ถ€ํ„ฐ Z, 0๋ถ€ํ„ฐ 9๊นŒ์ง€์˜ ๋ถ€ํ˜ธ๊ฐ€ ์ •๋ฆฌ๋ผ ์žˆ๋‹ค
๊ตญ์ œ ๋ชจ์Šค๋ถ€ํ˜ธ ํ‘œ โ€” ์  ํ•˜๋‚˜๊ฐ€ 1๋‹จ์œ„, ์„ ์€ 3๋‹จ์œ„, ๊ฐ™์€ ๊ธ€์ž ์•ˆ์˜ ๊ฐ„๊ฒฉ์€ 1๋‹จ์œ„, ๊ธ€์ž ์‚ฌ์ด๋Š” 3๋‹จ์œ„, ๋‹จ์–ด ์‚ฌ์ด๋Š” 7๋‹จ์œ„๋ผ๋Š” ๊ทœ์น™๊ณผ ํ•จ๊ป˜ A๋ถ€ํ„ฐ Z, 0๋ถ€ํ„ฐ 9๊นŒ์ง€์˜ ๋ถ€ํ˜ธ๊ฐ€ ์ •๋ฆฌ๋ผ ์žˆ๋‹ค

์ถœ์ฒ˜: Wikimedia Commons, public domain

์›๋ž˜ ๋ชจ์Šค๋Š” ํ•˜๋‚˜์˜ ์‹ ํ˜ธ์„ ์—์„œ "๋ˆŒ๋ฆฐ ์‹œ๊ฐ„์˜ ๊ธธ์ด"๋กœ ์ ๊ณผ ์„ ์„ ๊ตฌ๋ณ„ํ•œ๋‹ค. ์ด๋ฒˆ ๋ฌธ์ œ๋Š” ๊ทธ๊ฑธ ์กฐ๊ธˆ ๋ฐ”๊ฟ”์„œ ๋ฒ„ํŠผ ๋‘ ๊ฐœ์— ์ ๊ณผ ์„ ์„ ํ•˜๋‚˜์”ฉ ๋ฐฐ์ •ํ–ˆ๋‹ค. ํ”„๋ฆฌ์  ํ„ฐ์—๋Š” ์ด์ „/๋‹ค์Œ ๋ฒ„ํŠผ๋ฐ–์— ์—†์œผ๋‹ˆ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ณ€ํ˜•์ด๊ณ , ๋•๋ถ„์— ๋ˆŒ๋ฆฐ ์‹œ๊ฐ„์„ ์žด ํ•„์š” ์—†์ด ์–ด๋А ํ‚ค์ธ์ง€๋งŒ ๋ณด๋ฉด ๋œ๋‹ค.

๊ธ€์ž ์‚ฌ์ด ๊ฐ„๊ฒฉ์€ ๊ทธ๋Œ€๋กœ ์‚ด์•„ ์žˆ๋‹ค. ํ‘œ์˜ 4๋ฒˆ ๊ทœ์น™์ด ์‹ค์ œ ์บก์ณ์—์„œ 3.18์ดˆ ์ด์ƒ์˜ ๊ณต๋ฐฑ์œผ๋กœ ๋‚˜ํƒ€๋‚œ ๊ฒƒ์ด๋‹ค.

๋‹ค๋งŒ ๋‹จ์–ด ์‚ฌ์ด ๊ฐ„๊ฒฉ์€ ์“ฐ์ด์ง€ ์•Š์•˜๋‹ค. 7๋‹จ์œ„์งœ๋ฆฌ ๋” ํฐ ๊ณต๋ฐฑ์ด ์žˆ์–ด์•ผ ํ•˜๋Š”๋ฐ ๊ฐ„๊ฒฉ ์ตœ๋Œ“๊ฐ’์ด 7.8์ดˆ๋กœ ๊ธ€์ž ์‚ฌ์ด ๋ฒ”์œ„ ์•ˆ์— ๋“ค์–ด์˜จ๋‹ค. ๊ทธ๋ž˜์„œ ๋„์–ด์“ฐ๊ธฐ๋Š” ๋ถ€ํ˜ธ๋กœ ํ‘œํ˜„ํ–ˆ๋‹ค โ€” ๋’ค์— ๋‚˜์˜ค๋Š” ์–ธ๋”์Šค์ฝ”์–ด๊ฐ€ ๊ทธ ์—ญํ• ์„ ํ•œ๋‹ค.


๐ŸŽฏ ์ ๊ณผ ์„ ์˜ ๋ฐฉํ–ฅ ์ •ํ•˜๊ธฐ

์—ฌ๊ธฐ์„œ ํ•œ ๋ฒˆ ๋” ๊ฐˆ๋ฆฐ๋‹ค. ๋‘ ํ‚ค ์ค‘ ์–ด๋А ์ชฝ์ด ์ ์ด๊ณ  ์–ด๋А ์ชฝ์ด ์„ ์ธ๊ฐ€? ์บก์ณ ์–ด๋””์—๋„ ์•ˆ ์ ํ˜€ ์žˆ๋‹ค.

์ถ”์ธกํ•˜์ง€ ์•Š๊ณ  ๋‘˜ ๋‹ค ๋Œ๋ ค์„œ ๊ณจ๋ž๋‹ค. ํ”Œ๋ž˜๊ทธ ํ˜•์‹์ด KHK{} ๋กœ ์‹œ์ž‘ํ•œ๋‹ค๋Š” ๊ฒŒ ๋ฌธ์ œ ์„ค๋ช…์— ์žˆ์œผ๋‹ˆ, ์ฒซ ์„ธ ๊ธ€์ž๊ฐ€ KHK๋กœ ๋–จ์–ด์ง€๋Š” ์ชฝ์ด ์ •๋‹ต์ด๋‹ค.

#!/usr/bin/env python3
"""My Nervous PPT โ€” ์–ด๋А ํ‚ค๊ฐ€ ์ (dot)์ด๊ณ  ์–ด๋А ํ‚ค๊ฐ€ ์„ (dash)์ธ๊ฐ€.
 
ํ‚ค๊ฐ€ 0x51/0x52 ๋‘˜๋ฟ์ด๋ผ ๋ชจ์Šค๋กœ ์ฝ๋Š” ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€๋‹ค. ๋‘˜ ๋‹ค ๋Œ๋ ค ๋ณด๊ณ 
ํ”Œ๋ž˜๊ทธ ํ˜•์‹(KHK)์ด ๋‚˜์˜ค๋Š” ์ชฝ์„ ๊ณ ๋ฅธ๋‹ค. ์ถ”์ธก์ด ์•„๋‹ˆ๋ผ ๋Œ€์กฐ๋กœ ์ •ํ•œ๋‹ค.
 
์‚ฌ์šฉ๋ฒ•:  python3 orientation.py extracted/My_PPT.pcapng
"""
import sys
 
from solve import KEY_DOWN, KEY_UP, LETTER_GAP, MORSE, hid_reports, parse_pcapng
 
 
def groups(path):
    presses = [(ts, r[2]) for ts, r in hid_reports(parse_pcapng(path)) if r[2] != 0]
    out, cur, prev = [], [], None
    for ts, key in presses:
        if prev is not None and ts - prev > LETTER_GAP:
            out.append(cur)
            cur = []
        cur.append(key)
        prev = ts
    out.append(cur)
    return out
 
 
def decode(gs, dot_key):
    letters = ["".join("." if k == dot_key else "-" for k in g) for g in gs]
    return "".join(MORSE.get(c, "?") for c in letters), letters
 
 
def main():
    path = sys.argv[1] if len(sys.argv) > 1 else "extracted/My_PPT.pcapng"
    gs = groups(path)
    for label, dot_key in (("Down(0x51)=์  / Up(0x52)=์„ ", KEY_DOWN),
                           ("Up(0x52)=์  / Down(0x51)=์„ ", KEY_UP)):
        text, letters = decode(gs, dot_key)
        bad = sum(1 for c in text if c == "?")
        print(f"[{label}]")
        print(f"    ์•ž ์„ธ ๊ธ€์ž ๋ชจ์Šค: {' '.join(letters[:3])}")
        print(f"    ๋ณตํ˜ธ ๊ฒฐ๊ณผ      : {text}")
        print(f"    ๋ชจ์Šคํ‘œ์— ์—†๋Š” ๊ธ€์ž: {bad}๊ฐœ "
              f"โ†’ {'ํ”Œ๋ž˜๊ทธ ํ˜•์‹ KHK ๋กœ ์‹œ์ž‘, ์ฑ„ํƒ' if text.startswith('KHK') else 'ํ˜•์‹ ๋ถˆ์ผ์น˜, ๊ธฐ๊ฐ'}")
        print()
 
 
if __name__ == "__main__":
    main()
python3 orientation.py extracted/My_PPT.pcapng

orientation.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” Down ์„ ์ ์œผ๋กœ ๋†“์œผ๋ฉด ์ฒซ ์„ธ ๊ธ€์ž๊ฐ€ -.- .... -.- ์ฆ‰ KHK ๋กœ ๋–จ์–ด์ง€๊ณ  ๋ชจ์Šคํ‘œ์— ์—†๋Š” ๊ธ€์ž๊ฐ€ 0๊ฐœ์ธ ๋ฐ˜๋ฉด, ๋ฐ˜๋Œ€๋กœ ๋†“์œผ๋ฉด ์ฒซ ๊ธ€์ž๋ถ€ํ„ฐ ---- ์ฒ˜๋Ÿผ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ถ€ํ˜ธ๊ฐ€ ๋‚˜์™€ ๋ฌผ์Œํ‘œ๊ฐ€ 5๊ฐœ ์ƒ๊ธด๋‹ค
orientation.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” Down ์„ ์ ์œผ๋กœ ๋†“์œผ๋ฉด ์ฒซ ์„ธ ๊ธ€์ž๊ฐ€ -.- .... -.- ์ฆ‰ KHK ๋กœ ๋–จ์–ด์ง€๊ณ  ๋ชจ์Šคํ‘œ์— ์—†๋Š” ๊ธ€์ž๊ฐ€ 0๊ฐœ์ธ ๋ฐ˜๋ฉด, ๋ฐ˜๋Œ€๋กœ ๋†“์œผ๋ฉด ์ฒซ ๊ธ€์ž๋ถ€ํ„ฐ ---- ์ฒ˜๋Ÿผ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ถ€ํ˜ธ๊ฐ€ ๋‚˜์™€ ๋ฌผ์Œํ‘œ๊ฐ€ 5๊ฐœ ์ƒ๊ธด๋‹ค

ํ•œ์ชฝ์€ ๋ฌผ์Œํ‘œ๊ฐ€ 0๊ฐœ, ๋‹ค๋ฅธ ์ชฝ์€ 5๊ฐœ๋‹ค. ๋’ค์ง‘ํžŒ ์ชฝ์—์„œ ๋‘ ๋ฒˆ์งธ ๊ธ€์ž๊ฐ€ ----๋กœ ๋‚˜์˜ค๋Š”๋ฐ, ์ด๊ฑด ๋ชจ์Šค ํ‘œ์— ์•„์˜ˆ ์—†๋Š” ๋ถ€ํ˜ธ๋‹ค. ์šฐ์—ฐํžˆ ๋‘˜ ๋‹ค ๊ทธ๋Ÿด๋“ฏํ•˜๊ฒŒ ๋‚˜์˜ฌ ์—ฌ์ง€๊ฐ€ ์—†๋‹ค.

DownArrow๊ฐ€ ์ , UpArrow๊ฐ€ ์„ . ํ”„๋ฆฌ์  ํ„ฐ ๊ธฐ์ค€์œผ๋กœ๋Š” "๋‹ค์Œ ์Šฌ๋ผ์ด๋“œ"๊ฐ€ ์ ์ด๋‹ค. ์งง๊ฒŒ ํ†ก ์น˜๋Š” ์ชฝ์„ ์ ์œผ๋กœ ๋ฐฐ์ •ํ•œ ์…ˆ์ด๋ผ ๊ฐ๊ฐ์ ์œผ๋กœ๋„ ๋งž๋Š”๋‹ค.



๐Ÿš€ Full Exploit

์ •์ฐฐ์—์„œ ์“ฐ๋˜ ํŒŒ์„œ์— ๋ชจ์Šค ๋ณตํ˜ธ๋ฅผ ๋ถ™์ด๋ฉด ๋์ด๋‹ค. tshark ์—†์ด pcapng๋ฅผ ์ง์ ‘ ์ฝ์œผ๋ฏ€๋กœ ํŒŒ์ด์ฌ๋งŒ ์žˆ์œผ๋ฉด ๋Œ์•„๊ฐ„๋‹ค.

#!/usr/bin/env python3
"""My Nervous PPT (DreamHack, Silver 3 / misc-forensics) โ€” solver.
 
My_PPT.pcapng ๋Š” USBPcap ์œผ๋กœ ๋œฌ USB ์บก์ณ๋‹ค. ํ”„๋ฆฌ์  ํ„ฐ(๋ฌด์„  ๋ฆฌ๋ชจ์ปจ)๋Š” ํ˜ธ์ŠคํŠธ์—
ํ‰๋ฒ”ํ•œ USB HID ํ‚ค๋ณด๋“œ๋กœ ๋ณด์ด๊ณ , "๋‹ค์Œ ์Šฌ๋ผ์ด๋“œ"/"์ด์ „ ์Šฌ๋ผ์ด๋“œ" ๋ฒ„ํŠผ์€ ๊ฐ๊ฐ
Shift+Down(0x51) / Shift+Up(0x52) ๋ฆฌํฌํŠธ๋ฅผ ์˜ฌ๋ฆฐ๋‹ค.
 
์ด ์Šคํฌ๋ฆฝํŠธ๋Š” tshark ์—†์ด pcapng ๋ฅผ ์ง์ ‘ ํŒŒ์‹ฑํ•ด์„œ
  1) Enhanced Packet Block ์—์„œ USBPcap ์˜์‚ฌํ—ค๋”๋ฅผ ๋ฒ—๊ธฐ๊ณ 
  2) 8๋ฐ”์ดํŠธ HID ํ‚ค๋ณด๋“œ ๋ฆฌํฌํŠธ ์ค‘ keycode != 0 ์ธ ๊ฒƒ(=ํ‚ค ๋ˆŒ๋ฆผ)๋งŒ ๊ณจ๋ผ
  3) ๋ˆŒ๋ฆผ ์‚ฌ์ด ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์œผ๋กœ ๊ธ€์ž ๊ฒฝ๊ณ„๋ฅผ ์ž๋ฅธ ๋’ค
  4) Down=dot / Up=dash ๋กœ ๋ชจ์Šค๋ถ€ํ˜ธ๋ฅผ ๋ณตํ˜ธํ•œ๋‹ค.
 
์‚ฌ์šฉ๋ฒ•:  python3 solve.py extracted/My_PPT.pcapng
"""
import struct
import sys
 
# โ”€โ”€ pcapng ๋ธ”๋ก ํƒ€์ž…
SHB = 0x0A0D0D0A          # Section Header Block
IDB = 0x00000001          # Interface Description Block
EPB = 0x00000006          # Enhanced Packet Block
 
# โ”€โ”€ USB HID ํ‚ค๋ณด๋“œ usage ID (HID Usage Tables, Keyboard/Keypad page 0x07)
KEY_DOWN = 0x51           # Keyboard DownArrow  โ†’ ํ”„๋ฆฌ์  ํ„ฐ "๋‹ค์Œ ์Šฌ๋ผ์ด๋“œ"
KEY_UP = 0x52             # Keyboard UpArrow    โ†’ ํ”„๋ฆฌ์  ํ„ฐ "์ด์ „ ์Šฌ๋ผ์ด๋“œ"
 
LETTER_GAP = 2.0          # ์ดˆ. ์ด๋ณด๋‹ค ํฌ๋ฉด ๊ธ€์ž ๊ฒฝ๊ณ„
 
MORSE = {
    ".-": "A", "-...": "B", "-.-.": "C", "-..": "D", ".": "E", "..-.": "F",
    "--.": "G", "....": "H", "..": "I", ".---": "J", "-.-": "K", ".-..": "L",
    "--": "M", "-.": "N", "---": "O", ".--.": "P", "--.-": "Q", ".-.": "R",
    "...": "S", "-": "T", "..-": "U", "...-": "V", ".--": "W", "-..-": "X",
    "-.--": "Y", "--..": "Z",
    "-----": "0", ".----": "1", "..---": "2", "...--": "3", "....-": "4",
    ".....": "5", "-....": "6", "--...": "7", "---..": "8", "----.": "9",
    ".-.-.-": ".", "--..--": ",", "..--..": "?", "-..-.": "/", "-....-": "-",
    "-.--.": "(", "-.--.-": ")", "..--.-": "_", "-...-": "=", ".-.-.": "+",
    "---...": ":", ".----.": "'", ".-..-.": '"', "...-..-": "$", ".--.-.": "@",
}
 
 
def parse_pcapng(path):
    """pcapng ๋ฅผ ํ›‘์–ด (ํƒ€์ž„์Šคํƒฌํ”„[์ดˆ], ํŽ˜์ด๋กœ๋“œ bytes) ๋ฆฌ์ŠคํŠธ๋ฅผ ๋Œ๋ ค์ค€๋‹ค."""
    raw = open(path, "rb").read()
    endian, tsres = "<", 1e-6
    out, off = [], 0
    while off + 12 <= len(raw):
        btype = struct.unpack_from(endian + "I", raw, off)[0]
        if btype == SHB:
            # byte-order magic ์œผ๋กœ ์—”๋””์•ˆ์„ ํ™•์ •ํ•œ ๋’ค ๋ธ”๋ก ๊ธธ์ด๋ฅผ ๋‹ค์‹œ ์ฝ๋Š”๋‹ค
            endian = "<" if raw[off + 8:off + 12] == b"\x4d\x3c\x2b\x1a" else ">"
            btype = struct.unpack_from(endian + "I", raw, off)[0]
        blen = struct.unpack_from(endian + "I", raw, off + 4)[0]
        if blen < 12 or off + blen > len(raw):
            break
        body = raw[off + 8: off + blen - 4]
        if btype == IDB:
            # if_tsresol ์˜ต์…˜(์ฝ”๋“œ 9)์ด ์žˆ์œผ๋ฉด ํƒ€์ž„์Šคํƒฌํ”„ ํ•ด์ƒ๋„๋ฅผ ๊ฐฑ์‹ 
            opt = 8
            while opt + 4 <= len(body):
                ocode, olen = struct.unpack_from(endian + "HH", body, opt)
                if ocode == 0:
                    break
                if ocode == 9 and olen >= 1:
                    v = body[opt + 4]
                    tsres = 2.0 ** -(v & 0x7F) if v & 0x80 else 10.0 ** -v
                opt += 4 + ((olen + 3) & ~3)
        elif btype == EPB:
            hi, lo, caplen = struct.unpack_from(endian + "III", body, 4)
            ts = ((hi << 32) | lo) * tsres
            out.append((ts, body[20:20 + caplen]))
        off += blen
    return out
 
 
def hid_reports(packets):
    """USBPcap ์˜์‚ฌํ—ค๋”๋ฅผ ๋ฒ—๊ธฐ๊ณ  8๋ฐ”์ดํŠธ HID ํ‚ค๋ณด๋“œ ๋ฆฌํฌํŠธ๋งŒ ์ถ”๋ฆฐ๋‹ค."""
    for ts, pkt in packets:
        if len(pkt) < 2:
            continue
        hlen = struct.unpack_from("<H", pkt, 0)[0]   # USBPcap headerLen
        if hlen < 27 or len(pkt) < hlen:
            continue
        transfer = pkt[22]                           # 0x01 = interrupt
        data = pkt[hlen:]
        if transfer == 0x01 and len(data) == 8:
            yield ts, data
 
 
def main():
    path = sys.argv[1] if len(sys.argv) > 1 else "extracted/My_PPT.pcapng"
    packets = parse_pcapng(path)
 
    # keycode ๊ฐ€ 0 ์ธ ๋ฆฌํฌํŠธ๋Š” "๋–ผ๊ธฐ(release)" ๋ผ ๋ฒ„๋ฆฌ๊ณ  ๋ˆŒ๋ฆผ๋งŒ ๋‚จ๊ธด๋‹ค
    presses = [(ts, r[2]) for ts, r in hid_reports(packets) if r[2] != 0]
    if not presses:
        sys.exit("HID ๋ˆŒ๋ฆผ ์ด๋ฒคํŠธ๊ฐ€ ์—†๋‹ค")
    print(f"[*] ํŒจํ‚ท {len(packets)}๊ฐœ / HID ๋ˆŒ๋ฆผ {len(presses)}ํšŒ "
          f"/ ์‚ฌ์šฉ keycode {sorted({k for _, k in presses})}")
 
    # ๋ˆŒ๋ฆผ ์‚ฌ์ด ๊ฐ„๊ฒฉ์ด LETTER_GAP ์„ ๋„˜์œผ๋ฉด ๊ธ€์ž๋ฅผ ๋Š๋Š”๋‹ค
    letters, cur, prev = [], "", None
    for ts, key in presses:
        if prev is not None and ts - prev > LETTER_GAP:
            letters.append(cur)
            cur = ""
        cur += "." if key == KEY_DOWN else "-" if key == KEY_UP else "?"
        prev = ts
    letters.append(cur)
 
    print(f"[*] ๊ธ€์ž {len(letters)}๊ฐœ๋กœ ๋ถ„ํ• ")
    decoded = ""
    for i, code in enumerate(letters):
        ch = MORSE.get(code, "?")
        decoded += ch
        print(f"    {i:2d}  {code:<8}  {ch}")
 
    # ๋ชจ์Šค์—๋Š” ์ค‘๊ด„ํ˜ธ๊ฐ€ ์—†๋‹ค. ์ถœ์ œ์ž๋Š” ๊ด„ํ˜ธ (โ€ฆ) ๋กœ ๋Œ€์‹  ์ผ๋‹ค.
    flag = "KHK{" + decoded.split("(", 1)[1].rsplit(")", 1)[0] + "}" if "(" in decoded else decoded
    print(f"\n[*] ๋ชจ์Šค ๋ณตํ˜ธ ๊ฒฐ๊ณผ: {decoded}")
    print(f"[+] FLAG: {flag}")
 
 
if __name__ == "__main__":
    main()
python3 solve.py extracted/My_PPT.pcapng

solve.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ํŒจํ‚ท 310๊ฐœ์—์„œ ๋ˆŒ๋ฆผ 70ํšŒ๋ฅผ ๋ฝ‘์•„ 19๋ฉ์–ด๋ฆฌ๋กœ ์ž๋ฅด๊ณ  ๊ฐ ๋ฉ์–ด๋ฆฌ์˜ ๋ชจ์Šค ๋ถ€ํ˜ธ์™€ ๋Œ€์‘ ๊ธ€์ž๋ฅผ ์ฐจ๋ก€๋กœ ์ถœ๋ ฅํ•œ ๋’ค, ๋งˆ์ง€๋ง‰ ์ค„์— ๋ณตํ˜ธ ๊ฒฐ๊ณผ์™€ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ฐํžŒ๋‹ค
solve.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ํŒจํ‚ท 310๊ฐœ์—์„œ ๋ˆŒ๋ฆผ 70ํšŒ๋ฅผ ๋ฝ‘์•„ 19๋ฉ์–ด๋ฆฌ๋กœ ์ž๋ฅด๊ณ  ๊ฐ ๋ฉ์–ด๋ฆฌ์˜ ๋ชจ์Šค ๋ถ€ํ˜ธ์™€ ๋Œ€์‘ ๊ธ€์ž๋ฅผ ์ฐจ๋ก€๋กœ ์ถœ๋ ฅํ•œ ๋’ค, ๋งˆ์ง€๋ง‰ ์ค„์— ๋ณตํ˜ธ ๊ฒฐ๊ณผ์™€ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ฐํžŒ๋‹ค

19๊ธ€์ž๊ฐ€ ์ˆœ์„œ๋Œ€๋กœ ๋–จ์–ด์ง„๋‹ค. K H K ( L O O K _ A T _ M Y _ P P T ).

๋งˆ์ง€๋ง‰์— ์†์„ ํ•œ ๋ฒˆ ๋Œ€์•ผ ํ•œ๋‹ค. ๋ชจ์Šค๋ถ€ํ˜ธ์—๋Š” ์ค‘๊ด„ํ˜ธ๊ฐ€ ์—†๋‹ค. ๊ทธ๋ž˜์„œ ์ถœ์ œ์ž๋Š” ์—ฌ๋Š” ๊ด„ํ˜ธ -.--. ์™€ ๋‹ซ๋Š” ๊ด„ํ˜ธ -.--.- ๋ฅผ ๋Œ€์‹  ์ผ๋‹ค. ์ด ๋‘˜์€ ์‹ค์ œ๋กœ ๋ชจ์Šค ํ‘œ์— ์žˆ๋Š” ๋ถ€ํ˜ธ๋‹ค(๊ฐ๊ฐ KN, KK ๋กœ๋„ ์ฝํžŒ๋‹ค). ๊ด„ํ˜ธ๋ฅผ ์ค‘๊ด„ํ˜ธ๋กœ ๋ฐ”๊พธ๋ฉด ํ˜•์‹์ด ๋งž๋Š”๋‹ค.

๋„์–ด์“ฐ๊ธฐ ์ž๋ฆฌ์— ๋“ค์–ด๊ฐ„ ..--.- ๋Š” ์–ธ๋”์Šค์ฝ”์–ด๋‹ค. ์•ž์—์„œ ๋‹จ์–ด ๊ฐ„๊ฒฉ์ด ์•ˆ ์“ฐ์˜€๋‹ค๊ณ  ํ–ˆ๋˜ ๊ฒŒ ์—ฌ๊ธฐ์„œ ๋งž์•„๋–จ์–ด์ง„๋‹ค โ€” ์ถœ์ œ์ž๊ฐ€ ๊ณต๋ฐฑ ๋Œ€์‹  ๋ถ€ํ˜ธ๋ฅผ ๋„ฃ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ตณ์ด 7๋‹จ์œ„ ๊ฐ„๊ฒฉ์„ ๋งŒ๋“ค ํ•„์š”๊ฐ€ ์—†์—ˆ๋‹ค.

KHK(LOOK_AT_MY_PPT)  โ†’  KHK{LOOK_AT_MY_PPT}

ํ”Œ๋ž˜๊ทธ๋Š” KHK{LOOK_AT_MY_PPT}. "๋‚ด PPT ์ข€ ๋ด์ค˜"๋ผ๋Š”, ๋ฌธ์ œ ์„ค๋ช…๊ณผ ์ด์–ด์ง€๋Š” ๋ฌธ์žฅ์ด๋‹ค.


โœ… ๊ต์ฐจ ๊ฒ€์ฆ โ€” tshark ๋กœ ํ•œ ๋ฒˆ ๋”

์ž์ฒด ํŒŒ์„œ๊ฐ€ pcapng๋ฅผ ์ž˜๋ชป ์ฝ์—ˆ์„ ๊ฐ€๋Šฅ์„ฑ์„ ์—†์• ๋ ค๊ณ , ํŒŒ์„œ๋ฅผ ์ „ํ˜€ ๊ณต์œ ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ๋กœ๋กœ ํ•œ ๋ฒˆ ๋” ๋ณตํ˜ธํ–ˆ๋‹ค. tshark๊ฐ€ ๋ฝ‘์•„ ์ค€ ํ•„๋“œ๋ฅผ awk๋กœ ํ›‘์–ด ์ ๊ณผ ์„ ์„ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์ด๋‹ค.

#!/usr/bin/env bash
# My Nervous PPT โ€” tshark ๋กœ ๋…๋ฆฝ ๋ณตํ˜ธ. solve.py ์˜ ์ž์ฒด ํŒŒ์„œ์™€ ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™์€์ง€ ๋Œ€์กฐํ•œ๋‹ค.
#
# ์šฐ๋ถ„ํˆฌ 24.04 ์ดํ›„ tshark ๋Š” AppArmor ํ”„๋กœํŒŒ์ผ(/etc/apparmor.d/tshark)์— ๋ฌถ์—ฌ
# ํ™ˆ ๋””๋ ‰ํ† ๋ฆฌ์˜ pcap ์„ ๋ชป ์ฝ๋Š”๋‹ค. ํ—ˆ์šฉ๋œ ๊ณณ์ด /tmp ๋ผ ๊ฑฐ๊ธฐ๋กœ ๋ณต์‚ฌํ•ด์„œ ๋Œ๋ฆฐ๋‹ค.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
 
PCAP=${1:-extracted/My_PPT.pcapng}
cp "$PCAP" /tmp/mnp.pcapng
 
tshark -r /tmp/mnp.pcapng -Y usbhid.data \
       -T fields -e frame.time_relative -e usbhid.data 2>/dev/null |
awk '
  # ๋ฆฌํฌํŠธ 8๋ฐ”์ดํŠธ ์ค‘ 3๋ฒˆ์งธ(keycode) ์ž๋ฆฌ๋งŒ ๋ณธ๋‹ค. 00 ์ด๋ฉด ํ‚ค๋ฅผ ๋—€ ๊ฒƒ์ด๋ผ ๋ฒ„๋ฆฐ๋‹ค.
  { key = substr($2, 5, 2) }
  key == "00" { next }
  {
    if (prev != "" && $1 - prev > 2.0) printf " "   # 2์ดˆ ๋„˜๊ฒŒ ์‰ฌ๋ฉด ๊ธ€์ž ๊ฒฝ๊ณ„
    printf "%s", (key == "51" ? "." : "-")          # Down=์   Up=์„ 
    prev = $1
  }
  END { printf "\n" }
' | tee /tmp/mnp_morse.txt
 
python3 - /tmp/mnp_morse.txt <<'PY'
import sys
from solve import MORSE
codes = open(sys.argv[1]).read().split()
text = "".join(MORSE.get(c, "?") for c in codes)
print(f"๊ธ€์ž {len(codes)}๊ฐœ โ†’ {text}")
print("FLAG: KHK{" + text.split("(", 1)[1].rsplit(")", 1)[0] + "}")
PY
./crosscheck_tshark.sh

crosscheck_tshark.sh ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” tshark ์™€ awk ๋งŒ์œผ๋กœ ๋งŒ๋“  ๋ชจ์Šค ๋ฌธ์ž์—ด 19๋ฉ์–ด๋ฆฌ๊ฐ€ ํ•œ ์ค„๋กœ ์ถœ๋ ฅ๋˜๊ณ , ๊ทธ๊ฒƒ์„ ๋ณตํ˜ธํ•œ ๊ฒฐ๊ณผ์™€ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ž์ฒด ํŒŒ์„œ ์ชฝ๊ณผ ์ •ํ™•ํžˆ ๊ฐ™๊ฒŒ ๋‚˜์˜จ๋‹ค
crosscheck_tshark.sh ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” tshark ์™€ awk ๋งŒ์œผ๋กœ ๋งŒ๋“  ๋ชจ์Šค ๋ฌธ์ž์—ด 19๋ฉ์–ด๋ฆฌ๊ฐ€ ํ•œ ์ค„๋กœ ์ถœ๋ ฅ๋˜๊ณ , ๊ทธ๊ฒƒ์„ ๋ณตํ˜ธํ•œ ๊ฒฐ๊ณผ์™€ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ž์ฒด ํŒŒ์„œ ์ชฝ๊ณผ ์ •ํ™•ํžˆ ๊ฐ™๊ฒŒ ๋‚˜์˜จ๋‹ค

19๋ฉ์–ด๋ฆฌ, ๊ฐ™์€ ๋ฌธ์ž์—ด, ๊ฐ™์€ ํ”Œ๋ž˜๊ทธ. ๋‘ ๊ฒฝ๋กœ๊ฐ€ ์ผ์น˜ํ•˜๋‹ˆ pcapng ํŒŒ์‹ฑ๊ณผ ์ž„๊ณ„๊ฐ’ ์„ ํƒ์ด ๋ชจ๋‘ ๋งž๋‹ค๊ณ  ๋ด๋„ ๋œ๋‹ค.


๐Ÿ” ์žฌํ˜„

๋ฌธ์ œ ํด๋”๋งŒ ์žˆ์œผ๋ฉด ํ•œ ๋ฐฉ์— ๋‹ค์‹œ ๋Œ์•„๊ฐ€๋„๋ก ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‚จ๊ฒผ๋‹ค. ์„œ๋ฒ„๋„ VM๋„ ํ•„์š” ์—†๊ณ  ํŒŒ์ด์ฌ 3๋งŒ ์žˆ์œผ๋ฉด ๋œ๋‹ค.

#!/usr/bin/env bash
# My Nervous PPT (DreamHack 1757, Silver 3 misc/forensics) โ€” ํ•œ ๋ฐฉ ์žฌํ˜„.
# ๋ฐฐํฌ๋ณธ pcapng ์—์„œ flag ๋ฅผ ๋ฝ‘์•„ ๊ธฐ๋Œ€๊ฐ’๊ณผ ๋Œ€์กฐํ•œ๋‹ค. ์™ธ๋ถ€ ์„œ๋ฒ„ยทVM ๋ถˆํ•„์š”.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
 
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ œ.json'))['flag'])" 2>/dev/null \
         || echo 'KHK{LOOK_AT_MY_PPT}')
 
OUT=$(timeout 120 python3 solve.py extracted/My_PPT.pcapng 2>&1) || true
echo "$OUT" | tail -8
 
FLAG=$(printf '%s' "$OUT" | grep -aoE 'KHK\{[^}]+\}' | head -1)
if [ -n "$FLAG" ] && [ "$FLAG" = "$EXPECT" ]; then
  echo; echo "โœ… PASS  $FLAG"; exit 0
fi
echo; echo "โŒ FAIL (์–ป์€ ๊ฐ’: '${FLAG:-์—†์Œ}' / ๊ธฐ๋Œ€: '$EXPECT')"; exit 1
./reproduce.sh

reproduce.sh ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” solve.py ์ถœ๋ ฅ์˜ ๋งˆ์ง€๋ง‰ ์—ฌ๋Ÿ ์ค„์— ์ด์–ด ์–ป์€ ํ”Œ๋ž˜๊ทธ์™€ ๊ธฐ๋Œ€๊ฐ’์ด ์ผ์น˜ํ•œ๋‹ค๋Š” PASS ํ‘œ์‹œ๊ฐ€ ์ฐํžŒ๋‹ค
reproduce.sh ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” solve.py ์ถœ๋ ฅ์˜ ๋งˆ์ง€๋ง‰ ์—ฌ๋Ÿ ์ค„์— ์ด์–ด ์–ป์€ ํ”Œ๋ž˜๊ทธ์™€ ๊ธฐ๋Œ€๊ฐ’์ด ์ผ์น˜ํ•œ๋‹ค๋Š” PASS ํ‘œ์‹œ๊ฐ€ ์ฐํžŒ๋‹ค

๋กœ์ปฌ ํ™˜๊ฒฝ์€ Ubuntu 25.10 ยท Python 3.13 ยท tshark 4.4.x ๋‹ค. solve.py๋Š” ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ ์“ฐ๋ฏ€๋กœ tshark๊ฐ€ ์—†์–ด๋„ ๋˜๊ณ , ๊ต์ฐจ ๊ฒ€์ฆ ์Šคํฌ๋ฆฝํŠธ๋งŒ tshark๋ฅผ ์š”๊ตฌํ•œ๋‹ค.



๐Ÿ“ ๊ฒฐ๋ก 

ํ‚ค๋ณด๋“œ ์บก์ณ๋Š” "๋ฌด์—‡์„ ์ณค๋‚˜"๋ณด๋‹ค "์–ธ์ œ ์ณค๋‚˜"๊ฐ€ ๋‹ต์ผ ๋•Œ๊ฐ€ ์žˆ๋‹ค

USB HID ๋ฌธ์ œ๋ผ๊ณ  ํ•˜๋ฉด ๋ณดํ†ต keycode๋ฅผ ๋ฌธ์ž๋กœ ๋งคํ•‘ํ•ด์„œ ๋ฌธ์žฅ์„ ๋ณต์›ํ•œ๋‹ค. ์ด ๋ฌธ์ œ๋Š” ๊ทธ ๊ธธ์ด ๋ง‰ํ˜€ ์žˆ๋‹ค โ€” ๋“ฑ์žฅํ•˜๋Š” ํ‚ค๊ฐ€ ๋ฐฉํ–ฅํ‚ค ๋‘ ๊ฐœ๋ฟ์ด๋ผ ๋งคํ•‘ํ•  ๊ฒŒ ์—†๋‹ค.

๋Œ€์‹  ํƒ€์ž„์Šคํƒฌํ”„๊ฐ€ ์‚ด์•„ ์žˆ์—ˆ๋‹ค. ๋ˆŒ๋ฆผ ๊ฐ„๊ฒฉ์ด 0.9์ดˆ์™€ 3.1์ดˆ ์‚ฌ์ด์—์„œ ์™„์ „ํžˆ ๊ฐˆ๋ผ์ง„๋‹ค๋Š” ์‚ฌ์‹ค ํ•˜๋‚˜๋กœ ๊ธ€์ž ๊ฒฝ๊ณ„๊ฐ€ ์ •ํ•ด์กŒ๊ณ , ๊ทธ๋•Œ๋ถ€ํ„ฐ๋Š” ๊ทธ๋ƒฅ ๋ชจ์Šค๋ถ€ํ˜ธ์˜€๋‹ค. ํŒจํ‚ท ์บก์ณ์˜ ์‹œ๊ฐ„ ์ •๋ณด๋ฅผ ํŽ˜์ด๋กœ๋“œ๋งŒํผ ์ง„์ง€ํ•˜๊ฒŒ ๋ณผ ๊ฒƒ.

๋ถ€ํŠธ ํ”„๋กœํ† ์ฝœ์ด ๋ฆฌํฌํŠธ ํ˜•์‹์„ ๋ณด์ฆํ•œ๋‹ค

์ด ์บก์ณ์—๋Š” HID ๋ฆฌํฌํŠธ ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ์‹ค์ œ๋กœ ๋ฐ›์•„ ์˜ค๋Š” ํŒจํ‚ท์ด ์—†๋‹ค. 206๋ฐ”์ดํŠธ์งœ๋ฆฌ๊ฐ€ ์žˆ๋‹ค๊ณ  ์„ ์–ธ๋งŒ ๋ผ ์žˆ์„ ๋ฟ์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ๋„ 8๋ฐ”์ดํŠธ ๋ฆฌํฌํŠธ๋ฅผ ์ •ํ™•ํžˆ ํ•ด์„ํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ๊ฑด ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ subclass=0x01 (Boot)๋กœ ์„ ์–ธ๋ผ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ํŒŒ์‹ฑํ•ด ๋ณด๋Š” ๊ฑด ๊ท€์ฐฎ์•„ ๋ณด์—ฌ๋„, "์ด 8๋ฐ”์ดํŠธ๋ฅผ ์ด๋ ‡๊ฒŒ ์ฝ์–ด๋„ ๋˜๋Š”๊ฐ€"์— ๋Œ€ํ•œ ๋‹ต์„ ์ค€๋‹ค. ์ถ”์ธก์œผ๋กœ ๋„˜์–ด๊ฐ”๋‹ค๋ฉด modifier ๋ฐ”์ดํŠธ๋ฅผ keycode๋กœ ์˜คํ•ดํ•˜๋Š” ์‹์˜ ์‚ฌ๊ณ ๊ฐ€ ๋‚ฌ์„ ๊ฒƒ์ด๋‹ค.

์• ๋งคํ•œ ๊ฒฐ์ •์€ ๋‘ ๊ฐˆ๋ž˜ ๋‹ค ๋Œ๋ ค์„œ ๊ณ ๋ฅธ๋‹ค

์ ๊ณผ ์„ ์˜ ๋ฐฉํ–ฅ์€ ์บก์ณ ์–ด๋””์—๋„ ์—†์—ˆ๋‹ค. ๊ฐ์œผ๋กœ ์ •ํ•˜๊ณ  ๊ฒฐ๊ณผ๊ฐ€ ์ด์ƒํ•˜๋ฉด ๋’ค์ง‘๋Š” ๋ฐฉ์‹์œผ๋กœ๋„ ํ’€๋ฆฌ๊ธด ํ•˜์ง€๋งŒ, ๋‘ ๊ฒฝ์šฐ๋ฅผ ๋‹ค ๋Œ๋ ค์„œ "๋ชจ์Šค ํ‘œ์— ์—†๋Š” ๋ถ€ํ˜ธ๊ฐ€ ๋ช‡ ๊ฐœ ๋‚˜์˜ค๋‚˜"๋กœ ํŒ์ •ํ•˜๋ฉด ๊ทผ๊ฑฐ๊ฐ€ ๋‚จ๋Š”๋‹ค. ์‹ค์ œ๋กœ ๋’ค์ง‘์€ ์ชฝ์€ ---- ๊ฐ™์€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๋ถ€ํ˜ธ๋ฅผ ๋ฑ‰์—ˆ๋‹ค.

๋ฐฉ์–ด ๊ด€์  โ€” ๋ฌด์„  ํ”„๋ฆฌ์  ํ„ฐ๋Š” ๋ฌด์„  ํ‚ค๋ณด๋“œ๋‹ค

๋ฌธ์ œ์™€ ๋ณ„๊ฐœ๋กœ, ์ด ์บก์ณ๊ฐ€ ๋ณด์—ฌ ์ฃผ๋Š” ๊ทธ๋ฆผ ์ž์ฒด๊ฐ€ ํ•œ ๋ฒˆ์ฏค ์ƒ๊ฐํ•ด ๋ณผ ๋งŒํ•˜๋‹ค. ๋ฐœํ‘œ์šฉ ๋ฆฌ๋ชจ์ปจ์€ bInterfaceProtocol=0x01, ์ฆ‰ ํ˜ธ์ŠคํŠธ์—๊ฒŒ ํ‚ค๋ณด๋“œ๋กœ ์ž๊ธฐ๋ฅผ ์†Œ๊ฐœํ•œ๋‹ค. ํ™”๋ฉด์„ ๋„˜๊ธฐ๋Š” ๊ถŒํ•œ๋งŒ ์žˆ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์ž„์˜์˜ ํ‚ค ์ž…๋ ฅ์„ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค.

USB ์บก์ณ๊ฐ€ ๋‚จ์œผ๋ฉด ๊ทธ ํ‚ค ์ž…๋ ฅ์ด ์ „๋ถ€ ํ‰๋ฌธ์œผ๋กœ ๋ณต์›๋œ๋‹ค๋Š” ๊ฒƒ๋„ ๊ฐ™์ด ๋ด์•ผ ํ•œ๋‹ค. ์ด๋ฒˆ์—” ๋ฐฉํ–ฅํ‚ค๋ฟ์ด๋ผ ๋ฌดํ•ดํ–ˆ์ง€๋งŒ, ๊ฐ™์€ ์ž๋ฆฌ์— ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ณค๋‹ค๋ฉด usbhid.data ๋ช‡ ์ค„๋กœ ๊ทธ๋Œ€๋กœ ์ƒˆ์–ด ๋‚˜์˜จ๋‹ค. ๊ฐ•์˜์žฅ์ด๋‚˜ ํšŒ์˜์‹ค์— ๋†“์ธ ์ •์ฒด๋ถˆ๋ช…์˜ USB ๋™๊ธ€์„ ๊ทธ๋ƒฅ ๊ฝ‚๋Š” ์Šต๊ด€์ด ์™œ ์œ„ํ—˜ํ•œ์ง€์— ๋Œ€ํ•œ ๊ฝค ๊ตฌ์ฒด์ ์ธ ๊ทธ๋ฆผ์ด๋‹ค.

์ด ๊ธ€์ด ๋„์›€์ด ๋๋‚˜์š”?

Comments

๋Œ“๊ธ€

0๊ฐœ

๋Œ“๊ธ€์„ ๋‚จ๊ธฐ๋ ค๋ฉด ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ด์š”. (๋„ค์ด๋ฒ„ ยท ๊ตฌ๊ธ€ ๊ณ„์ •)

๋Œ“๊ธ€ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘โ€ฆ

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅˆ Silver 4] JPEG ๋ ๋’ค์˜ tar.bz2 ์™€ IEND ๋’ค์˜ AES ํ‚ค โ€” DreamHack Gyul Box ํ’€์ด
blog

[๐Ÿฅˆ Silver 4] JPEG ๋ ๋’ค์˜ tar.bz2 ์™€ IEND ๋’ค์˜ AES ํ‚ค โ€” DreamHack Gyul Box ํ’€์ด

๊ทค ์‚ฌ์ง„ ๋‘ ์žฅ์ด ์ „๋ถ€์ธ ํฌ๋ Œ์‹ ๋ฌธ์ œ. ํ•˜๋‚˜๋Š” JPEG ๋งˆ์ปค๊ฐ€ ๋๋‚œ ๋’ค์— tar.bz2 ๋ฅผ ๋‹ฌ๊ณ  ์žˆ์—ˆ๊ณ , ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” IEND ์ฒญํฌ ๋’ค์— AES ํ‚ค๋ฅผ ํ‰๋ฌธ์œผ๋กœ ํ˜๋ฆฌ๊ณ  ์žˆ์—ˆ๋‹ค. ์ƒ์ž๋ฅผ ๋„ค ๊ฒน ๋ฒ—๊ธฐ๋ฉด ROT ์น˜ํ™˜๊ณผ AES-256-ECB ๊ฐ€ ์ฐจ๋ก€๋กœ ๋‚˜์˜ค๊ณ , ๋งˆ์ง€๋ง‰ ํ”Œ๋ž˜๊ทธ๋Š” ๋ฌธ์ž์—ด์ด ์•„๋‹ˆ๋ผ ๊ทธ๋ฆผ์œผ๋กœ ๋ Œ๋”๋ผ ์žˆ๋‹ค.
#dreamhack#ctf#forensics+7
2026-08-20#dreamhack +4
[๐Ÿฅ‰ Bronze 2] 6523๊ฐœ CAN ํ”„๋ ˆ์ž„ ์ค‘ ํŠ€๋Š” ํ•˜๋‚˜ โ€” DreamHack Read My Car ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 2] 6523๊ฐœ CAN ํ”„๋ ˆ์ž„ ์ค‘ ํŠ€๋Š” ํ•˜๋‚˜ โ€” DreamHack Read My Car ํ’€์ด

์ •๋น„์†Œ์—์„œ ๋ฐ›์•„์˜จ ์ฐจ๋Ÿ‰ ์ฃผํ–‰ ๋กœ๊ทธ๋ผ๋ฉฐ candump ํŒŒ์ผ ํ•˜๋‚˜๋งŒ ์ค€๋‹ค. ์—ด์–ด ๋ณด๋ฉด 1953๊ฐ€์ง€ CAN ID๊ฐ€ ๋’ค์„ž์ธ ๋‚œ์ˆ˜ ๋ฉ์–ด๋ฆฌ์ธ๋ฐ, ๋“ฑ์žฅ ํšŸ์ˆ˜๋ฅผ ์„ธ๋ฉด ๋”ฑ ํ•˜๋‚˜๋งŒ ๋‹ค๋ฅธ ID๋“ค๋ณด๋‹ค 4๋ฐฐ ๋„˜๊ฒŒ ํŠ„๋‹ค. ๊ทธ ID์˜ ํŽ˜์ด๋กœ๋“œ๋ฅผ ASCII๋กœ ์ฝ์œผ๋ฉด flag๊ฐ€ ๋‚˜์˜จ๋‹ค.
#dreamhack#ctf#misc+5
2026-07-24#dreamhack +4
[๐Ÿฅˆ Silver 4] ์ค„ ๋ ๊ณต๋ฐฑ์— ์ˆจ์€ ํ”Œ๋ž˜๊ทธ โ€” SNOW ์Šคํ…Œ๊ฐ€๋…ธ๊ทธ๋ž˜ํ”ผ (DreamHack Snowing! ํ’€์ด)
blog

[๐Ÿฅˆ Silver 4] ์ค„ ๋ ๊ณต๋ฐฑ์— ์ˆจ์€ ํ”Œ๋ž˜๊ทธ โ€” SNOW ์Šคํ…Œ๊ฐ€๋…ธ๊ทธ๋ž˜ํ”ผ (DreamHack Snowing! ํ’€์ด)

flag.txt ์—๋Š” ๋ฏธ๋ผ DH{fake_snow} ๋’ค๋กœ ๋ˆˆ์— ์•ˆ ๋ณด์ด๋Š” ๊ณต๋ฐฑ๊ณผ ํƒญ์ด ์ž”๋œฉ ๋ถ™์–ด ์žˆ๋‹ค. ๋ฌธ์ œ๋ช… "Snowing!" ์ด ๊ฐ€๋ฆฌํ‚ค๋Š” SNOW ํ™”์ดํŠธ์ŠคํŽ˜์ด์Šค ์Šคํ…Œ๊ฐ€๋…ธ๊ทธ๋ž˜ํ”ผ๋‹ค. ๋ฐ”์ดํŠธ/ํŒจํ„ด์„ ์‹œ๊ฐํ™”ํ•ด ์ •์ฒด๋ฅผ ํ™•์ธํ•˜๊ณ , ๊ณ๋“ค์—ฌ์ง„ Snow.jpeg ๊ฐ€ ํ•จ์ •์ž„์„ ํฌ๋ Œ์‹์œผ๋กœ ๋ฐฐ์ œํ•œ ๋’ค, stegsnow -C ๋กœ ๋ณตํ˜ธํ•ด Snow ๋ฐ˜๋ณต ์‚ฌ์ด์˜ ์ง„์งœ ํ”Œ๋ž˜๊ทธ๋ฅผ ๊บผ๋‚ธ๋‹ค.
#dreamhack#ctf#forensics+5
2026-07-08#dreamhack +4