Juniors CTF 2016 - Joy500 Oldschool NES Rom Write Up

Published on Sunday, 27 November 2016 in CTF, Security ; tagged with juniors, ctf, write-up, misc, 500, challenge, nes, rom ; text version

I played the Juniors CTF 2016 this weekend with some friends of mine and it was quite fun! Since we missed the registration deadline, I sneakily joined the Securimag team and played with them.

Oldschool challenge

One of the challenges was a modified NES ROM of Castlevania II - Simon's Quest. The challenge was not really hard after all but @xarkes and I thought that it was refreshing to play with a NES ROM for once :)

NES ROM: Castlevania II - Simon's Quest

Based on the challenge description, we supposed that we had to finish the game in order to get the flag. The first thing we did was downloading the original ROM and diffing the two files:

depierre$ radiff2 Castlevania-II_eng.nes Castlevania\ II\ -\ Simon\'s\ Quest\ \(USA\).nes 
0x000125c6 d3cad8edccc6d2cad8ed0101 => c6d1d9cdd4dacccd01d9cdca 0x000125c6
0x000125d5 c6d7caeddc => c8d4d3cbd7 0x000125d5
0x000125dc c9cad7cbdad1 => d9c6d9ced4d3 0x000125dc
0x000125e5 01010101010101 => c7cad9dccacad3 0x000125e5
0x000125ed 0101010101 => d8ced2d4d3 0x000125ed
0x000125f5 010101 => c6d3c9 0x000125f5
0x000125f9 01010101010101 => c9d7c6c8dad1c6 0x000125f9
0x00012603 010101 => cdc6d8 0x00012603
0x00012607 01010101010101010101 => c8d4d3c8d1dac9cac9ec 0x00012607
0x00012614 0101010101 => d8ced2d4d3 0x00012614
0x0001261a 0101010101010101 => c8d4dad1c9d3eed9 0x0001261a
0x00012625 01010101010101 => d8dad7dbcedbca 0x00012625
0x0001262d 010101 => cdced8 0x0001262d
0x00012633 0101010101 => cbc6d9c6d1 0x00012633
0x00012639 01010101010101 => dcd4dad3c9d8eb 0x00012639
0x00012643 0101010101010101010101010101 => d9d7c6d3d8ded1dbc6d3cec6eed8 0x00012643
0x00012654 01010101 => d4d3d1de 0x00012654
0x00012659 01010101 => cdd4d5ca 0x00012659
0x0001265e 0101 => ced8 0x0001265e
0x00012661 01 => c6 0x00012661
0x00012665 0101010101 => ded4dad3cc 0x00012665
0x0001266b 010101 => d2c6d3 0x0001266b
0x0001266f 010101 => dccdd4 0x0001266f
0x00012675 01010101 => dcced1d1 0x00012675
0x0001267a 01010101010101 => d9d7cedad2d5cd 0x0001267a
0x00012684 01010101 => d4dbcad7 0x00012684
0x00012689 01010101 => cadbced1 0x00012689
0x0001268e 010101 => c6d3c9 0x0001268e
0x00012694 010101 => d7cec9 0x00012694
0x00012698 010101 => d9cdca 0x00012698
0x0001269c 01010101 => c8ced9de 0x0001269c
0x000126a3 0101 => d4cb 0x000126a3
0x000126a6 010101010101010101 => c9d7c6c8dad1c6eed8 0x000126a6
0x000126b2 010101010101 => c9cac6c9d1de 0x000126b2
0x000126b9 010101010101 => c8dad7d8caeb 0x000126b9

The patch is very small. Since the two ROMs are very similar so we thought that:

What was patched?

We found a tool named NESRomTool that can extract the PRG (program data) as well as the CHR (character data).

Running NESRomTool on each ROM and using binwally to diff the files showed that there was no difference in the sprites:

depierre$ ./NESRomTool -f Castlevania-II_eng.nes -xs +a +a f1
depierre$ ./NESRomTool -f Castlevania\ II\ -\ Simon\'s\ Quest\ \(USA\).nes -xs +a +a f2
depierre$ ./binwally.py f1 f2 | grep -i differs
depierre$

It would mean that the code of the ROM has been patched maybe. So we diffed the program data:

depierre$ ./NESRomTool -f Castlevania-II_eng.nes -xp +a g1
depierre$ ./NESRomTool -f Castlevania\ II\ -\ Simon\'s\ Quest\ \(USA\).nes -xp +a g2
depierre$ ./binwally.py g1 g2 | grep -i differs
99 differs Castlevania-II_eng.nes.5.prg
depierre$

Trying to dissassemble the patch didn't yield anything meaningful to us, mostly due to the fact that NES ASM is quite obscure (in a sense that you need to understand how the NES handles banks, sprites, sounds, etc. otherwise you have no idea what the code is doing).

Instead, we decided to play the game. Or more specifically, have someone very, very good play for us :)

Who could beat the game every single time and as fast as possible? TAS of course!

The latest TAS finishes the game in 29:45.02 and I am sure that the NES emulator will allow us to increase the speed of the emulation so we can go down to a couple of minutes only ;)

We used FCEUX in order to emulate the ROM, since it seemed to be the only one capable of supporting the .fcm TAS file:

TAS ending

Sadly, there was no flag showing up when finishing the game... Even slowing down the emulation speed to 3% (in case the flag was only shown for a frame or two) didn't help. What the hell man? Did they lie about the challenge?

Using the emulator, we decided to patch the patch and replace everything with the BRK opcode, hoping that the game will freeze or crash when reaching the end:

Patched patched ROM

But nothing froze, nothing crashed...

No but really, what was patched?

Ok, let's take a step back. What is that patch?

It doesn't seem to be a patch for the sprites or a patch for the code. We decided that maybe it was time for us to better understand how the Castelvania ROM was organized.

We looked on the Internet trying to find some information about the offset 0x000125c6 and if it had any specific meaning.

And we found it:

124CC-127BF - Ending Dialog

They only patched the ending dialogs of the ROM. But how come don't we see the flag when running the TAS? Let's parse the new dialogs anyway...

Using the information from the wiki, we parsed the new dialogues with a quick and dirty python script:

table = {
    '\x01': ' ', '\x02': '/', '\x21': '\\', '\xC6': 'A', '\xC7': 'B', '\xC8': 'C',
    '\xC9': 'D', '\xCA': 'E', '\xCB': 'F', '\xCC': 'G', '\xCD': 'H', '\xCE': 'I', '\xCF': 'J',
    '\xD0': 'K', '\xD1': 'L', '\xD2': 'M', '\xD3': 'N', '\xD4': 'O', '\xD5': 'P', '\xD6': 'Q',
    '\xD7': 'R', '\xD8': 'S', '\xD9': 'T', '\xDA': 'U', '\xDB': 'V', '\xDC': 'W', '\xDD': 'X',
    '\xDE': 'Y', '\xDF': 'Z', '\xE0': '0', '\xE1': '1', '\xE2': '2', '\xE3': '3', '\xE4': '4',
    '\xE5': '5', '\xE6': '6', '\xE7': '7', '\xE8': '8', '\xE9': '9', '\xEA': '?', '\xEB': '.',
    '\xEC': ',', '\xED': '-', '\xEE': '\'', '\xEF': '&'}

o_beg = 0x124cc
o_end = 0x127bf
ending = ''
raw_ending = ''

with open('Castlevania-II_eng.nes', 'rb') as f:
    f.read(o_beg)
    raw_ending = f.read(o_end - o_beg)

for i in range(0, len(raw_ending), 1):
    ending += table.get(raw_ending[i:i+1], ' ')

print ending

And lo and behold!

depierre$ python2 read.py
THE BATTLE HAS/ 'CONSUMMATED./\ NOW PEACE AND/\ SERENITY HAVE   BEEN RESTORED/ 'TO TRANSYLVANIA/\ AND THE PEOPLE/\ ARE FREE OF   DRACULA'S/
 'CURSE FOREVER./\ AND YOU, SIMON/\ BELMONT, WILL   ALWAYS BE/ 'REMEMBERED FOR/\ YOUR BRAVERY/\ AND COURAGE.   NES-GAMES-  / 'ARE-WONDERFUL/
\              /\                             / '              /\            /\                               / '              /
\              /\                             / '            /\             /\                 THE ENCOUNTER/ 'WITH DRACULA/\ IS TERMINATED./
\ SIMON BELMONT   HAS PUT AN END/ 'TO THE ETERNAL/\ DARKNESS IN/\ TRANSYLVANIA.   HIS BLOOD AND/ 'SWEAT HAVE/\ PENETRATED THE/
\ EARTH AND WILL   INDUCE MAGIC &/ 'HAPPINESS FOR/\ THOSE WHO WALK/\ ON THIS LAND

Flag: NES-GAMES-ARE-WONDERFUL

Indeed, Castlevania has 3 endings...

Castlevania II - Simon's Quest has in fact 3 different endings, depending on how fast you finish the game. Since the TAS we used finishes the game as fast as possible, we got the best ending. But the flag was only shown for the normal ending...

I feel sorry if any of the teams played the game too well and didn't get the flag at the end :D

Juniors 2016 Scoreboard

By the end of the CTF, we solved quite a lot of challenges and reached the 5th place :)


contactdepier.re License WTFPL2