i give up on can
This commit is contained in:
51
2026/cscg/misc/can-i-have-flag/analyze_export.py
Normal file
51
2026/cscg/misc/can-i-have-flag/analyze_export.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import csv
|
||||||
|
|
||||||
|
# 1. Load the raw edges from the CSV
|
||||||
|
edges = []
|
||||||
|
with open('capture.csv', 'r') as f:
|
||||||
|
reader = csv.reader(f)
|
||||||
|
next(reader) # Skip the header row
|
||||||
|
for row in reader:
|
||||||
|
# row[0] is time in seconds, row[1] is logic state (0 or 1)
|
||||||
|
edges.append((float(row[0]), int(row[1])))
|
||||||
|
|
||||||
|
# 2. Reconstruct the physical bitstream (1 Mbps = 1 microsecond per bit)
|
||||||
|
bit_time = 1e-6
|
||||||
|
bits = []
|
||||||
|
|
||||||
|
for i in range(len(edges)-1):
|
||||||
|
duration = edges[i+1][0] - edges[i][0]
|
||||||
|
num_bits = int(round(duration / bit_time))
|
||||||
|
bits.extend([edges[i][1]] * num_bits)
|
||||||
|
|
||||||
|
# 3. Parse the CAN frames directly (Ignoring Bit Stuffing Rules)
|
||||||
|
idx = 0
|
||||||
|
print("Hunting for payloads...\n")
|
||||||
|
|
||||||
|
while idx < len(bits) - 50:
|
||||||
|
# Look for Start of Frame (0) after an idle bus (1)
|
||||||
|
if bits[idx] == 0 and (idx == 0 or bits[idx-1] == 1):
|
||||||
|
idx += 1 # Skip SOF
|
||||||
|
|
||||||
|
id_bits = bits[idx : idx+11] # 11-bit ID
|
||||||
|
idx += 11
|
||||||
|
|
||||||
|
idx += 3 # Skip RTR, IDE, and r0 control bits
|
||||||
|
|
||||||
|
dlc_bits = bits[idx : idx+4] # 4-bit Data Length Code
|
||||||
|
dlc = int("".join(map(str, dlc_bits)), 2)
|
||||||
|
idx += 4
|
||||||
|
|
||||||
|
# If payload exists, decode it into ASCII
|
||||||
|
if 0 < dlc <= 8:
|
||||||
|
payload = []
|
||||||
|
for _ in range(dlc):
|
||||||
|
byte_bits = bits[idx : idx+8]
|
||||||
|
byte_val = int("".join(map(str, byte_bits)), 2)
|
||||||
|
payload.append(chr(byte_val))
|
||||||
|
idx += 8
|
||||||
|
print(f"Decoded Payload: {''.join(payload)}")
|
||||||
|
else:
|
||||||
|
idx += 1 # Step forward if it was a false start
|
||||||
|
else:
|
||||||
|
idx += 1
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import struct
|
|
||||||
|
|
||||||
with open("digital-0.bin", "rb") as f:
|
|
||||||
data = f.read()
|
|
||||||
|
|
||||||
num_transitions = (len(data) - 44) // 8
|
|
||||||
|
|
||||||
# Read integers as little-endian (normal), but doubles as big-endian
|
|
||||||
initial_state = struct.unpack_from('<I', data, 16)[0]
|
|
||||||
begin_time = struct.unpack_from('>d', data, 20)[0] # big-endian
|
|
||||||
end_time = struct.unpack_from('>d', data, 28)[0] # big-endian
|
|
||||||
|
|
||||||
print(f"initial_state={initial_state}, begin={begin_time:.9f}, end={end_time:.9f}")
|
|
||||||
|
|
||||||
transitions = []
|
|
||||||
for i in range(num_transitions):
|
|
||||||
t = struct.unpack_from('>d', data, 44 + i * 8)[0]
|
|
||||||
transitions.append(t)
|
|
||||||
|
|
||||||
for i, t in enumerate(transitions[:20]):
|
|
||||||
print(f" [{i}] {t:.9f}s")
|
|
||||||
4585
2026/cscg/misc/can-i-have-flag/capture.csv
Normal file
4585
2026/cscg/misc/can-i-have-flag/capture.csv
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,23 +0,0 @@
|
|||||||
import struct
|
|
||||||
|
|
||||||
with open("digital-0.bin", "rb") as f:
|
|
||||||
data = f.read()
|
|
||||||
|
|
||||||
# Skip header (already known to be mangled)
|
|
||||||
offset = 16
|
|
||||||
|
|
||||||
# Try Version 0 interpretation
|
|
||||||
v0_initial_state = struct.unpack_from('<I', data, offset)[0]
|
|
||||||
v0_begin_time = struct.unpack_from('<d', data, offset + 4)[0]
|
|
||||||
v0_end_time = struct.unpack_from('<d', data, offset + 12)[0]
|
|
||||||
v0_num_trans = struct.unpack_from('<Q', data, offset + 20)[0]
|
|
||||||
print(f"V0: init={v0_initial_state}, begin={v0_begin_time:.4f}, end={v0_end_time:.4f}, transitions={v0_num_trans}")
|
|
||||||
|
|
||||||
# Try Version 1 interpretation
|
|
||||||
v1_chunk_count = struct.unpack_from('<Q', data, offset)[0]
|
|
||||||
v1_initial_state = struct.unpack_from('<I', data, offset + 8)[0]
|
|
||||||
v1_sample_rate = struct.unpack_from('<d', data, offset + 12)[0]
|
|
||||||
v1_begin_time = struct.unpack_from('<d', data, offset + 20)[0]
|
|
||||||
v1_end_time = struct.unpack_from('<d', data, offset + 28)[0]
|
|
||||||
v1_num_trans = struct.unpack_from('<Q', data, offset + 36)[0]
|
|
||||||
print(f"V1: chunks={v1_chunk_count}, init={v1_initial_state}, rate={v1_sample_rate:.0f}, begin={v1_begin_time:.4f}, end={v1_end_time:.4f}, transitions={v1_num_trans}")
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import struct
|
|
||||||
|
|
||||||
with open("digital-0.bin", "rb") as f:
|
|
||||||
data = f.read()
|
|
||||||
|
|
||||||
num_transitions = (len(data) - 44) // 8
|
|
||||||
SAMPLE_RATE = 125_000_000
|
|
||||||
|
|
||||||
initial_state = struct.unpack_from('<I', data, 16)[0]
|
|
||||||
print(f"initial_state={initial_state}")
|
|
||||||
|
|
||||||
transitions = []
|
|
||||||
for i in range(num_transitions):
|
|
||||||
raw = struct.unpack_from('<Q', data, 44 + i * 8)[0] # uint64 little-endian
|
|
||||||
t = raw / SAMPLE_RATE
|
|
||||||
transitions.append(t)
|
|
||||||
if i < 30:
|
|
||||||
print(f" [{i}] raw={raw}, time={t:.9f}s")
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
from collections import Counter
|
|
||||||
|
|
||||||
|
|
||||||
HEADER = bytes.fromhex("4769495E417C4370")
|
|
||||||
|
|
||||||
|
|
||||||
idx = []
|
|
||||||
|
|
||||||
with open("./digital-0.bin", "rb") as f:
|
|
||||||
data = f.read()
|
|
||||||
four_bytes = [data[i:i+4] for i in range(0, len(data), 4)]
|
|
||||||
eight_bytes = [data[i:i+8] for i in range(0, len(data), 8)]
|
|
||||||
|
|
||||||
counts_four = Counter(four_bytes)
|
|
||||||
counts_eight = Counter(eight_bytes)
|
|
||||||
|
|
||||||
print(counts_four)
|
|
||||||
print(counts_eight)
|
|
||||||
|
|
||||||
for i in range(len(idx)):
|
|
||||||
if i > 0:
|
|
||||||
print(idx[i] - idx[i-1])
|
|
||||||
else:
|
|
||||||
print(idx[i] )
|
|
||||||
|
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,17 +0,0 @@
|
|||||||
import struct
|
|
||||||
|
|
||||||
with open("digital-0.bin", "rb") as f:
|
|
||||||
data = f.read()
|
|
||||||
|
|
||||||
file_size = len(data)
|
|
||||||
num_transitions = (file_size - 44) // 8
|
|
||||||
|
|
||||||
print(f"File size: {file_size}")
|
|
||||||
print(f"Computed transitions: {num_transitions}")
|
|
||||||
|
|
||||||
# Parse transitions
|
|
||||||
transitions = struct.unpack_from(f'<{num_transitions}d', data, 44)
|
|
||||||
|
|
||||||
# Print first few
|
|
||||||
for i, t in enumerate(transitions[:20]):
|
|
||||||
print(f" [{i}] {t:.9f}s")
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
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')
|
|
||||||
Reference in New Issue
Block a user