Hack.lu 2k14 At Gunpoint - re200

Published on Thursday, 23 October 2014 in CTF, Security, Reverse Engineering ; tagged with hack.lu, ctf, write-up, reverse, 200, challenge, security, radare2, gameboy, rom, bgb, hackgyver ; text version

From Tuesday to Thursday, hack.lu 2014 CTF was taking place.

I wished I had more time to spend on the challenges. It could be nice to see this CTF over the weekend. Nevertheless the few challenges I did were fun, as expected from hack.lu.

Gunpoint Home Screen

I spent a couple of hours working on one specific challenge: At Gunpoint reverse engineering (200) and I thought I would do the write-up.

At Gunpoint - The challenge

From the web page, I downloaded the challenge and ran the obvious-first command-ever-been-run file:

depierre$ file gunpoint_2daf5fe3fb236b398ff9e5705a058a7f.dat
gunpoint_2daf5fe3fb236b398ff9e5705a058a7f.dat: Gameboy ROM: "FLUX", [ROM ONLY], ROM: 256Kbit

Oh... I never tried to reverse GameBoy ROM before so this could be fun :)

The challenge says that I should find the secret handshake, which means the secret inputs sequence since it is a GameBoy ROM.

Checking on Archlinux's AUR packages, I found VisualBoyAdvance but sadly, its debug mode wouldn't work (not F11, nor -d option). The other one I found is running on Windows and is named bgb.

Gunpoint Home Screen

When emulating the ROM, I have on my screen a badass pixel-art cowboy. Not knowing what do to, I try different inputs but nothing changes.

Gunpoint Warning Screen

After 2 minutes, the cowboy gets angry and I am warned with the message above.

Well, I have no idea what to do so let's ask radare2 :)

GameBoy and r2

I first check the entry point. According to the Internet, a GameBoy ROM will start at 0x100 as shown by r2.

From there I jump to the main function (identified as such by r2) starting at 0x150.

Sadly, I only read a lot of operations but I have no concrete idea on what they do. Showing the code wouldn't be wise since it occupies some pages.

Sooooooo... What to do? It is so disturbing to see for the first time a complete new ASM.

Even though some atomic operations are similar to what I am used to, such as xor a, call 0xdead, inc, dec and so on.

It feels like programming a GameBoy ROM is 90% ld / 10% other.

Sadly, r2 current GameBoy ROM support is pretty lame at detecting functions. For instance the last call 0x1b54 is not recognized as a function until using the af command (not even sure that it gives the right result).

[0x00000100]> pdf @ 0x1b54
Cannot find function at 0x00001b54
[0x00000100]> af @ 0x1b54
[0x00000100]> pdf @ 0x1b54
|          ; CALL XREF from 0x000001cb (fcn.00000123)
|          ; CALL XREF from 0x000020bd (fcn.000020af)
/ (fcn) fcn.00001b54 25
|          0x00001b54    e1           pop hl
|          0x00001b55    faabc0       ld a, [0xc0ab]
|          0x00001b58    f5           push af
|          0x00001b59    5e           ld e, [hl]
|          0x00001b5a    23           inc hl
|          0x00001b5b    56           ld d, [hl]
|          0x00001b5c    23           inc hl
|          0x00001b5d    2a           ldi a, [hl]
|          0x00001b5e    23           inc hl
|          0x00001b5f    e5           push hl
|          0x00001b60    eaabc0       ld [0xc0ab], a
|          ;  Bankswitch
|          0x00001b63    ea0020       ld [0x2000], a
|          0x00001b66    216d1b       ld hl, fcn.00001b6d
|          0x00001b69    e5           push hl
|          0x00001b6a    6b           ld l, e
|          0x00001b6b    62           ld h, d
\          0x00001b6c    e9           jp hl

I want to say that in this post, my use of r2 is pretty lame and not really nice. I mostly used pd and that is all (i.e. no function renaming, variable identification and co). Why? First, I was in a hurry when reversing the ROM and second, I wanted to keep the blog post as close as what really happened.

Anyway, the static analysis does not help me much right now. I will come back to that when I will understand how the ROM works.

ROM Profiler

What do I have so far? Not much to be honest.

When the ROM boots, a cowboy appears. After 2 minutes without the right inputs, a warning message is shown. That is all I know.

But even that is something. The ROM will wait for inputs until the correct sequence is found. That means that there should be some kind of interrupts and interrupt handler, where the interrupts are generated by the JOYPAD and the handler would be some check_flag-like function (or at least drive me close to that function).

According to the GameBoy CPU Manual, the halt instruction I see at the end of the main:

|          ; JMP XREF from 0x000001d3 (fcn.00000123)
|          0x000001d2    76           halt
\          0x000001d3    18fd         jr 0xfd ; (main)

Will halt the program until an interrupt is raised. So what I am looking for is some kind of a callback function that will retrieve the JOYPAD pressed button.

Interrupt handler

Thanks to r2, this is easy because I will only check where the JOYPAD is referenced and disassemble there.

[0x00000100]> CC*~JOYPAD
"CCu JOYPAD" @ 0x00001e63
"CCu JOYPAD" @ 0x00001e65
"CCu JOYPAD" @ 0x00001e67
"CCu JOYPAD" @ 0x00001e71
"CCu JOYPAD" @ 0x00001e73
"CCu JOYPAD" @ 0x00001e75
"CCu JOYPAD" @ 0x00001e77
"CCu JOYPAD" @ 0x00001e79
"CCu JOYPAD" @ 0x00001e7b
"CCu JOYPAD" @ 0x00001e7d
"CCu JOYPAD" @ 0x00001e88

Looking for a function around the first JOYPAD reference, r2 finds:

[0x00000100]> pdf @ 0x00001e63
|          ; CALL XREF from 0x00001e54 (fcn.00001e50)
|          ; CALL XREF from 0x00001e8d (fcn.00001e8d)
|          ; CALL XREF from 0x00001e94 (fcn.00001e94)
/ (fcn) fcn.00001e60 45
|          0x00001e60    c5           push bc
|          0x00001e61    3e20         ld a, 0x20
|          0x00001e63    e000         ld [0xff00], a             ; JOYPAD
|          0x00001e65    f000         ld a, [0xff00]             ; JOYPAD
|          0x00001e67    f000         ld a, [0xff00]             ; JOYPAD
|          0x00001e69    2f           cpl
|          0x00001e6a    e60f         and 0x0f
|          0x00001e6c    cb37         swap a
|          0x00001e6e    47           ld b, a
|          0x00001e6f    3e10         ld a, 0x10
|          0x00001e71    e000         ld [0xff00], a             ; JOYPAD
|          0x00001e73    f000         ld a, [0xff00]             ; JOYPAD
|          0x00001e75    f000         ld a, [0xff00]             ; JOYPAD
|          0x00001e77    f000         ld a, [0xff00]             ; JOYPAD
|          0x00001e79    f000         ld a, [0xff00]             ; JOYPAD
|          0x00001e7b    f000         ld a, [0xff00]             ; JOYPAD
|          0x00001e7d    f000         ld a, [0xff00]             ; JOYPAD
|          0x00001e7f    2f           cpl
|          0x00001e80    e60f         and 0x0f
|          0x00001e82    b0           or b
|          0x00001e83    cb37         swap a
|          0x00001e85    47           ld b, a
|          0x00001e86    3e30         ld a, 0x30
|          0x00001e88    e000         ld [0xff00], a             ; JOYPAD
|          0x00001e8a    78           ld a, b
|          0x00001e8b    c1           pop bc
\          0x00001e8c    c9           ret

I feel lucky but this snippet looks a lot alike the one from the CPU Manual, the one about the interrupts (page 36).

Setting a breakpoint on 0x00001e60 in bgb helps me to understand how this works. Depending on what JOYPAD button I press, the a register will vary.

For instance when pressing A:

BGB Joypad BGB Before Handler BGB After Handler

(Notice the a = 0x10)

When pressing B:

BGB Joypad 2 BGB Before Handler 2 BGB After Handler 2

(Notice the a = 0x20)

+-------------+--------------+
| $80 - Start | $40 - Select |
+-------------+--------------+
|   $4 - Up   |  $8 - Down   |
+-------------+--------------+
|  $2 - Left  |  $1 - Right  |
+-------------+--------------+
|   $20 - B   |    $10 - A   |
+-------------+--------------+

From the manual (page 37), A == 0x10 and B == 0x20. That means I found the interrupt hanlder, sweet :)

To interrupt handler, and beyond

Now that I have located the handler that retrieves the pressed button from the interruption, I simply follow the execution flow with bgb.

0x00001e60() ; main
0x00001e97    5f           ld e, a
0x00001e98    c9           ret

After retrieving the current pressed button into a, the ROM will save a into the e register:

Then it continues on 0xd72.

Meaning of the variables

0x00000d72    21a2c0       ld hl, 0xc0a2
0x00000d75    73           ld [hl], e
0x00000d76    21a1c0       ld hl, 0xc0a1
0x00000d79    7e           ld a, [hl]
0x00000d7a    21a2c0       ld hl, 0xc0a2
0x00000d7d    be           cp [hl]
0x00000d7e    2003         jr nZ, 0x03
0x00000d80    c3f60e       jp 0x0ef6

I do not know what is stored at 0xc0a2 nor 0xc0a1 but I will use bgb and set a write access breakpoint on these offsets.

BGB Access Breakpoint

When resuming, bgb stops here:

0x00000ef1    21a0c0       ld hl, 0xc0a0
0x00000ef4    3600         ld [hl], 0x00  ; 0xc0ac <- 0
0x00000ef6    21a2c0       ld hl, 0xc0a2
0x00000ef9    7e           ld a, [hl]     ; a <- 0xc0a2
0x00000efa    21a1c0       ld hl, 0xc0a1
0x00000efd    77           ld [hl], a     ; 0xc0a1 <- a
0x00000efe    c36f0d       jp 0x0d6f
0x00000f01    c9           ret

From the values stored in 0xc0a2 that I monitored using the emulator, it corresponds to the current pressed button.

Therefore, the previous snippet:

That means the rest of the function only runs when the user presses a button different from the previous one (a way to trigger the rest when the first input is met).

0x00000d7d    be           cp [hl]
0x00000d7e    2003         jr nZ, 0x03
; . . .
0x00000d83    21a0c0       ld hl, 0xc0a0
0x00000d86    7e           ld a, [hl]
0x00000d87    fe0d         cp 0x0d
0x00000d89    c2990d       jp nZ, 0x0d99

Here I can see once again 0xc0a0 which is compared to 0x0d (13). It really looks like a counter (let's say i) and that the secret handshake is composed of 13 different inputs (0 to 12).

So far:

The algorithm

0x00000d87    fe0d         cp 0x0d
0x00000d89    c2990d       jp nZ, 0x0d99
; . . .
0x00000d99    21a2c0       ld hl, 0xc0a2
0x00000d9c    7e           ld a, [hl]       ; get current input
0x00000d9d    e604         and 0x04         ; is it UP?
0x00000d9f    2003         jr nZ, 0x03      ; YES
0x00000da1    c3c90d       jp 0x0dc9
0x00000da4    21a0c0       ld hl, 0xc0a0
0x00000da7    7e           ld a, [hl]       ; retrieve i
0x00000da8    b7           or a             ; is i == 0 ?
0x00000da9    caba0d       jp Z, 0x0dba     ; YES
0x00000dac    21a0c0       ld hl, 0xc0a0
0x00000daf    7e           ld a, [hl]       ; retrieve i
0x00000db0    fe04         cp 0x04          ; is i == 4?
0x00000db2    c2c10d       jp nZ, 0x0dc1    ; NO then goto RESET i
0x00000db5    1803         jr 0x03
0x00000db7    c3c10d       jp 0x0dc1
0x00000dba    21a0c0       ld hl, 0xc0a0
0x00000dbd    34           inc [hl]         ; i += 1
0x00000dbe    c38a0e       jp 0x0e8a        ; goto end
0x00000dc1    21a0c0       ld hl, 0xc0a0
0x00000dc4    3600         ld [hl], 0x00    ; RESET i
0x00000dc6    c38a0e       jp 0x0e8a        ; goto end

First it checks if the current input corresponds to UP. If so, is it the first or the fifth input? If they are then 0xc0a0 is incremented.

The rest of that function is very similar. It checks which button is currently pressed (one after the other) and what is the current value of i (to check if it is the correct sequence).

In other words, it looks like:

if input == UP:
    if sequence_index == 0 or sequence_index == 4:
        sequence_index += 1
    else
        sequence_index = 0
elif input == RIGHT:
    if sequence_index == 1 or sequence_index == 5:
        sequence_index += 1
    else
        sequence_index = 0
# . . .

Using a piece of paper and a pencil, I find the following inputs:

Up Right Down Left Up Right Down Left B B A A Start

Gunpoint Flag Screen

Hence the flag, sweet sweet flag:

tkCXDJheQDNRN

\o/


contactdepier.re License WTFPL2