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.
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.
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.
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
:
(Notice the a = 0x10
)
When pressing B
:
(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.
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:
0xc0a2 <- e
(remember thate
is the current input)a <- 0xc0a1
(retrieve the previous input)a != e?
(if they are equals, it jumps further and exit)
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:
0xc0a0
is the index of the current input0xc0a1
is the previous user input0xc0a2
is the current user input
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
Hence the flag, sweet sweet flag:
tkCXDJheQDNRN
\o/