Đề bài

Giải
Bài này cho mình một file âm thanh wav
Đối với bài này thì mình làm như sau:
- Kiểm tra file audio
- Mở Disco.wav bằng Audacity (hoặc bất kỳ editor audio nào) hoặc đọc bằng script
- Ngay ở thời điểm 0s, trước khi nhạc bắt đầu, ta thấy có một đoạn rất ngắn toàn mẫu bị clip cực đại (giá trị xấp xỉ ±32767) ở cả hai kênh
- Nhận ra kiểu mã hóa
- Các mẫu đầu này chỉ khác nhau ở dấu (+ hoặc -), biên độ gần như cực đại
- Ý tưởng tự nhiên:
- sample > 0 → bit 1
- sample < 0 → bit 0
- Lấy kênh trái (left channel) là đủ
- Trích xuất dãy bit
- Đọc một lượng nhỏ frame đầu (ví dụ 2000 frame)
- Tìm các mẫu có |amplitude| rất lớn (gần max tuyệt đối). Gọi max_abs là giá trị |mẫu| lớn nhất trong đoạn đó, đặt ngưỡng (threshold) = 0.9 * max_abs
- Lấy các mẫu |sample| >= threshold → đây là các xung mã hoá bit
- Với file này:
- Ta thu được đúng 105 mẫu "xung"
- 105 bit ⇒ 105 / 7 = 15 ký tự ⇒ rất giống ASCII 7-bit
- Giải mã ASCII 7-bit
- Ghép 105 bit thành string, chia thành block 7 bit:
- block 1 → char 1
- block 2 → char 2
- ...
- Convert từng block 7 bit sang số thập phân rồi sang chr()
- Ghép 105 bit thành string, chia thành block 7 bit:
Script
#!/usr/bin/env python3
import wave
import struct
import sys
def extract_flag(wav_path: str) -> str:
# Mở file WAV
with wave.open(wav_path, 'rb') as w:
n_channels = w.getnchannels()
sampwidth = w.getsampwidth()
# Challenge này dùng 16-bit PCM, stereo
if sampwidth != 2:
raise ValueError(f"Expected 16-bit PCM WAV, got sampwidth={sampwidth}")
if n_channels < 1:
raise ValueError(f"Expected at least 1 channel, got n_channels={n_channels}")
# Đọc một ít frame đầu (2000 là dư để chứa 105 xung đầu)
num_frames = 2000
raw_frames = w.readframes(num_frames)
# Mỗi frame có n_channels mẫu 16-bit: format '<hh' cho stereo, '<h' cho mono
fmt = '<' + 'h' * n_channels
frames_iter = struct.iter_unpack(fmt, raw_frames)
# Lấy kênh trái (sample[0])
left_samples = [frame[0] for frame in frames_iter]
if not left_samples:
raise ValueError("No samples read from WAV file")
# Tìm biên độ lớn nhất để đặt ngưỡng
max_abs = max(abs(s) for s in left_samples)
if max_abs == 0:
raise ValueError("All samples are zero; nothing to decode")
# Đặt ngưỡng hơi thấp hơn cực đại để bắt các xung mã hoá
threshold = int(max_abs * 0.9)
# Lấy các mẫu có |sample| >= threshold → các xung bit
pulses = [s for s in left_samples if abs(s) >= threshold]
# Challenge này dùng đúng 105 xung để mã hoá 105 bit
pulses = pulses[:105]
if len(pulses) < 105:
raise ValueError(f"Expected at least 105 pulses, got {len(pulses)}")
# Đổi dấu → bit: dương = 1, âm = 0
bits = ''.join('1' if s > 0 else '0' for s in pulses)
# Chia thành block 7 bit, giải mã ASCII 7-bit
if len(bits) % 7 != 0:
raise ValueError(f"Bit length {len(bits)} is not a multiple of 7")
chars = []
for i in range(0, len(bits), 7):
chunk = bits[i:i+7]
value = int(chunk, 2)
chars.append(chr(value))
return ''.join(chars)
def main():
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <path_to_Disco.wav>")
sys.exit(1)
wav_path = sys.argv[1]
flag = extract_flag(wav_path)
print(flag)
if __name__ == "__main__":
main()
Flag
Flag: CHH{W0W*funny}
'WriteUp > Steganography' 카테고리의 다른 글
| 0x0 - Cookie Arena (0) | 2025.11.22 |
|---|---|
| Here with your eyes - Cookie Arena (0) | 2025.11.22 |
| Can you see me ? - Cookie Arena (0) | 2025.11.22 |
| Split - Cookie Arena (0) | 2025.11.21 |
