Swiss Hacking Challenge 2023 - ezsignal
Information
Challenge category: misc
Challenge Description
More Hardware stuff. Can you SEE the flag?
Files
We are given an ezsignal.zip
file containing EZsignal.sal
file. It’s a Saleae Logic file, see my writeup of bbsignal
for more information on it.
Analysis
Overview
The file opened in Logic looks like the following:
We see three channels with very similar signals and another channel with a much slower signal that switches between the blocks of data in the other ones.
Based off the description “Can you SEE the flag?” the first three channels might be R/G/B and the third line means a newline aka. HSYNC in VGA terms.
Exporting the data
Logic sadly has no “VGA emulator” so I had to hack something together myself.
We can hit File > Export Data---
to generate a CSV file. I used the following settings:
- All channels selected
- Time Range:
All Time
- Export Format:
CSV
- Use ISO8601 timestamps:
no
Parsing the data
The python script I wrote will do the following:
- Read the CSV
- Loop through all of the CSV entries and split the data by rows if the fourth channel (3) is low
- Go through every row per line and draw pixels on an image (multiplied based on the timing in the first row (0))
- Save the image and open it
Here’s the code:
#!/usr/bin/python3
# Image generation
from PIL import Image, ImageDraw
import os
# Load the csv file into a dict of lists
def load_csv(filename):
with open(filename) as f:
data = f.readlines()
data = [row.strip().split(",") for row in data]
return data
# Open digital.csv
data = load_csv("digital.csv")
# Rows of image
pixelrows = []
# For each row in the csv file
current = []
# The image + draw object
img = Image.new("RGB", (1000, 1000), color="white")
draw = ImageDraw.Draw(img)
# remove header
data = data[1:]
for row in data:
# If the third channel is not 0 aka. next line
if row[2] == "1":
# Add the current row to the list
current.append(row)
# line break!
else:
# Add the current row to the list
pixelrows.append(current)
# Reset the current list
current = []
# we need to keep track of the position
pos = (0, 0)
# loop through rows
for row in pixelrows:
for pixel in row:
# print as many as time delay to previous one divided by 20ns
self_time = float(pixel[0])
try:
next_time = float(row[row.index(pixel) + 1][0])
delay = next_time - self_time
# delay is in seconds, so divide by 19ns (smallest time unit)
delay = delay / 0.000000019
except IndexError:
delay = 1
if pixel[1] == "1":
# create a line from pos + delay
draw.line((pos[0], pos[1], pos[0] + delay, pos[1]), fill="black", width=1)
# space to the next pixel
pos = (pos[0] + delay, pos[1])
# new line
pos = (0, pos[1] + 1)
# save the image
img.save("ezsignal.png")
# open the image
os.system("xdg-open ezsignal.png")
Flag
The generated image contains the flag written out:
Conclusion
Figuring out the timing thing was what cost me the most time. I also had issues identifying the signals in the beginning, was a very fun challenge tough and I learned some things about VGA.