Writeup of my unintended solve of the Gotta Dump ‘Em All misc challenge @ k!nd4SUS CTF 2025 I played with fibonhack.




1. Intro#

We are given a .sav file, which is a save file from a Nintendo DS Pokémon game.

The challenge description asks us to retrieve the names of the Pokémon lost by the trainer.

Using a tool like PKHeX (or any online Pokémon save file analyzer) we can identify the game version (Diamond and Pearl).

We notice there is only one Pokémon in our party, and another one in the PC box that has a “corrupted” nickname.




2. Structure of the Pokémon save files#

The save file structure changes with every Pokémon game generation. In our case (Generation 4), the save file is broken up into two save slots (primary and backup). Each slot has one general (small) block and one storage (big) block.

  • First slot addresses: 0x00000 - 0x3FFFF
  • Second slot addresses: 0x40000 - 0x7FFFF

The game doesn’t always read from slot 1. Instead, it checks the Save Counter located in the 20-byte footer of each block.

  1. The engine calculates the CRC-16-CCITT checksum of the data block.
  2. It compares the checksum with the value stored in the footer.
  3. If both slots are valid, it loads the one with the highest Save Counter.



3. Solve#

I initially thought I needed to recover Sandslash’s nickname by fixing its PID.

A Pokémon’s PID (Personality Value) defines how its data block is encrypted and shuffled.

So, I spent a lot of time researching the save file structure and how to reproduce the algorithms used to encrypt the data and correctly reorder the blocks.
I also tried to run the game in an emulator, load the save file and see if I could retrieve useful info or find a way to inject a correct PID.

Note
Loading this save in an emulator requires a small tweak.
You’ll need to use a save file editor to update the Hall of Fame date: if it precedes the game’s initial start date, the entire save data will not be recognized.


I discovered this by accident; I don’t believe it’s written anywhere in the docs online.

If you start the game, you won’t gain any useful info. But wait, what if the second slot contains the correct Pokémon?
I tried swapping the two slots, but the one with the corrupted Pokémon will always be loaded because of its Save Counter priority. The save data is not seen as corrupted, so it’s processed by the game as a valid one.

Then I thought that if I just copied the second slot (the “correct” slot) and pasted it into the first one, maybe the correct data would be loaded!
I tried it using an online hex editor and it worked, lol.
By copying the entire second slot, the first one will contain the correct Pokémon data along with the correct checksum and save counter. There are two perfectly valid and identical slots with the same priority, and the game will simply load the first one.

So, there was no need to write complex scripts to fix the blocks! Our target Pokémon will be the ones in the player’s party!

The intended solve was to reproduce the checksum algorithm to correctly fix the data blocks.
This challenge was really interesting!

You can find more info about the save file structure here:#