OverTheWire Advent Bonanza 2019 Challenge 14 - tiny runes Writeup

Another writeup for a challenge imitating game asset reverse engineering as a part of OverTheWire’s Advent Bonanza 2019. Day 14 of the Advent Bonanza challenges was given as follows:

tiny runes
Points: 137
Solves: 106
One of Santa’s Little Helpers received an unusual Christmas wish, a copy of the yet to be released Deus Hex game. All they managed to find were fragments from the dialogue system. Can you decode the last one?
Download: d4037209017d4730edc598fe62e6b17f5573ee259b6ad7c8723bac962cf0b328-tiny-runes.tar.gz


Downloading and extracting the file gave the following file structure:

1
2
3
4
5
6
7
8
9
10
11
$ tar -xvzf d4037209017d4730edc598fe62e6b17f5573ee259b6ad7c8723bac962cf0b328-tiny-runes.tar.gz
x lines1.bin
x lines1.png
x lines1.txt
x lines2.bin
x lines2.png
x lines2.txt
x lines3.bin
x lines3.png
x lines3.txt
x lines4.bin

The general idea with this challenge seems to be that the .bin files are the raw game assets. The .png and the .txt files are the decoded assets, respectively. The flag was contained in the decoding of lines4.bin which has not yet been decoded.

To start, I took a look at each of the accompanying .png and .txt files.

lines1.png

lines1.txt

1
2
JC Denton: "Paul... I... I..."
JC Denton: "I thought you were a GEP gun."

lines2.png

lines2.txt

1
2
3
4
Paul Denton: "We want to power down the whole system."
Lobster: "That will be your butt."
Paul Denton: "Yes sir!"
Lobster: "Get PILLS against my orders! Get moving!"

lines3.png

lines3.txt

1
2
Agent Orange: "I do not move out of the way."
JC Denton: "Forgive my interruption, my vision is augmented."

Sure enough, the files match up in their text. I then took a look at the .bin files:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$ file lines1.bin
lines1.bin: data
$ binwalk lines1.bin

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
36 0x24 PNG image, 64 x 96, 1-bit colormap, non-interlaced
95 0x5F Zlib compressed data, best compression

$ foremost lines1.bin
Processing: lines1.bin
|*|
$ cat output/audit.txt

... snip ...

------------------------------------------------------------------
File: lines1.bin
... snip ...
Length: 965 B (965 bytes)

Num Name (bs=512) Size File Offset Comment

0: 00000000.png 769 B 36 (64 x 96)
... snip ...

1 FILES EXTRACTED

png:= 1
------------------------------------------------------------------

$ file output/png/00000000.png
00000000.png: PNG image data, 64 x 96, 1-bit colormap, non-interlaced

It looks like the .bin files have a .png embedded in them. What is interesting is that all four provided .bin files have the exact same .png embedded in them at the same location. This .png looks like:

00000000.png

Given that the image is 64 x 96 pixels and the image shows rows of 8 characters and columns of 12 characters, I could only assume that there was some sort of indexing scheme into this image which would then build the resulting asset. The key would be figuring out how this indexing is done. I ran hexdump -C on each .bin file and output this to individual files lines1.hex, lines2.hex, and lines3.hex and ran sdiff -s to compare them in pairs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ sdiff -s lines1.hex lines2.hex 
00000010 00 08 00 08 08 0c 00 48 00 02 00 2a 54 78 54 72 | | 00000010 00 08 00 08 08 0c 00 a2 00 04 00 36 54 78 54 72 |
00000320 44 ae 42 60 82 4c 69 4e 65 00 00 00 3c 05 01 03 | | 00000320 44 ae 42 60 82 4c 69 4e 65 00 00 00 6c 00 04 03 |
00000330 0a 04 08 06 09 03 07 00 05 00 0a 01 0b 00 05 02 | | 00000330 0b 07 0a 05 0b 04 08 06 09 03 07 00 05 00 0a 01 |
00000340 07 04 08 06 07 00 04 03 0b 07 0a 05 0b 05 09 05 | | 00000340 0b 00 05 02 07 04 08 06 07 04 09 03 07 04 08 00 |
00000350 09 05 09 04 08 02 05 05 09 05 09 05 09 04 08 02 | | 00000350 06 03 0b 00 05 00 0a 04 08 00 0a 01 0b 04 08 02 |
00000360 05 05 09 05 09 05 09 06 07 4c 69 4e 65 00 00 00 | | 00000360 06 01 0b 00 06 03 07 07 03 04 08 01 03 01 0b 00 |
00000370 54 05 01 03 0a 04 08 06 09 03 07 00 05 00 0a 01 | | 00000370 06 00 05 04 08 00 0a 00 09 03 07 04 08 00 06 00 |
00000380 0b 00 05 02 07 04 08 06 07 02 05 04 08 00 0a 00 | | 00000380 09 01 0b 05 0b 03 07 04 08 00 02 05 07 00 02 00 |
00000390 09 01 0b 07 0a 01 07 00 09 00 0a 04 08 05 07 01 | | 00000390 0a 03 07 04 02 05 09 06 07 4c 69 4e 65 00 00 00 |
000003a0 0b 07 0a 04 08 00 06 03 07 07 03 03 07 04 08 03 | | 000003a0 44 02 01 01 0b 04 07 00 02 00 0a 03 07 07 03 02 |
000003b0 0b 04 08 01 06 04 03 00 04 04 08 01 07 07 0a 00 | | 000003b0 07 04 08 06 07 04 0a 00 09 03 0b 00 0a 04 08 00 |
000003c0 05 05 09 06 07 | | 000003c0 06 01 04 05 0b 05 0b 04 08 04 07 03 07 04 08 05 |
000003c5 | 000003d0 07 01 0b 07 0a 07 03 04 08 04 07 07 0a 00 0a 00 |
> 000003e0 0a 05 09 06 07 4c 69 4e 65 00 00 00 2e 00 04 03 |
> 000003f0 0b 07 0a 05 0b 04 08 06 09 03 07 00 05 00 0a 01 |
> 00000400 0b 00 05 02 07 04 08 06 07 06 00 03 07 00 02 04 |
> 00000410 08 00 02 01 04 07 03 05 08 06 07 4c 69 4e 65 00 |
> 00000420 00 00 66 02 01 01 0b 04 07 00 02 00 0a 03 07 07 |
> 00000430 03 02 07 04 08 06 07 01 06 03 07 00 0a 04 08 00 |
> 00000440 04 02 05 02 01 02 01 06 06 04 08 03 0b 01 07 03 |
> 00000450 0b 01 04 00 05 00 02 00 0a 04 08 04 02 05 07 04 |
> 00000460 08 01 0b 07 03 01 03 03 07 07 03 00 02 05 08 04 |
> 00000470 08 01 06 03 07 00 0a 04 08 04 02 01 0b 07 07 01 |
> 00000480 04 00 05 01 07 05 08 06 07 |
> 00000489
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ sdiff -s lines1.hex lines3.hex 
00000010 00 08 00 08 08 0c 00 48 00 02 00 2a 54 78 54 72 | | 00000010 00 08 00 08 08 0c 00 6a 00 02 00 3d 54 78 54 72 |
00000320 44 ae 42 60 82 4c 69 4e 65 00 00 00 3c 05 01 03 | | 00000320 44 ae 42 60 82 4c 69 4e 65 00 00 00 5a 07 04 01 |
00000330 0a 04 08 06 09 03 07 00 05 00 0a 01 0b 00 05 02 | | 00000330 07 03 07 00 05 00 0a 04 08 04 05 07 03 03 0b 00 |
00000340 07 04 08 06 07 00 04 03 0b 07 0a 05 0b 05 09 05 | | 00000340 05 01 07 03 07 02 07 04 08 06 07 02 05 04 08 01 |
00000350 09 05 09 04 08 02 05 05 09 05 09 05 09 04 08 02 | | 00000350 03 01 0b 04 08 00 05 01 0b 00 0a 04 08 04 02 01 |
00000360 05 05 09 05 09 05 09 06 07 4c 69 4e 65 00 00 00 | | 00000360 0b 07 07 03 07 04 08 01 0b 07 0a 00 0a 04 08 01 |
00000370 54 05 01 03 0a 04 08 06 09 03 07 00 05 00 0a 01 | | 00000370 0b 06 02 04 08 00 0a 00 09 03 07 04 08 00 06 03 |
00000380 0b 00 05 02 07 04 08 06 07 02 05 04 08 00 0a 00 | | 00000380 0b 05 07 05 09 06 07 4c 69 4e 65 00 00 00 7a 05 |
00000390 09 01 0b 07 0a 01 07 00 09 00 0a 04 08 05 07 01 | | 00000390 01 03 0a 04 08 06 09 03 07 00 05 00 0a 01 0b 00 |
000003a0 0b 07 0a 04 08 00 06 03 07 07 03 03 07 04 08 03 | | 000003a0 05 02 07 04 08 06 07 00 07 01 0b 07 03 01 07 01 |
000003b0 0b 04 08 01 06 04 03 00 04 04 08 01 07 07 0a 00 | | 000003b0 04 07 07 03 07 04 08 04 02 05 07 04 08 01 04 00 |
000003c0 05 05 09 06 07 | | 000003c0 05 00 0a 03 07 07 03 07 03 07 0a 02 06 00 0a 01 |
000003c5 | 000003d0 04 01 0b 00 05 07 00 04 08 04 02 05 07 04 08 07 |
> 000003e0 07 01 04 00 02 01 04 01 0b 00 05 04 08 01 04 00 |
> 000003f0 02 04 08 03 0b 07 0a 01 07 04 02 03 07 00 05 00 |
> 00000400 0a 03 07 01 03 05 09 06 07 |
> 00000409
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ sdiff -s lines2.hex lines3.hex 
00000010 00 08 00 08 08 0c 00 a2 00 04 00 36 54 78 54 72 | | 00000010 00 08 00 08 08 0c 00 6a 00 02 00 3d 54 78 54 72 |
00000320 44 ae 42 60 82 4c 69 4e 65 00 00 00 6c 00 04 03 | | 00000320 44 ae 42 60 82 4c 69 4e 65 00 00 00 5a 07 04 01 |
00000330 0b 07 0a 05 0b 04 08 06 09 03 07 00 05 00 0a 01 | | 00000330 07 03 07 00 05 00 0a 04 08 04 05 07 03 03 0b 00 |
00000340 0b 00 05 02 07 04 08 06 07 04 09 03 07 04 08 00 | | 00000340 05 01 07 03 07 02 07 04 08 06 07 02 05 04 08 01 |
00000350 06 03 0b 00 05 00 0a 04 08 00 0a 01 0b 04 08 02 | | 00000350 03 01 0b 04 08 00 05 01 0b 00 0a 04 08 04 02 01 |
00000360 06 01 0b 00 06 03 07 07 03 04 08 01 03 01 0b 00 | | 00000360 0b 07 07 03 07 04 08 01 0b 07 0a 00 0a 04 08 01 |
00000370 06 00 05 04 08 00 0a 00 09 03 07 04 08 00 06 00 | | 00000370 0b 06 02 04 08 00 0a 00 09 03 07 04 08 00 06 03 |
00000380 09 01 0b 05 0b 03 07 04 08 00 02 05 07 00 02 00 | | 00000380 0b 05 07 05 09 06 07 4c 69 4e 65 00 00 00 7a 05 |
00000390 0a 03 07 04 02 05 09 06 07 4c 69 4e 65 00 00 00 | | 00000390 01 03 0a 04 08 06 09 03 07 00 05 00 0a 01 0b 00 |
000003a0 44 02 01 01 0b 04 07 00 02 00 0a 03 07 07 03 02 | | 000003a0 05 02 07 04 08 06 07 00 07 01 0b 07 03 01 07 01 |
000003b0 07 04 08 06 07 04 0a 00 09 03 0b 00 0a 04 08 00 | | 000003b0 04 07 07 03 07 04 08 04 02 05 07 04 08 01 04 00 |
000003c0 06 01 04 05 0b 05 0b 04 08 04 07 03 07 04 08 05 | | 000003c0 05 00 0a 03 07 07 03 07 03 07 0a 02 06 00 0a 01 |
000003d0 07 01 0b 07 0a 07 03 04 08 04 07 07 0a 00 0a 00 | | 000003d0 04 01 0b 00 05 07 00 04 08 04 02 05 07 04 08 07 |
000003e0 0a 05 09 06 07 4c 69 4e 65 00 00 00 2e 00 04 03 | | 000003e0 07 01 04 00 02 01 04 01 0b 00 05 04 08 01 04 00 |
000003f0 0b 07 0a 05 0b 04 08 06 09 03 07 00 05 00 0a 01 | | 000003f0 02 04 08 03 0b 07 0a 01 07 04 02 03 07 00 05 00 |
00000400 0b 00 05 02 07 04 08 06 07 06 00 03 07 00 02 04 | | 00000400 0a 03 07 01 03 05 09 06 07 |
00000410 08 00 02 01 04 07 03 05 08 06 07 4c 69 4e 65 00 | | 00000409
00000420 00 00 66 02 01 01 0b 04 07 00 02 00 0a 03 07 07 | <
00000430 03 02 07 04 08 06 07 01 06 03 07 00 0a 04 08 00 | <
00000440 04 02 05 02 01 02 01 06 06 04 08 03 0b 01 07 03 | <
00000450 0b 01 04 00 05 00 02 00 0a 04 08 04 02 05 07 04 | <
00000460 08 01 0b 07 03 01 03 03 07 07 03 00 02 05 08 04 | <
00000470 08 01 06 03 07 00 0a 04 08 04 02 01 0b 07 07 01 | <
00000480 04 00 05 01 07 05 08 06 07 | <
00000489 <

Interestingly, it looks like the files only differ from bytes 0x17-0x1b and then from bytes 0x32c and onwards. Looking more closely, I saw that there are a lot of bytes in the range of 0x00 and 0x0b, inclusive. If I treat the .png file found in all of the .bin files as a grid, the columns can be indexed by 0x00 to 0x07 (8 columns) and the rows can be indexed by 0x00 to 0x0b (12 rows). Starting at byte 0x32d of file lines2.bin, the byte pairs are (0x00, 0x04), (0x03, 0x0b), (0x07, 0x0a). Matching these to characters in the reference .png, I get the letters Pau which matches the first 3 characters of lines2.txt!

When looking at the ASCII of the .bin files, I saw that preceding each line of text were the ASCII characters LiNe, which must have denoted a new line of the text.

With all of that knowledge, I wrote the following script to extract out the bytes after byte 0x325 in the file and perform the decoding:

solve.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import sys

data = ['Q?0\\H$Y,', 'R-L^KJ?k', 's#_/m=f9', '7d-NE4qr', 'Pi?V`&XA', 'n3I?O*;Z', 'wGpB8cSj', 'Fg:eby"v', '%+?1 !M@', 'h{2xW.D}', 'tU|CTz6u', '|o>a5l<\'']

res = []
lines = open('lines4.bin', 'rb').read()[0x325:]

spl = lines.split('LiNe')

for line in spl:
if line:
working_set = line[4:]
constructed_line = ''
for i in range(0, len(working_set), 2):
constructed_line += data[ord(working_set[i+1])][ord(working_set[i])]
constructed_line += '\n'
res += constructed_line

print ''.join(res)

Running the above script outputs the flag:

1
2
3
$ python solve.py
Jock: "Oh my God! JC! A flag!"
JC Denton: "A flag! AOTW{wh4t_4_r0tt3n_fi13_f0rm4t}"

A fun challenge which is a bit of a toy version of what one would find when reversing actual game asset files!