Tags: logic-analyzer keylogger hardware atari 

Rating: 0

**Description**

> We have a photo and a CSV file. NOTE: The flag does not follow the CTF{...} format, but is clearly marked as the flag. Please add the CTF{...} around the flag manually when submitting.

**Files provided**

- [a ZIP file](https://github.com/Aurel300/empirectf/blob/master/writeups/2018-06-23-Google-CTF-Quals/files/wired-csv.zip) containing:
- `data.7z` - archive containing `data.csv`
- `wires.jpg`

**Solution**

Looking at the photo given:

![](https://github.com/Aurel300/empirectf/raw/master/writeups/2018-06-23-Google-CTF-Quals/screens/wires.jpg)

We can see a Saleae logic analyser connected via some jumper cables and probes to a chip. The text on the chip reads:

AMI 8327VT
CO12294B-01
CO3051
© KOREA

With a quick search for the above strings, we can identify the chip as [Atari POKEY](https://en.wikipedia.org/wiki/Atari_POKEY). This chip has many functions on the Atari 8-bit computers. It is probably most notable for being a great sound synthesiser chip (for its price and time).

Here is a pin-out diagram for the chip:

![](https://github.com/Aurel300/empirectf/raw/master/writeups/2018-06-23-Google-CTF-Quals/screens/wires-pinout.png)

Based on the notch in the top-left corner of the chip, we can figure out which pins of the chip are being probed with the logic analyser. In the top-left corner, two probes are connected to the VCC, which should just be a constant +5V (within tolerance).

More importantly, 8 probes are connected to the pins `K0`, `K1`, ..., `K5`, `KR1` and `KR2`. According to [this datasheet](http://krap.pl/mirrorz/atari/homepage.ntlworld.com/kryten_droid/Atari/800XL/atari_hw/pokey.htm#keyboard%20scan), these pins are related to the keyboard scan functionality of the POKEY.

The keyboard can be thought of as an 8x8 grid. Wires connect the rows and the columns of this grid. At each intersection of horizontal and vertical wire, there is a switch (i.e. the keyboard key). To figure out whether or not a particular key is currently being held, its column is pulled high. If the key is held, this connects the column to the row. Then we just check whether the key's row is high.

The above process is automated in the Atari computers with the assistance of the POKEY chip. Three lines (`K0`, `K1`, and `K2`) lead to a 3-to-8 multiplexer, which interprets the three inputs as a binary number and outputs a digital high on one of its eight outputs (connected to the columns of keyboard grid).

Three other lines (`K3`, `K4`, and `K5`) are connected to a 8-way selector. Once again, the three lines are interpreted as a binary number. One of the selector's 8 inputs (connected to the rows of the keyboard grid) are selected based on the binary number and its value is then available as `KR1`.

![](https://github.com/Aurel300/empirectf/raw/master/writeups/2018-06-23-Google-CTF-Quals/screens/wires-grid.png)

Naturally, the computer does not know which keys to check beforehand; all keys need to be checked all the time. So the POKEY emits a repeating binary sequence on its `K0` ... `K5` pins, decoding a different key everytime the number changes. The numbers change at 15.7 KHz, so with 64 keys in total, this means all the keys of the keyboard are checked roughly every 4ms.

So we know what is going on with the pins that are being probed. Given that we are looking for a flag, we can expect that somebody typed the flag on the Atari keyboard and the logic analyser recorded this interaction happening. Let's finally have a look at the CSV file.

$ head data.csv
Time [s],Wire6-Analog,Wire7-Analog, Time [s],Wire0-Digital, Time [s],Wire1-Digital, Time [s],Wire2-Digital, Time [s],Wire3-Digital, Time [s],Wire4-Digital, Time [s],Wire5-Digital, Time [s],Wire6-Digital, Time [s],Wire7-Digital
0.000000000000000, 4.768121242523193, 4.773899555206299, 0.000000000000000, 0, 0.000000000000000, 0, 0.000000000000000, 0, 0.000000000000000, 1, 0.000000000000000, 0, 0.000000000000000, 0, 0.000000000000000, 1, 0.000000000000000, 1
0.000008000000000, 4.768121242523193, 4.773899555206299, 0.000000990000000, 1, 0.000065560000000, 1, 0.000194380000000, 1, 0.000451750000000, 0, 0.000452070000000, 1, 0.001480790000000, 1, 1.468471380000000, 0, 2.503182740000000, 0
0.000016000000000, 4.773141384124756, 4.778934478759766, 0.000065230000000, 0, 0.000194070000000, 0, 0.000451450000000, 0, 0.000965990000000, 1, 0.001480440000000, 0, 0.003537600000000, 0, 1.468535670000000, 1, 2.503689840000000, 1
0.000024000000000, 4.773141384124756, 4.773899555206299, 0.000129540000000, 1, 0.000322660000000, 1, 0.000708600000000, 1, 0.001480170000000, 0, 0.002508920000000, 1, 0.005594510000000, 1, 1.472585100000000, 0, 2.507288860000000, 0
0.000032000000000, 4.773141384124756, 4.773899555206299, 0.000193780000000, 0, 0.000451180000000, 0, 0.000965660000000, 0, 0.001994420000000, 1, 0.003537300000000, 0, 0.007651320000000, 0, 1.472649390000000, 1, 2.507799430000000, 1
0.000040000000000, 4.773141384124756, 4.773899555206299, 0.000258100000000, 1, 0.000579770000000, 1, 0.001222810000000, 1, 0.002508600000000, 0, 0.004565780000000, 1, 0.009708230000000, 1, 1.476698830000000, 0, 2.511395640000000, 0
0.000048000000000, 4.778161048889160, 4.778934478759766, 0.000322340000000, 0, 0.000708280000000, 0, 0.001479880000000, 0, 0.003022850000000, 1, 0.005594170000000, 0, 0.011765040000000, 0, 1.476763110000000, 1, 2.511904320000000, 1
0.000056000000000, 4.778161048889160, 4.778934478759766, 0.000386650000000, 1, 0.000836870000000, 1, 0.001737030000000, 1, 0.003537030000000, 0, 0.006622640000000, 1, 0.013821950000000, 1, 1.480812550000000, 0, 2.515507680000000, 0
0.000064000000000, 4.773141384124756, 4.778934478759766, 0.000450890000000, 0, 0.000965390000000, 0, 0.001994100000000, 0, 0.004051280000000, 1, 0.007651030000000, 0, 0.015878770000000, 0, 1.480876830000000, 1, 2.516015620000000, 1

The format is easy enough to parse, but there is a problem - the digital wires are not labelled. Which wire is `K0`? Which is the `KR1` output?

Well, `K0` through `K5` emit a repeating binary sequence, generated by the POKEY that is completely independent of any user input. We should be able to find this sequence easily:

![](https://github.com/Aurel300/empirectf/raw/master/writeups/2018-06-23-Google-CTF-Quals/screens/wires-keyscan.png)

In the CSV file, each of the digital wires has its own set of timestamps, and only changes in the detected values are shown. Our first step is to take samples at 15.7 KHz with the correct current values of each of the digital wires. The simplest way I could think of implementing this is to have a separate file reader per wire, reading lines whenever needed, otherwise reporting the last value.

One tiny detail you might notice from the pinout diagram above is that pin names are prefixed with `!` - we simply need to invert them to get the "actual" value. We still need to pay careful attention to the notation, since sometimes `K` values are used directly, sometimes `!K` values are specified.

With this we can visualise a sample of the data captured on the digital wires:

([visualisation script](https://github.com/Aurel300/empirectf/blob/master/writeups/2018-06-23-Google-CTF-Quals/scripts/wired-csv/VisualiseWires.hx))

$ haxe --run VisualiseWires
▌▌▌ ▌▌
▌▌ ▌▌
▌ ▌ ▌▌
▌ ▌▌
▌▌ ▌▌
▌ ▌▌
▌ ▌▌
▌▌
▌▌▌▌ ▌
▌▌▌ ▌
▌ ▌▌ ▌
▌▌ ▌
▌▌ ▌ ▌
▌ ▌ ▌
▌ ▌ ▌
▌ ▌
▌▌▌ ▌
▌▌ ▌
▌ ▌ ▌
▌ ▌

It is quite clear that the first six digital wires are, in order, `K0` through `K5`, since `K0` changes the most rapidly, `K1` half as fast, and so forth.

Now we simply need to see what number is indicated by `K0` ... `K5` whenever `KR1` (or rather its inverse) goes high. We need to use a table like [this](https://www.atariarchives.org/c3ba/page004.php).

But there is another problem: printing out the keys whenever `KR1` is high produces almost unreadable data:

([raw decoder script](https://github.com/Aurel300/empirectf/blob/master/writeups/2018-06-23-Google-CTF-Quals/scripts/wired-csv/RawCodes.hx))

$ haxe -main RawCodes -neko rc.n
$ neko rc.n
F F F F F L L L L L A G > S CAPS D > A G > S CAPS D > A G > G G G G G ; ; ; ; ;
(etc)

There is a lot of garbage data there, but we can see `FLAG` in the beginning, which should be a good sign. The problem is that each letter is repeated multiple times. But this actually makes sense - what if a letter is held for longer than a single full keyboard scan? It will be decoded multiple times. Considering the keyboard is fully scanned each 4ms, it is not very surprising that a key press would be registered on multiple scans.

Additionally, we see some data that is not just letters being repeated. Circuits are not perfect, and we can see some glitches as a result of this. Looking through the actual data, it seems many "ghost" keys are registered immediately after "real" keys are pressed. Another useful observation is that the keys that actually spell out the flag are typed relatively slowly.

So let's only print out key presses which happen at least 100ms after any other, and remove duplicates:

([fixed decoder script](https://github.com/Aurel300/empirectf/blob/master/writeups/2018-06-23-Google-CTF-Quals/scripts/wired-csv/Codes.hx))

$ haxe -main Codes -neko c.n
$ neko c.n
F L S G ; 8 - B I T - H A R D W S R E - K E Y L O G E R

It is not perfect, but it is close. `A` got misread as `S` in the word `FLAG`, and it seems the same happened in the word `HARDWARE` as well. The `G` got de-duplicated in `KEYLOGGER` with our script. And indeed, probing the pins of the POKEY chip is a form of a hardware keylogger!

`CTF{8-BIT-HARDWARE-KEYLOGGER}`