Overview

Three objectives show up in my badge with no location information on them:

image-20240103203429655

Each just says to find the cartridge and beat the game, with difficulties of 1, 3, and 3 respectively. Each is located on a different island.

Elf The Dwarf’s, Gloriously, Unfinished, Adventure! - Vol1!

Getting To

The first cartridge is located at Tarnished Trove on the Island of Misfit Toys, which is located on the goose’s leg:

image-20240103203651859

Location Layout

Tarnished Trover has a bunch of misfit toys, but nothing super interesting beyond a game cartridge:

image-20231227171914448Click for full size image

Challenge

Dusty Giftwrap stands near the dock with a hint about buried treasure:

Dusty Giftwrap

Dusty Giftwrap

Arrr, matey, shiver me timbers! There be buried treasure herrrrre.

Just kidding, I’m not really a pirate, I was just hoping it would make finding the treasure easier.

I guess you heard about the fabled buried treasure, too? I didn’t expect to see anyone else here. This uncharted islet was hard to find.

There are 3 buried treasures in total, each in its own uncharted area around Geese Islands.

I’ve been searching for hours now with no luck, and these strange toys are starting to give me the creeps.

Maybe you’ll be able to find it. Here, use my Gameboy Cartridge Detector. Go into your items and test it to make sure it’s still working.

When you get close to the treasure, it’ll start sounding off. The closer you get, the louder the sound.

No need to activate or fiddle with it. It just works!

I bet one of these creepy toys has the treasure, and I’m sure not going anywhere near them!

If you find the treasure, come back and show me, and I’ll tell you what I was able to research about it.

Good luck!

The detector is in my Badge Items:

image-20231226132339582

For what it’s worth, whichever of the three elves at the three cartridge locations I talk to first will provide the detector.

Find Cartridge

When I approach the hat that looks like it may belong to Ed at the top left of the island, the Game Boy Cartridge Detector starts beaping.

image-20231227172705067

Once I walk over it, I get the cartridge in the Items area of my badge:

image-20231227172724731

The button opens the game.

Before playing, I’ll talk again to Dusty:

Dusty Giftwrap

Dusty Giftwrap

Whoa, you found it!

It’s a… video game cartridge? Coooooollll… I mean, arrrrrr….

So, here’s what my research uncovered. Not sure what it all means, maybe you can make sense of it.

Dusty provides some hints:

  1. Giving things a little push never hurts.
  2. Out of sight but not out of ear-shot
  3. You think you fixed the QR code? Did you scan it and see where it leads?

Game

The game loads with a splash screen that includes the keyboard buttons that map to Game Boy keys:

image-20231227173056399

After a bit of story, I’m sent out as Elf the Dwarf, eventually finding a big area with a QRcode, Kody the dog at the top.

image-20231227205916841

There are 7 blocks out of place in the QRCode. I need to sing (B-button, which is “r” or “x” on the keyboard) which will shoot out music notes, and if one hits a block that needs moving, it will light up and show where it should be:

image-20231227210033896

I’m able to push blocks into place:

image-20231227210115276

It is possible to just play the game without cheating, and once I find and fix all seven, it zooms out and gives (a completely different) QRCode:

image-20231227205425257

The decodes to “8bitelf.com”.

oxdf@hacky$ curl https://8bitelf.com/
<html>
        <body>
                <p>flag:santaconfusedgivingplanetsqrcode</p>
        </body>
</html>

Entering that flag into the badge completes the challenge.

Cheats

Find Bindings

In looking at the game in the dev console, there’s a big block of code starting at line 478 in script.js that handles binding keys to functions. At the very top, there are keys that aren’t in the help at the top of the screen:

  bindKeys() {
    this.keyFuncs = {
      Backspace: this.keyRewind.bind(this),
      " ": this.keyPause.bind(this),
      "[": this.keyPrevPalette.bind(this),
      "]": this.keyNextPalette.bind(this),
    };

Space doesn’t seem to do anything, but the other three do.

Color Palette

”[” and “]” change the color palette. Most of the color palettes change things universally, but some of them have different colors for the blocks that need to be moved. For example, in this section, there are four blocks that need moving with a redish color as opposed to the others in black:

image-20231227210720322

This makes walking through the blocks much easier.

Rewind

The backspace key is mapped to a “rewind” function. If I push a block by accident, rather than starting over, I can hold down backspace to rewind:

Elf The Dwarf’s, Gloriously, Unfinished, Adventure! - Vol2!

Getting To

The second cartridge is located in Driftbit Grotto, which is located on the north side of Pixel Island:

image-20231227170041721

Location Layout

Much like Rainraster Cliffs, the topology presents a 2-d space with movement left and right, and up and down:

image-20231227170529613Click for full size image

Despite the large appearance with lots of levels, the only place I can walk is on the level with the boat and Tinsel.

Challenge

Tinsel tells me about another cartridge located in this cavern:

Tinsel Upatree

Tinsel Upatree

I can’t believe I was actually able to find this underground cavern!

I discovered what looked liike an old pirate map in the attic of one of those huts in Rainraster Cliffs, and it actually led somewhere!

But now that I’ve seen where it leads, I think this might’ve been a bad idea. This place is scary! Maybe you want to take it from here?

I’m sure that cartridge is right nearby. Start walking around!

Once you run into it, check back with me and I’ll tell you what I know about winning.

Good luck!

Walking to the left-most point in the map provides the cartridge in the badge’s Items list:

image-20231227213524969

Tinsel is impressed:

Tinsel Upatree

Tinsel Upatree

Whoa, you found it!

What version is it?

Did you know that many games had multiple versions released? Word is: volume 2 has 2 versions!

Game

The game starts with some dialog before I walk out into an open field where T-Wiz is waiting by the only gap in the trees:

image-20231228064618797

If I try to walk up through the path, T-Wiz says:

image-20231228064710648 image-20231228064730836 image-20231228064750469

And then my character walks back to where I came.

Versions

Identify

If I load the game a few more times in the browser, I’ll notice that sometimes the world is mirrored:

image-20231228065105459

In this world, I’m trying to go down through a gap, and T-Wiz is blocking.

Source

In the dev console, there’s a script.js that manages loading the emulator:

image-20231228065212439

binjgb.js and binjgb.wasm seem like they are likely this Webassembly Game Boy emulator, which make them less interesting things to study at this point.

At line 140 of script.js, there’s this function that loads the Game Boy ROM:

// Load a ROM.
(async function go() {
  let ranNum = Math.round(Math.random()).toString()
  let filename = ROM_FILENAME + ranNum + ".gb";
  console.log(filename);
  let response = await fetch(filename);
  let romBuffer = await response.arrayBuffer();
  const extRam = new Uint8Array(JSON.parse(localStorage.getItem("extram")));
  Emulator.start(await binjgbPromise, romBuffer, extRam);
  emulator.setBuiltinPalette(vm.palIdx);
})();

ROM_FILENAME is set to “rom/game” at line 10. Math.random() will get a number between 0 and 1, and then Math.round() will round that to 0 or 1. If I watch in the Network tab and refresh a few times, I’ll see it download rom/game0.gb and rom/game1.gb.

image-20231228065642697

Comparison

I’ll download both files to my VM and compare them. Both are (unsurprisingly) Game Boy ROM images:

oxdf@hacky$ file game?.gb
game0.gb: Game Boy ROM image: "VOL" (Rev.01) [MBC5+RAM+BATT], ROM: 1Mbit, RAM: 256Kbit
game1.gb: Game Boy ROM image: "VOL" (Rev.01) [MBC5+RAM+BATT], ROM: 1Mbit, RAM: 256Kbit

Both are exactly the same size:

oxdf@hacky$ stat -c %s game?.gb
131072
131072

A trick I love to do binary comparisons takes advantage of the process substitution operator in bash, <(). Whatever command is inside the () is run, and the results are handled as if they were in a file. So I’ll run diff <(xxd file1) <(xxd file 2). This will create hex dumps of each file, and then compare the results and show the lines that are different.

For these ROMs, it’s not very much:

oxdf@hacky$ diff <(xxd game0.gb) <(xxd game1.gb )
21c21
< 00000140: 0000 0000 3030 001b 0203 0033 0142 71b3  ....00.....3.Bq.
---
> 00000140: 0000 0000 3030 001b 0203 0033 0142 7186  ....00.....3.Bq.
90c90
< 00000590: 5405 050b 4b9a 2300 0000 0000 06ad 4210  T...K.#.......B.
---
> 00000590: 5405 05d2 ac3d 2d00 0000 0000 06ad 4210  T....=-.......B.
5801c5801
< 00016a80: 2080 0c80 0300 000f f807 0000 0000 0f10   ...............
---
> 00016a80: 2080 0c80 0b00 000f f807 0000 0000 0f10   ...............
5804c5804
< 00016ab0: 0000 0000 2000 0600 0900 000f f807 0000  .... ...........
---
> 00016ab0: 0000 0000 2000 0600 0600 000f f807 0000  .... ...........
6089c6089
< 00017c80: 0200 fe80 002a 0013 fffe fffb 13ff ffff  .....*..........
---
> 00017c80: 0100 fe80 002a 0013 fffe fffb 13ff ffff  .....*..........
6225,6226c6225,6226
< 00018500: 1204 2103 c60d 5701 1400 00ff fc14 0280  ..!...W.........
< 00018510: fffd 140b 80ff fe35 fffc 3200 fffc 2703  .......5..2...'.
---
> 00018500: 1204 2103 c60d 5701 1400 00ff fc14 0300  ..!...W.........
> 00018510: fffd 1404 00ff fe35 fffc 3200 fffc 2703  .......5..2...'.

Bypass Wizard

I’ll show two ways to hack this ROM and get past T-Wiz.

Swap Redirection

So little is different between the two ROMs, it must be that the differences above are what determine which map I get. It could be that both worlds exist, and something above is a flag that tells the game which to use. Or both maps exist, and the data that’s different defines starting positions for my Elf and T-Wiz.

I’ll create a copy of game0.gb and try playing with it in a hex editor. The bytes at offset 0x17c80 is just a 02 vs a 01. I’ll change the 02 in my game0-mod.gb to the value from game1.gb, 01 and save it. I’ll load the modified ROM in an emulator (I’m using bgb, which is a Windows .exe, but with wine runs on Linux as well). When I load the ROM, it shows a warning:

image-20231228071457810

The game loads fine, and when I get to T-Wiz, he still says the same thing, but then my character is forced up through the gap:

In fact, it won’t let me walk back to the bottom, instead pushing me up to the top.

Modify String

For some reason, just messing with the “You shall not pass!!!” string is enough to have T-Wiz not block the path. I’ll create a fresh copy of the game0.gb (called game0-mod.gb) and open it in a hex editor. I’ll find the string and remove the “not”, adding nulls to the end to make sure not to change the overall length of the binary:

image-20231227224815307

Now T-Wiz doesn’t say anything and I walk right on through.

Decode Morse

On the other side there’s a portal:

image-20231228110501807

Entering leads to a room with two items. On the left, ChatNPT says:

image-20231228110558571

Interacting with the radio stops the music and starts a series of long and short beeps. After about 15 seconds, there’s a longer break, and then it loops.

I’ll record the beeps and upload it to a morse code decoder site:

image-20231227224351552

The flag is “gl0ry”.

Elf The Dwarf’s, Gloriously, Unfinished, Adventure! - Vol3!

Getting To

The third cartridge if located on Steampunk Island, at Rust Quay on the goose’s backside under a wing:

image-20240103211959786

Location Layout

The area is almost P shaped, with a rusty maze making up the top part:

image-20231228150621608Click for full size image

Challenge

Angel Candysalt tells me about the Gameboy Cartridge treasure:

Angel Candysalt

Angel Candysalt

The name’s Angel Candysalt, the great treasure hunter!

A euphemism? No, why do people always ask me that??

Anyways, I came here to nab the treasure hidden in this ship graveyard, only to discover it’s protected by this rusted maze.

That must be why all these old ships are here. Their crew came to find the treasure, only to get lost in the labyrinth.

At least it’s obvious where this one is. See that shiny spot over to the right? That’s gotta be where it is! If only I had a bird’s eye view.

But how to get there? Up? Down? Left? Right? Oh well, that’s your problem now!

Come back if you can find your way to it, and I’ll tell you some secrets I’ve heard about this one.

Get Cartridge

Locate Cartridge

The cartridge is relatively easy to find, as it’s visible in the screen while I’m talking to Angel:

image-20231226132459621

Using the TamperMonkey script I wrote last year also shows it’s coordinates, and that it’s basically at the same row as Angel:

image-20231226132545220

Navigating the maze is a bit trickier. I’ll open the browser dev tools and go to the network tab. There, I’ll select only “img”:

image-20231226132804915

I’ll refresh the page, and in the body it’ll show all the HTTP requests for images:

image-20231226132907707

spi-rustyquay_floor.png seems like a good starting place. Double-clicking it opens https://2023.holidayhackchallenge.com/images/fabric/spi-rustyquay_floor.png in a new tab where I get the raw image. I’ll open it in a image editor and draw out the path from the game cartridge back out:

Once I navigate the maze and get close, the beep noise from the detector is quite loud:

image-20231226134001624

When I pick up the item, it shows up under Items in my badge:

image-20231228111638847

More From Angel

Angel is impressed:

Angel Candysalt

Angel Candysalt

The life of a treasure hunter isn’t easy, but it sure is exciting!

Oh it’s a video game, I love video games! But you’ve claimed this treasure, nicely done.

Now, about those secrets I’ve been told. They’re pretty cryptic, but they are. Hopefully that helps with something!

These challenges don’t have to be solved in order, but because I now have all three, Angel adds:

Angel Candysalt

Angel Candysalt

You have all three? Wow, you must be the greatest treasure hunter that ever lived!

Talking to Angel unlocks three hints:

  1. This one is a bit long, it never hurts to save your progress!
  2. 8bit systems have much smaller registers than you’re used to.
  3. Isn’t this great?!? The coins are OVERFLOWing in their abundance.

Game

Overview

This time, Elf is in a 2-D left/right scroll game with coins and little monsters:

image-20231228141116615

Jumping on the little monsters kills them, though running into them kills you. The coins are worth either 1, 10, or 100 coins, which updates the counter at the bottom left.

After three stretches of coins and monsters, I meet Jared:

image-20231228141749211

At the end of this stretch, there’s just an empty void (and jumping into it just sends Elf back to the start):

image-20231228141846432

999 Coins

It seems clear I need 999 coins. The challenge is that when I try to collect these honestly, when I get the coin that would put me at 999, an error message comes up:

image-20231228142227012

Then my coins are set to 0:

image-20231228142244739

Hacking

Find Coin Addresses

bgb, the Gameboy emulator I’m using, has a “cheat searcher” function that’s really neat (there’s a nice walkthrough on this page in the documentation). The idea is that you start with all the memory values, and then over time filter out ones that don’t match my expectation. Given that these are 8-bit registers, I’m going to look for three bytes, each controlling ones, tens, and hundreds.

I’ll start the game with 000 coins, and find “cheat searcher” in the right-click menu:

image-20231228142634388

The window has a big open space with buttons at the bottom:

image-20231228142814819

I’ll select 8-bit values and click start. All the bytes from memory and their values are populated into the space:

image-20231228142853378

I’ll note there are tons of values here (see the size of the scrollbar). It doesn’t have to be true, but I’ll start by assuming that 0 coins means some byte has 0 value. So I’ll set the “keep values which are” to “equal to” and then “this value: 0”, and click search:

image-20231228143107271

There’s still a lot, but less. I’ll play the game and grab a coin, giving me 001. I’ll update “this value:” to 1, and click search:

image-20231228143206141

There are only six bytes in memory that have followed my requirements. I’ll avoid the 10 and 100 coins, and grab some more 1 coins, updating each time, until there are only two bytes left:

image-20231228143330927

I can do the same thing with the 10- and the 100-coins, until I have six bytes, two sets of three, that seem to correspond to the coins:

ones tens hundreds
0xC0F8 0xC12C 0xC160
0xCBA2 0xCB9C 0xCB9E

A lot of experimentation leads me to think that the first set is something associated with the display, whereas the second is the actual value of coins. Having just the first hacked is not enough to win the game, but just the second is.

Fix Coins

I’ll right-click on an address and select “go here in debugger”:

image-20231228143741155

It’s in the dump window at the bottom of the debugger:

image-20231228143815250

Right clicking on the byte offers “Freeze ram address”:

image-20231228143858403

I’ll give it the value of 9, and now it shows up as yellow in the debugger memory:

image-20231228143928318

Changing the first set of addresses above (that I think are related to the display) updates the display immediately, where as just setting the second only updates the next time a coin is collected. I’ll freeze the other two bytes from the second set of addresses as well:

image-20231228144109516

Now when I grab a coin, the counter doesn’t change, but rather stays at 999 (though it may through the error again, but still stays at 999).

Success

I’ll play the game through to the end of the level where the gap was before. It’s very useful to use the memory snapshot features in the emulator. For example, in bgb, F2 will save the current state, and F4 will reload it. So I just save every 10 seconds or so, and if I die, F4 takes me back without much lost progress.

At the end, there’s now a platform where before there was none:

image-20231228144500762

On the other side, there’s a door. Inside I’ll find Tom Listen, ChatNPT, and a heavy rock:

image-20231228144629496

There’s a good bit of dialog with Liston, and then he gives a passphrase to give ChatNPT:

image-20231228145723786 expand

ChatNPT accepts it, and makes the rock movable:

image-20231228145912118

Now I can push it aside and reveal stairs:

image-20231228150213967

Tom joins down the stairs and the flag is shown on the screen:

image-20231228150231782