Files
ctf/2026/cscg/misc/can-i-have-flag/saleae_parser.py
2026-03-09 21:44:25 +01:00

55 lines
2.3 KiB
Python

import struct
def parse_saleae_analog(filepath):
with open(filepath, 'rb') as f:
# 1. Parse the Main Header
# Format: < (little-endian), 8s (8-byte string), i (int32), i (int32), Q (uint64)
header_data = f.read(24) # 8 + 4 + 4 + 8 bytes
identifier, version, data_type, waveform_count = struct.unpack('<8s i i Q', header_data)
print(f"Identifier: {identifier.decode('ascii', errors='ignore')}")
print(f"Version: {version}")
print(f"Type: {data_type}")
print(f"Waveforms: {waveform_count}")
print("-" * 20)
# 2. Iterate through each waveform
for w in range(waveform_count):
# Parse Waveform Header
# Format: <, d (double), d (double), d (double), q (int64), Q (uint64)
wave_header_data = f.read(40) # 8 + 8 + 8 + 8 + 8 bytes
begin_time, trigger_time, sample_rate, downsample, num_samples = struct.unpack('<d d d q Q', wave_header_data)
print(f"Waveform {w+1}:")
print(f" Sample Rate: {sample_rate} Hz")
print(f" Total Samples: {num_samples}")
# 3. Read the Voltage Samples
# Format: <, f (float32). Reading chunk by chunk is much faster than loop by loop.
# We construct a dynamic format string based on num_samples
chunk_format = f'<{num_samples}f'
chunk_size = num_samples * 4 # 4 bytes per float32
raw_floats = f.read(chunk_size)
voltages = struct.unpack(chunk_format, raw_floats)
# Print the first 10 voltages just to verify
print(f" First 10 Voltages: {voltages[:10]}")
# --- CTF EXTRACTION LOGIC ---
# Let's turn these voltages back into a digital signal
# You will need to adjust this threshold based on what voltages you see!
threshold = 2.5
bitstream = []
for v in voltages:
if v > threshold:
bitstream.append('0') # Dominant
else:
bitstream.append('1') # Recessive
print(f" First 50 raw digital bits: {''.join(bitstream[:50])}")
# Run the parser
parse_saleae_analog('./patched.bin')