<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://jamchamb.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://jamchamb.net/" rel="alternate" type="text/html" /><updated>2025-11-25T18:50:37+00:00</updated><id>https://jamchamb.net/feed.xml</id><title type="html">jamchamb’s blog</title><subtitle>jamchamb&apos;s blog</subtitle><entry><title type="html">Modifying Embedded Filesystems in ARM Linux zImages</title><link href="https://jamchamb.net/2022/01/02/modify-vmlinuz-arm.html" rel="alternate" type="text/html" title="Modifying Embedded Filesystems in ARM Linux zImages" /><published>2022-01-02T00:00:00+00:00</published><updated>2022-01-02T00:00:00+00:00</updated><id>https://jamchamb.net/2022/01/02/modify-vmlinuz-arm</id><content type="html" xml:base="https://jamchamb.net/2022/01/02/modify-vmlinuz-arm.html"><![CDATA[<p>Ever run <code class="language-plaintext highlighter-rouge">binwalk</code> on an embedded Linux device’s kernel image and find
its entire fileystem contained inside? Ever want to change one little line
inside to enable root shell on that device that’s just mocking you with
its lack of boot security, only to be thwarted by a bit of compressed data
entangled in machine code?</p>

<p>The mechanism for this built-in filesystem is known as the initial ramdisk,<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>
which often takes the form of a CPIO archive (<code class="language-plaintext highlighter-rouge">initramfs</code>).
The initial ramdisk is embedded in the kernel binary proper (<code class="language-plaintext highlighter-rouge">vmlinux</code>), which is in
turn compressed and packed into a wrapper program (<code class="language-plaintext highlighter-rouge">vmlinuz</code>, <code class="language-plaintext highlighter-rouge">zImage</code>, <code class="language-plaintext highlighter-rouge">bzImage</code>).
The wrapper performs initial setup, decompresses <code class="language-plaintext highlighter-rouge">vmlinux</code>, and then jumps into it.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>
The compressed <code class="language-plaintext highlighter-rouge">vmlinux</code> blob tends to be referred to as the “piggy” in Linux boot code.</p>

<p>While it’s quite easy to run <code class="language-plaintext highlighter-rouge">extract-vmlinux</code> or <code class="language-plaintext highlighter-rouge">binwalk</code> on these
kernel images and unleash a flood of shell scripts, config files, and programs
that one might have many reasons to want to modify, figuring out how to
package these files back up into an image fit for execution is not so straightforward.</p>

<p>This article will demonstrate how to replace the piggy in a 32-bit ARM zImage
without worrying about size constraints or finding the right toolchain and exact
configuration options necessary to recompile the <code class="language-plaintext highlighter-rouge">vmlinuz</code> wrapper code.
It’s not intended as a universal solution, but rather a guide that should
provide one with enough understanding to make whatever tweaks needed for their
specific use case.</p>

<p>While this information is intended to help a neighbor make modifications
to proprietary kernel images that they can’t simply rebuild from source,
I’ve decided to use the 32-bit ARM virt build of OpenWRT for demonstration purposes.
May a thin layer of obfuscation by compression never get in the way of your
path to proofs of concept again!</p>

<h2 id="setup">Setup</h2>

<p>First, download the OpenWRT ARM virt zImage-initramfs image:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ wget -q https://downloads.openwrt.org/releases/17.01.0/targets/armvirt/generic/lede-17.01.0-r3205-59508e3-armvirt-zImage-initramfs -O zImage-initramfs
$ sha256sum zImage-initramfs
5ad269e95b2db16aea3794dd0e97dabb6f9712184d79b0764bb10a810f8d7639  zImage-initramfs
</code></pre></div></div>

<p>We can boot this in qemu with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ qemu-system-arm -serial stdio -M virt -m 1024 -kernel zImage-initramfs
</code></pre></div></div>

<p>After booting, press enter to activate the console. The following banner is displayed:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BusyBox v1.25.1 () built-in shell (ash)

     _________
    /        /\      _    ___ ___  ___
   /  LE    /  \    | |  | __|   \| __|
  /    DE  /    \   | |__| _|| |) | _|
 /________/  LE  \  |____|___|___/|___|                      lede-project.org
 \        \   DE /
  \    LE  \    /  -----------------------------------------------------------
   \  DE    \  /    Reboot (17.01.0, r3205-59508e3)
    \________\/    -----------------------------------------------------------

=== WARNING! =====================================
There is no root password defined on this device!
Use the "passwd" command to set up a new password
in order to prevent unauthorized SSH logins.
--------------------------------------------------
root@LEDE:/#
</code></pre></div></div>

<p>This looks like a good target for a proof of concept modification.
Let’s use the shell to check the base Linux kernel version:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@LEDE:/# uname -a
Linux LEDE 4.4.50 #0 SMP Mon Feb 20 17:13:44 2017 armv7l GNU/Linux
</code></pre></div></div>

<p>The core pieces of the zImage wrapper code are unlikely to change much
from the original, if at all, so we can look up the assembly source of the
wrapper for that version of Linux. Bootlin’s Elixir Cross Referencer provides
a nice interface for browsing Linux source code across different versions.
Open a browser tab and navigate to <a href="https://elixir.bootlin.com/linux/v4.4.50/source/">https://elixir.bootlin.com/linux/v4.4.50/source/</a>,
or clone the linux repo to search through the code.</p>

<p>Most of the files we’re interested in can be found in the <code class="language-plaintext highlighter-rouge">arch/arm/boot/compressed</code>
directory.</p>

<h2 id="piggy-extraction">Piggy Extraction</h2>

<p>We need to extract the piggy before we can modify it.
The well known <code class="language-plaintext highlighter-rouge">extract-vmlinux</code> script<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> performs a brute
force search for the magic bytes of commonly used compression
schemes, runs the associated decompressor program on them,
and checks if the output is an ELF.</p>

<p>For this image it fails – we’ll see why in a minute.
<code class="language-plaintext highlighter-rouge">binwalk</code> identifies XZ compressed data:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ binwalk zImage-initramfs

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Linux kernel ARM boot executable zImage (little-endian)
15400         0x3C28          xz compressed data
15632         0x3D10          xz compressed data
</code></pre></div></div>

<p>There’s clearly an XZ stream header magic at <code class="language-plaintext highlighter-rouge">0x3d10</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00003D10   FD 37 7A 58  5A 00 00 01  69 22 DE 36  02 01 07 00  .7zXZ...i".6....
00003D20   21 01 1A 00  AF BB 14 35  E2 84 87 EF  FF 5D 00 20  !......5.....].
</code></pre></div></div>

<p>As well an XZ stream footer magic at <code class="language-plaintext highlighter-rouge">0x2bf10c</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>002BF100   70 AB A0 CD  9B E3 51 40  03 00 00 00  00 01 59 5A  p.....Q@......YZ
002BF110   28 CE 8A 00  00 00 00 00  00 00 00 00  00 00 00 00  (...............
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">binwalk</code> can successfully extract the compressed <code class="language-plaintext highlighter-rouge">vmlinux</code>, but what we
now need to know for certain is exactly which start and end offsets the
decompressor code uses to delineate the piggy.</p>

<p>Notice that there are several <code class="language-plaintext highlighter-rouge">piggy.*.S</code> files in <code class="language-plaintext highlighter-rouge">arch/arm/boot/compressed</code>.
These include the content of the piggy as a binary blob and store its start
and end offsets in the globals <code class="language-plaintext highlighter-rouge">input_data</code> and <code class="language-plaintext highlighter-rouge">input_data_end</code>:</p>

<figure class="highlight"><pre><code class="language-m68k" data-lang="m68k">	<span class="p">.</span><span class="kr">section</span> <span class="p">.</span><span class="nv">piggydata</span><span class="p">,</span><span class="nd">#</span><span class="nv">alloc</span>
	<span class="p">.</span><span class="nv">globl</span>	<span class="nv">input_data</span>
<span class="nl">input_data:</span>
	<span class="p">.</span><span class="kr">incbin</span>	<span class="s">"arch/arm/boot/compressed/piggy.xzkern"</span>
	<span class="p">.</span><span class="nv">globl</span>	<span class="nv">input_data_end</span>
<span class="nl">input_data_end:</span></code></pre></figure>

<p>These globals are referenced in the <code class="language-plaintext highlighter-rouge">decompress_kernel</code> function in
<code class="language-plaintext highlighter-rouge">arch/arm/boot/compressed/misc.c</code>, which prints a telltale string
before calling <code class="language-plaintext highlighter-rouge">do_decompress</code> with the piggy start offset and length as arguments:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">putstr</span><span class="p">(</span><span class="s">"Uncompressing Linux..."</span><span class="p">);</span>
<span class="n">ret</span> <span class="o">=</span> <span class="n">do_decompress</span><span class="p">(</span><span class="n">input_data</span><span class="p">,</span> <span class="n">input_data_end</span> <span class="o">-</span> <span class="n">input_data</span><span class="p">,</span>
    <span class="n">output_data</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ret</span><span class="p">)</span>
    <span class="n">error</span><span class="p">(</span><span class="s">"decompressor returned an error"</span><span class="p">);</span>
<span class="k">else</span>
    <span class="nf">putstr</span><span class="p">(</span><span class="s">" done, booting the kernel.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span></code></pre></figure>

<p>We can use cross-references to the “Uncompressing Linux…” string in
the disassembled <code class="language-plaintext highlighter-rouge">zImage-initramfs</code> binary to locate the call to <code class="language-plaintext highlighter-rouge">do_decompress</code>, which
will point us to the values of <code class="language-plaintext highlighter-rouge">input_data</code> and <code class="language-plaintext highlighter-rouge">input_data_end</code>.</p>

<p>I loaded the image up as a raw little endian<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup> ARM binary in Ghidra to find this
call in the disassembly.
Ghidra detects that <code class="language-plaintext highlighter-rouge">0x3d10</code> and <code class="language-plaintext highlighter-rouge">0x2bf114</code> are loaded from the Global Offset Table (GOT) section at the
end of the zImage to set up the first two registers for the call to <code class="language-plaintext highlighter-rouge">do_decompress</code>.</p>

<p>These addresses match up with the XZ stream header and YZ stream footer bytes
as seen above, but there is an extra word that comes just after the
stream footer. A bit more digging into the source code confirms that this
represents the uncompressed size of the XZ data, and that it’s expected to
break decompression with the normal <code class="language-plaintext highlighter-rouge">unxz</code> command.<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup></p>

<p>Let’s carve out the piggy:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ dd if=zImage-initramfs of=vmlinux.xz ibs=1 skip=$[0x3d10] count=$[0x2bf114-0x3d10]
2864132+0 records in
5594+1 records out
2864132 bytes (2.9 MB, 2.7 MiB) copied, 2.85465 s, 1.0 MB/s
</code></pre></div></div>

<p>Use the <code class="language-plaintext highlighter-rouge">--single-stream</code> option to avoid the “Unexpected end of input”
error when decompressing it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ unxz --verbose --single-stream vmlinux.xz
vmlinux.xz (1/1)
  100 %   2,797.0 KiB / 8,883.5 KiB = 0.315
</code></pre></div></div>

<p>We now know the exact size and location of the piggy within the zImage.
It’s 2864132 (<code class="language-plaintext highlighter-rouge">0x2bb404</code>) bytes long, located at <code class="language-plaintext highlighter-rouge">0x3d10</code> - <code class="language-plaintext highlighter-rouge">0x2bf114</code>.</p>

<h2 id="modification">Modification</h2>

<h3 id="direct-replacement">Direct replacement</h3>

<p>For the test modification, I will modify the bytes after the <code class="language-plaintext highlighter-rouge">WARNING!</code> string in the
banner. These bytes show up within the <code class="language-plaintext highlighter-rouge">initramfs</code> section of the decompressed <code class="language-plaintext highlighter-rouge">vmlinux</code>
binary, which consists of an uncompressed CPIO archive with no checksums.
It’s simple enough to directly edit with a hex editor:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0076AC30   61 74 20 3C  3C 20 45 4F  46 0A 3D 3D  3D 20 57 41  at &lt;&lt; EOF.=== WA
0076AC40   52 4E 49 4E  47 21 20 3D  4D 6F 64 69  66 69 65 64  RNING! =Modified
0076AC50   21 20 68 65  6C 6C 6F 20  6E 65 69 67  68 62 6F 72  ! hello neighbor
0076AC60   73 3D 3D 3D  3D 3D 3D 3D  3D 3D 3D 3D  0A 54 68 65  s===========.The
</code></pre></div></div>

<p>If we try to naively recompress the modified image, it comes out significantly
larger than the original piggy. Trying all of the compression presets
<code class="language-plaintext highlighter-rouge">-0</code> through <code class="language-plaintext highlighter-rouge">-9</code>, even with the <code class="language-plaintext highlighter-rouge">--extreme</code> flag, results in at best
a 2994568 byte output.
In fact, even if we just recompress the original <code class="language-plaintext highlighter-rouge">vmlinux</code> unchanged,
it ends up at 2994540 bytes in the best case. That’s 130408 bytes larger!</p>

<p>Digging around the Linux boot files we can find the xz options used
to compress the original piggy. The command is in <code class="language-plaintext highlighter-rouge">xz_wrap.sh</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xz --check=crc32 --arm --lzma2=$LZMA2OPTS,dict=32MiB
</code></pre></div></div>

<p>Let’s try with those options:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ xz --check=crc32 --arm --lzma2=,dict=32MiB &lt; vmlinux-mod-warning &gt; /tmp/vmlinux-mod-warntest.xz
$ wc -c /tmp/vmlinux-mod-warntest.xz
2864204 /tmp/vmlinux-mod-warntest.xz
</code></pre></div></div>

<p>Close, but it’s still 76 bytes too large (including the four extra bytes
needed for the inflated size word).
After digging through xz’s man page and experimenting with compression options, I
found a useful setting that resulted in a smaller output:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ xz --check=crc32 --arm --lzma2=,dict=32MiB,nice=128 &lt; vmlinux-mod-warning &gt; /tmp/vmlinux-mod-warntest.xz
$ wc -c /tmp/vmlinux-mod-warntest.xz
2863580 /tmp/vmlinux-mod-warntest.xz
</code></pre></div></div>

<p>Here’s the description of the <code class="language-plaintext highlighter-rouge">nice</code> option from the xz man page:</p>

<blockquote>
  <p>Specify what is considered to be a nice length for a match.  Once a match of at least nice bytes is found, the algorithm stops looking for possibly better matches.
Nice can be 2-273 bytes.  Higher values tend to give better compression ratio at the expense of speed.  The default depends on the preset.</p>
</blockquote>

<p>A nice option indeed.
Now that we have a smaller output, we can append the inflated <code class="language-plaintext highlighter-rouge">vmlinux</code> size to the new piggy
and try to replace the original piggy with it.</p>

<p>Make a copy of the kernel image and zero out the piggy area:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cp zImage-initramfs zImage-initramfs-warnmod
$ dd if=/dev/zero of=zImage-initramfs-warnmod bs=1 seek=$[0x3d10] count=$[0x2bf114-0x3d10] conv=notrunc
2864132+0 records in
2864132+0 records out
2864132 bytes (2.9 MB, 2.7 MiB) copied, 7.53578 s, 380 kB/s
</code></pre></div></div>

<p>The size of the uncompressed <code class="language-plaintext highlighter-rouge">vmlinux</code> is still the same (9096744 bytes), so append that to the end
of the new piggy as a little endian 32 bit integer (<code class="language-plaintext highlighter-rouge">28 ce 8a 00</code>). Then copy the new piggy into the piggy area:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ echo -en "\x28\xce\x8a\x00" &gt;&gt; vmlinux-mod-warning.xz
$ dd if=vmlinux-mod-warning.xz of=zImage-initramfs-warnmod bs=1 seek=$[0x3d10] conv=notrunc
2864044+0 records in
2864044+0 records out
2864044 bytes (2.9 MB, 2.7 MiB) copied, 7.86938 s, 364 kB/s
</code></pre></div></div>

<p>Update the <code class="language-plaintext highlighter-rouge">input_data_end</code> word in the GOT near the end of the image (at <code class="language-plaintext highlighter-rouge">0x2bf124</code>).
The piggy now ends at <code class="language-plaintext highlighter-rouge">0x2beef0</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>002BF110   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................
002BF120   50 F1 2B 00  F0 EE 2B 00  68 F5 2B 00  10 3D 00 00  P.+...+.h.+..=..
002BF130   64 F5 2B 00  64 F1 2B 00  54 F1 2B 00  40 09 00 00  d.+.d.+.T.+.@...
002BF140   5C F1 2B 00  60 F1 2B 00  58 F1 2B 00  00 00 00 00  \.+.`.+.X.+.....
002BF150
</code></pre></div></div>

<p>Then attempt to load it in qemu:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ qemu-system-arm -serial stdio -M virt -m 1024 -kernel zImage-initramfs-warnmod
</code></pre></div></div>

<p>It doesn’t work! With a quick debugging session and another look at the code,
it’s clear the decompressor is still checking for the inflated piggy size at its
original location, <code class="language-plaintext highlighter-rouge">0x2bf110</code>. Because we zeroed out the original piggy area,
the decompressor will read zero as the inflated size of the piggy.</p>

<p>The location of the inflated size word shows up in a block of addresses in the main
assembly code for the decompressor:<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LC0:	.word	LC0			@ r1
		.word	__bss_start		@ r2
		.word	_end			@ r3
		.word	_edata			@ r6
		.word	input_data_end - 4	@ r10 (inflated size location)
		.word	_got_start		@ r11
		.word	_got_end		@ ip
		.word	.L_user_stack_end	@ sp
		.word	_end - restart + 16384 + 1024*1024
		.size	LC0, . - LC0
</code></pre></div></div>

<p>Easy enough to fix with the hex editor again. <code class="language-plaintext highlighter-rouge">0x002bf110</code> is at offset <code class="language-plaintext highlighter-rouge">0x258</code> in the image
and we can update it to the new inflated size word location, <code class="language-plaintext highlighter-rouge">0x2beef0 - 4 = 0x2beeec</code>. Now it boots:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BusyBox v1.25.1 () built-in shell (ash)

     _________
    /        /\      _    ___ ___  ___
   /  LE    /  \    | |  | __|   \| __|
  /    DE  /    \   | |__| _|| |) | _|
 /________/  LE  \  |____|___|___/|___|                      lede-project.org
 \        \   DE /
  \    LE  \    /  -----------------------------------------------------------
   \  DE    \  /    Reboot (17.01.0, r3205-59508e3)
    \________\/    -----------------------------------------------------------

=== WARNING! =Modified! hello neighbors===========
There is no root password defined on this device!
Use the "passwd" command to set up a new password
in order to prevent unauthorized SSH logins.
--------------------------------------------------
root@LEDE:/#
</code></pre></div></div>

<p>It turns out we could’ve made things simpler by leaving the inflated
size word in its original location. The extra zeros at the end of the XZ data
don’t bother the XZ decompressor, and it would also save us from needing
to update the location of the size word in LC0.</p>

<p>This approach works as long as we can recompress the modified <code class="language-plaintext highlighter-rouge">vmlinux</code> to a size
equal to or smaller than the original. Some scripts such as <code class="language-plaintext highlighter-rouge">repack-zImage.sh</code> will
perform modifications along these lines and attempt to optimize compression, but can’t
repack an <code class="language-plaintext highlighter-rouge">initramfs</code> once modifications increase its compressed size.<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">7</a></sup></p>

<h3 id="extending-the-image">Extending the image</h3>

<p>But what if no amount of compressor option tuning can save us? What if we <em>must</em> increase
the size of the piggy? To figure out what needs to change if we move the end of the
piggy to a higher address we can use the layout of the image as described in
the linker script <code class="language-plaintext highlighter-rouge">arch/arm/boot/compressed/vmlinux.lds.S</code>.<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">8</a></sup></p>

<p>The GOT at the end of the table needs to be moved up, along with the <code class="language-plaintext highlighter-rouge">bss</code> section
address. Nearly all of the references to the locations of these sections are baked
into the LC0 object shown above.</p>

<p>We’ll need to update the addresses of anything that’s located after the start of the
piggy, including:</p>

<ul>
  <li>Addresses in LC0 object
    <ul>
      <li><code class="language-plaintext highlighter-rouge">__bss_start</code></li>
      <li><code class="language-plaintext highlighter-rouge">_end</code> - end of program (including bss)</li>
      <li><code class="language-plaintext highlighter-rouge">_edata</code> - end of image</li>
      <li>inflated piggy size location</li>
      <li><code class="language-plaintext highlighter-rouge">_got_start</code></li>
      <li><code class="language-plaintext highlighter-rouge">_got_end</code></li>
      <li><code class="language-plaintext highlighter-rouge">user_stack_end</code></li>
      <li><code class="language-plaintext highlighter-rouge">end - restart + 16384 + 1024*1024</code></li>
    </ul>
  </li>
  <li>Any entries in the GOT that come after <code class="language-plaintext highlighter-rouge">input_data</code></li>
</ul>

<figure class="image">
  <a href="/assets/img/piggy/lc0_map_small.jpg">
    <img src="/assets/img/piggy/lc0_map_small.jpg" alt="Mapping of LC0 pointers to vmlinuz locations. Anything pointing to a location after &lt;tt&gt;.piggydata&lt;/tt&gt; must be updated." />
  </a>
  <figcaption>
    Mapping of LC0 pointers to vmlinuz locations. Anything pointing to a location after <tt>.piggydata</tt> must be updated.
  </figcaption>
</figure>

<p>At this point I started using a Python script to automate the editing.
The LC0 object is easy to locate dynamically because it starts with its own address
(e.g., for this zImage the word <code class="language-plaintext highlighter-rouge">0x00000248</code> is at offset <code class="language-plaintext highlighter-rouge">0x248</code>).
We can pull the GOT location from LC0 and use it to get <code class="language-plaintext highlighter-rouge">input_data</code>
and <code class="language-plaintext highlighter-rouge">input_data_end</code> (i.e., piggy start and end).<sup id="fnref:9" role="doc-noteref"><a href="#fn:9" class="footnote" rel="footnote">9</a></sup>
For each value in the LC0 and GOT that’s greater than the piggy start offset,
we increase it by the amount we’re increasing the size of the image.
Then we can extend the image and insert the new larger piggy over the original one.</p>

<p>One more thing to fix up is the <code class="language-plaintext highlighter-rouge">_magic_end</code> value near the beginning
of the image: it matches the size of the <code class="language-plaintext highlighter-rouge">zImage</code> file. (This didn’t
have any effect on whether qemu booted the image.)</p>

<p>Does it work yet? Nope! Another debugging session shows that the arguments
for <code class="language-plaintext highlighter-rouge">do_decompress</code> are wrong: the input location, length, and error function
pointer are all zero. Notice that these are all values in the GOT.</p>

<p>What’s happening here is that the handful of functions compiled from C code (<code class="language-plaintext highlighter-rouge">misc.c</code> and so on)
have offset tables appended to them which are used to locate entries in the GOT.
The first offset in the table is a PC-relative offset to the GOT itself. The subsequent
offsets locate specific entries within it. Those entries contain a fixed up pointer to
their global symbol.</p>

<p><code class="language-plaintext highlighter-rouge">input_data</code> and <code class="language-plaintext highlighter-rouge">input_data_end</code> are globals referenced in the GOT,
so we’ll have to fix this. Luckily we only have to fix the base GOT offset
for each function.</p>

<p>There are some simple constraints we can use to implement a quick and dirty
search and update routine for these values:</p>

<ul>
  <li>These words should only exist in between LC0 and the piggy.</li>
  <li>The rough minimum possible GOT base offset is from where the code ends
to where the GOT starts:  <code class="language-plaintext highlighter-rouge">got_start - piggy_start</code>.</li>
  <li>The rough maximum offset is from the beginning of the code after LC0
to the GOT start: <code class="language-plaintext highlighter-rouge">got_start - lc0_end</code>.</li>
</ul>

<p>Updating each of these words with the size increase delta works and
fixes the extended image! Here’s a demonstration:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cp vmlinux vmlinux-mod-big
$ # changing /etc/banner...
$ xz --check=crc32 &lt; vmlinux-mod-big &gt; vmlinux-mod-big.xz
$ # add inflated size to end of XZ data
$ echo -en "\x28\xce\x8a\x00" &gt;&gt; vmlinux-mod-big.xz
$ wc -c vmlinux-mod-big.xz
2994648 vmlinux-mod-big.xz
$ # that is 130516 (0x1fdd4) bytes larger than original
$ ./arm_zimg_extend.py zImage-initramfs bigpig --replace vmlinux-mod-big.xz
LC0 @ 0x0248 - 0x026c
  0x00: 0x00000248
  0x01: 0x002bf150
  0x02: 0x002bf56c
  0x03: 0x002bf150
  0x04: 0x002bf110
  0x05: 0x002bf120
  0x06: 0x002bf14c
  0x07: 0x002c0570
  0x08: 0x003c34a4
GOT @ 0x002bf120 - 0x002bf14c
  0x00: 0x002bf150
  0x01: 0x002bf114
  0x02: 0x002bf568
  0x03: 0x00003d10
  0x04: 0x002bf564
  0x05: 0x002bf164
  0x06: 0x002bf154
  0x07: 0x00000940
  0x08: 0x002bf15c
  0x09: 0x002bf160
  0x0a: 0x002bf158
piggy data @ 0x00003d10 - 0x002bf114
piggy compressed size: 0x002bb404
piggy inflated size @ 0x002bf110
piggy inflated size: 0x008ace28
piggy new compressed size: 0x002db1d8
extending image by 0x0001fdd4
LC0 extended:
  0x00: 0x00000248
  0x01: 0x002def24
  0x02: 0x002df340
  0x03: 0x002def24
  0x04: 0x002deee4
  0x05: 0x002deef4
  0x06: 0x002def20
  0x07: 0x002e0344
  0x08: 0x003e3278
GOT extended:
  0x00: 0x002def24
  0x01: 0x002deee8
  0x02: 0x002df33c
  0x03: 0x00003d10
  0x04: 0x002df338
  0x05: 0x002def38
  0x06: 0x002def28
  0x07: 0x00000940
  0x08: 0x002def30
  0x09: 0x002def34
  0x0a: 0x002def2c
Searching for GOT offsets...
Candidate GOT offset @ 0x09d8: 0x002be750
Candidate GOT offset @ 0x0ac0: 0x002be6f0
Candidate GOT offset @ 0x0b78: 0x002be618
Candidate GOT offset @ 0x0cc4: 0x002be498
Candidate GOT offset @ 0x1080: 0x002be24c
Candidate GOT offset @ 0x1184: 0x002be074
Candidate GOT offset @ 0x3360: 0x002bcbb4
Candidate GOT offset @ 0x350c: 0x002bbd84
magic start: 0x00000000
magic end: 0x002bf150
magic end updated: 0x002def24
wrote new image
$ qemu-system-arm -serial stdio -M virt -m 1024 -kernel bigpig
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.4.50 (buildbot@builds-02.infra.lede-project.org) (gcc version 5.4.0 (LEDE GCC 5.4.0 r3101-bce140e) ) #0 SMP Mon Feb 20 17:13:44 2017
...
BusyBox v1.25.1 () built-in shell (ash)

         ^,    ,^
        /  ----  \
       / _\    /_ \  Ful
       |  / __ \  |
       |   /oo\   |            ,-.
       |   \__/   |____________.:'
       \   .__.   /            \ '
        '.______.'              \
            \                   |
             |  /____...-----\  |
             |  |            |  |
             |^^|            |^^|
 Big piggy mod!

=== WARNING! =====================================
There is no root password defined on this device!
Use the "passwd" command to set up a new password
in order to prevent unauthorized SSH logins.
--------------------------------------------------
root@LEDE:/#
</code></pre></div></div>

<p>The source code for this script can be found at <a href="https://gist.github.com/jamchamb/243e6973aeb5c9a2e302a4d4f57f16e1">https://gist.github.com/jamchamb/243e6973aeb5c9a2e302a4d4f57f16e1</a>.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>In the context of PCs the initial ramdisk is a more limited filesystem
used for an intermediary “early userspace” stage. These images can still
contain interesting programs, such as those used to get a disk decryption key
from the user or TPM. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p><a href="https://people.kernel.org/linusw/how-the-arm32-linux-kernel-decompresses">https://people.kernel.org/linusw/how-the-arm32-linux-kernel-decompresses</a> <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p><a href="https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux">https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux</a> <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>If binwalk hadn’t already told us the image is little endian, the magic endianness value
of <code class="language-plaintext highlighter-rouge">0x04030201</code> would. It’s stored as <code class="language-plaintext highlighter-rouge">01 02 03 04</code> near the beginning of the image, which tells
us it’s stored little endian. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p><a href="https://elixir.bootlin.com/linux/v4.4.50/source/scripts/Makefile.lib#L374">https://elixir.bootlin.com/linux/v4.4.50/source/scripts/Makefile.lib#L374</a> <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p><a href="https://elixir.bootlin.com/linux/v4.4.50/source/arch/arm/boot/compressed/head.S#L576">https://elixir.bootlin.com/linux/v4.4.50/source/arch/arm/boot/compressed/head.S#L576</a> <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p><a href="https://forum.xda-developers.com/t/script-repack-zimage-sh-unpack-and-repack-a-zimage-without-kernel-source-v-5.901152/">https://forum.xda-developers.com/t/script-repack-zimage-sh-unpack-and-repack-a-zimage-without-kernel-source-v-5.901152/</a> <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:8" role="doc-endnote">
      <p><a href="https://elixir.bootlin.com/linux/v4.4.50/source/arch/arm/boot/compressed/vmlinux.lds.S">https://elixir.bootlin.com/linux/v4.4.50/source/arch/arm/boot/compressed/vmlinux.lds.S</a> <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:9" role="doc-endnote">
      <p>I’ve used the known indices of these values in my code, but some smarts could be
added to automatically detect the right entries based on compression header magic (piggy start)
and greatest offset before the GOT (piggy end). <a href="#fnref:9" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="Reverse Engineering" /><category term="Linux" /><category term="ARM" /><summary type="html"><![CDATA[Ever run binwalk on an embedded Linux device’s kernel image and find its entire fileystem contained inside? Ever want to change one little line inside to enable root shell on that device that’s just mocking you with its lack of boot security, only to be thwarted by a bit of compressed data entangled in machine code?]]></summary></entry><entry><title type="html">Reversing the Pokémon Snap Station without a Snap Station</title><link href="https://jamchamb.net/2021/08/17/snap-station.html" rel="alternate" type="text/html" title="Reversing the Pokémon Snap Station without a Snap Station" /><published>2021-08-17T00:00:00+00:00</published><updated>2021-08-17T00:00:00+00:00</updated><id>https://jamchamb.net/2021/08/17/snap-station</id><content type="html" xml:base="https://jamchamb.net/2021/08/17/snap-station.html"><![CDATA[<p>Back in 1999 when the original Pokémon Snap was released for the Nintendo 64, one of its coolest features was that you could print out the photos you took in-game on sticker sheets using a Snap Station. Snap Stations could only be found at a Blockbuster video store (or a Lawson convenience store in Japan), and you’d have to pay for credits in the form of Pokémon-styled smart cards each time you wanted to print out a sheet of stickers. I’ve had one of the Charmander cards sitting around with my collection of Nintendo stuff for a while, which got me thinking about what it would be like to hack one of these kiosks.</p>

<figure class="image">
  <a href="/assets/img/snap-station/nintendo_power_june99_cutout.jpg">
    <img src="/assets/img/snap-station/nintendo_power_june99_cutout.jpg" alt="Description of the Snap Station from the June 1999 issue of Nintendo Power" />
  </a>
  <figcaption>
    Description of the Snap Station from the June 1999 issue of Nintendo Power
  </figcaption>
</figure>

<p>The Snap Stations themselves are hard to come by these days, but while watching some YouTube videos made by collectors I recalled that, in order to print your photos, you would insert your own Pokémon Snap game cartridge into the station. Furthermore, some videos showing the interior of the station revealed that the printer hardware was actually connected to the Nintendo 64 through its fourth controller port. That suggested that the code for handling some amount of the printing behavior might be present on all retail copies of the game.</p>

<p>By looking into the Pokémon Snap ROM, I was able to quickly confirm that the print menu text was present in the retail copy of the game:</p>

<figure class="highlight"><pre><code class="language-hexdump" data-lang="hexdump">a08270 63 72 65 65 6e 2e 00 00 49 66 20 79 6f 75 20 6c  &gt;creen...If you l&lt;
a08280 69 6b 65 20 74 68 65 73 65 20 70 69 63 74 75 72  &gt;ike these pictur&lt;
a08290 65 73 2c 20 70 6c 65 61 73 65 0a 6d 61 6b 65 20  &gt;es, please.make &lt;
a082a0 73 75 72 65 20 61 20 70 72 69 6e 74 20 63 72 65  &gt;sure a print cre&lt;
a082b0 64 69 74 20 65 78 69 73 74 73 0a 74 68 65 6e 20  &gt;dit exists.then &lt;
a082c0 70 72 65 73 73 20 5c 61 20 74 6f 20 70 72 69 6e  &gt;press \a to prin&lt;
a082d0 74 2e 00 00 52 65 74 75 72 6e 20 74 6f 20 74 68  &gt;t...Return to th&lt;
a082e0 65 20 54 69 74 6c 65 20 53 63 72 65 65 6e 0a 62  &gt;e Title Screen.b&lt;
a082f0 79 20 73 61 76 69 6e 67 2e 00 00 00 47 61 6c 6c  &gt;y saving....Gall&lt;</code></pre></figure>

<p>It was much trickier to identify the actual code responsible for handling the print functionality for reasons I’ll describe later, but with a combination of static analysis, dynamic analysis, and a custom FPGA-based hardware tool, I was able to reverse engineer the Snap Station’s control protocol without having access to one. (With access to a real Snap Station, all I would’ve had to do was use a logic analyzer to observe what was being transmitted over the fourth controller port.)</p>

<p>Using this information, I’ve implemented a Snap Station simulator in the Project64 emulator, as well as a hardware implementation using an iCEBreaker FPGA board. The code for each can be found at:</p>
<ul>
  <li>Project64 fork: <a href="https://github.com/jamchamb/project64/tree/snapstation">https://github.com/jamchamb/project64/tree/snapstation</a></li>
  <li>iCEBreaker/iCE40UP5k FPGA design: <a href="https://github.com/jamchamb/cojiro">https://github.com/jamchamb/cojiro</a></li>
</ul>

<p>The following video shows the Snap Station simulator in Project64:</p>

<video autoplay="" loop="" muted="" playsinline="" controls="">
  <source src="/assets/img/snap-station/snapstation_emu2.mp4" type="video/mp4" />
</video>

<p>I recommend watching a video of the real Snap Station in action if you haven’t yet in order to help make sense of what happens at the end.</p>

<p>When the player presses the Print button, the selection of photos chosen for printing is saved to the game cartridge. The station then resets the console and instructs the game to enter a photo display mode after the boot logo screen. The printer in the Snap Station uses a video pass-through input to capture photos directly from the Nintendo 64’s video output. Each time a photo is displayed, the station instructs the printer to perform a screen capture. After all 16 photos have been displayed and captured the station can print out the sticker sheet.</p>

<p>The end result doesn’t look like much without any printer hardware involved, but using the FPGA design I’ve released, you could implement a full setup with a printer if you really wanted to. The big takeway for me with this project was learning how to reverse engineer and simulate peripherals with an FPGA.</p>

<h2 id="snap-station-protocol-summary">Snap Station protocol summary</h2>

<p>The Snap Station protocol itself is pretty simple, and I will briefly describe how it works before going into the details of how I reverse engineered it.</p>

<p>The Snap Station acts like a controller with a peripheral plugged into it, much like a Rumble Pak or Controller Pak (memory card). Communication happens via the same read and write commands used by the Controller Pak, which read or write 32 bytes of data at a specified address. In this case the address indicates the type of message rather than an actual memory location.</p>

<p>To signal to the game that printing functionality is enabled, the station indicates a peripheral is plugged in. This causes the game to start querying what type of peripheral is connected through the controller. These messages use the address <code class="language-plaintext highlighter-rouge">0x8000</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>write cmd: 8000 (addr CRC-5 01)
  fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
  fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
  response: e1
read cmd: 8000 (addr CRC-5 01)
  response: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
            00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
            00
write cmd: 8000 (addr CRC-5 01)
  85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
  85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
  response: f5
read cmd: 8000 (addr CRC-5 01)
  response: 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
            85 85 85 85 85 85 85 85 85 85 85 85 85 85 85 85
            f5
</code></pre></div></div>

<p>The console first sends a message with repeating <code class="language-plaintext highlighter-rouge">FE</code> bytes, which is something like a reset or initialization message, followed by <code class="language-plaintext highlighter-rouge">85</code> repeating, which is the peripheral ID it’s checking for. The station should respond with <code class="language-plaintext highlighter-rouge">85</code>, where a different device like the Rumble Pak would respond with <code class="language-plaintext highlighter-rouge">80</code>. If this happens during the initial boot screen, the game goes into the photo display mode. Otherwise it just registers that the station is available, which enables the Print button in the Gallery.</p>

<p>Messages that correspond to the state of the print flow use the address <code class="language-plaintext highlighter-rouge">0xC000</code>. When the player presses the Print button in the Gallery menu, the following values are sent to station:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>read cmd: C000 (addr CRC-5 1b)
  response: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
            00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
            00
write cmd: C000 (addr CRC-5 1b)
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 cc
  response: 27
read cmd: C000 (addr CRC-5 1b)
  response: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
            00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 cc
            27
read cmd: C000 (addr CRC-5 1b)
  response: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
            00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 cc
            27
write cmd: C000 (addr CRC-5 1b)
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33
  response: aa
read cmd: C000 (addr CRC-5 1b)
  response: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
            00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33
            aa
read cmd: C000 (addr CRC-5 1b)
  response: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
            00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33
            aa
write cmd: C000 (addr CRC-5 1b)
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5a
  response: 59
read cmd: C000 (addr CRC-5 1b)
  response: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
            00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 5a
            59
</code></pre></div></div>

<p>Only the last data byte of each message matters, so in short the sequence is:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">0xCC</code></li>
  <li><code class="language-plaintext highlighter-rouge">0x33</code></li>
  <li><code class="language-plaintext highlighter-rouge">0x5A</code></li>
</ul>

<p>There isn’t much meaning to the values themselves, they’re just bit patterns (<code class="language-plaintext highlighter-rouge">1100 1100</code>, <code class="language-plaintext highlighter-rouge">0011 0011</code>, and <code class="language-plaintext highlighter-rouge">0101 0101</code>). The first two (<code class="language-plaintext highlighter-rouge">CC</code>, <code class="language-plaintext highlighter-rouge">33</code>) are sent before and after the save operation is performed, which saves the selected photos for printing. The last one (<code class="language-plaintext highlighter-rouge">5A</code>) signals that it’s time to do a soft reset on the console, which I believe was handled in the Snap Station by directly triggering the console’s reset button or cycling the power.</p>

<p>The station remains active during the reset, so it will be present during the Nintendo logo boot screen, which triggers the photo display mode. In this mode, the console sends the following bytes to the <code class="language-plaintext highlighter-rouge">0xC000</code> address:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">0x01</code></li>
  <li><code class="language-plaintext highlighter-rouge">0x02</code> (16 times in a row)</li>
  <li><code class="language-plaintext highlighter-rouge">0x04</code></li>
</ul>

<p>Knowing that 16 photos are displayed, it’s easy to guess that <code class="language-plaintext highlighter-rouge">01</code> signals the start of the display, <code class="language-plaintext highlighter-rouge">02</code> is sent each time a photo is displayed, and <code class="language-plaintext highlighter-rouge">04</code> signals the end of the display (when the screen goes blank).</p>

<p>At any point during the communications with the <code class="language-plaintext highlighter-rouge">C000</code> address the station can return a value of <code class="language-plaintext highlighter-rouge">08</code> to trigger a busy loop, which can help synchronize the state of the station and the game code. In the gallery menu, I’ve used this to keep the game frozen at the <code class="language-plaintext highlighter-rouge">5A</code> message until the console has been reset, which keeps the “Now Saving…” message displayed while the Print button is darkened. If the game were to proceed past reading <code class="language-plaintext highlighter-rouge">5A</code> back, the player would just remain in the Gallery menu and nothing would appear to happen, which looks awkward. In the photo display mode I use the busy loop to keep the photos displayed a little longer so that they don’t flash by too quickly. With a real printer involved in the setup, these pauses could help ensure that each photo is captured and stored in the print buffer.</p>

<p>Take a look at the <code class="language-plaintext highlighter-rouge">Source/Project64-core/N64System/Mips/Snapstation.cpp</code> file in the Project64 fork linked above to see how this is described in code.</p>

<h2 id="reverse-engineering">Reverse engineering</h2>

<h3 id="static-analysis-difficulties">Static analysis difficulties</h3>

<p>The toughest part of this project was simply finding code relevant to the Snap Station within the Pokémon Snap ROM. Disassembling the game code was complicated due to code moving around at runtime, with some mutually exclusive segments of code even occupying the same areas of memory when navigating through different menus or gameplay. Most of this was probably due to the use of <a href="https://web.archive.org/web/20180810110618/level42.ca/projects/ultra64/Documentation/man/pro-man/pro10/index10.3.html">overlays</a>, a technique for conserving RAM where “some segments share the same memory region during different phases of execution.” This caused most of the automatic disassembly to fail, because the addresses of the code as loaded into the disassembler didn’t match where they would be at runtime. Disassemblers generally perform automatic code discovery by following jump instructions, which wouldn’t work here.</p>

<p>Besides a couple of debug and error strings, there were no symbol names to work with. Without the benefit of accurate cross-reference information for code and data, this made it very hard to build up any context with static analysis, which is crucial for understanding complex code without any metadata information about it. I was able to to configure the memory layout in Ghidra to get one decent chunk of code to disassemble correctly, but loading the entire game ROM properly would’ve been an intensive task.</p>

<h3 id="joybus-fpga-tool">JoyBus FPGA tool</h3>

<p>At this point I decided to switch over to working on the FPGA-based hardware interface for the JoyBus protocol used by Nintendo 64 controllers in the hopes that probing the controller port would provide quicker results. There are a few good resources with unofficial documentation on how JoyBus works online; in particular I found <a href="https://sites.google.com/site/consoleprotocols/home/nintendo-joy-bus-documentation">https://sites.google.com/site/consoleprotocols/home/nintendo-joy-bus-documentation</a> helpful. Here’s a quick recap of how the hardware interface works:</p>

<ul>
  <li>There’s one bi-directional data line used by the console and controller to communicate with each other. The other two pins in a controller port are power and ground.</li>
  <li>The data line is an “open drain” output. This means the line is held at the high logic level by default, and devices on the bus communicate by pulling the line down to ground. For the FPGA implementation this means the JoyBus pin will be left in high impedance mode except when transmitting a 0 bit.</li>
  <li>A pull-up resistor on the data line helps the signal rise back to the high logic level faster, resulting in a more square waveform.</li>
  <li>Every bit transmission starts with pulling the line low for a microsecond, holding it at high for a 1 bit or low for a 0 bit for two microseconds, and always returning high for the fourth microsecond. That’s four microseconds per bit, so the bit rate is roughly 250,000 bits per second.</li>
</ul>

<p>For the physical connection to the console I cut an N64 controller extension cable in half and broke the wires out to a breadboard. Besides some jumper cables to connect to the iCEBreaker, the only other component on the breadboard is a 330 Ohm pull-up resistor between the 3.3V power line and the data line.</p>

<figure class="image">
  <a href="/assets/img/snap-station/n64_controller_breakout.jpg">
    <img src="/assets/img/snap-station/n64_controller_breakout.jpg" alt="N64 controller extension cord breakout" />
  </a>
  <figcaption>
    N64 controller extension cord breakout
  </figcaption>
</figure>

<p>Console commands consist of a one byte command ID followed by optional data bytes, such as a 16-bit read address for the Controller Pak read command. I started by implementing the two basic commands needed to simulate a regular N64 controller. The first (<code class="language-plaintext highlighter-rouge">FF</code> or <code class="language-plaintext highlighter-rouge">00</code>) queries the type of device connected and its status, and the second (<code class="language-plaintext highlighter-rouge">01</code>) checks the current state of the buttons and analog stick.</p>

<video autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/img/snap-station/fpga_controller_demo.mp4" type="video/mp4" />
</video>

<p>I connected the iCEBreaker to the fourth controller port while Pokémon Snap was running and tried returning different values to the query command to see if different device type IDs or status values would be recognized. With the normal controller ID <code class="language-plaintext highlighter-rouge">0500</code>, just changing the status to <code class="language-plaintext highlighter-rouge">01</code> to indicate a peripheral was plugged in caused the console to send a few more requests to the device. At the time I noticed this by watching an oscilloscope hooked up to the data line; to get a better look at the data I added UART forwarding to send the request packets over to my PC. With the UART forwarding I was able to see the write command with the <code class="language-plaintext highlighter-rouge">FEFEFEFE...</code> payload:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>03 8001 fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe
</code></pre></div></div>

<p>Knowing that reporting a controller with a peripheral plugged in on the fourth port did something interesting, I switched back to dynamic analysis with the Project64 emulator to see if I could identify code that checked the status of the fourth controller.</p>

<h3 id="dynamic-analysis">Dynamic analysis</h3>

<p>Using the Project64 debugger to set watchpoints on reads or writes to the controller state memory was fruitless because they were constantly triggered by code that I assume was generic system-level code checking and updating the controller state every frame.</p>

<p>Instead I tried a more indirect approach with memory scanning on the Gallery menu. First I tracked down the location of the current button index with the standard technique of repeatedly updating the selection and then scanning memory for the newly changed value. Then I could set read watchpoints on the button index value to see if I could identify where the menu handling code was.</p>

<p>Initially this also caused the watchpoint to trigger repeatedly. By patching out the read instruction that was repeatedly triggered, I saw it was caused by the glowing orange cursor that shows above the currently selected button. After removing that read instruction I got much more useful results: the watchpoint triggered when the menu description text for the currently selected button was changed, and when I pressed A to trigger the currently selected button.</p>

<p>With this approach I was finally able to find that, in the menu code, there were some conditional checks related to the button index where the Print button <em>would</em> appear (6, right before the Save button which has index 7). One of the checks was a call to a function that simply checked if a certain global variable had a value of 5. On a hunch that this was some kind of state related to printing, I patched the function to always return true, which resulted in the Print button appearing:</p>

<figure class="image">
  <a href="/assets/img/snap-station/print_button_hack.png">
    <img src="/assets/img/snap-station/print_button_hack.png" alt="Forcing Print button to appear" />
  </a>
  <figcaption>
    Forcing Print button to appear
  </figcaption>
</figure>

<p>Tracking down the code that would set this particular variable to 5 finally led me to the code that appeared to send out the <code class="language-plaintext highlighter-rouge">FE</code> and <code class="language-plaintext highlighter-rouge">85</code> sequences on the JoyBus. Looking at the code, I could see that for the first command the controller should not return <code class="language-plaintext highlighter-rouge">FE</code> in kind, and for the second it should return back the repeating <code class="language-plaintext highlighter-rouge">85</code> sequence. Implementing this behavior on the FPGA unlocked the core behavior of enabling the Print button and triggering the photo display mode when the game first boots. With the UART forwarding feature, the rest of the protocol became evident when pressing the Print button or during the photo display, and with a little more reversing I found what the messages sent from the Gallery menu meant and how to trigger the busy loops by returning a value of 8.</p>

<h2 id="conclusion">Conclusion</h2>

<p>For the final FPGA tool setup, I use a button for advancing the state of the station. The first button press enables the station, which helps avoid launching immediately into the photo display when you start the game. Next it waits to receive the <code class="language-plaintext highlighter-rouge">5A</code> message and maintains a busy loop to keep the “Now Saving…” message displayed until the player resets the console  manually. Once the console reboots into the photo display, pressing the button will advance the photo display one at a time until all 16 photos are displayed.</p>

<figure class="image">
  <a href="/assets/img/snap-station/tv_setup2.jpg">
    <img src="/assets/img/snap-station/tv_setup2.jpg" alt="Snap Station simulator implemented with the iCEBreaker FPGA development board" />
  </a>
  <figcaption>
    Snap Station simulator implemented with the iCEBreaker FPGA development board
  </figcaption>
</figure>]]></content><author><name></name></author><category term="Reverse Engineering" /><category term="Hardware" /><category term="Nintendo 64" /><category term="Pokémon Snap" /><summary type="html"><![CDATA[Back in 1999 when the original Pokémon Snap was released for the Nintendo 64, one of its coolest features was that you could print out the photos you took in-game on sticker sheets using a Snap Station. Snap Stations could only be found at a Blockbuster video store (or a Lawson convenience store in Japan), and you’d have to pay for credits in the form of Pokémon-styled smart cards each time you wanted to print out a sheet of stickers. I’ve had one of the Charmander cards sitting around with my collection of Nintendo stuff for a while, which got me thinking about what it would be like to hack one of these kiosks.]]></summary></entry><entry><title type="html">Dumping K360 wireless keyboard firmware with a GreatFET</title><link href="https://jamchamb.net/2021/05/29/dumping-k360-firmware.html" rel="alternate" type="text/html" title="Dumping K360 wireless keyboard firmware with a GreatFET" /><published>2021-05-29T00:00:00+00:00</published><updated>2021-05-29T00:00:00+00:00</updated><id>https://jamchamb.net/2021/05/29/dumping-k360-firmware</id><content type="html" xml:base="https://jamchamb.net/2021/05/29/dumping-k360-firmware.html"><![CDATA[<p>I recently decided to do some keyboard hacking for fun, so I started with
one of the cheapest Logitech wireless keyboard models available: the K360.
This model is a little old and the main chip inside it, as well as the
Logitech Unifying wireless protocol it uses, have been well
covered before. See Marc Newlin’s <a href="https://youtu.be/00A36VABIA4">MouseJack presentation</a>,
Travis Goodspeed’s <a href="http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html">nRF24 sniffing work</a>, and the <a href="http://www.remote-exploit.org/articles/keykeriki_v2_0__8211_2_4ghz/index.html">KeyKeriki</a> research mentioned in each.
I’m doing this more as an exercise rather than novel research, and I didn’t
know what I’d find going in.
That said, I thought this was a neat little example of extracting bare metal
firmware from on-chip flash.</p>

<p>I’m taking a hardware-first approach to reverse engineering the keyboard, so the first step is to extract the firwmare.
Disassembling the keyboard is pretty easy after removing the adhesive plate from the front,
which exposes all the screws keeping the shell together.
Inside there’s the rubber dome key matrix and a small PCB with very few components on it.
It’s basically just an nRF24LE1 chip:</p>

<figure class="image">
  <a href="/assets/img/k360/pcb_front.jpg">
    <img src="/assets/img/k360/pcb_front.jpg" alt="PCB front" />
  </a>
  <figcaption>
    PCB front
  </figcaption>
</figure>

<figure class="image">
  <a href="/assets/img/k360/pcb_back.jpg">
    <img src="/assets/img/k360/pcb_back.jpg" alt="PCB back" />
  </a>
  <figcaption>
    PCB back
  </figcaption>
</figure>

<p>On the left of the nRF24LE1 there’s a grid of six large test pads.
This seemed like an interesting interface to poke at, so I checked these
pads first using a logic analyzer and some pogo pin probes.
TP8 seemed to pulse out a short clock at boot, and TP4 was ground, but I didn’t observe much interesting
activity here with passive probing. It became clear that the group
of six words silkscreened on the upper right of the board
were labels for these test pads due to the clock, ground, and VMCU
label positions all matching up with the corresponding pad positions.</p>

<p>To actively probe the SPI interface I then wired up
the test pads to a header using some enameled wire. I also wired up
TP5 and TP7. According to the nRF24LE1 datasheet,
TP7 connects to the PROG pin which is used to “enable flash programming,”
and TP5 connects to RESET (also labelled right next to TP5). Reset is
active low in this case.</p>

<figure class="image">
  <a href="/assets/img/k360/nrf24le1_48pin.png">
    <img src="/assets/img/k360/nrf24le1_48pin.png" alt="Pin assignment diagram from the nRF24LE1 datasheet" />
  </a>
  <figcaption>
    Pin assignment diagram from the nRF24LE1 datasheet
  </figcaption>
</figure>

<p>I left the smaller test pads 10-13 alone at this point because they connected to generic
GPIO pins and sat on the traces out to the key matrix.</p>

<figure class="image">
  <a href="/assets/img/k360/spi_wires.jpg">
    <img src="/assets/img/k360/spi_wires.jpg" alt="SPI wires" />
  </a>
  <figcaption>
    SPI wires
  </figcaption>
</figure>

<p>I connected the SPI pads to the default SPI pins on the GreatFET, GND to one of the ground pins,
and assigned a GPIO pin for the reset pad. I also used a 3V3 pin on the GreatFET to directly power the nRF24 chip through the VMCU pad.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gf</span> <span class="o">=</span> <span class="n">GreatFET</span><span class="p">()</span>
<span class="n">reset_pin</span> <span class="o">=</span> <span class="n">gf</span><span class="p">.</span><span class="n">gpio</span><span class="p">.</span><span class="n">get_pin</span><span class="p">(</span><span class="s">'J1_P4'</span><span class="p">)</span>
<span class="n">reset_pin</span><span class="p">.</span><span class="n">high</span><span class="p">()</span>
</code></pre></div></div>

<p>I tried to send some simple test commands using the built-in SPI code,
but didn’t get anything back in this state:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>In [6]: gf.spi.transmit([0x05], receive_length=1)
Out[6]: b'\xff'

In [7]: gf.spi.transmit([0x03, 0x00, 0x00], receive_length=4)
Out[7]: b'\xff\xff\xff\xff'
</code></pre></div></div>

<p>I went back over the datasheet some more and found that the flash programming SPI interface
enabled by the PROG pin has its own set of assigned pins.
These pins did not match up with the test pads I just wired up, so I checked to see
if those were broken out on the remaining test pads.
I used a multimeter continuity test to trace where these pins lead to, but you
can also follow the traces in the picture of the nRF24 side of the PCB.
Luckily the smaller test pads T10 through T13 do connect to these flash SPI interface pins
(P1.2, P1.5, P1.6, and P2.0). Since there were only four
more pads, I used my PCBite probes again instead of soldering more wires up.</p>

<figure class="image">
  <a href="/assets/img/k360/firmwaredump2.jpg">
    <img src="/assets/img/k360/firmwaredump2.jpg" alt="Probing the flash programming SPI" />
  </a>
  <figcaption>
    Probing the flash programming SPI
  </figcaption>
</figure>

<p>Enabling the flash programming SPI interface requires holding the PROG pin
high and resetting the device by pulsing RESET low. I set up another
GPIO pin on the GreatFET for the PROG pin, and then added a function to
my test script for pulsing the RESET pin low.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span><span class="kn">import</span> <span class="nn">hexdump</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">from</span> <span class="nn">greatfet</span> <span class="kn">import</span> <span class="n">GreatFET</span>

<span class="k">def</span> <span class="nf">reset</span><span class="p">(</span><span class="n">gf</span><span class="p">,</span> <span class="n">reset_pin</span><span class="p">):</span>
    <span class="n">reset_pin</span><span class="p">.</span><span class="n">low</span><span class="p">()</span>
    <span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.001</span><span class="p">)</span>
    <span class="n">reset_pin</span><span class="p">.</span><span class="n">high</span><span class="p">()</span>
    <span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.001</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">gf</span> <span class="o">=</span> <span class="n">GreatFET</span><span class="p">()</span>
    <span class="n">reset_pin</span> <span class="o">=</span> <span class="n">gf</span><span class="p">.</span><span class="n">gpio</span><span class="p">.</span><span class="n">get_pin</span><span class="p">(</span><span class="s">'J1_P4'</span><span class="p">)</span>
    <span class="n">prog_pin</span> <span class="o">=</span> <span class="n">gf</span><span class="p">.</span><span class="n">gpio</span><span class="p">.</span><span class="n">get_pin</span><span class="p">(</span><span class="s">'J1_P6'</span><span class="p">)</span>

    <span class="c1"># Reset is active low
</span>    <span class="n">reset_pin</span><span class="p">.</span><span class="n">high</span><span class="p">()</span>

    <span class="c1"># Enter prog mode
</span>    <span class="n">prog_pin</span><span class="p">.</span><span class="n">high</span><span class="p">()</span>
    <span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.01</span><span class="p">)</span>
    <span class="n">reset</span><span class="p">(</span><span class="n">gf</span><span class="p">,</span> <span class="n">reset_pin</span><span class="p">)</span>

    <span class="c1"># ...
</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</code></pre></div></div>

<p>After resetting the device I’d attempt to send one of the flash SPI commands
to get the flash status and flash protection status registers, as well as do
a test read:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fsr</span> <span class="o">=</span> <span class="nb">ord</span><span class="p">(</span><span class="n">gf</span><span class="p">.</span><span class="n">spi</span><span class="p">.</span><span class="n">transmit</span><span class="p">([</span><span class="mh">0x05</span><span class="p">],</span> <span class="n">receive_length</span><span class="o">=</span><span class="mi">1</span><span class="p">))</span>
<span class="n">fpcr</span> <span class="o">=</span> <span class="nb">ord</span><span class="p">(</span><span class="n">gf</span><span class="p">.</span><span class="n">spi</span><span class="p">.</span><span class="n">transmit</span><span class="p">([</span><span class="mh">0x89</span><span class="p">],</span> <span class="n">receive_length</span><span class="o">=</span><span class="mi">1</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'flash status register: </span><span class="si">{</span><span class="n">fsr</span><span class="si">:</span><span class="c1">#02x</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'flash protect register: </span><span class="si">{</span><span class="n">fpcr</span><span class="si">:</span><span class="c1">#02x</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

<span class="c1"># test read
</span><span class="k">print</span><span class="p">(</span><span class="s">'test read:'</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">gf</span><span class="p">.</span><span class="n">spi</span><span class="p">.</span><span class="n">transmit</span><span class="p">([</span><span class="mh">0x03</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">],</span> <span class="n">receive_length</span><span class="o">=</span><span class="mi">256</span><span class="p">)</span>
<span class="n">hexdump</span><span class="p">.</span><span class="n">hexdump</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</code></pre></div></div>

<p>I still wasn’t getting any response (just the same series of <code class="language-plaintext highlighter-rouge">0xFF</code> bytes), so I did a bit of debugging with my oscilloscope to make
sure all the pins were working correctly. I ran a simple script on the GreatFET to keep
resetting the device and triggered the oscilloscope on the reset pin going back up high
so I could look at the state of the scope probes soon after boot.
In addition to checking the RESET and PROG pins,
I took a simple power consumption measurement with a shunt resistor to see if the device
was really resetting. Note the difference in the power consumption when the device
successfully enters PROG mode:</p>

<figure class="image">
  <a href="/assets/img/k360/scope_app.jpg">
    <img src="/assets/img/k360/scope_app.jpg" alt="Reset with PROG low" />
  </a>
  <figcaption>
    Reset with PROG low
  </figcaption>
</figure>

<figure class="image">
  <a href="/assets/img/k360/scope_prog.jpg">
    <img src="/assets/img/k360/scope_prog.jpg" alt="Reset with PROG high" />
  </a>
  <figcaption>
    Reset with PROG high
  </figcaption>
</figure>

<p>One of the flash SPI probes might’ve been off, but after checking everything was OK
on the oscilloscope I tried this process again and was able to read out some
intelligible data from the flash:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./test.py
flash status register: 0x80
flash protect register: 0x0
<span class="nb">test read</span>:
00000000: 80 A3 A3 02 00 03 78 FF  E4 F6 D8 FD 90 00 00 7F  ......x.........
00000010: 00 7E 04 E4 F0 A3 DF FC  DE FA 75 81 7E 02 07 82  .~........u.~...
00000020: FC 00 FF D9 00 11 01 FF  E0 FF E0 00 01 02 FF E1  ................
00000030: FF E1 00 01 02 FF E2 FF  E6 00 01 02 FF E7 FF EB  ................
00000040: 00 01 02 FF EC FF EF 00  04 02 FF F0 FF FF 00 01  ................
00000050: 00 57 69 72 65 6C 65 73  73 20 4B 65 79 62 6F 61  .Wireless Keyboa
00000060: 72 64 20 00 34 D9 1D F0  40 01 00 00 00 61 02 20  rd .4...@....a.
...
</code></pre></div></div>

<p>Note the “Wireless Keyboard” string. After that I did a single read for
18432 bytes, the maximum allowed according to the data sheet.
The output looked liked a sensible dump of the program flash.
The nRF24LE1 uses the 8051 instruction set, so to confirm it was code I loaded it
into Ghidra as an 8051 binary blob.
There appears to be an initialization routine near the beginning of the blob,
suggesting it’s valid code.</p>

<figure class="image">
  <a href="/assets/img/k360/ghidra_initial.png">
    <img src="/assets/img/k360/ghidra_initial.png" alt="Test disassembly in Ghidra" />
  </a>
  <figcaption>
    Test disassembly in Ghidra
  </figcaption>
</figure>

<p>To verify the flash readback protection and hardware debug enable settings
I also wanted to read out the InfoPage mentioned in the datasheet:</p>

<figure class="image">
  <a href="/assets/img/k360/infopage.png">
    <img src="/assets/img/k360/infopage.png" alt="InfoPage section of nRF24LE1 datasheet" />
  </a>
  <figcaption>
    InfoPage section of nRF24LE1 datasheet
  </figcaption>
</figure>

<p>Reading the InfoPage requires setting the INFEN bit in the flash status register,
so to do that I just had to send a “write flash status register” command before
performing the read:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">read_fsr</span><span class="p">(</span><span class="n">gf</span><span class="p">):</span>
    <span class="n">fsr</span> <span class="o">=</span> <span class="n">gf</span><span class="p">.</span><span class="n">spi</span><span class="p">.</span><span class="n">transmit</span><span class="p">([</span><span class="mh">0x05</span><span class="p">],</span> <span class="n">receive_length</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
    <span class="k">return</span> <span class="nb">ord</span><span class="p">(</span><span class="n">fsr</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">write_fsr</span><span class="p">(</span><span class="n">gf</span><span class="p">,</span> <span class="n">fsr</span><span class="p">):</span>
    <span class="n">fsr</span> <span class="o">&amp;=</span> <span class="mh">0xff</span>
    <span class="n">gf</span><span class="p">.</span><span class="n">spi</span><span class="p">.</span><span class="n">transmit</span><span class="p">([</span><span class="mh">0x01</span><span class="p">,</span> <span class="n">fsr</span><span class="p">])</span>

<span class="k">def</span> <span class="nf">read_flash</span><span class="p">(</span><span class="n">gf</span><span class="p">,</span> <span class="n">address</span><span class="p">,</span> <span class="n">count</span><span class="p">):</span>
    <span class="n">command</span> <span class="o">=</span> <span class="n">struct</span><span class="p">.</span><span class="n">pack</span><span class="p">(</span><span class="s">'&gt;BH'</span><span class="p">,</span> <span class="mh">0x03</span><span class="p">,</span> <span class="n">address</span><span class="p">)</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">gf</span><span class="p">.</span><span class="n">spi</span><span class="p">.</span><span class="n">transmit</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="n">receive_length</span><span class="o">=</span><span class="n">count</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">data</span>

<span class="k">def</span> <span class="nf">get_infoblock</span><span class="p">(</span><span class="n">gf</span><span class="p">):</span>
    <span class="n">flash_stat_reg</span> <span class="o">=</span> <span class="n">read_fsr</span><span class="p">(</span><span class="n">gf</span><span class="p">)</span>

    <span class="c1"># INFEN is bit 3 (2^3)
</span>    <span class="n">write_fsr</span><span class="p">(</span><span class="n">gf</span><span class="p">,</span> <span class="n">flash_stat_reg</span> <span class="o">|</span> <span class="mi">8</span><span class="p">)</span>
    <span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.001</span><span class="p">)</span>

    <span class="n">infoblock</span> <span class="o">=</span> <span class="n">read_flash</span><span class="p">(</span><span class="n">gf</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">512</span><span class="p">)</span>

    <span class="c1"># Unset INFEN bit
</span>    <span class="n">write_fsr</span><span class="p">(</span><span class="n">gf</span><span class="p">,</span> <span class="n">flash_stat_reg</span> <span class="o">&amp;</span> <span class="p">(</span><span class="o">~</span><span class="mi">8</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">))</span>

    <span class="k">return</span> <span class="n">infoblock</span>
</code></pre></div></div>

<p>Now I can read out the InfoPage to confirm the readback protection
and HW debug settings. Although the fact that my initial read attempts
returned program data also indicated that readback protection was disabled,
going through these steps helped me build confidence that I was using the
flash programming interface correctly. It looks like a backup of the
InfoPage would also be important to have in case I erase the flash later on.</p>

<p>With a few more simple additions it’s easy to use this code to dump
the program flash and/or InfoPage out to files:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./k360_spi.py <span class="nt">--dump</span> flashdump.bin
flash status register: 0x80
flash protect register: 0x0
InfoBlock content:
00000000: 00 A3 A3 48 31 57 54 79  70 14 0A 12 FF FF 98 04  ...H1WTyp.......
00000010: 79 7C 88 23 B1 50 0F 05  FF FF FF FF 82 79 FF FF  y|.#.P.......y..
00000020: FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
00000030: FF FF FF 4C 45 31 4F FF  FF FF FF FF FF FF FF FF  ...LE1O.........
...
000001F0: FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
Flash readback protection: False <span class="o">(</span>ff<span class="o">)</span>
HW debug enabled: False <span class="o">(</span>ff<span class="o">)</span>
wrote flash dump to flashdump.bin
</code></pre></div></div>

<p>The full source code for <code class="language-plaintext highlighter-rouge">k360_spi.py</code> can be found at <a href="https://gist.github.com/jamchamb/b2892a22ac0760346d4d617fedf9b541">https://gist.github.com/jamchamb/b2892a22ac0760346d4d617fedf9b541</a>. The next step will be to analyze the firmware.</p>]]></content><author><name></name></author><category term="Reverse Engineering" /><category term="Hardware" /><summary type="html"><![CDATA[I recently decided to do some keyboard hacking for fun, so I started with one of the cheapest Logitech wireless keyboard models available: the K360. This model is a little old and the main chip inside it, as well as the Logitech Unifying wireless protocol it uses, have been well covered before. See Marc Newlin’s MouseJack presentation, Travis Goodspeed’s nRF24 sniffing work, and the KeyKeriki research mentioned in each. I’m doing this more as an exercise rather than novel research, and I didn’t know what I’d find going in. That said, I thought this was a neat little example of extracting bare metal firmware from on-chip flash.]]></summary></entry><entry><title type="html">CSCG 2020 Maze game hacking challenge writeups</title><link href="https://jamchamb.net/2020/06/21/cscg2020-maze-writeups.html" rel="alternate" type="text/html" title="CSCG 2020 Maze game hacking challenge writeups" /><published>2020-06-21T00:00:00+00:00</published><updated>2020-06-21T00:00:00+00:00</updated><id>https://jamchamb.net/2020/06/21/cscg2020-maze-writeups</id><content type="html" xml:base="https://jamchamb.net/2020/06/21/cscg2020-maze-writeups.html"><![CDATA[<p>A couple of months ago I took a crack at the Maze challenges in the CSCG 2020 CTF
and thought a few of the challenges were really interesting, so I wanted to share how
I solved them.</p>

<p>I found out about the Maze challenge while watching LiveOverflow’s
<a href="https://www.youtube.com/watch?v=RDZnlcnmPUA&amp;list=PLhixgUqwRTjzzBeFSHXrw9DnQtssdAwgG">Pwn Adventure 3 video series</a>.
The Maze challenge was created by LiveOverflow, so I figured there would be some similarities
to the Pwn Adventure 3 CTF and thought it would be a good opportunity to try some online game
hacking (without the risk of getting accounts banned :p).</p>

<p>This gave me a chance to get into some Unity game hacking, as well as Cheat Engine.
I was familiar with the basic concepts of Cheat Engine, such as dynamic memory scanning
and pointer scanning, but I hadn’t used it much before.</p>

<p>I was able to solve the first few challenges just using standard Cheat Engine techniques.
The first was the “secret emoji” challenge.</p>

<h2 id="emoji">Emoji</h2>

<p>When you first start the game and make an account you’re only able
to use two emojis:</p>

<p><img src="/assets/img/maze/emojibar.png" alt="Emoji bar" /></p>

<p>This game was built with Unity, which is based on the Mono development framework.
Using Cheat Engine’s Mono dissector, which is a built-in feature for analyzing
metadata about object classes in a Mono binary, I found that there was a <code class="language-plaintext highlighter-rouge">sendEmoji</code>
method on the server class. By setting a breakpoint on this function I could
intercept calls to it and change the first argument, a 16-bit emoji ID.</p>

<p>I solved it by pressing the button for one of the available emojis and changing
the argument register (<code class="language-plaintext highlighter-rouge">RDX</code>) to a new value each time using the debugger,
and then observing what showed up. Eventually I reached the ID value of <code class="language-plaintext highlighter-rouge">0x0D</code>,
which resulted in the flag emoji being triggered. Here’s what it looked like:</p>

<p><img src="/assets/img/maze/emoji1.png" alt="Cheat Engine debugger window and solution" /></p>

<p>And here’s how the emoji looks, since I didn’t capture it in the first screenshot:</p>

<p><img src="/assets/img/maze/emoji2.png" alt="Emoji flag graphic" /></p>

<h2 id="flying--teleporting-across-very-short-distances">Flying &amp; Teleporting (across very short distances)</h2>

<p>The next two challenges, “The Floor is Lava” and “Tower”, involved getting to hard to reach locations.
I figured that in order to attempt this I should try to use Cheat Engine to figure out my player
character’s coordinates in memory and see how I could tamper with them.</p>

<p>First I used the memory scanning feature to discover the player’s coordinates, and then I used
pointer scanning to discover a reliable way to reference the player object in memory
so that I wouldn’t need to find it again every time. There are a bunch of videos online
about how to do this, here’s the one from the Pwn Adventure 3 series:
<a href="https://youtu.be/yAl_6qg6ZnA">https://youtu.be/yAl_6qg6ZnA</a>.</p>

<p>There’s a fence in the maze at the very beginning of the path to these locations
that blocks you from proceeding. This was good for testing out a simple teleport hack.</p>

<p><img src="/assets/img/maze/fence.png" alt="The fence" /></p>

<p>Once I had the coordinates visible in the Cheat Engine address view, I could watch
how they changed as I approached the fence. I noticed that the Z axis coordinate
increased as I approached it, so once I got stuck I edited that value and increased
it by 1.</p>

<video autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/img/maze/fence_teleport.mp4" type="video/mp4" />
</video>

<p>This didn’t cause any issues, the server accepted the small change in location
despite there being an obstacle in the way in the game client. I also tried setting
my coordinates to <code class="language-plaintext highlighter-rouge">0, 0, 0</code>, but that caused the server to teleport me back to
my previous location. There was clearly some sort of distance limit on how far
you could go between position updates.</p>

<p>To do a simple fly hack I used the “Memory that writes to this location” feature
on the Y coordinate, which corresponds to altitude, and NOP’d out the instructions
that updated that coordinate one by one. Then I was able to set my altitude to about
30 or 40 and not fall back to the ground, giving me an overview of the map:</p>

<p><img src="/assets/img/maze/flying.png" alt="Floating above the map" /></p>

<p>By freezing updates to all of the coordinates I could teleport
myself anywhere in the map and see myself there in the game client, but the server
clearly did not accept the position my game client was reporting.
As long as I remained in the new location, it would keep trying to teleport me back to my
last legitimate position.
While blocking this allowed me to explore the map, any interactions with the world that
depended on my location would not be recognized by the server, so it was unlikely that
I’d be able to get any flags this way.</p>

<h2 id="the-floor-is-lava">The Floor is Lava</h2>

<p><img src="/assets/img/maze/lava.png" alt="The floor is actually lava" /></p>

<p>While I was able to move around using the fly hack, I still couldn’t go
over the maze walls even though I wasn’t colliding with them. It was also difficult to
pass through them using the short-distance teleport hack. It seemed like the server
was enforcing the maze wall barriers rather than just relying on the game client to
block movement this time.
However, if I positioned myself just right, I could do a short teleport of about 5 units
in a given direction and pass through the wall without the server forcing me back.</p>

<p>To make the hack a little easier to use, I set up some key bindings in Cheat Engine to
increase or decrease the X or Z coordinate by 5.0, giving me an arrow-key style way
to move through the air and through walls. This allowed me to head right for the lava
pit, over the lava, and to the treasure chest island:</p>

<p><img src="/assets/img/maze/lava2.png" alt="Getting the lava flag" /></p>

<h2 id="tower">Tower</h2>

<p>Using the same fly hack solution, I was able to head over to the tower as well:</p>

<p><img src="/assets/img/maze/tower.png" alt="Getting the tower flag" /></p>

<hr />

<p>For the rest of the challenges I decided to dig deeper into the game disassembly
and network protocol. This game was built with <a href="https://github.com/Perfare/Il2CppDumper">IL2CPP</a>,
making it a little trickier to work with than a Mono/.NET binary (which could be decompiled
into something that looks much like the original source code using a tool like
<a href="https://github.com/icsharpcode/ILSpy">ILSpy</a> or <a href="https://github.com/0xd4d/dnSpy">dnSpy</a>).</p>

<p>I used Ghidra to disassemble the game binary and <a href="https://github.com/Perfare/Il2CppDumper">Il2CppDumper</a>
to extract its metadata and recreate function symbols.</p>

<p>To work with the network protocol I used the example network proxy script from the Pwn Adventure 3
video series as a base. The main modification I had to make was to convert it from using TCP
connections to UDP packets - the Maze game uses UDP, but otherwise works similar to
Pwn Adventure 3.</p>

<p>When it first connects to the server it’s actually hitting some HTTP endpoints to get
information about the game servers, such as the range of ports available to connect to.
To intercept this traffic I used Burp Suite and redirected all traffic for
<code class="language-plaintext highlighter-rouge">maze.liveoverflow.com</code> to my proxy VM.</p>

<p><img src="/assets/img/maze/burp_proxy.png" alt="Proxying HTTP traffic through Burp Suite" /></p>

<p>The original range of UDP ports used by the game servers was <code class="language-plaintext highlighter-rouge">1337</code> to <code class="language-plaintext highlighter-rouge">1357</code>, but to simplify
things in the network proxy I eventually added an auto-replace rule for the <code class="language-plaintext highlighter-rouge">/api/max_port</code> response
so that the game would always connect to the same port.</p>

<p>Once I had the basic network proxy for the UDP game server traffic working it was clear that
the packets were encrypted. Using Ghidra I found two suspect pieces of code in the send
and receive packet functions for the server class, which performed XOR operations on the
packet data. The recreated encryption routine looks like this:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">encrypt_data</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="n">key_x</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">)</span>
    <span class="n">key_y</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">)</span>

    <span class="n">encrypted</span> <span class="o">=</span> <span class="p">[</span><span class="n">key_x</span><span class="p">,</span> <span class="n">key_y</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="mi">0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">data</span><span class="p">]</span>

    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)):</span>
        <span class="n">encrypted</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">key_x</span> <span class="o">^</span> <span class="nb">ord</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
        <span class="n">new_key</span> <span class="o">=</span> <span class="n">key_x</span> <span class="o">+</span> <span class="n">key_y</span>
        <span class="n">key_x</span> <span class="o">=</span> <span class="p">(</span><span class="n">new_key</span> <span class="o">+</span> <span class="p">(</span><span class="n">new_key</span> <span class="o">/</span> <span class="mh">0xff</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0xff</span>

    <span class="k">return</span> <span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">([</span><span class="nb">chr</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">encrypted</span><span class="p">])</span></code></pre></figure>

<p>With encryption and decryption routines added to the proxy I could begin to analyze
and tamper with the network protocol, using the disassembled binary as an aid for
figuring out what the packets meant.</p>

<h2 id="map-radar">Map Radar</h2>

<blockquote>
  <p>“There are rumours of a player who found a secret place and walks in a weird pattern. A radar map could be useful.”</p>
</blockquote>

<p>The Map Radar hack was one of the first challenges that was going to require more than
manual Cheat Engine hacks. The two possibilities I saw were doing a DLL injection hack
to extract player locations and add an actual GUI radar to the game, which would be awesome
but time consuming, or
reading player position information out of the network packets.</p>

<p>Using the network proxy I could see that periodically I’d receive an “I” packet
containing a bunch of player names, and a “P” packet with a big chunk of data in it -
much more than any of the other packet types.</p>

<pre>
[1337] &lt;- 5787493713ffff00051054686520576869746520526162626974
00000000: 57 87 49 37 13 FF FF 00  05 10 54 68 65 20 57 68  W.I7......The Wh
00000010: 69 74 65 20 52 61 62 62  69 74                    ite Rabbit
</pre>
<p><em>Player info packet (server to client)</em><br /></p>

<p>The “I” packet contains a 32-bit player ID, 16-bit unlocked abilities value,
and finally the length of the player name and the player name string. One interesting
“player” that kept showing up was “The White Rabbit” with an ID of <code class="language-plaintext highlighter-rouge">0xFFFF1337</code>.</p>

<p>These player IDs corresponded to values in the “P” packets, which I could tell contained
position data  based on the disassembly. Each entry in the position packet
contains the player ID, timestamp, coordinates, “trigger” (whether they are jumping
or landing, as far as I can tell), and animation blend values (show running vs. jumping/falling
animation).</p>

<pre>
[1337] &lt;- 048a50110000001f27b5000...
00000000: 04 8A 50 11 00 00 00 1F  27 B5 00 00 00 00 00 11  ..P.....'.......
00000010: 45 2B 00 00 00 00 00 BA  5F 23 00 00 00 00 00 D9  E+......_#......
00000020: FE 30 00 00 00 00 00 00  00 00 00 00 50 37 13 FF  .0..........P7..
00000030: FF 63 0C 3F 00 00 00 00  00 B4 F8 21 00 48 77 FF  .c.?.......!.Hw.
00000040: FF F3 86 03 00 D7 EB 36  00 64 EE 1D 00 00 00 00  .......6.d......
00000050: 00 00 C8 00 00 00 50 8E  01 00 00 00 E3 59 8F 01  ......P......Y..
00000060: 00 00 00 30 38 1F 00 20  4E 00 00 A8 AD 1D 00 00  ...08.. N.......
00000070: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
</pre>
<p><em>Position packet (server to client)</em><br /></p>

<p>Using this data I could figure out the position of “The White Rabbit”: they were
somewhere underneath the map! Using the fly/teleport hack I moved underground
(which works the same as “flying” - just freeze the altitude with a negative value),
and found the area where the character was running around. It’s actually accessible
through a special wall in the spaceship area of the map:</p>

<video autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/img/maze/whiterabbit.mp4" type="video/mp4" />
</video>

<p>This character continuously navigates an unusual path through this underground space,
so at this point I figured I should record its coordinates to get an overhead view
of what was happening.</p>

<p>By adding a hook to the position data packet parser I recorded all coordinates
received for this character and saved them to a text file. Then I used
<a href="https://matplotlib.org/"><code class="language-plaintext highlighter-rouge">matplotlib</code></a> to plot the points on a graph, which
revealed the flag:</p>

<p><img src="/assets/img/maze/radar.png" alt="Radar flag" /></p>

<h2 id="maze-runner">Maze Runner</h2>

<p>The timed maze race challenges were the most interesting to me in this set.
The first challenge was to simply complete the race without timing out, which was impossible
without hacking the game. You needed to reach each consecutive checkpoint in the maze race within
10 seconds of the last or you would fail, and it was only possible to get to the third
checkpoint before timing out.</p>

<p><img src="/assets/img/maze/race_failed.png" alt="Timeout" /></p>

<p>I noticed that there was one type of packet the server would send to the client
when a checkpoint was reached, which only contained the checkpoint ID. This packet
would trigger the <code class="language-plaintext highlighter-rouge">gotCheckpoint</code> method of the <code class="language-plaintext highlighter-rouge">RaceManager</code> class with the provided
waypoint ID. The first thing I tried was to send one of these packets for each checkpoint
to the client in quick succession - it did not trigger the flag, so all of the race
handling code must implemented server-side. (I also tried the same attack via DLL injection
before I had the network proxy set up, directly invoking the function for each checkpoint ID,
but the result was the same.)</p>

<p>That didn’t mean that the checkpoint packets
were useless, though - they were still valuable for telling me when the server believed
my character had reached a waypoint.</p>

<p>Since the server needed to believe I reached each of the checkpoints legitimately in
order to send me the flag, I first wanted to trace out a path from the start to the
finish by extracting data from the client position packets, similar to the solution
for the Map Radar challenge. I added “record”, “save recording”, “load recording”,
and “play recording” commands to the network proxy to achieve this.</p>

<p>The “record” command would enable the logic for collecting the outgoing position
coordinates, and the save command would save it into a JSON file. The load command
would load coordinates from a JSON file so I could restore the recording later.</p>

<p>I disabled the in-game timeout logic so that I could see where all of the glowing checkpoints
were within the maze, and then ran through it while recording my position.
The resulting recording had 448 coordinates.
Here’s what it looks like plotted on a graph:</p>

<p><img src="/assets/img/maze/recording_plot.png" alt="Recorded coordinates" /></p>

<p>The play command used server teleport packets to replay recorded positions.
This was unusual but easier than implementing the client position packets, which I
wasn’t able to get working right away.</p>

<p>The teleport packet type is what the server used to block illegitimate movements, as
described earlier. It’s also used for the magic teleport gates found in the courtyard you
land in after logging in.
By injecting this type of packet I could force the game client to update
the character’s position, causing the client to send out a position update to the server. Because the
positions recorded were legitimate and played back with their original timing, the
updated positions were accepted by the server.</p>

<p>It looks a little choppy, but it worked:</p>

<video autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/img/maze/playrec1.mp4" type="video/mp4" />
</video>

<p>To beat the first challenge, I decreased the time between position updates in the
playback. Reducing the interval from the original timing of about 0.2 seconds per packet
to 0.16 seconds, I was able to beat the race without timing out.</p>

<video autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/img/maze/playrec3.mp4" type="video/mp4" />
</video>

<p>However, this took about a minute to complete, and the next challenge was to complete the
race within five seconds. Going any lower than the 0.16 second interval would cause the
server to start rejecting the position packets, resulting in rubber banding.</p>

<h2 id="m4z3-runn3r">M4z3 Runn3r</h2>

<p>This was the hardest challenge, but the most fun to figure out. Here are the key points
about how the maze race works, based on the previous solutions:</p>

<ul>
  <li>The race progress is kept track of server-side. In order to beat the race, the server must believe the player character has reached each of the checkpoints in succession. (Verified this by trying to teleport to each of the checkpoint coordinates in order and see if the server thought the race was completed anyway.)</li>
  <li>There is a limit on how far the player can travel between position updates.</li>
  <li>Trying to play back the path recording too quickly failed.</li>
</ul>

<p>Also, until this point my attempts to directly send position packets to the server resulted
in the server kicking me from the game. There was one important part of the position
packet that I wasn’t handling properly yet, which was the timestamp. I was trying to just
set it to 0 or a value similar to the last seen timestamp in a genuine position
packet, but that didn’t work.</p>

<p>Before trying to handle timestamps correctly, I wanted to figure out why the position
packets were getting rejected. I used Cheat Engine to disable the usual rate limit on
position updates (one per 0.2 seconds), as well as the logic for only sending an update
when the position in the client changes. This caused the game to constantly send out
position updates. This never resulted in a kick, so I knew the problem was not
how frequently I was sending the packets; it had to be something related to the distance.</p>

<p>Another quick hack I tried was to decrease the rate limit and artificially set a distance value
to an amount I had seen work before. Normally, while sprinting and using the default
rate limit of 0.2 seconds, I observed that the character would move just about 1 unit
of distance. I tried turning the rate limit down to 0.01 while keeping the distance
travelled at 1.0 units per packet, which also failed.</p>

<p>Based on all of this information, it seemed likely the server was enforcing a distance
check based on the player’s speed, i.e., distance over time. Based on the observed
1 unit of movement per 0.2 seconds, the default sprint speed appeared to be
5 units per second. It seemed that defeating this check would require handling the
position packet timestamps correctly.</p>

<p>In order to send the correct timestamp, I’d have to synchronize the time value with
the game client’s time. The game client keeps track of time as the number of seconds that
have passed since launching the game, and the number of seconds that have passed since the
player last connected to a game server.</p>

<p>There is a “heartbeat” packet type constantly being sent back and forth between client and server.
The game client reports its current time, and the server responds back with the same timestamp
as well as the real date time as a Unix epoch timestamp, presumably the real time that it received
the heartbeat. Each position update from the client to the server also includes a timestamp
value based on the game client’s time. Once I understood how this worked, I could use the
proxy to keep track of the time values and inject new values if needed.</p>

<p>Having control over the timestamp values enabled me to do some more checks. For one, I
was able to determine that the timestamp value always had to be increasing.
Going “back in time” or trying to freeze time would cause the server to boot me.</p>

<p>While running these tests a possibility occurred to me: what if I artificially sped
up time so that I could pass the distance over time check? For example, if I wanted
to move 100 units I could pretend that 20 seconds had passed by artificially
increasing the timestamp value.</p>

<p>To implement this I added some more logic to the recording replay code. Based on a given
units per second speed value, I would calculate a new scaled time interval between two
positions that satisfied a distance over time check for a maximum speed of ~5 units per
second. I rewrote the timestamps for all heartbeats and position packets, and made sure
to keep track of how much extra time was accrued so that I never went backwards on the
timestamp value and got kicked.</p>

<p><img src="/assets/img/maze/dist_time_proxy.png" alt="Changing timestamps with the proxy" /></p>

<p>This turned out to be the solution! By tweaking this code I was able to get a finish
time of 0.969 seconds using the original recording with 448 points.</p>

<video autoplay="" loop="" muted="" playsinline="">
  <source src="/assets/img/maze/playrec5.mp4" type="video/mp4" />
</video>
<p><br /></p>

<p><strong>Update (July 10th, 2020)</strong>: I’ve uploaded the challenge solution code to GitHub at
<a href="https://github.com/jamchamb/cscg2020-maze-proxy">https://github.com/jamchamb/cscg2020-maze-proxy</a>.</p>]]></content><author><name></name></author><category term="Reverse Engineering" /><category term="Game Hacking" /><summary type="html"><![CDATA[A couple of months ago I took a crack at the Maze challenges in the CSCG 2020 CTF and thought a few of the challenges were really interesting, so I wanted to share how I solved them.]]></summary></entry><entry><title type="html">Making a GameCube memory card editor with Raspberry Pi</title><link href="https://jamchamb.net/2018/12/03/gamecube-memory-card-raspi.html" rel="alternate" type="text/html" title="Making a GameCube memory card editor with Raspberry Pi" /><published>2018-12-03T00:00:00+00:00</published><updated>2018-12-03T00:00:00+00:00</updated><id>https://jamchamb.net/2018/12/03/gamecube-memory-card-raspi</id><content type="html" xml:base="https://jamchamb.net/2018/12/03/gamecube-memory-card-raspi.html"><![CDATA[<p>I started this project because I wanted to be able to use save file modifications
I was testing in the Dolphin emulator on real GameCube hardware. One, because
some of the weirder features of the Animal Crossing NES emulator and exploit payloads
might behave differently in an emulator, and two, because it’s more fun to see
things working on a real console.</p>

<p>While it’s possible to transfer files between a PC and memory card using an <a href="https://www.gc-forever.com/wiki/index.php?title=SDGecko">“SDGecko”</a>
SD adapter and homebrew software on a Wii (or GameCube with something like the SD Launcher kit),
I don’t have a Wii and it seemed like overkill to buy these things just to edit memory cards.
There’s also this obscure
<a href="http://www.hkems.com/product/gc/gc%20usb.htm">GameCube memory card with a built-in USB adapter</a>
that allowed editing with custom PC software, but it seems like it went out of
production a while ago.</p>

<p>Instead of hunting down out-of-print adapters or buying another console just to
transfer some files, I thought it would be an interesting
hardware reverse engineering project to figure out how GameCube memory cards work, and
how I could simulate or edit them.</p>

<p>The first steps to understanding how the memory card works are:</p>

<ul>
  <li>Mapping out the physical interface between the memory card and console</li>
  <li>Capturing electrical signals transmitted between the console and memory card
to figure out the low level transmission protocol</li>
  <li>Capturing transmitted data</li>
  <li>Interpreting captured data to figure out the format of commands that the console
sends to the memory card to perform read and write operations</li>
</ul>

<p>After understanding how read and write commands are sent to the memory card,
and how the memory card should respond, it will be possible to simulate a
memory card or edit it directly.</p>

<h1 id="physical-interface">Physical interface</h1>

<p>Luckily there are already some diagrams of the memory card’s pins, such as in
this post on the GC-Forever forum by Ashen: <a href="https://www.gc-forever.com/forums/viewtopic.php?t=666">https://www.gc-forever.com/forums/viewtopic.php?t=666</a>.</p>

<p>The first row of pins that enters the console are for power and ground connections.
The second row has all of the pins involved in data transfer, and there’s also a “sense”
pin in each row so that the console can tell when the card is plugged all the way in.</p>

<p>Here’s what the inside of a third-party memory card looks like, with the pins and
main components labelled:</p>

<p><img src="/assets/img/memcard/memcard_pins_full.png" alt="Memory card pinout" /></p>

<p>I assumed DI and DO stood for data in/data out, and the clock was the clock signal.
INT and CS were less clear, as there was no protocol described to give context,
but I guess that INT stands for interrupt, and CS is the “chip select” pin from
<a href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface">SPI</a>.</p>

<h1 id="capturing-signals">Capturing signals</h1>

<p>To figure out what exactly these pins are used for and what the protocol is,
I’d have to have some way to capture the signal going through them while the
GameCube accesses the memory card.</p>

<p>To do this I soldered some thin enameled wire to each of the pins on the bottom
row (DI, DO, CS, INT, CLK). Here’s how the first attempt turned out:</p>

<p><img src="/assets/img/memcard/first_solder.jpg" alt="First soldered card" /></p>

<p><img src="/assets/img/memcard/first_card_cover.jpg" alt="First soldered card with cover" /></p>

<p>This card stopped getting a clean signal after a
while, so the second time around I kept the wires shorter with more consistent lengths,
and added some extra hot glue for support:</p>

<p><img src="/assets/img/memcard/second_solder.jpg" alt="Second soldered card" /></p>

<p><em>(Note that I also added wires to the power and ground pins - this was for directly
connecting to the card from a Raspberry Pi later on.)</em></p>

<p>It’s a bit tricky to solder this up, and if you’re just looking
to make the memory card editor it would be much cleaner to use a spare memory
card slot from an actual console.</p>

<p>With the pins wired up, I could finally capture the signal from them using a logic
analyzer. I used a saleae Logic 8 and connected to all of the data row pins, and then
performed captures while doing things like inserting the card or copying and deleting
files with the system memory card manager.</p>

<p><img src="/assets/img/memcard/logic_analyzer_console.jpg" alt="Logic analyzer capture" /></p>

<p>Besides INT, the data pins map neatly to the standard SPI channels, and using the
SPI analyzer turned up a byte stream without much fuss:</p>

<p><img src="/assets/img/memcard/logic_spi_zoom.png" alt="Logic analyzer SPI" /></p>

<ul>
  <li>DI - MOSI</li>
  <li>DO - MISO</li>
  <li>CS - CS / Enable</li>
  <li>Clock - Clock</li>
</ul>

<p>This bit of ASCII text that says “Broken File” appearing in the data stream from the
card made it easy to check that the settings were correct:</p>

<p><img src="/assets/img/memcard/logic_broken_file.png" alt="ASCII text in SPI capture" /></p>

<p>Now it’s clear that the communication protocol is almost entirely standard SPI, save
for the INT pin. (If you’re not familiar with SPI, this Sparkfun tutorial is a good resource:
<a href="https://learn.sparkfun.com/tutorials/serial-peripheral-interface-spi/all">https://learn.sparkfun.com/tutorials/serial-peripheral-interface-spi/all</a>.)
Luckily the INT pin doesn’t appear to do much, and its signal doesn’t change often,
so you don’t need to worry about it just yet.</p>

<h1 id="reading-the-traffic">Reading the traffic</h1>

<p>With the low level transmission protocol figured out, it was time to figure out
what the bytes being sent between the console and memory card meant. The first thing
I wanted to figure out was the read commands so that I’d be able to copy out the
contents of the card, whether directly or from a logic analyzer dump.</p>

<p>The “Yet Another Gamecube Documentation” (YAGCD)
<a href="http://hitmen.c02.at/files/yagcd/yagcd/chap10.html#sec10.7">memory card section</a>,
while incomplete, was helpful for identifying the main commands.</p>

<p>For example, “read block” commands start with <code class="language-plaintext highlighter-rouge">0x52</code> and an offset address.
Here’s the beginning of a command to read the first block, at offset zero:</p>

<p><img src="/assets/img/memcard/logic_read_first.png" alt="Request to read first block" /></p>

<p>After that there are 128 filler bytes with the value <code class="language-plaintext highlighter-rouge">0xFF</code> (possibly to give the card time
to start reading), and finally the card will
begin to return data beginning from the requested offset as long as the console continues
reading. Write commands are similar: they begin with <code class="language-plaintext highlighter-rouge">0xF2</code> and an offset address, and
then continue with bytes to be written starting from that offset.</p>

<p>When a card is inserted in the console while using the memory card manager,
all of the data blocks will be read.
By writing a simple parser in Python I was able to reconstruct most of the content of my
memory card by using the read commands and responses from a logic analyzer dump
of this process.</p>

<h1 id="interfacing-with-card-on-raspberry-pi">Interfacing with card on Raspberry Pi</h1>

<p>To read all of the contents of the memory card flash, and to start sending my own
write commands to it, I’d need to interface directly with the card from hardware that I
could program. This is where the Raspberry Pi comes in. It has some dedicated SPI pins
that can be used via the Linux spidev interface. This allows me to write a program that
will act like the console (SPI master) to the memory card.</p>

<p>Connecting the card to the RasPi requires adding the 3.3V power and ground pins,
as seen in the picture of the second card I soldered, and connecting the dedicated
SPI pins to the corresponding memory card pins.</p>

<ul>
  <li>RasPi SPI Clock -&gt; Clock</li>
  <li>RasPi SPI MOSI -&gt; DI</li>
  <li>RasPi SPI MISO -&gt; DO</li>
  <li>RasPi SPI CS -&gt; CS</li>
</ul>

<p>I wrote a simple program in Python that used a Python spidev library to read each block
from the card. To get a reliable read you need to use a clock speed that the RasPi and
memory card can handle. The average clock speed used by the console is 12.5 MHz, so I’ve
been using 12 MHz as the clock speed.</p>

<p><img src="/assets/img/memcard/first_raspi_hookup.jpg" alt="Reading the card with RasPi" /></p>

<p>It takes a little while to read every single block, but it worked and I was able to
reliably read all the data out of the card. This also happened to reveal the source
of the flash chips used on these third-party memory cards from Amazon: all of the ones
I’ve looked at have leftover firmware for some Super-H based TV related device on them.</p>

<p>The final step was to implement write commands, as well as a few minor commands related
to getting and setting the status of the card. I encountered a pretty painful bug at this
point: I would send over a bunch of write commands, read the data back, and see that
nothing changed. The first thing I tried was setting up an extra GPIO pin on the RasPi
to use as the INT pin. It wasn’t necessary for getting read commands to work, but I thought
maybe it was required for writes. That still didn’t fix it.</p>

<p>Finally, I hooked the logic analyzer up to the Raspberry Pi to debug my SPI traffic:</p>

<p><img src="/assets/img/memcard/second_raspi_hookup.jpg" alt="Logic analyzer with RasPi" /></p>

<p>It turns out that I just never added the data I meant to send to the write command
buffers! After fixing that, it still behaved oddly: only the first write command would
work. This time I had to tweak the timing between commands, as well as use of the INT pin
and status commands, to get all of the write commands to work in sequence.</p>

<p>The code is a little rough, but I’ve made it available at <a href="https://github.com/jamchamb/gc-memcard-adapter">https://github.com/jamchamb/gc-memcard-adapter</a>.</p>

<p>I had orignally planned to directly simulate memory cards with a Raspberry Pi after
figuring out the communication protocol, but it turns out that it’s only practical to
use as the SPI master (it’s possible to “bit bang” this without the direct SPI hardware support,
but it would be too slow to meet the required 12.5 MHz clock speed). I’ll have to look
at some other options for creating a memory card simulating device, but for now, here’s
a video of me loading a Mega Man ROM and my Dolphin save file on a real GameCube to play
Mega Man with the NES emulator:</p>

<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true" data-theme="dark"><p lang="en" dir="ltr">Here&#39;s the Mega Man ROM running on real hardware finally <a href="https://t.co/3i27xNO3nY">pic.twitter.com/3i27xNO3nY</a></p>&mdash; James Chambers (@jamchamb_) <a href="https://twitter.com/jamchamb_/status/1064258456797028352?ref_src=twsrc%5Etfw">November 18, 2018</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>]]></content><author><name></name></author><category term="Reverse Engineering" /><category term="GameCube" /><category term="Hardware" /><summary type="html"><![CDATA[I started this project because I wanted to be able to use save file modifications I was testing in the Dolphin emulator on real GameCube hardware. One, because some of the weirder features of the Animal Crossing NES emulator and exploit payloads might behave differently in an emulator, and two, because it’s more fun to see things working on a real console.]]></summary></entry><entry><title type="html">Finding and exploiting hidden features of Animal Crossing’s NES emulator</title><link href="https://jamchamb.net/2018/07/11/animal-crossing-nes-emulator-hacks.html" rel="alternate" type="text/html" title="Finding and exploiting hidden features of Animal Crossing’s NES emulator" /><published>2018-07-11T00:00:00+00:00</published><updated>2018-07-11T00:00:00+00:00</updated><id>https://jamchamb.net/2018/07/11/animal-crossing-nes-emulator-hacks</id><content type="html" xml:base="https://jamchamb.net/2018/07/11/animal-crossing-nes-emulator-hacks.html"><![CDATA[<p>While looking for ways to activate the developer menus left over in Animal Crossing,
including the NES emulator game selection menu, I found an interesting feature that exists
in the original game that was always active, but never used by Nintendo.
In addition to the NES/Famicom games that can be obtained in-game, it was possible to
load new NES games from the memory card.
I was also able to find a way to exploit this ROM loader to patch custom code and data into
the game, allowing for code execution via the memory card.</p>

<h2 id="introduction---the-nes-console-items">Introduction - The NES console items</h2>

<p>The normal NES games that you could obtain in Animal Crossing each came as an individual
furniture piece that appeared as an NES console with a single game box on top of it.
When you placed the item in your house and interacted with it, it would only play that one game.
Pictured below are the Excitebike and Golf items.</p>

<p><img src="/assets/img/nes-emulator/single_game_consoles.png" alt="Single-game consoles" /></p>

<p>There was also a generic “NES Console” item that did not feature any of the built-in games.
You could buy this item from Redd, or sometimes obtain it through random events such as
town bulletin-board message stating that one has been buried in a random location in town.</p>

<p><img src="/assets/img/nes-emulator/nes_buried.png" alt="Random NES spawn" /></p>

<p>This item appeared as the NES console with no game boxes on top of it.</p>

<p><img src="/assets/img/nes-emulator/generic_console.png" alt="Single-game consoles" /></p>

<p>The problem with this item is that it was thought to be unplayable. Every time you
interacted with it, you would just see a message indicating that you didn’t have any
software to play.</p>

<p><img src="/assets/img/nes-emulator/nes_no_software.png" alt="&quot;No software&quot; message" /></p>

<p>It turns out that this generic console item actually attempts to scan the memory card for
specially constructed files that contain NES ROM images! The NES emulator used to play
the built-in games is apparently a complete, generic NES emulator for the GameCube, and
it’s capable of playing most games thrown at it.</p>

<p>Before demonstrating these features, I’ll explain the process of reverse engineering them.</p>

<h2 id="finding-the-memory-card-rom-loader">Finding the memory card ROM loader</h2>

<h3 id="looking-for-dev-menus">Looking for dev menus</h3>

<p>My original intention was to find code that activates the various developer menus, such
as the map select menu or NES emulator game select menu. The
<a href="https://tcrf.net/Animal_Crossing#Map_Select">“Forest Map Select” menu</a>,
which makes it easy to instantly load directly into different locations in the game,
was easy enough to locate just by searching for the “FOREST MAP SELECT” string that
appears at the top of the screen (as seen in various videos and screenshots online).</p>

<p>The “FOREST MAP SELECT” had a data cross-reference to a function called <code class="language-plaintext highlighter-rouge">select_print_wait</code>,
which lead to a bunch of other functions that also had the <code class="language-plaintext highlighter-rouge">select_*</code> prefix,
including one called <code class="language-plaintext highlighter-rouge">select_init</code>. These happen to be the functions that handle
the map select menu.</p>

<p>The <code class="language-plaintext highlighter-rouge">select_init</code> function lead to another interesting function called
<code class="language-plaintext highlighter-rouge">game_get_next_game_dlftbl</code>. This one ties together all the other menus and “scenes”
that can run: the Nintendo logo screen, the title screen, the map select menu,
the NES (Famicom) emulator menu, and so on. It runs early in the main procedure
of the game, looks up which scene initialization function it should run, and finds its
entry in a table data structure called <code class="language-plaintext highlighter-rouge">game_dlftbls</code>. This table holds references to
the different scene handling functions, as well as some other data.</p>

<p><img src="/assets/img/nes-emulator/game_init.png" alt="game_get_next_game_dlftbl" /></p>

<p>A close up of the first block of the function shows that it loads the “next game init”
function, and then starts comparing it to a series of known init functions:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">first_game_init</code></li>
  <li><code class="language-plaintext highlighter-rouge">select_init</code></li>
  <li><code class="language-plaintext highlighter-rouge">play_init</code></li>
  <li><code class="language-plaintext highlighter-rouge">second_game_init</code></li>
  <li><code class="language-plaintext highlighter-rouge">trademark_init</code></li>
  <li><code class="language-plaintext highlighter-rouge">player_select_init</code></li>
  <li><code class="language-plaintext highlighter-rouge">save_menu_init</code></li>
  <li><code class="language-plaintext highlighter-rouge">famicom_emu_init</code></li>
  <li><code class="language-plaintext highlighter-rouge">prenmi_init</code></li>
</ul>

<p><img src="/assets/img/nes-emulator/game_init_begin.png" alt="game_get_next_game_dlftbl first block" /></p>

<p>One of the function pointers it checks for is <code class="language-plaintext highlighter-rouge">famicom_emu_init</code>, which is responsible for
starting up the NES/Famicom emulator. By forcing the result of <code class="language-plaintext highlighter-rouge">game_get_next_game_init</code>
to be <code class="language-plaintext highlighter-rouge">famicom_emu_init</code> or <code class="language-plaintext highlighter-rouge">select_init</code> in the Dolphin debugger, I can get the special
menus to display. The next step is to figure out how these pointers would normally be
set during runtime. All the <code class="language-plaintext highlighter-rouge">game_get_next_game_init</code> function does is load a value
at offset <code class="language-plaintext highlighter-rouge">0xC</code> of the first argument to <code class="language-plaintext highlighter-rouge">game_get_next_game_dlftbl</code>.</p>

<p>Tracking how these values got set across various data structures was a bit tedious,
so I’ll just cut to the chase. The main things I found were:</p>

<ul>
  <li>When the game starts up normally, it goes through this sequence:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">first_game_init</code></li>
      <li><code class="language-plaintext highlighter-rouge">second_game_init</code></li>
      <li><code class="language-plaintext highlighter-rouge">trademark_init</code></li>
      <li><code class="language-plaintext highlighter-rouge">play_init</code></li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">player_select_init</code> will set the next init to <code class="language-plaintext highlighter-rouge">select_init</code>. This screen is supposed to
allow for player selection just before map selection, but didn’t seem to be working correctly.</li>
</ul>

<p>There was also one unnamed function that would set the emulator init function, but nothing
appeared to set the init function to the player or map select inits.</p>

<p>At this point I realized I had another silly issue with how I loaded function names
into IDA, where I was missing any function names that began with a capital letter
due to the regular expression I used to cut out lines in the debug symbol file.
The function that would set up <code class="language-plaintext highlighter-rouge">famicom_emu_init</code> looked related to scene transitions,
and indeed its name turned out to be <code class="language-plaintext highlighter-rouge">Game_play_fbdemo_wipe_proc</code>.</p>

<p><code class="language-plaintext highlighter-rouge">Game_play_fbdemo_wipe_proc</code> handles scene transitions such as screen wipes and fades.
Under certain conditions, the screen transition leads from normal gameplay into the
emulator display. That’s what will set the emulator init function.</p>

<h3 id="console-furniture-handling">Console furniture handling</h3>

<p>What causes the screen transition handler to switch over to the emulator  is
actually the furniture item handler functions for the NES consoles.
<code class="language-plaintext highlighter-rouge">aMR_FamicomEmuCommonMove</code> is called when a player interacts with
one of the consoles.</p>

<p>When this function is called, <code class="language-plaintext highlighter-rouge">r6</code> holds an index value corresponding to the numbers seen
in the filenames of the NES games in <code class="language-plaintext highlighter-rouge">famicom.arc</code>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">01_nes_cluclu3.bin.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">02_usa_balloon.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">03_nes_donkey1_3.bin.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">04_usa_jr_math.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">05_pinball_1.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">06_nes_tennis3.bin.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">07_usa_golf.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">08_punch_wh.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">09_usa_baseball_1.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">10_cluclu_1.qd.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">11_usa_donkey3.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">12_donkeyjr_1.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">13_soccer.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">14_exbike.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">15_usa_wario.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">16_usa_icecl.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">17_nes_mario1_2.bin.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">18_smario_0.nes.szs</code></li>
  <li><code class="language-plaintext highlighter-rouge">19_usa_zelda1_1.nes.szs</code></li>
</ul>

<p>(<code class="language-plaintext highlighter-rouge">.arc</code> is a proprietary file archive format.)</p>

<p>When <code class="language-plaintext highlighter-rouge">r6</code> is non-zero, it’s passed along in a call to <code class="language-plaintext highlighter-rouge">aMR_RequestStartEmu</code>.
This eventually triggers the emulator transition.</p>

<p><img src="/assets/img/nes-emulator/nes_furniture_move.png" alt="aMR_FamicomEmuCommonMove" /></p>

<p>However, if <code class="language-plaintext highlighter-rouge">r6</code> is zero, a function named <code class="language-plaintext highlighter-rouge">aMR_RequestStartEmu_MemoryC</code> is called instead.
Setting the value to zero in the debugger, I got the “I don’t have any software” message.
I didn’t recall the generic “NES Console” item right away to see if that’s what would
cause <code class="language-plaintext highlighter-rouge">r6</code> to be zero, but it is - index zero is used for the generic console item.</p>

<p>While <code class="language-plaintext highlighter-rouge">aMR_RequestStartEmu</code> just stores the index value to some data structure,
<code class="language-plaintext highlighter-rouge">aMR_RequestStartEmu_MemoryC</code> does something much more complex…</p>

<p><img src="/assets/img/nes-emulator/request_emu_mc.png" alt="aMR_RequestStartEmu_MemoryC" /></p>

<p>That third code block calls <code class="language-plaintext highlighter-rouge">aMR_GetCardFamicomCount</code> and checks for a non-zero result,
or else it will short-circuit past most of the interesting stuff on the left side of
the function graph.</p>

<p><code class="language-plaintext highlighter-rouge">aMR_GetCardFamicomCount</code> calls into <code class="language-plaintext highlighter-rouge">famicom_get_disksystem_titles</code>, which then calls
into <code class="language-plaintext highlighter-rouge">memcard_game_list</code>, which is where things start to get really interesting.</p>

<p><code class="language-plaintext highlighter-rouge">memcard_game_list</code> will mount the memory card and start looping through its file entries,
checking some values on each one. By tracing through it in the debugger, I could see what
it was comparing the values to on each of my memory card files.</p>

<p><img src="/assets/img/nes-emulator/memcard_game_list_filenames.png" alt="aMR_RequestStartEmu_MemoryC" /></p>

<p>Whether or not the function decides to load in a file depends on a few string comparison checks.
First, it checks for the presence of the strings “GAFE” and “01”, which are the game ID
and company ID, respectively. The 01 refers to Nintendo, “GAFE” refers to Animal Crossing.
My guess is that it’s short for “GameCube Animal Forest English”.</p>

<p>Then it checks for the strings “DobutsunomoriP_F_” and “SAVE”. In this case,
the first string should match, but not the second. “DobutsunomoriP_F_SAVE” happens to be
the name of the file that stores save data for the built-in NES games.
So, any file besides that with the “DobutsunomoriP_F_” prefix will be loaded.</p>

<p>By using the Dolphin debugger to skip over the “SAVE” string comparison and trick
the game into thinking my “SAVE” file was OK to load, I got this menu to show up when
I used the NES console:</p>

<p><img src="/assets/img/nes-emulator/load_save_trick.png" alt="Force loading the SAV file" /></p>

<p>I answered yes and attempted to load the save file up as a game, and got the built-in
crash screen for the first time:</p>

<p><img src="/assets/img/nes-emulator/crash_screen.png" alt="Crash screen" /></p>

<p>Cool! Now that I know it is in fact trying to load games from the memory card,
I can start figuring out the format for the save files to see how to load up a real
ROM.</p>

<p>One of the first things I tried to do was find out where the game name was being
read from in the memory card file. By searching for the string “FEFSC” that appears in
the “Would you like to play &lt;name&gt;?” message, I found the offset where it was being read
from in the file: <code class="language-plaintext highlighter-rouge">0x642</code>. By copying the save file, changing the filename to
“DobutsunomoriP_F_TEST”, setting the bytes at offset <code class="language-plaintext highlighter-rouge">0x642</code> to “TESTING”, and re-importing
the edited save, I could get the desired title name to display in the menu.</p>

<p>Adding multiple files in this format resulted in more options being added to the menu,
as seen here:</p>

<p><img src="/assets/img/nes-emulator/multiple_games.png" alt="Game menu options" /></p>

<h3 id="booting-a-rom-file">Booting a ROM file</h3>

<p>If <code class="language-plaintext highlighter-rouge">aMR_GetCardFamicomCount</code> returned non-zero, some memory is allocated on the heap,
<code class="language-plaintext highlighter-rouge">famicom_get_disksystem_titles</code> is called again directly, and then a bunch of random
offsets in a data structure get set. Instead of deciphering where all these values
were going to be read, I started looking at the list of <code class="language-plaintext highlighter-rouge">famicom</code> functions.</p>

<p><code class="language-plaintext highlighter-rouge">famicom_rom_load</code> turned out to be the right place to look. It handles ROM loading,
whether from a memory card or the internal game resources.</p>

<p><img src="/assets/img/nes-emulator/famicom_rom_load_notes.png" alt="famicom_rom_load" /></p>

<p>The most significant thing in the “memory card load” block is that it calls
<code class="language-plaintext highlighter-rouge">memcard_game_load</code>. This mounts the file on the memory card once again, reads it in,
and parses it. The most important features of the file format become apparent here.</p>

<h4 id="checksum-value">Checksum value</h4>

<p>The first thing that happens after the file is loaded is a checksum calculation.
The <code class="language-plaintext highlighter-rouge">calcSum</code> function is called, which is a very simple algorithm that sums up
the values of all the bytes in the memory card data. The low eight bits of the
result must be zero. So, to pass this check, you have to sum up the values of all
the bytes in your original file, figure out what value to add to that sum to
cause the low eight bits to be zero, and then set a checksum byte in your file
to that value.</p>

<p>If the check fails, you get a message stating that the memory card couldn’t
be read correctly, and nothing happens.
During the debugging process, all I have to do is skip over this check.</p>

<h4 id="copying-the-rom">Copying the ROM</h4>

<p>Down near the end of <code class="language-plaintext highlighter-rouge">memcard_game_load</code>, another interesting thing happens.
There are some more interesting code blocks between this and the checksum, but none of
them will result in a branch that skips over this behavior.</p>

<p><img src="/assets/img/nes-emulator/memcard_game_load_rom.png" alt="memcard_game_load ROM copying" /></p>

<p>If a certain 16-bit integer read from the card is non-zero, a function will be called to check for
a compression header on a buffer. It checks for some proprietary Nintendo compression formats by
looking for “Yay0” or “Yaz0” at the beginning of the buffer. If one of these is found,
a decompression function is called. Otherwise, a simple memory copy function is performed.
Either way, a variable called <code class="language-plaintext highlighter-rouge">nesinfo_data_size</code> is updated afterwards.</p>

<p>Another context clue here is that the ROM files for the built-in NES games use “Yaz0” compression,
and have that string in their file header.</p>

<p>By observing the value that’s checked for zero and the buffer that’s passed to the compression
check functions, I can quickly identify where in the memory card file the game is reading from.
The zero-check is performed against part of a 32 byte buffer that’s copied from offset <code class="language-plaintext highlighter-rouge">0x640</code>
in the file, which is likely a header for the ROM. Other parts of it are also checked throughout
this function, and it’s where the game title is located (starting from the third byte of the header).</p>

<p>With the specific code path I hit, the ROM buffer is located immediately after this 32 byte
header buffer.</p>

<p><img src="/assets/img/nes-emulator/memcard_game_load_notes.png" alt="memcard_game_load annotated" /></p>

<p>This is enough information to attempt to construct a valid ROM file. I simply took one of the other
Animal Crossing save files and edited it with a hex editor to change the name of the file to
<code class="language-plaintext highlighter-rouge">DobutsunomoriP_F_TEST</code> and clear out the areas where I needed to insert data.</p>

<p>I used the Pinball ROM that’s already present in the game for this test run, and copied its content
in after the 32 byte header for a test. Instead of calculating the checksum value, I also set some
breakpoints so that I could just skip over <code class="language-plaintext highlighter-rouge">calcSum</code>, as well as observe the results of other checks
that might cause a branch that skips past loading the ROM.</p>

<p>Finally, I imported the new file through the Dolphin memory card manager, restarted the game,
and went to try it out on the console.</p>

<p><img src="/assets/img/nes-emulator/pinball_working.png" alt="Pinball menu option" /></p>

<p><img src="/assets/img/nes-emulator/boot_pinball.png" alt="Pinball booting" /></p>

<p>It worked! There were some graphical quirks caused by Dolphin settings that affect the
graphics mode used by the NES emulator, but the game played just fine.
(In newer Dolphin builds it should work by default.)</p>

<p>To be sure that other games would work, I tried out some more ROMs that weren’t already present
in the game. Battletoads would start up, but not continue past the intro text (with some more
tweaking later on, it did become playable).
Mega Man, on the other hand, worked perfectly:</p>

<p><img src="/assets/img/nes-emulator/megaman.png" alt="Playing Mega Man in Animal Crossing" /></p>

<p>To be able to generate more ROM files that could load without any debugger intervention
I’d have to start writing code and dig into the file format parsing some more.</p>

<h3 id="the-external-rom-file-format">The external ROM file format</h3>

<p>Most of the critical file parsing happens in <code class="language-plaintext highlighter-rouge">memcard_game_load</code>. There are six main sections
to the parsing code blocks in this function:</p>

<ul>
  <li>Checksum</li>
  <li>Save file name</li>
  <li>ROM file header</li>
  <li>Unknown buffer that’s copied without any processing</li>
  <li>Text comment, icon, and banner loader (for new save file creation)</li>
  <li>ROM loader</li>
</ul>

<p><img src="/assets/img/nes-emulator/memcard_game_load_sections_notes.png" alt="memcard_game_load sections" /></p>

<h4 id="checksum">Checksum</h4>

<p>The low eight bits of the sum of all the byte values in the save file must be zero.
Here’s some simple Python code that generates a checksum byte that can achieve that:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">checksum</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">byte_val</span> <span class="ow">in</span> <span class="n">new_data_tmp</span><span class="p">:</span>
    <span class="n">checksum</span> <span class="o">+=</span> <span class="n">byte_val</span>
    <span class="n">checksum</span> <span class="o">=</span> <span class="n">checksum</span> <span class="o">%</span> <span class="p">(</span><span class="mi">2</span><span class="o">**</span><span class="mi">32</span><span class="p">)</span>  <span class="c1"># keep it 32 bit
</span>
<span class="n">checkbyte</span> <span class="o">=</span> <span class="p">(</span><span class="mi">256</span> <span class="o">-</span> <span class="p">(</span><span class="n">checksum</span> <span class="o">%</span> <span class="mi">256</span><span class="p">))</span> <span class="o">%</span> <span class="mi">256</span>
<span class="n">new_data_tmp</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">checkbyte</span></code></pre></figure>

<p>There’s probably a designated location to store the checksum byte, but just
placing it in empty padding space at the very end of the save file works fine.</p>

<h4 id="file-name">File name</h4>

<p>Just to reiterate, the save file name must begin with “DobutsunomoriP_F_” and end
with something other than “SAVE”. This filename is copied a couple of times,
and in one case the letter “F” is replaced with “S”. This will be the name of
save files for the given NES game (“DobutsunomoriP_S_NAME”).</p>

<h4 id="rom-header">ROM header</h4>

<p>A direct copy of the 32 byte header is loaded into memory. A few of the values
in this header are used to determine how to handle the upcoming sections.
It mainly includes some 16-bit size values and packed setting bits.</p>

<p>If you trace the pointer that the header is copied to
all the way to the beginning of the function and figure out its argument position,
the function signature below reveals that its type is in fact <code class="language-plaintext highlighter-rouge">MemcardGameHeader_t*</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>memcard_game_load(unsigned char *, int, unsigned char **, char *, char *, MemcardGameHeader_t *, unsigned char *, unsigned long, unsigned char *, unsigned long)
</code></pre></div></div>

<h4 id="unknown-buffer">Unknown buffer</h4>

<p>A 16-bit size value from the header is checked. If it’s non-zero, that number
of bytes will be directly copied from the file buffer into a new block of allocated
memory. This advances a data
pointer in the file buffer so that copying can resume from the next section
later on.</p>

<h4 id="banner-icon-and-comment">Banner, icon, and comment</h4>

<p>Another size value is checked in the header, and if it’s non-zero the compression check
function is called. If necessary the decompression algorithm will run, and then <code class="language-plaintext highlighter-rouge">SetupExternCommentImage</code>
is called.</p>

<p>This function handles three things: a “comment”, a banner image, and an icon. For each one there’s a code
in the ROM header that indicates how it should be handled. The options are:</p>

<ol>
  <li>Use a default value</li>
  <li>Copy from the ROM file banner/icon/comment section</li>
  <li>Copy from an alternate buffer</li>
</ol>

<p>The default value code will cause the icon or banner to be loaded from an on-disk resource,
and the save file name and comment (a text description of the file) to be set to
“Animal Crossing” and “NES Cassette Save Data” respectively. This is how it would look:</p>

<p><img src="/assets/img/nes-emulator/default_banner_icon.png" alt="Default banner, icon, and file description" /></p>

<p>The second code value will just copy the game name from the ROM file (some alternative to
“Animal Crossing”), and then attempt to find the string “] ROM” in the file comment and replace it
with “] SAVE”. Presumably, the files Nintendo intended to release would have a name format like
“Game Name [NES] ROM”, or something similar.</p>

<p>For the icon and banner it would attempt to figure out the format of the image, get a fixed size value
according to that format, and then copy the image over.</p>

<p>For the last code value, the file name and description would be copied from another buffer without
any changes, and the icon and banner would be loaded from the alternate buffer as well.</p>

<h4 id="rom">ROM</h4>

<p>If you look carefully at the <code class="language-plaintext highlighter-rouge">memcard_game_load</code> screenshot of the ROM copying,
the 16-bit value that’s checked for zero is left shifted by 4 bits (multiplied by 16)
and then used as the size for the <code class="language-plaintext highlighter-rouge">memcpy</code> function when no compression is detected. This is
another size value present in the header.</p>

<p>If the size is non-zero, the ROM data is checked for compression and then copied over.</p>

<h3 id="the-unknown-buffer-and-the-search-for-bugs">The unknown buffer and the search for bugs</h3>

<p>While getting new ROMs to load up was pretty cool, one of the most interesting things about this ROM loader to me
was that it’s virtually the only thing in the game that accepts variable-size user input and copies it to different
places in memory. Almost everything else uses fix-sized buffers. Things like names and letter text might seem like
they’re variable in size, but the empty space is basically filled with space characters. Null-terminated strings are
not used often, preventing some common memory corruption bugs such as using <code class="language-plaintext highlighter-rouge">strcpy</code> on a buffer that’s too small
for the string being copied over to it.</p>

<p>I was really interested in finding a save file based exploit in the game, and this seemed like the best bet.</p>

<p>Most of the ROM file handling described above also used fixed-size copies, except for the unknown buffer and ROM data.
Unfortunately, the code that handles this buffer allocates just as much space as is needed to copy it, so there’s no overflow,
and setting really large ROM file sizes wasn’t very useful.</p>

<p>Still, I wanted to know what was going on with that buffer that would be directly copied without any handling.</p>

<h4 id="the-nes-info-tag-processors">The NES Info Tag processors</h4>

<p>Revisiting <code class="language-plaintext highlighter-rouge">famicom_rom_load</code>, a few functions are called after a ROM gets loaded from the memory card or disk:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">nesinfo_tag_process1</code></li>
  <li><code class="language-plaintext highlighter-rouge">nesinfo_tag_process2</code></li>
  <li><code class="language-plaintext highlighter-rouge">nesinfo_tag_process3</code></li>
</ul>

<p>By tracing where the unknown buffer was copied to, I verified that it was being operated on by these functions.
These start by calling <code class="language-plaintext highlighter-rouge">nesinfo_next_tag</code>, which goes through a simple algorithm:</p>

<ul>
  <li>Check if the given pointer matches the pointer in <code class="language-plaintext highlighter-rouge">nesinfo_tags_end</code>. If it’s less than <code class="language-plaintext highlighter-rouge">nesinfo_tags_end</code>, or <code class="language-plaintext highlighter-rouge">nesinfo_tags_end</code>
is zero, it checks if the string “END” is present at the head of the pointer.
    <ul>
      <li>If “END” has been reached, or the pointer has advanced up to or past <code class="language-plaintext highlighter-rouge">nesinfo_tags_end</code>, the function returns zero (null).</li>
      <li>Otherwise, the byte at offset <code class="language-plaintext highlighter-rouge">0x3</code> of the pointer is added to 4 and the current pointer, and that value is returned.</li>
    </ul>
  </li>
</ul>

<p>This suggests a tag format of some three letter name, a data size value, and data. The result is a pointer to the next tag,
as the current tag will be skipped over (<code class="language-plaintext highlighter-rouge">cur_ptr + 4</code> skips the three byte name and one byte size, and <code class="language-plaintext highlighter-rouge">size_byte</code> skips over the data).</p>

<p>If the result is non-zero, the tag processing function then goes through a series of string comparisons to figure out
what tag to handle. Some of the tag names checked for in <code class="language-plaintext highlighter-rouge">nesinfo_tag_process1</code>  are VEQ, VNE, GID, GNO, BBR, and QDS.</p>

<p><img src="/assets/img/nes-emulator/nesinfo_tag_process1_notes.png" alt="nesinfo_tag_process1" /></p>

<p>If a tag is matched, some handler code is executed. Some of the handlers do nothing but print the tag to a debug message.
Others have more complex handlers. After a tag is processed, the function attempts to get the next tag and continue
processing.</p>

<p>Luckily, there are a bunch of descriptive debug messages that get printed out
when these tags are found. They’re all in Japanese, so they have to be Shift-JIS decoded and translated first.
The messages for QDS, for example, can say “Load Disk Save Area” or “Since it is the first play, keep the disk save area”.
The messages for BBR say “battery backup load” or “because it is the first play, clear”.</p>

<p>Both of these codes also load some values from their tag data section and use them to calculate an offset into the ROM data
and then perform copy operations.
It’s apparent that they’re responsible for designating parts of the ROM memory that are related to saving state.</p>

<p>There’s also an “HSC” tag that has a debug message indicating that this handles high scores. It takes an offset
into the ROM from its tag data, as well as an initial high score value. These tags can be used to mark where high score
values are kept in the NES game’s memory, probably so that it can be saved and restored later.</p>

<p>These tags provide a fairly complex system for loading metadata about the ROMs. Even better, many of them result
in <code class="language-plaintext highlighter-rouge">memcpy</code> calls based on values provided in the tag data.</p>

<h4 id="bug-hunting">Bug hunting</h4>

<p>Most of the tags that caused memory manipulation weren’t going to be very useful for exploits, because they all
had maximum offset and size values represented by 16-bit integers. This is all that would be needed to handle
the 16-bit address space of the NES, but doesn’t provide much range for writing over useful targets such
as function pointers or return addresses on the stack in the 32-bit address space of the GameCube.</p>

<p>However, there were a few cases where offsets or size values passed to <code class="language-plaintext highlighter-rouge">memcpy</code> could exceed <code class="language-plaintext highlighter-rouge">0xFFFF</code>.</p>

<h5 id="qds">QDS</h5>

<p>QDS actually loads a 24-bit offset from its tag data, as well as a 16-bit size value.</p>

<p>The good thing is that the offset is used to calculate the destination of a copy operation.
The base address for the offset is the beginning of the loaded ROM data, the source of the copy
is in the memory card ROM file, and the size is the given 16-bit size value from the tag.</p>

<p>A 24-bit offset has a maximum value of <code class="language-plaintext highlighter-rouge">0xFFFFFF</code>, which is well above what’s needed to write
outside the boundary of the loaded ROM data. There are some problems, though…</p>

<p>The first is that even though the maximum size value is <code class="language-plaintext highlighter-rouge">0xFFFF</code>, it’s initially used to zero
out a section of memory. If the size value is too high (not much more than <code class="language-plaintext highlighter-rouge">0x1000</code>), this will
actually zero out the “QDS” tag in the game’s code.</p>

<p>This is a problem because <code class="language-plaintext highlighter-rouge">nesinfo_tag_process1</code> actually gets called twice. The first time, it will
collect some information about space it needs to set up for save data. The QDS and BBR tags are not
fully processed on the first run. After the first run, some space is set up for save data, and
the function is called again. This time the QDS and BBR tags would be fully processed,
but it’s impossible to match the tags again if the tag name strings have all been cleared out of
memory!</p>

<p>So, setting a smaller size value can avoid that. The other problem is that the offset value
can only go forwards in memory, and the NES rom data is located on the heap fairly close
to the end of usable memory.</p>

<p>There are only a few heap entries that come after it, none of which had anything super useful
like obvious function pointers.</p>

<p>Normally it might be possible to use this for a heap overflow exploit, but the <code class="language-plaintext highlighter-rouge">malloc</code> implemenation
used for this heap actually adds a load of sanity check bytes into the <code class="language-plaintext highlighter-rouge">malloc</code> blocks. It’s possible
to write over pointer values in the subsequent heap blocks. Without the sanity checking, this could be
used to write an arbitrary value to an arbitrary location in memory when <code class="language-plaintext highlighter-rouge">free</code> is called on the
affected heap block.</p>

<p>However, the <code class="language-plaintext highlighter-rouge">malloc</code> implementation used here will check for a specific byte pattern (<code class="language-plaintext highlighter-rouge">0x7373</code>) at the beginning of the
next and previous blocks it’s going to manipulate upon the call to <code class="language-plaintext highlighter-rouge">free</code>. If it doesn’t find those bytes,
it calls <code class="language-plaintext highlighter-rouge">OSPanic</code> and the game stops.</p>

<p><img src="/assets/img/nes-emulator/get_block_next.png" alt="get_block_next calling OSPanic if it doesn't find 0x7373" /></p>

<p>Without being able to influence those bytes to be present at
some target location, it’s not possible to write there. In other words, you can’t write something to
an arbitrary location without already being able to write something right next to that location.
There could be some way to get the value <code class="language-plaintext highlighter-rouge">0x73730000</code> to be stored on the stack right before a return address,
<em>and</em> the location referenced by the value you want to write to the destination address (it will also be checked
as if it’s a pointer to a heap block), but it’d be difficult to find and exploit.</p>

<h5 id="nesinfo_update_highscore"><code class="language-plaintext highlighter-rouge">nesinfo_update_highscore</code></h5>

<p>Another function involving the QDS, BBR, and HSC tags is <code class="language-plaintext highlighter-rouge">nesinfo_update_highscore</code>.
The QDS, BBR, and OFS (offset) tag size values are used to calculate an offset to write to, and an
HSC tag triggers a write to that location. This function runs for every frame processed
by the NES emulator.</p>

<p>The maximum offset value per tag in this case, even for QDS, is <code class="language-plaintext highlighter-rouge">0xFFFF</code>.
However, during the tag processing loop, size
values from BBR and QDS tags actually get <em>accumulated</em>. This means that multiple tags
can be used to calculate just about any offset value. The limit is the number of tags
that can be fit in the ROM tag data section of the memory card file,
which has a maximum size of <code class="language-plaintext highlighter-rouge">0xFFFF</code> as well.</p>

<p>The base address that the offset gets added to is <code class="language-plaintext highlighter-rouge">0x800C3180</code>, the save data buffer.
This is at a much lower address than the ROM data, providing more freedom in choosing
where to write to. Writing over the function’s return address on the stack at <code class="language-plaintext highlighter-rouge">0x812F95DC</code>,
for example, would be fairly easy.</p>

<p>Unfortunately, this doesn’t work either. <code class="language-plaintext highlighter-rouge">nesinfo_tag_process1</code> happens to also figure out
the accumulated size of the offsets from these tags, and uses that size to initialize
some space like this:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">bzero</span><span class="p">(</span><span class="n">nintendo_hi_0</span><span class="p">,</span> <span class="p">((</span><span class="n">offset_sum</span> <span class="o">+</span> <span class="mh">0xB</span><span class="p">)</span> <span class="o">*</span> <span class="mi">4</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x40</span><span class="p">)</span></code></pre></figure>

<p><img src="/assets/img/nes-emulator/bzero.png" alt="The culprit" /></p>

<p>With the offset value I tried to calculate, this resulted in <code class="language-plaintext highlighter-rouge">0x48D91EC</code> (76,386,796)
bytes of  memory getting wiped out, causing the game to crash spectacularly.</p>

<h3 id="the-pat-tag">The PAT tag</h3>

<p>It was starting to look hopeless, as all of the tags that made unsafe calls to <code class="language-plaintext highlighter-rouge">memcpy</code> would
end up causing a crash before they could be useful.
I decided to switch over to just documenting the purpose of each tag, and eventually reached the
tags in <code class="language-plaintext highlighter-rouge">nesinfo_tag_process2</code>.</p>

<p>Most of the tag handlers in <code class="language-plaintext highlighter-rouge">nesinfo_tag_process2</code> will never run because they only work
when the pointer <code class="language-plaintext highlighter-rouge">nesinfo_rom_start</code> is not null. Nothing in the code ever sets that pointer
to be non-null. It gets initialized to zero, and never gets used again.
Only <code class="language-plaintext highlighter-rouge">nesinfo_data_start</code> is set when a ROM gets loaded, so this looks like a piece of dead code.</p>

<p>There is one tag that can still operate when <code class="language-plaintext highlighter-rouge">nesinfo_rom_start</code> is null, though: PAT.
This is the most complex tag in the <code class="language-plaintext highlighter-rouge">nesinfo_tag_process2</code> function.</p>

<p><img src="/assets/img/nes-emulator/pat_tag_handler.png" alt="PAT tag handler" /></p>

<p>It still uses <code class="language-plaintext highlighter-rouge">nesinfo_rom_start</code> as a pointer, but never performs a null check on it.
The PAT tag will read through its own tag data buffer, processing codes that calculate offsets.
Those offsets are added to the <code class="language-plaintext highlighter-rouge">nesinfo_rom_start</code> pointer to calculate a destination address,
and then bytes are copied from the patch buffer into that location. This copy is performed with
load and store byte instructions, rather than <code class="language-plaintext highlighter-rouge">memcpy</code>, which is why I hadn’t noticed it
sooner.</p>

<p>Each PAT tag data buffer has an 8-bit type code, 8-bit patch size, and 16-bit offset value,
followed by the patch data.</p>

<ul>
  <li>If the code is 2, the offset value is added to the current offset sum.</li>
  <li>If the code is 9, the offset is shifted up 4 bits and added to the current offset sum.</li>
  <li>If the code is 3, the offset sum is reset to 0.</li>
</ul>

<p>The largest size an NES info tag can have is 255, so the largest possible PAT entry patch
size is 251 bytes. Multiple PAT tags are allowed, though, so it’s possible to patch more
than 251 bytes, as well as patch non-contiguous locations.</p>

<p>So long as there’s a series of code 2 or code 9 PAT sub-tags, the destination pointer offset continues to accumulate.
It will be reset to zero when patch data gets copied, but using a patch size of zero avoids this.
Writing this now, it’s clear that this could be used to calculate some arbitrary offset
against the null pointer in <code class="language-plaintext highlighter-rouge">nesinfo_rom_start</code> by using lots of PAT tags.</p>

<p>However, there are two more code value checks…</p>

<ul>
  <li>If the code is between <code class="language-plaintext highlighter-rouge">0x80</code> and <code class="language-plaintext highlighter-rouge">0xFF</code>, it gets added to <code class="language-plaintext highlighter-rouge">0x7F80</code> and then shifted
up 16 bits. Finally, this is added to the 16-bit offset value and used as the destination
address to patch.</li>
</ul>

<p>This allows setting any address in the range <code class="language-plaintext highlighter-rouge">0x80000000</code> to <code class="language-plaintext highlighter-rouge">0x807FFFFF</code> as the destination
for the patch! That’s where a bunch of the code for Animal Crossing lives in memory.
This means its possible to patch Animal Crossing’s code itself using the ROM metadata
tags from a file on the memory card.</p>

<p>With a small loader patch, it’d be possible to easily load even larger patches to any address
from the memory card.</p>

<p>For a quick test, I set up a patch that would turn on “zuru mode 2” (the game’s developer mode, described
in my last blog post) when the user loads a ROM from the game card. It turns out that
the button cheat combo only activates “zuru mode 1”, which doesn’t have access to all the
same features that mode 2 has. With this patcher, it’s now possible to get full access
to developer mode on real hardware using a memory card.</p>

<p><img src="/assets/img/nes-emulator/patch_z2_1.png" alt="Patcher ROM step 1" /></p>

<p>The patch tags will be processed as the ROM is loaded up.</p>

<p><img src="/assets/img/nes-emulator/patch_z2_2.png" alt="Patcher ROM step 2" /></p>

<p>After the ROM loads, exit the NES emulator to see the result.</p>

<p><img src="/assets/img/nes-emulator/patch_z2_3.png" alt="Patcher ROM step 3" /></p>

<p>It works!</p>

<h4 id="patcher-info-tag-format">Patcher info tag format</h4>

<p>The info tags in the save file that performs this patch look like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>000000 5a 5a 5a 00 50 41 54 08 a0 04 6f 9c 00 00 00 7d  &gt;ZZZ.PAT...o....}&lt;
000010 45 4e 44 00                                      &gt;END.&lt;
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">ZZZ \x00</code>: An ignored beginning tag. <code class="language-plaintext highlighter-rouge">0x00</code> is the size of its data buffer: zero.</li>
  <li><code class="language-plaintext highlighter-rouge">PAT \x08 \xA0 \x04 \x6F\x9C \x00\x00\x00\x7D</code>: Patches <code class="language-plaintext highlighter-rouge">0x80206F9C</code> to <code class="language-plaintext highlighter-rouge">0x0000007D</code>.
    <ul>
      <li><code class="language-plaintext highlighter-rouge">0x08</code> is the size of the tag buffer.</li>
      <li><code class="language-plaintext highlighter-rouge">0xA0</code>, when added to <code class="language-plaintext highlighter-rouge">0x7F80</code>, is <code class="language-plaintext highlighter-rouge">0x8020</code>, the upper 16 bits of the destination address.</li>
      <li><code class="language-plaintext highlighter-rouge">0x04</code> is the size of the patch data (<code class="language-plaintext highlighter-rouge">0x0000007D</code>).</li>
      <li><code class="language-plaintext highlighter-rouge">0x6F9C</code> is the lower 16-bits of the destination address.</li>
      <li><code class="language-plaintext highlighter-rouge">0x0000007D</code> is the patch data.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">END \x00</code>: The end marker tag.</li>
</ul>

<p>If you want to experiment with creating patcher or ROM save files yourself, I have some simple
code at <a href="https://github.com/jamchamb/ac-nesrom-save-generator">https://github.com/jamchamb/ac-nesrom-save-generator</a> for generating the files.
A patch like the one above can be generated with the following command:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ./patcher.py Patcher /dev/null zuru_mode_2.gci -p 80206F9c 0000007D
</code></pre></div></div>

<h3 id="arbitrary-code-execution">Arbitrary code execution</h3>

<p>With this tag it’s possible to gain arbitrary code execution in Animal Crossing.</p>

<p>There’s one last hurdle: using patches against data works fine, but something’s wrong
with patching code instructions.</p>

<p>While the patches do get written, the game continues to execute the old instructions that
were there before. It seems like a caching issue, and in fact it is.
The GameCube CPU had instruction caches, as seen in <a href="https://en.wikipedia.org/wiki/Nintendo_GameCube_technical_specifications">https://en.wikipedia.org/wiki/Nintendo_GameCube_technical_specifications</a>.</p>

<p>To figure out how the cache could be cleared, I started looking up cache related functions
in the GameCube SDK documentation, and found <code class="language-plaintext highlighter-rouge">ICInvalidateRange</code>. This function
will invalidate cached blocks of instructions at a given memory address, allowing modified instruction
memory to execute with the updated code.</p>

<p>Without a way to get initial code to run, it’d still be impossible to call <code class="language-plaintext highlighter-rouge">ICInvalidateRange</code>,
though. Getting successful code execution will require one more trick.</p>

<p>While looking over the <code class="language-plaintext highlighter-rouge">malloc</code> implementation to figure out if a heap overflow exploit was possible,
I learned that the <code class="language-plaintext highlighter-rouge">malloc</code> implementation functions could be switched out dynamically through a data structure
and function named <code class="language-plaintext highlighter-rouge">my_malloc</code>. <code class="language-plaintext highlighter-rouge">my_malloc</code> would load a pointer to the current  <code class="language-plaintext highlighter-rouge">malloc</code> or <code class="language-plaintext highlighter-rouge">free</code> implementation
function from a static location in memory, and then call that function while passing along whatever arguments were
given to <code class="language-plaintext highlighter-rouge">my_malloc</code>.</p>

<p>The NES emulator used <code class="language-plaintext highlighter-rouge">my_malloc</code> heavily to allocate and free memory for NES ROM-related data, so I
knew it would be triggered multiple times around the same time that the PAT tags get processed.</p>

<p>Because <code class="language-plaintext highlighter-rouge">my_malloc</code> would load a pointer from memory and then branch to it, I could alter the control flow
of the program just by overwriting the pointer for the current <code class="language-plaintext highlighter-rouge">malloc</code> or <code class="language-plaintext highlighter-rouge">free</code> functions. Instruction caching
would not prevent this from running, as none of the instructions in <code class="language-plaintext highlighter-rouge">my_malloc</code> need to be changed.</p>

<p>Cuyler, the developer of the Dōbutsu no Mori e+ fan translation project, <a href="https://cuyler36.github.io/2018/07/14/creating-a-nes-patch-loader.html">implemented a loader in PowerPC assembly</a>
and demonstrates using it to inject new code in this video: <a href="https://www.youtube.com/watch?v=BdxN7gP6WIc">https://www.youtube.com/watch?v=BdxN7gP6WIc</a>.
(Dōbutsu no Mori e+ was the last iteration of Animal Crossing on GameCube, which has the most updates and was
only released in Japan.)
After being injected with PAT tags, the loader can read much larger patches from the memory card,
bypassing the size restrictions of the tag info section in ROM files.
In the demonstration video it loads in some code that allows the player to spawn any object by typing its ID into
a letter and then pressing the Z button.</p>

<p>With that, it will be possible to load mods, cheats, and homebrew using a regular copy of Animal
Crossing on a real GameCube.</p>

<p><strong>Update</strong>: The previous video has been taken down, so here’s another example of injecting custom code
that prints text to the screen and in-game debug console:</p>

<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/eF-baqLpt_0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>]]></content><author><name></name></author><category term="Animal Crossing" /><category term="Reverse Engineering" /><category term="ROM Hacking" /><category term="GameCube" /><category term="NES" /><category term="Emulator" /><category term="Exploits" /><summary type="html"><![CDATA[While looking for ways to activate the developer menus left over in Animal Crossing, including the NES emulator game selection menu, I found an interesting feature that exists in the original game that was always active, but never used by Nintendo. In addition to the NES/Famicom games that can be obtained in-game, it was possible to load new NES games from the memory card. I was also able to find a way to exploit this ROM loader to patch custom code and data into the game, allowing for code execution via the memory card.]]></summary></entry><entry><title type="html">Reverse engineering Animal Crossing’s developer mode</title><link href="https://jamchamb.net/2018/06/09/animal-crossing-developer-mode.html" rel="alternate" type="text/html" title="Reverse engineering Animal Crossing’s developer mode" /><published>2018-06-09T00:00:00+00:00</published><updated>2018-06-09T00:00:00+00:00</updated><id>https://jamchamb.net/2018/06/09/animal-crossing-developer-mode</id><content type="html" xml:base="https://jamchamb.net/2018/06/09/animal-crossing-developer-mode.html"><![CDATA[<p>Last summer I began reverse engineering Animal Crossing for the GameCube to explore
the possibility of creating mods for the game. I also wanted to document the process
to create tutorials for people interested in ROM hacking and reverse engineering.
In this post I explore the developer debugging features that are still left in
the game, and how I discovered a cheat combo that can be used to unlock them.</p>

<h3 id="new_debug_mode"><code class="language-plaintext highlighter-rouge">new_Debug_mode</code></h3>

<p>While looking around at some leftover debug symbols,
I noticed functions and variable names that contained the word “debug”, and thought it would be
interesting to see what debug functionality might be left in the game. If there were any debugging
or developer features I could activate, it might also help with the process of creating mods.</p>

<p>The first function I took a look at was <code class="language-plaintext highlighter-rouge">new_Debug_mode</code>.
It’s called by the <code class="language-plaintext highlighter-rouge">entry</code> function, which runs right after the Nintendo
trademark screen finishes. All it does is allocate a <code class="language-plaintext highlighter-rouge">0x1C94</code> byte structure and
save its pointer.</p>

<p>After it gets called in <code class="language-plaintext highlighter-rouge">entry</code>, a value of 0 is set at offset <code class="language-plaintext highlighter-rouge">0xD4</code> in the allocated structure,
right before <code class="language-plaintext highlighter-rouge">mainproc</code> is called.</p>

<p><img src="/assets/img/debugmode/entry-new-debug.PNG" alt="Disassembly of the entry function" /></p>

<p>To see what happens when the value is non-zero, I patched the <code class="language-plaintext highlighter-rouge">li r0, 0</code> instruction at
<code class="language-plaintext highlighter-rouge">80407C8C</code> to <code class="language-plaintext highlighter-rouge">li r0, 1</code>. The raw bytes for the instruction <code class="language-plaintext highlighter-rouge">li r0, 0</code> are <code class="language-plaintext highlighter-rouge">38 00 00 00</code>,
where the assigned value is at the end of the instruction, so you can just change this
to <code class="language-plaintext highlighter-rouge">38 00 00 01</code> to get <code class="language-plaintext highlighter-rouge">li r0, 1</code>. For a more reliable way to assemble instructions,
you could use something like <code class="language-plaintext highlighter-rouge">kstool</code>:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span>kstool ppc32be <span class="s2">"li 0, 1"</span>
<span class="go">li 0, 1 = [ 38 00 00 01 ]</span></code></pre></figure>

<p>You can apply this patch in the Dolphin emulator by going to the “Patches” tab of the game’s
properties and entering it like so:</p>

<p><img src="/assets/img/debugmode/entry-debug-patch.PNG" alt="Debug performance meter" /></p>

<p>Setting this value to 1 caused an interesting looking graph to
appear at the bottom of the screen:</p>

<p><img src="/assets/img/debugmode/debug-meter.PNG" alt="Debug performance meter" /></p>

<p>It looked like a performance meter, with the little bars at the bottom of the screen
growing and shrinking. (Later on when I looked up the names of the functions that draw the
graph, I found that they do in fact display metrics for CPU and memory usage.)
This was neat, but not particularly useful. Setting the value above 1 actually stopped my
town from loading up, so it didn’t seem like there was much else to do with this.</p>

<h3 id="zuru-mode">Zuru mode</h3>

<p>I started to look around at other references to debug-related things, and saw something
called “zuru mode” pop up a few times. Branches to code blocks that had debug functionality
often checked a variable called <code class="language-plaintext highlighter-rouge">zurumode_flag</code>.</p>

<p><img src="/assets/img/debugmode/game_move_first.PNG" alt="game_move_first function" /></p>

<p>In the <code class="language-plaintext highlighter-rouge">game_move_first</code> function pictured above, <code class="language-plaintext highlighter-rouge">zzz_LotsOfDebug</code> (a name I made up)
only gets called if <code class="language-plaintext highlighter-rouge">zurumode_flag</code> is non-zero.</p>

<p>Looking for functions related to this value yields the following:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">zurumode_init</code></li>
  <li><code class="language-plaintext highlighter-rouge">zurumode_callback</code></li>
  <li><code class="language-plaintext highlighter-rouge">zurumode_update</code></li>
  <li><code class="language-plaintext highlighter-rouge">zurumode_cleanup</code></li>
</ul>

<p>At first glance they’re all a bit obscure, twiddling around
various bits at offsets in a variable called <code class="language-plaintext highlighter-rouge">osAppNMIBuffer</code>.
Here’s a first look at what these functions do:</p>

<h4 id="zurumode_init"><code class="language-plaintext highlighter-rouge">zurumode_init</code></h4>

<ul>
  <li>Set <code class="language-plaintext highlighter-rouge">zurumode_flag</code> to 0</li>
  <li>Check some bits in <code class="language-plaintext highlighter-rouge">osAppNMIBuffer</code></li>
  <li>Store a pointer to the <code class="language-plaintext highlighter-rouge">zurumode_callback</code> function in the <code class="language-plaintext highlighter-rouge">padmgr</code> structure</li>
  <li>Call <code class="language-plaintext highlighter-rouge">zurumode_update</code></li>
</ul>

<h4 id="zurumode_update"><code class="language-plaintext highlighter-rouge">zurumode_update</code></h4>

<ul>
  <li>Check some bits in <code class="language-plaintext highlighter-rouge">osAppNMIBuffer</code></li>
  <li>Conditionally update <code class="language-plaintext highlighter-rouge">zurumode_flag</code> based on these bits</li>
  <li>Print out a format string to the OS console.</li>
</ul>

<p>This kind of thing is usually useful
for giving context to the code, but there were a bunch of unprintable characters in the
string. The only recognizable text was “zurumode_flag” and “%d”.</p>

<p><img src="/assets/img/debugmode/shift-jis-zuru.PNG" alt="zuru mode format string" /></p>

<p>Guessing it might be Japanese
text using a multi-byte character encoding, I ran the string through a character encoding detection
tool and found out it was Shift-JIS encoded. The translated string just means “zurumode_flag has
been changed from %d to %d”. That doesn’t provide much new information, but knowing about
the use of Shift-JIS does, as there are many more strings in the binaries and string tables that
use this encoding.</p>

<h4 id="zurumode_callback"><code class="language-plaintext highlighter-rouge">zurumode_callback</code></h4>

<ul>
  <li>Calls <code class="language-plaintext highlighter-rouge">zerumode_check_keycheck</code></li>
  <li>Check some bits in <code class="language-plaintext highlighter-rouge">osAppNMIBuffer</code></li>
  <li>Prints value of <code class="language-plaintext highlighter-rouge">zurumode_flag</code> somewhere</li>
  <li>Calls <code class="language-plaintext highlighter-rouge">zurumode_update</code></li>
</ul>

<p><code class="language-plaintext highlighter-rouge">zerumode_check_keycheck</code> didn’t show up before because of the different
spelling.. what is it?</p>

<p><img src="/assets/img/debugmode/zurukeycheck.PNG" alt="zerumode_check_keycheck" /></p>

<p>A huge complex function that does lots more bit twiddling on values without names.
At this point I decided to back off and look for other debug-related functions and
variables, as I wasn’t even sure what the significance of zuru mode was. I also
wasn’t sure what “key check” meant here. Could it be a cryptographic key?</p>

<h3 id="back-to-debug">Back to debug</h3>

<p>Around this time I noticed that there was an issue with the way I loaded the debug symbols
into IDA. The <code class="language-plaintext highlighter-rouge">foresta.map</code> file on the game disc contains a bunch of addresses and names
for functions and variables. I hadn’t noticed initially that the addresses for each section
started over at zero, so I just set up a simple script to add a name entry for each line
in the file.</p>

<p>I set up new some IDA scripts to fix up the symbol map loading for the different sections of the program:
<code class="language-plaintext highlighter-rouge">.text</code>, <code class="language-plaintext highlighter-rouge">.rodata</code>, <code class="language-plaintext highlighter-rouge">.data</code>, and <code class="language-plaintext highlighter-rouge">.bss</code>.  The <code class="language-plaintext highlighter-rouge">.text</code> section is where all the functions are,
so I set the script to automatically detect functions at each address when setting a name this time.</p>

<p>For the data sections, I set it to create a segment for each binary object (such as <code class="language-plaintext highlighter-rouge">m_debug.o</code>,
which would be compiled code for something called <code class="language-plaintext highlighter-rouge">m_debug</code>), and set up space and names for each piece of data.
This gives me much more information than I had before, although I now had to manually define the data
type for each piece of data, as I set each data object to be a simple byte array. (In hindsight it would
have been better to at least assume any data with a size that’s a multiple of 4 bytes contained 32-bit
integers, as there are so many of them, and many contain addresses to functions and data that are important
for building up cross-references.)</p>

<p>While looking through the new <code class="language-plaintext highlighter-rouge">.bss</code> segment for <code class="language-plaintext highlighter-rouge">m_debug_mode.o</code>, I saw some variables like <code class="language-plaintext highlighter-rouge">quest_draw_status</code> and
<code class="language-plaintext highlighter-rouge">event_status</code>. These are interesting because I want to get debug mode to display some more useful stuff than
the performance graph. Luckily, there were cross-references from these data entries to a huge piece of code
that checks <code class="language-plaintext highlighter-rouge">debug_print_flg</code>.</p>

<p>Using the Dolphin debugger, I set a breakpoint on the function where <code class="language-plaintext highlighter-rouge">debug_print_flg</code> gets checked
(at <code class="language-plaintext highlighter-rouge">8039816C</code>) to see how the check works. The breakpoint never hit.</p>

<p>Let’s check why: this function is called by <code class="language-plaintext highlighter-rouge">game_debug_draw_last</code>. Guess what value is checked before conditionally
calling it? <code class="language-plaintext highlighter-rouge">zurumode_flag</code>. What the heck is it?</p>

<p><img src="/assets/img/debugmode/zuru_nop1.PNG" alt="zurumode_flag check" /></p>

<p>I set a breakpoint on that check (<code class="language-plaintext highlighter-rouge">80404E18</code>) and it broke immediately. The value of <code class="language-plaintext highlighter-rouge">zurumode_flag</code> was zero, so it
would normally skip calling this function. I NOPped out the branch instruction (replaced it with an instruction that
does nothing) to see what would happen when the function does get called.</p>

<p>In the Dolphin debugger you can do this by pausing the game, right-clicking on an instruction, and
then clicking “Insert nop”:</p>

<p><img src="/assets/img/debugmode/zuru_nop2.png" alt="Dolphin debugger NOPping" /></p>

<p>Nothing happened. Then I checked what has happening inside the function, and found another branch statement that could
short circuit past all of the interesting stuff at <code class="language-plaintext highlighter-rouge">803981a8</code>. I NOPped that out as well, and the letter “D”
appeared at the top right corner of the screen.</p>

<p><img src="/assets/img/debugmode/zuru_nop3.PNG" alt="Debug mode letter D" /></p>

<p>There was a bunch more interesting looking code in this function at <code class="language-plaintext highlighter-rouge">8039816C</code> (I called it <code class="language-plaintext highlighter-rouge">zzz_DebugDrawPrint</code>),
but none of it was getting called. If you look at the graph view of this function, you can see that there’s a
series of branch statements that skip over blocks throughout the entire function:</p>

<p><img src="/assets/img/debugmode/zzz_debug_draw_branches.PNG" alt="Branches in zzz_DebugDrawPrint" /></p>

<p>By NOPping out more of these branch statements, I started to see different things get printed to the screen:</p>

<p><img src="/assets/img/debugmode/field_assessment.PNG" alt="More debug stuff getting printed" /></p>

<p>The next question is how to activate these debug features without modifying the code.
Also, <code class="language-plaintext highlighter-rouge">zurumode_flag</code> appears again for some branch statements made in this debug draw function.
I added another patch so that <code class="language-plaintext highlighter-rouge">zurumode_flag</code> is always set to 2 in <code class="language-plaintext highlighter-rouge">zurumode_update</code>, because it’s
usually compared specifically with 2 when it’s not being compared with 0.
After restarting the game, I saw this “msg. no” message displayed at the top right of the screen.</p>

<p><img src="/assets/img/debugmode/message_no.PNG" alt="message number display" /></p>

<p>The number 687 is entry ID of the most recently displayed message. I checked this using a simple
table viewer I made early on, but you can also check it with a <a href="https://github.com/jamchamb/ac-editor-stringtables">full GUI string table editor</a> I made for ROM hacking. Here’s what the message looks like in the editor:</p>

<p><img src="/assets/img/debugmode/message_687.PNG" alt="Message 687 in the string table editor" /></p>

<p>At this point it was clear that figuring out zuru mode was no longer avoidable; it’s
tied directly into the debugging features of the game.</p>

<h3 id="zuru-mode-revisited">Zuru mode revisited</h3>

<p>Returning to <code class="language-plaintext highlighter-rouge">zurumode_init</code>, it initializes a few things:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">0xC(padmgr_class)</code> is set to the address of <code class="language-plaintext highlighter-rouge">zurumode_callback</code></li>
  <li><code class="language-plaintext highlighter-rouge">0x10(padmgr_class)</code> is set to the address of <code class="language-plaintext highlighter-rouge">padmgr_class</code> itself</li>
  <li><code class="language-plaintext highlighter-rouge">0x4(zuruKeyCheck)</code> is set to the last bit of a word loaded from <code class="language-plaintext highlighter-rouge">0x3C(osAppNMIBuffer)</code>.</li>
</ul>

<p>I looked into what <code class="language-plaintext highlighter-rouge">padmgr</code> means, and it’s short for “gamepad manager”. This suggests there could be
a special key (button) combination to enter on the gamepad to activate zuru mode, or possibly some
special debugging device or development console feature that could be used to send a signal to
activate it.</p>

<p><code class="language-plaintext highlighter-rouge">zurumode_init</code> only runs the first time the game is loaded (pressing reset button doesn’t trigger it).</p>

<p>Setting a breakpoint at <code class="language-plaintext highlighter-rouge">8040efa4</code>, where <code class="language-plaintext highlighter-rouge">0x4(zuruKeyCheck)</code> is set, we can see that during boot
without holding down any keys, it’s going to be set to 0. Replacing this with 1 causes an interesting thing to happen:</p>

<p><img src="/assets/img/debugmode/title_full_zuru.PNG" alt="Title screen with zuru mode" /></p>

<p>The letter “D” shows up in the upper right corner again (green instead of yellow this time),
and there’s also some build info:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[CopyDate: 02/08/01 00:16:48 ]
[Date: 02-07-31 12:52:00]
[Creator:SRD@SRD036J]
</code></pre></div></div>

<p>A patch to have <code class="language-plaintext highlighter-rouge">0x4(zuruKeyCheck)</code> always set to 1 on start:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>8040ef9c 38c00001
</code></pre></div></div>

<p>This seems like it’s the correct way to get zuru mode initialized. After that,
there may be different actions we need to take in order to get certain debug information
to display. Starting up the game and walking around and talking to a villager didn’t show any
of the displays mentioned previously (besides the letter “D” in the corner).</p>

<p>The likely suspects are <code class="language-plaintext highlighter-rouge">zurumode_update</code> and <code class="language-plaintext highlighter-rouge">zurumode_callback</code>.</p>

<h4 id="zurumode_update-1"><code class="language-plaintext highlighter-rouge">zurumode_update</code></h4>

<p><code class="language-plaintext highlighter-rouge">zurumode_update</code> is first called by <code class="language-plaintext highlighter-rouge">zurumode_init</code>, and then repeatedly gets called by
<code class="language-plaintext highlighter-rouge">zurumode_callback</code>.</p>

<p>It checks the last bit of <code class="language-plaintext highlighter-rouge">0x3C(osAppNMIBuffer)</code> again and then updates <code class="language-plaintext highlighter-rouge">zurumode_flag</code>
based on its value.</p>

<p>If the bit is zero, the flag is set to zero.</p>

<p>If not, the following instruction runs with <code class="language-plaintext highlighter-rouge">r5</code> being the full value of <code class="language-plaintext highlighter-rouge">0x3c(osAppNMIBuffer)</code>:</p>

<figure class="highlight"><pre><code class="language-asm" data-lang="asm">extrwi r3, r5, 1, 28</code></pre></figure>

<p>This extracts the 28th bit from <code class="language-plaintext highlighter-rouge">r5</code> and saves it into <code class="language-plaintext highlighter-rouge">r3</code>.
Then 1 is added to the result, so the final result is always 1 or 2.</p>

<p><code class="language-plaintext highlighter-rouge">zurumode_flag</code> is then compared to the previous result, depending on how many
of the 28th and last bits are set in <code class="language-plaintext highlighter-rouge">0x3c(osAppNMIBuffer)</code>: 0, 1, or 2.</p>

<p>This value is written to <code class="language-plaintext highlighter-rouge">zurumode_flag</code>. If it didn’t change anything, the
function ends and returns the current value of the flag. If it does change the value,
a much more complex chain of code blocks executes.</p>

<p>A message in Japanese is printed: this is the “zurumode_flag has been changed from %d to %d”
message mentioned earlier.</p>

<p>Then a series of functions are called with different arguments depending on whether the flag
was changed to zero or not. The assembly for this part is tedious, so the pseudo code of it
looks like this:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="k">if</span> <span class="p">(</span><span class="n">flag_changed_to_zero</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">JC_JUTAssertion_changeDevice</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
    <span class="n">JC_JUTDbPrint_setVisible</span><span class="p">(</span><span class="n">JC_JUTDbPrint_getManager</span><span class="p">(),</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">}</span> <span class="k">else</span> <span class="nf">if</span> <span class="p">(</span><span class="n">BIT</span><span class="p">(</span><span class="n">nmiBuffer</span><span class="p">,</span> <span class="mi">25</span><span class="p">)</span> <span class="o">||</span> <span class="n">BIT</span><span class="p">(</span><span class="n">nmiBuffer</span><span class="p">,</span> <span class="mi">31</span><span class="p">))</span> <span class="p">{</span>
    <span class="n">JC_JUTAssertion_changeDevice</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
    <span class="n">JC_JUTDbPrint_setVisible</span><span class="p">(</span><span class="n">JC_JUTDbPrint_getManager</span><span class="p">(),</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">}</span></code></pre></figure>

<p>Notice that if the flag is zero, <code class="language-plaintext highlighter-rouge">JC_JUTDbPrint_setVisible</code> is given an argument of 0.
If the flag is not zero AND bit 25 or bit 31 are set in <code class="language-plaintext highlighter-rouge">0x3C(osAppNMIBuffer)</code>, the
<code class="language-plaintext highlighter-rouge">setVisible</code> function is passed an argument of 1.</p>

<p>This is the first key to activating zuru mode: the last bit of <code class="language-plaintext highlighter-rouge">0x3C(osAppNMIBuffer)</code>
must be set to 1 in order to make the debug displays visible and set <code class="language-plaintext highlighter-rouge">zurumode_flag</code>
to a non-zero value.</p>

<h4 id="zurumode_callback-1"><code class="language-plaintext highlighter-rouge">zurumode_callback</code></h4>

<p><code class="language-plaintext highlighter-rouge">zurumode_callback</code> is at <code class="language-plaintext highlighter-rouge">8040ee74</code> and is probably called by a function related to
the gamepad. Setting a breakpoint on it in Dolphin debugger, the callstack
shows that it is indeed called from <code class="language-plaintext highlighter-rouge">padmgr_HandleRetraceMsg</code>.</p>

<p>One of the first things it does is run <code class="language-plaintext highlighter-rouge">zerucheck_key_check</code>. It’s complex, but overall it seems
to read and then update the value of <code class="language-plaintext highlighter-rouge">zuruKeyCheck</code>. I decided to see how that value is used in
the rest of the callback function before going any further into the keycheck function.</p>

<p>Next it check some bits in <code class="language-plaintext highlighter-rouge">0x3c(osAppNMIBuffer)</code> again. If bit 26 is set, or else if bit 25 is
set and <code class="language-plaintext highlighter-rouge">padmgr_isConnectedController(1)</code> returns non-zero, the last bit in <code class="language-plaintext highlighter-rouge">0x3c(osAppNMIBuffer)</code>
is set to 1!</p>

<p>If neither of those bits are set, or if bit 25 is at least set but <code class="language-plaintext highlighter-rouge">padmgr_isConnectedController(1)</code>
returns 0, then it checks if the byte at <code class="language-plaintext highlighter-rouge">0x4(zuruKeyCheck)</code> is 0. If it is,
then it will zero out the last bit in the original value and write it back to <code class="language-plaintext highlighter-rouge">0x3c(osAppNMIBuffer)</code>.
If not, then it still sets the last bit to 1.</p>

<p>In pseudo-code this looks like:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="n">x</span> <span class="o">=</span> <span class="n">osAppNMIBuffer</span><span class="p">[</span><span class="mh">0x3c</span><span class="p">]</span>

<span class="k">if</span> <span class="p">(</span><span class="n">BIT</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">26</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">BIT</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">25</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">isConnectedController</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span> <span class="o">||</span> <span class="n">zuruKeyCheck</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">osAppNMIBuffer</span><span class="p">[</span><span class="mh">0x3c</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span> <span class="o">|</span> <span class="mi">1</span>   <span class="c1">// set last bit</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">osAppNMIBuffer</span><span class="p">[</span><span class="mh">0x3c</span><span class="p">]</span> <span class="o">=</span> <span class="n">x</span> <span class="o">&amp;</span> <span class="o">~</span><span class="mi">1</span>  <span class="c1">// clear last bit</span>
<span class="p">}</span></code></pre></figure>

<p>After that, if bit 26 is not set, it shorts to calling <code class="language-plaintext highlighter-rouge">zurumode_update</code> and then finishes.</p>

<p>If it is set, then if <code class="language-plaintext highlighter-rouge">0x4(zuruKeyCheck)</code> is not zero, it loads up a format string where it
appears that it’s going to print out: “ZURU %d/%d”.</p>

<h4 id="recap">Recap</h4>

<p>Here’s what happens:</p>

<p><code class="language-plaintext highlighter-rouge">padmgr_HandleRetraceMsg</code> calls the <code class="language-plaintext highlighter-rouge">zurumode_callback</code>.
My guess is that “handle retrace message” means it has just scanned key presses
on the controller. Each time it scans, it may call a series of different callbacks.</p>

<p>When <code class="language-plaintext highlighter-rouge">zurumode_callback</code> runs, it checks the current key (button) presses.
This seems to check for a specific button or combination of buttons.</p>

<p>The last bit in the NMI Buffer is updated based on specific bits in its current
value, as well as the value of one of the <code class="language-plaintext highlighter-rouge">zuruKeyCheck</code> bytes (<code class="language-plaintext highlighter-rouge">0x4(zuruKeyCheck)</code>).</p>

<p>Then <code class="language-plaintext highlighter-rouge">zurumode_update</code> runs and checks that bit. If it’s 0, the zuru mode flag will be
set to 0. If it’s 1, the mode flag is updated to 1 or 2 based on whether bit 28 is set.</p>

<p><strong>The three ways to activate zuru mode are:</strong></p>

<ol>
  <li>Bit 26 is set in <code class="language-plaintext highlighter-rouge">0x3C(osAppNMIBuffer)</code></li>
  <li>Bit 25 is set in <code class="language-plaintext highlighter-rouge">0x3C(osAppNMIBuffer)</code>, and a controller is connected to port 2</li>
  <li><code class="language-plaintext highlighter-rouge">0x4(zuruKeyCheck)</code> is not zero</li>
</ol>

<h4 id="osappnmibuffer">osAppNMIBuffer</h4>

<p>Wondering what <code class="language-plaintext highlighter-rouge">osAppNMIBuffer</code> was, I started by searching for “NMI”, and found references
to “non-maskable interrupt” in the context of Nintendo. It turns out that the entire variable
name also shows up in the developer documentation for the Nintendo 64:</p>

<blockquote>
  <p>osAppNMIBuffer is a 64-byte buffer that is cleared on a cold reset. If the system reboots
because of a NMI, this buffer is unchanged.</p>
</blockquote>

<p>Basically, this is a small piece of memory that persists across soft reboots. A game can
use this buffer to store whatever it wants as long as the console is powered on.
The original Animal Crossing game was actually released on Nintendo 64, so it makes sense
that something like this would show up in the code.</p>

<p>Switching over to the <code class="language-plaintext highlighter-rouge">boot.dol</code> binary (everything above is from <code class="language-plaintext highlighter-rouge">foresta.rel</code>),
there are a lot of references to <code class="language-plaintext highlighter-rouge">osAppNMIBuffer</code> in the <code class="language-plaintext highlighter-rouge">main</code> function. A quick look shows
that there are series of checks that can result in various bits of <code class="language-plaintext highlighter-rouge">0x3c(osAppNMIBuffer)</code>
getting set with OR operations.</p>

<p>Interesting OR operand values to look out for would be:</p>

<ul>
  <li>Bit 31: 0x01</li>
  <li>Bit 30: 0x02</li>
  <li>Bit 29: 0x04</li>
  <li>Bit 28: 0x08</li>
  <li>Bit 27: 0x10</li>
  <li>Bit 26: 0x20</li>
</ul>

<p>Remember that bits 25, 26, and 28 are especially interesting: 25 and 26 determine
whether zuru mode is enabled, and bit 28 determines the level of the flag (1 or 2).
Bit 31 is also interesting, but primarily seems to be updated based on the values
of the others.</p>

<h5 id="bit-26">Bit 26</h5>

<p>First up: at <code class="language-plaintext highlighter-rouge">800062e0</code> there’s an <code class="language-plaintext highlighter-rouge">ori r0, r0, 0x20</code> instruction on the buffer value
at <code class="language-plaintext highlighter-rouge">0x3c</code>. This would set bit 26, which always results in zuru mode being enabled.</p>

<p><img src="/assets/img/debugmode/bit26.PNG" alt="Setting bit 26" /></p>

<p>For the bit to be set, the 8th byte returned from <code class="language-plaintext highlighter-rouge">DVDGetCurrentDiskID</code> has to be <code class="language-plaintext highlighter-rouge">0x99</code>.
This ID is located at the very beginning of the game disc image, and is loaded up at
<code class="language-plaintext highlighter-rouge">80000000</code> in memory. For a regular retail release of the game, the ID looks like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>47 41 46 45 30 31 00 00    GAFE01..
</code></pre></div></div>

<p>Patching the last byte of the ID to <code class="language-plaintext highlighter-rouge">0x99</code> causes the following to happen when starting up
the game:</p>

<p><img src="/assets/img/debugmode/zurumode2_enable.PNG" alt="Game version ID 0x99" /></p>

<p>And in the OS console, the following is printed:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>06:43:404 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: ZURUMODE2 ENABLE
08:00:288 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: osAppNMIBuffer[15]=0x00000078
</code></pre></div></div>

<p>All of the other patches can be removed, and the letter D also appears in the top right corner of
the screen again, but none of the other debug displays are activated.</p>

<h5 id="bit-25">Bit 25</h5>

<p>Bit 25 is used in conjunction with performing the port 2 controller check. What
causes it to be enabled?</p>

<p><img src="/assets/img/debugmode/bit25_28.PNG" alt="Bit 25 and 28" /></p>

<p>This turns out to have the same check used for bit 28: the version must be
greater than or equal to <code class="language-plaintext highlighter-rouge">0x90</code>. It bit 26 was set (ID is <code class="language-plaintext highlighter-rouge">0x99</code>), both of these
bits will also be set, and zuru mode will be enabled anyway.</p>

<p>If the version is between <code class="language-plaintext highlighter-rouge">0x90</code> and <code class="language-plaintext highlighter-rouge">0x98</code>, though, zuru mode is not immediately enabled.
Recalling the check made in <code class="language-plaintext highlighter-rouge">zurumode_callback</code>, it will only be enabled if bit 25 is set
<em>and</em> <code class="language-plaintext highlighter-rouge">padmgr_isConnectedController(1)</code> returns non-zero.
Once a controller is plugged into port 2 (the argument to <code class="language-plaintext highlighter-rouge">isConnectedController</code> is zero-indexed),
zuru mode gets activated. The letter D and the build info display on the title screen, and…
pressing buttons on the second controller controls the debug displays!</p>

<p>Some buttons also do things beside changing the display, such as increasing the speed of the game.</p>

<h4 id="zerucheck_key_check"><code class="language-plaintext highlighter-rouge">zerucheck_key_check</code></h4>

<p>The last mystery is <code class="language-plaintext highlighter-rouge">0x4(zuruKeyCheck)</code>. It turns out that this value gets updated by
the giant complex function shown before:</p>

<p><img src="/assets/img/debugmode/zurukeycheck.PNG" alt="zerumode_check_keycheck" /></p>

<p>Using the Dolphin debugger, I was able to determine that the value checked by this function is a set
of bits corresponding to button presses on the second controller. The button press trace is
stored in a 16-bit value at <code class="language-plaintext highlighter-rouge">0x2(zuruKeyCheck)</code>. When there’s no controller plugged in,
the value is <code class="language-plaintext highlighter-rouge">0x7638</code>.</p>

<p>The 2 bytes containing flags for the controller 2 button presses are loaded and then
updated near the beginning of <code class="language-plaintext highlighter-rouge">zerucheck_key_check</code>. The new value is passed in with
register <code class="language-plaintext highlighter-rouge">r4</code> by <code class="language-plaintext highlighter-rouge">padmgr_HandleRetraceMsg</code> when it calls the callback function.</p>

<p><img src="/assets/img/debugmode/zkc_p1.PNG" alt="key check end" /></p>

<p>Down near the end of <code class="language-plaintext highlighter-rouge">zerucheck_key_check</code>, there’s actually another place where <code class="language-plaintext highlighter-rouge">0x4(zuruKeyCheck)</code>
is updated. It didn’t appear in the list of cross-references because it’s using <code class="language-plaintext highlighter-rouge">r3</code> as the base
address, and we can only figure out what <code class="language-plaintext highlighter-rouge">r3</code> is by looking at what it’s set to any time
this function is about to be called.</p>

<p>At <code class="language-plaintext highlighter-rouge">8040ed88</code> the value of <code class="language-plaintext highlighter-rouge">r4</code> is written to <code class="language-plaintext highlighter-rouge">0x4(zuruKeyCheck)</code>. It’s loaded from the
same location and then XORd with 1 just before that. What this should do is toggle the value
of the byte (really just the last bit) between 0 and 1. (If it’s 0, the result of
XORing it with 1 will be 1. If it’s 1, the result will be 0. Look up the truth table
for XOR to see this.)</p>

<p><img src="/assets/img/debugmode/zkc_p2.PNG" alt="key check end" /></p>

<p>I didn’t notice this behavior while watching the memory values before, but I’ll try
breaking on this instruction in the debugger to see what’s happening. The original value is
loaded at <code class="language-plaintext highlighter-rouge">8040ed7c</code>.</p>

<p>Without touching any buttons on the controllers, I don’t hit this breakpoint during
the title screen. To reach this code block, the value of <code class="language-plaintext highlighter-rouge">r5</code> must be <code class="language-plaintext highlighter-rouge">0xb</code> before the
branch instruction that comes before it (<code class="language-plaintext highlighter-rouge">8040ed74</code>). Of the many different paths that lead
up to that block, there’s one that will set <code class="language-plaintext highlighter-rouge">r5</code> to <code class="language-plaintext highlighter-rouge">0xb</code> before it, at <code class="language-plaintext highlighter-rouge">8040ed68</code>.</p>

<p><img src="/assets/img/debugmode/zkc_p3_crop.png" alt="Setting r5 to 0xb" /></p>

<p>Note that in order to reach the block that sets <code class="language-plaintext highlighter-rouge">r5</code> to <code class="language-plaintext highlighter-rouge">0xB</code>, <code class="language-plaintext highlighter-rouge">r0</code> must have been
equal to <code class="language-plaintext highlighter-rouge">0x1000</code> just before. Following the blocks up the chain to the beginning
of the function, we can see the constraints necessary to reach this block:</p>

<ul>
  <li>8040ed74: <code class="language-plaintext highlighter-rouge">r5</code> must be <code class="language-plaintext highlighter-rouge">0xB</code></li>
  <li>8040ed60: <code class="language-plaintext highlighter-rouge">r0</code> must be <code class="language-plaintext highlighter-rouge">0x1000</code></li>
  <li>8040ebe8: <code class="language-plaintext highlighter-rouge">r5</code> must be <code class="language-plaintext highlighter-rouge">0xA</code></li>
  <li>8040ebe4: <code class="language-plaintext highlighter-rouge">r5</code> must be less than <code class="language-plaintext highlighter-rouge">0x5B</code></li>
  <li>8040eba4: <code class="language-plaintext highlighter-rouge">r5</code> must be greater than <code class="language-plaintext highlighter-rouge">0x7</code></li>
  <li>8040eb94: <code class="language-plaintext highlighter-rouge">r6</code> must be 1</li>
  <li>8040eb5c: <code class="language-plaintext highlighter-rouge">r0</code> must not be 0</li>
  <li>8040eb74: Port 2 button values must have changed</li>
</ul>

<p><img src="/assets/img/debugmode/tracing.PNG" alt="Tracing the code path" /></p>

<p>Here we reach the point where the old button values are loaded and the new values
are stored. Afterwards there are a couple of operations applied to the new and old
values:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>old_vals = old_vals XOR new_vals
old_vals = old_vals AND new_vals
</code></pre></div></div>

<p>The XOR operation will mark all of the bits that have changed between the two
values. The AND operation then masks the new input to unset any bits that are
not currently set. The result in <code class="language-plaintext highlighter-rouge">r0</code> is the set of new bits (button presses) in the
new value. If it’s not empty, we’re on the right path.</p>

<p>For <code class="language-plaintext highlighter-rouge">r0</code> to be <code class="language-plaintext highlighter-rouge">0x1000</code>, the 4th out of the 16 button trace bits must have just changed.
By setting a breakpoint after the XOR/AND operation I can figure out which
button press causes this: it’s the START button.</p>

<p>The next question is how to get <code class="language-plaintext highlighter-rouge">r5</code> to start out as <code class="language-plaintext highlighter-rouge">0xA</code>. <code class="language-plaintext highlighter-rouge">r5</code> and <code class="language-plaintext highlighter-rouge">r6</code> are loaded from
<code class="language-plaintext highlighter-rouge">0x0(zuruKeyCheck)</code> at the beginning of the key check function, and updated near the end when we don’t
hit the code block that toggles <code class="language-plaintext highlighter-rouge">0x4(zuruKeyCheck)</code>.</p>

<p>There are a few places just before where <code class="language-plaintext highlighter-rouge">r5</code> gets set to <code class="language-plaintext highlighter-rouge">0xA</code>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">8040ed50</code></li>
  <li><code class="language-plaintext highlighter-rouge">8040ed00</code></li>
  <li><code class="language-plaintext highlighter-rouge">8040ed38</code></li>
</ul>

<h5 id="8040ed38">8040ed38</h5>

<ul>
  <li><code class="language-plaintext highlighter-rouge">8040ed34</code>: <code class="language-plaintext highlighter-rouge">r0</code> must be <code class="language-plaintext highlighter-rouge">0x4000</code> (B button was pressed)</li>
  <li><code class="language-plaintext highlighter-rouge">8040ebe0</code>: <code class="language-plaintext highlighter-rouge">r5</code> must be <code class="language-plaintext highlighter-rouge">0x5b</code></li>
  <li><code class="language-plaintext highlighter-rouge">8040eba4</code>: <code class="language-plaintext highlighter-rouge">r5</code> must be greater than <code class="language-plaintext highlighter-rouge">0x7</code></li>
  <li>same as before from here on…</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">r5</code> must start at <code class="language-plaintext highlighter-rouge">0x5b</code></p>

<h5 id="8040ed00"><code class="language-plaintext highlighter-rouge">8040ed00</code></h5>

<ul>
  <li><code class="language-plaintext highlighter-rouge">8040ecfc</code>: <code class="language-plaintext highlighter-rouge">r0</code> must be <code class="language-plaintext highlighter-rouge">0xC000</code> (A and B pressed)</li>
  <li><code class="language-plaintext highlighter-rouge">8040ebf8</code>: <code class="language-plaintext highlighter-rouge">r5</code> must be &gt;= 9</li>
  <li><code class="language-plaintext highlighter-rouge">8040ebf0</code>: <code class="language-plaintext highlighter-rouge">r5</code> must be less than 10</li>
  <li><code class="language-plaintext highlighter-rouge">8040ebe4</code>: <code class="language-plaintext highlighter-rouge">r5</code> must be less than <code class="language-plaintext highlighter-rouge">0x5b</code></li>
  <li><code class="language-plaintext highlighter-rouge">8040eba4</code>: <code class="language-plaintext highlighter-rouge">r5</code> must be greater than <code class="language-plaintext highlighter-rouge">0x7</code></li>
  <li>same as before from here on…</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">r5</code> must start at 9</p>

<h5 id="8040ed50"><code class="language-plaintext highlighter-rouge">8040ed50</code></h5>

<ul>
  <li><code class="language-plaintext highlighter-rouge">8040ed4c</code>: <code class="language-plaintext highlighter-rouge">r0</code> must be <code class="language-plaintext highlighter-rouge">0x8000</code> (A was pressed)</li>
  <li><code class="language-plaintext highlighter-rouge">8040ec04</code>: <code class="language-plaintext highlighter-rouge">r5</code> must be less than <code class="language-plaintext highlighter-rouge">0x5d</code></li>
  <li><code class="language-plaintext highlighter-rouge">8040ebe4</code>: <code class="language-plaintext highlighter-rouge">r5</code> must be greater than <code class="language-plaintext highlighter-rouge">0x5b</code></li>
  <li><code class="language-plaintext highlighter-rouge">8040eba4</code>: <code class="language-plaintext highlighter-rouge">r5</code> must be greater than <code class="language-plaintext highlighter-rouge">0x7</code></li>
  <li>same as before from here on…</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">r5</code> must start at <code class="language-plaintext highlighter-rouge">0x5c</code></p>

<p>It seems there’s some kind of state between button presses, and then a certain
sequence of button combos need to be entered, ending with START. It seems like A and/or B come
just before START.</p>

<p>Following the code path that sets <code class="language-plaintext highlighter-rouge">r5</code> to 9, a pattern emerges: <code class="language-plaintext highlighter-rouge">r5</code> is an incrementing
value that can either be increased when the correct button press value is found in <code class="language-plaintext highlighter-rouge">r0</code>,
or reset to 0. The weirder cases where it’s not a value between <code class="language-plaintext highlighter-rouge">0x0</code> and <code class="language-plaintext highlighter-rouge">0xB</code> occur
when handling multi-button steps, such as pressing A and B at the same time. A person
trying to input this combo usually isn’t going to press both buttons at the exact
same time the pad trace occurs, so it has to handle either button being pressed
before the other.</p>

<p>Continuing with the different code paths:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">r5</code> is set to 9 when RIGHT is pressed at <code class="language-plaintext highlighter-rouge">8040ece8</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">r5</code> is set to 8 when C-stick right is pressed at <code class="language-plaintext highlighter-rouge">8040eccc</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">r5</code> is set to 7 when C-stick left is pressed at <code class="language-plaintext highlighter-rouge">8040ecb0</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">r5</code> is set to 6 when LEFT is pressed at <code class="language-plaintext highlighter-rouge">8040ec98</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">r5</code> is set to 5 (and r6 to 1) when DOWN is pressed at <code class="language-plaintext highlighter-rouge">8040ec7c</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">r5</code> is set to 4 when C-stick up is pressed at <code class="language-plaintext highlighter-rouge">8040ec64</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">r5</code> is set to 3 when C-stick down is pressed at <code class="language-plaintext highlighter-rouge">8040ec48</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">r5</code> is set to 2 when UP is pressed at <code class="language-plaintext highlighter-rouge">8040ec30</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">r5</code> is set to 1 (and <code class="language-plaintext highlighter-rouge">r6</code> to 1) when Z is pressed at <code class="language-plaintext highlighter-rouge">8040ec1c</code>.</li>
</ul>

<p>The current sequence is:</p>

<p>Z, UP, C-DOWN, C-UP, DOWN, LEFT, C-LEFT, C-RIGHT, RIGHT, A+B, START</p>

<p>One more condition is checked before the Z check: while the newly pressed button
must be Z, the current flags must be <code class="language-plaintext highlighter-rouge">0x2030</code>: the left and right bumpers must also
be pressed (they have values of <code class="language-plaintext highlighter-rouge">0x10</code> and <code class="language-plaintext highlighter-rouge">0x20</code>). Also, the UP/DOWN/LEFT/RIGHT are the
D-pad buttons, not analog stick.</p>

<h4 id="the-cheat-code">The cheat code</h4>

<p>The full combo is:</p>

<ol>
  <li>Hold L+R bumpers and press Z</li>
  <li>D-UP</li>
  <li>C-DOWN</li>
  <li>C-UP</li>
  <li>D-DOWN</li>
  <li>D-LEFT</li>
  <li>C-LEFT</li>
  <li>C-RIGHT</li>
  <li>D-RIGHT</li>
  <li>A+B</li>
  <li>START</li>
</ol>

<p>It works! Attach a controller to the second port and enter the code, and the debug displays
will show up. After that you can start pressing buttons on the second (or even third) controller
to start doing things.</p>

<p>This combo will work without patching the version number of the game.
You can even use this on a regular retail copy of the game without any cheat tools
or console mods. Entering the combo a second time turns the zuru mode back off.</p>

<p><img src="/assets/img/debugmode/debug_real_gc_crop.jpg" alt="Using the code on a real GameCube" /></p>

<p>The “ZURU %d/%d” message in <code class="language-plaintext highlighter-rouge">zurumode_callback</code> is used to print out the status of this combination if you enter it
when the disk ID is already <code class="language-plaintext highlighter-rouge">0x99</code> (presumably for debugging the cheat code itself). The first number
is your current position in the sequence, matching <code class="language-plaintext highlighter-rouge">r5</code>. The second is set to 1 while certain buttons
in the sequence are held down, these might correspond to when <code class="language-plaintext highlighter-rouge">r6</code> is set to 1.</p>

<p>Most of the displays don’t explain what they are on the screen, so to figure out what they’re doing
you have to find the functions that handle them. For example, the long line of blue and red
asterisks that appear at the top of the screen are placeholders for displaying the status of different
quests. When a quest is active some numbers will appear there, indicating the state of the quest.</p>

<p>The black screen that shows up when you press Z is
a console for printing debug messages, but specifically for low level stuff such as memory allocation
and heap errors or other bad exceptions. The behavior of <code class="language-plaintext highlighter-rouge">fault_callback_scroll</code> suggests it may be for
displaying those errors before the system is rebooted. I didn’t trigger any of these errors,
but I was able to cause it to print a couple of garbage characters with some NOPs. I think this would be
really useful for printing custom debug messages later on:</p>

<p><img src="/assets/img/debugmode/jutconsole_text.PNG" alt="JUTConsole garbage characters" /></p>

<p>After doing all this, I found out that getting debug mode by patching the version ID
to <code class="language-plaintext highlighter-rouge">0x99</code> is already known: <a href="https://tcrf.net/Animal_Crossing#Debug_Mode">https://tcrf.net/Animal_Crossing#Debug_Mode</a>. (They also have some good
notes on what the various displays are, and more things you can do using a controller in port 3.)
As far as I can tell, the cheat combination has not been published yet, though.</p>

<p>That’s all for this post. There are still some more developer features that I’d like to explore,
such as the debug map screen and NES emulator select screen, and how to activate them without using patches.</p>

<p><img src="/assets/img/debugmode/forest_map_select.PNG" alt="Map select screen" /></p>

<p>I’ll also be posting write ups about reversing the dialog, event, and quest systems for the purpose
of making mods.</p>

<p><strong>Update</strong>: The slides for the talk I did on this can be found <a href="/assets/pdf/secrets_of_animal_crossing.pdf">here</a>.</p>]]></content><author><name></name></author><category term="Animal Crossing" /><category term="Reverse Engineering" /><category term="ROM Hacking" /><summary type="html"><![CDATA[Last summer I began reverse engineering Animal Crossing for the GameCube to explore the possibility of creating mods for the game. I also wanted to document the process to create tutorials for people interested in ROM hacking and reverse engineering. In this post I explore the developer debugging features that are still left in the game, and how I discovered a cheat combo that can be used to unlock them.]]></summary></entry></feed>