mirror of
https://gitea.tendokyu.moe/eamuse/docs.git
synced 2024-11-28 00:20:52 +01:00
Restructure security pages
This commit is contained in:
parent
8a739d50b5
commit
697eeeb99e
3
docs.py
3
docs.py
@ -49,8 +49,9 @@ SEGA_CONTENTS = {
|
||||
"software": ("Software", {
|
||||
"pcp": ("PCP", {"libpcp.html": "libpcp"}),
|
||||
"drivers": ("Device drivers", None),
|
||||
"security.html": "Security",
|
||||
"security": ("Security", {
|
||||
"alphadvd.html": "AlphaDVD",
|
||||
}),
|
||||
"groovemaster.html": "GrooveMaster.ini",
|
||||
}),
|
||||
"manual": ("Manual", {
|
||||
|
@ -1,526 +0,0 @@
|
||||
{% extends "sega.html" %}
|
||||
{% block title %}{% endblock %}
|
||||
{% block body %}
|
||||
<h1>System Security</h1>
|
||||
<p>The Ring* series have a number of security measures in place, some easy to bypass, others requiring more work. They
|
||||
are listed here in, roughly, the order in which each layer is applied.</p>
|
||||
|
||||
<h2 id="ata">Drive ATA Password</h2>
|
||||
<p>The SSD contained within the system has an ATA password set. The system BIOS contains a password derivation function
|
||||
that derives the specific password for that drive based on its serial number.</p>
|
||||
<p>This can be bypassed either by extracting the password used, or by first powering on the Ring* system with the drive
|
||||
connected, then hotplugging the SATA data cable on the drive while keeping the drive powered.</p>
|
||||
|
||||
<details>
|
||||
<summary>Why does this work?</summary>
|
||||
<p>The following is the sequence of possible security modes for an ATA drive:</p>
|
||||
<svg viewbox="-0.5 -0.5 800 400" style="width: 800px">
|
||||
<defs>
|
||||
<marker id="arrowhead-good" markerWidth="10" markerHeight="8" refX="0" refY="4" orient="auto">
|
||||
<polygon points="0 0, 10 4, 0 8" fill="currentColor" class="ata-good" />
|
||||
</marker>
|
||||
</defs>
|
||||
<defs>
|
||||
<marker id="arrowhead-ignore" markerWidth="10" markerHeight="8" refX="0" refY="4" orient="auto">
|
||||
<polygon points="0 0, 10 4, 0 8" fill="currentColor" class="ata-ignore" />
|
||||
</marker>
|
||||
</defs>
|
||||
<defs>
|
||||
<marker id="arrowhead-bad" markerWidth="10" markerHeight="8" refX="0" refY="4" orient="auto">
|
||||
<polygon points="0 0, 10 4, 0 8" fill="currentColor" class="ata-bad" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<g class="ata-ignore">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC0
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="43" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Power: off
|
||||
</text>
|
||||
<text x="75" y="57" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: No
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g class="ata-ignore">
|
||||
<text x="200" y="30" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Power on
|
||||
</text>
|
||||
<line x1="150" y1="40" x2="240" y2="40" stroke="currentColor" marker-end="url(#arrowhead-ignore)"></line>
|
||||
|
||||
<text x="450" y="20" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Freeze
|
||||
</text>
|
||||
<line x1="400" y1="30" x2="490" y2="30" stroke="currentColor" marker-end="url(#arrowhead-ignore)"></line>
|
||||
<line x1="500" y1="50" x2="410" y2="50" stroke="currentColor" marker-end="url(#arrowhead-ignore)"></line>
|
||||
<text x="450" y="60" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
HW Reset
|
||||
</text>
|
||||
|
||||
<line x1="325" y1="80" x2="325" y2="150" stroke="currentColor"></line>
|
||||
</g>
|
||||
|
||||
<g transform="translate(250 0)" class="ata-ignore">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC1
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="43" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: No
|
||||
</text>
|
||||
<text x="75" y="57" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Frozen: No
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(500 0)" class="ata-ignore">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC2
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="43" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: No
|
||||
</text>
|
||||
<text x="75" y="57" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Frozen: Yes
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(250 150)" class="ata-good">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC5
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="36" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: Yes
|
||||
</text>
|
||||
<text x="75" y="50" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Unlocked: Yes
|
||||
</text>
|
||||
<text x="75" y="64" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Frozen: No
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(250 300)" class="ata-bad">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC4
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="36" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: Yes
|
||||
</text>
|
||||
<text x="75" y="50" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Unlocked: No
|
||||
</text>
|
||||
<text x="75" y="64" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Frozen: No
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(500 225)" class="ata-good">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC6
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="36" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: Yes
|
||||
</text>
|
||||
<text x="75" y="50" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Unlocked: Yes
|
||||
</text>
|
||||
<text x="75" y="64" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Frozen: Yes
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g class="ata-bad">
|
||||
<text x="200" y="330" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Power on
|
||||
</text>
|
||||
<line x1="150" y1="340" x2="240" y2="340" stroke="currentColor" marker-end="url(#arrowhead-bad)"></line>
|
||||
|
||||
<text x="470" y="340" fill="currentColor" dominant-baseline="middle" text-anchor="start"
|
||||
font-family="monospace">
|
||||
HW reset
|
||||
</text>
|
||||
<line x1="575" y1="305" x2="410" y2="340" stroke="currentColor" marker-end="url(#arrowhead-bad)"></line>
|
||||
</g>
|
||||
|
||||
<g class="ata-good">
|
||||
<text x="318" y="265" fill="currentColor" dominant-baseline="middle" text-anchor="end"
|
||||
font-family="monospace">
|
||||
Unlock
|
||||
</text>
|
||||
<line x1="325" y1="300" x2="325" y2="240" stroke="currentColor" marker-end="url(#arrowhead-good)"></line>
|
||||
|
||||
<line x1="400" y1="195" x2="490" y2="265" stroke="currentColor" marker-end="url(#arrowhead-good)"></line>
|
||||
<text x="435" y="210" fill="currentColor" dominant-baseline="middle" text-anchor="start"
|
||||
font-family="monospace">
|
||||
Freeze
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0 300)" class="ata-bad">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC3
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="43" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Power: off
|
||||
</text>
|
||||
<text x="75" y="57" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: Yes
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
<p>When the drive has a password set, it initially starts in SEC3. The RingEdge then transitions the drive to SEC5
|
||||
then SEC6. Importantly, however, as long as power is never lost, the drive will remain in SEC6 mode, even if we
|
||||
connect it to a different system.</p>
|
||||
<p>This allows us full read-write access to the drive without ever knowing the password!</p>
|
||||
</details>
|
||||
|
||||
<h2 id="windows">Windows Password</h2>
|
||||
<p>It seems silly to mention, but it's worth noting. <code>AppUser</code> will automatically log in, but if you need to
|
||||
log back in, or wish to login as <code>SystemUser</code>, you'll need the passwords.</p>
|
||||
<p><code>AppUser</code>'s password is <code>segahard</code>.</p>
|
||||
<p><code>SystemUser</code>'s password is <code><6/=U=#tpe!$*3!5</code>. <b>NOTE:</b> if a debugger is attached to
|
||||
<code>mxprestartup</code>, <code>Miflac=Ifme9Jfp0</code> will be attempted as the password for
|
||||
<code>SystemUser</code> instead. This is not the correct password for a production unit.
|
||||
</p>
|
||||
|
||||
<details>
|
||||
<summary>Finding SystemUser's password</summary>
|
||||
|
||||
<p>When AppUser logs in, mxprestartup is executed. This binary constructs SystemUser's password then elevates
|
||||
permissions. When no debugger is present, the following steps are performed:</p>
|
||||
|
||||
<ul>
|
||||
<li>Add <code>153815264b5839090b0d1c1a423c02241633130673071a1e38443912410b47380f213c1d</code> and
|
||||
<code>2e0247311e162b666c6640393737724157001f56045b4b4f24333457335a26381f4c3349</code> together (these are
|
||||
strings contained within the binary).
|
||||
</li>
|
||||
<ul>
|
||||
<li>Result: <code>C:\Windows\System32\wbem\wmitemp.mof</code></li>
|
||||
</ul>
|
||||
<li>Read <code>C:\Windows\System32\wbem\wmitemp.mof</code>.
|
||||
</li>
|
||||
<ul>
|
||||
<li>Result: <code>270a2a053b29042b261d1b22070d140c</code></li>
|
||||
</ul>
|
||||
<li>Add <code>152c05381a141f494a48060223260d29</code> to this value.
|
||||
</li>
|
||||
<ul>
|
||||
<li>Result: <code><6/=U=#tpe!$*3!5</code></li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<p>If a debugger is present, a similar process is performed:</p>
|
||||
|
||||
<ul>
|
||||
<li>Add <code>d0b1c034a32243340505a343659517c121f0319583d205c593a690719604846000e062a6</code> and
|
||||
<code>63f10541f0c201c0703022f332a13094b542f130c25491a1c280a65440831296e2563590</code> together, with each
|
||||
byte
|
||||
modulo 255 (not 256!).
|
||||
</li>
|
||||
<ul>
|
||||
<li>Result: <code>34a3c57594e444f47535c53797374756d63323c546279667562737c5d68796f6e2379737</code></li>
|
||||
</ul>
|
||||
<li>Flip the nibbles of each byte.</li>
|
||||
<ul>
|
||||
<li>Result: <code>C:\WINDOWS\system32\drivers\mxio.sys</code></li>
|
||||
</ul>
|
||||
<li>Read <code>C:\WINDOWS\system32\drivers\mxio.sys</code>.</li>
|
||||
<ul>
|
||||
<li>Result: <code>9160e5c22392918371e43573f2b095b1</code></li>
|
||||
</ul>
|
||||
<li>Add <code>43368004f2a34211f4f12120b1b57151</code> to this value, with each byte modulo 255.
|
||||
</li>
|
||||
<ul>
|
||||
<li>Result: <code>d49666c61636d39466d65693a4660703</code></li>
|
||||
</ul>
|
||||
<li>Flip the nibbles of each byte.</li>
|
||||
<ul>
|
||||
<li>Result: <code>Miflac=Ifme9Jfp0</code></li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<p>Why is the process for a debugged process more elaborate? I'm not sure.</p>
|
||||
</details>
|
||||
|
||||
<h2 id="sdrive">System binaries encryption</h2>
|
||||
<p>The system binaries, normally located on <code>S:</code>, are a TrueCrypt partition. The partition file can be found
|
||||
at <code>C:\System\Execute\System</code>. It has password <code>segahardpassword</code>, and used the <a
|
||||
href="https://www.sans.org/blog/alternate-data-streams-overview/" rel="noopener noreferrer">alternate data
|
||||
stream</a> loated at
|
||||
<code>C:\System\Execute\DLL:SystemKeyFile</code> as a keyfile.
|
||||
</p>
|
||||
<p><code>C:\System\Execute\DLL:UpdateKeyFile</code> is also present here, which is used for encryption of update
|
||||
data (but is not utilised in the process of mounting <code>S:</code>).</p>
|
||||
|
||||
<details>
|
||||
<summary>What's an Alternate Data Stream?</summary>
|
||||
<p>An alternate data stream, or ADS for short, is a feature of the NTFS filesystem where additional data can be
|
||||
stored alongside a file or directory. On modern Windows systems, they can be shown using <code>dir /R</code>. If
|
||||
you are performing analysis on the system using digital forensics software, which you probably should be, all
|
||||
major packages will show these streams clearly.</p>
|
||||
|
||||
<figure>
|
||||
<img class="graphic" src="{{ROOT}}/images/ftk.png">
|
||||
<figcaption>The alternate data streams, in FTK Imager</figcaption>
|
||||
</figure>
|
||||
|
||||
<p>The <a href="https://www.sans.org/blog/alternate-data-streams-overview/" rel="noopener noreferrer">SANS Institude
|
||||
website</a> is a great resource for more information and links.</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Finding this information</summary>
|
||||
|
||||
<p>The decryption of the system partition is the responsibility of <code>mxstartup</code>. This file contains pairs
|
||||
of hex strings which sum to produce the paths to the alternate data streams, and the volume password.</p>
|
||||
</details>
|
||||
|
||||
<h3 id="keyfiles">Keyfile downloads</h3>
|
||||
<ul>
|
||||
<li><a href="{{ROOT}}/static/keys/SystemKeyFile">SystemKeyFile</a></li>
|
||||
<li><a href="{{ROOT}}/static/keys/UpdateKeyFile">UpdateKeyFile</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="keychip">Keychip</h2>
|
||||
<p>This is the first point during the boot process where a physical keychip is required in order to continue the system
|
||||
boot process.</p>
|
||||
<p>With the exception of <a href="{{ROOT}}/sega/software/mx/mxkeychip.html"><code>mxkeychip</code></a>, processes do not
|
||||
directly communicate with the keychip. Instead, they communicate with <a
|
||||
href="{{ROOT}}/sega/software/mx/mxkeychip.html"><code>mxkeychip</code> via a PCP</a>. <a
|
||||
href="{{ROOT}}/sega/software/mx/mxkeychip.html"><code>mxkeychip</code></a> itself is then responsible for
|
||||
communicating with the keychip. These communications are AES encrypted with keys agreed during the initial
|
||||
handshake.</p>
|
||||
|
||||
<p>The keychip is responsible for a number of functions:</p>
|
||||
<ul>
|
||||
<li>Each keychip is assigned a region, and this region must match the region stored in the Ring*'s EEPROM.</li>
|
||||
<li>It contains configuration tables for the network setup.</li>
|
||||
<li>It is able to perform basic encryption and decryption, which is used to derive the game encryption key.</li>
|
||||
<li>It includes a large challenge-response table used to authenticate with the specific game.</li>
|
||||
<li>It contains Aime billing information regarding how many credits are allowed on this machine before
|
||||
re-authenticating with network services.</li>
|
||||
<li>It contains a small amount of writable storage used to store trace logs, such as when the system booted or
|
||||
authenticated with network services.</li>
|
||||
</ul>
|
||||
|
||||
<p>This security layer can be bypassed by replacing the <code>mxkeychip.exe</code> binary with a custom binary,
|
||||
eliminating the need to emulate the physical parallel device, and its encryption.</p>
|
||||
|
||||
<h2 id="game-data">Game data encryption</h2>
|
||||
<p>Once the system has verified it is allowed to continue booting, it proceeds to decrypt the game partition. This is
|
||||
done by performing a <code>keychip.decrypt</code> request.</p>
|
||||
<p>The specific key used varies by game. The easiest way to retrieve this key is to, well, ask the keychip for it. We
|
||||
can first start a listener on port <code>40106</code> to retrieve the value that the boot process is passing.
|
||||
Following that, we can start the real mxkeychip, and request the same decryption. It will then provide us with the
|
||||
key to be used for the game encryption!</p>
|
||||
<p>TODO: Some proper digging into the mx binaries to determine exactly where it pulls the encrypted string in the
|
||||
request</p>
|
||||
<p>The value we have now should be 16 bytes, and is the contents for a keyfile.</p>
|
||||
<p>Like the <code>S:</code> drive, game data is a TrueCrypt partition. Check the <a
|
||||
href="{{ROOT}}/sega/misc/partition.html">SEGA partition description</a> to determine which partition contains
|
||||
the game data. If in doubt, just try they all until one works :).</p>
|
||||
|
||||
<h2 id="game-handshake">Game-keychip handshake</h2>
|
||||
<p>There is one final security step present, however I believe this to only be present in some games.</p>
|
||||
<p>When the game boots, it makes requests to <code>keychip.ssd.proof</code> and <code>keychip.ds.compute</code>. These
|
||||
are challenge-response queries, which the keychip will internally look up in a large table of possible values.</p>
|
||||
<p>While we could dump the EEPROM chip located on the keychip, there is no need for this. As the game also needs a copy
|
||||
of the responses to validate against, we can just grab that file, as we have already decrypted the game partition by
|
||||
this point. The file will likely be named <code>[game ID]_Table.dat</code>.</p>
|
||||
|
||||
<p>I'll flush this out later, but for now, here's the structure of that file:</p>
|
||||
<pre>{% highlight "c" %}
|
||||
struct {
|
||||
struct {
|
||||
char challenge[7];
|
||||
char responses[4][20];
|
||||
} ds[10000];
|
||||
struct {
|
||||
char challenge[16];
|
||||
char response[16];
|
||||
} ssd[10000];
|
||||
}
|
||||
{% endhighlight %}</pre>
|
||||
|
||||
<p>The responses are scrambled as described below:</p>
|
||||
<h4>DS Scramble:</h4>
|
||||
<table class="code">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Output index</td>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
<td>4</td>
|
||||
<td>5</td>
|
||||
<td>6</td>
|
||||
<td>7</td>
|
||||
<td>8</td>
|
||||
<td>9</td>
|
||||
<td>10</td>
|
||||
<td>11</td>
|
||||
<td>12</td>
|
||||
<td>13</td>
|
||||
<td>14</td>
|
||||
<td>15</td>
|
||||
<td>16</td>
|
||||
<td>17</td>
|
||||
<td>18</td>
|
||||
<td>19</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Input index</td>
|
||||
<td>15</td>
|
||||
<td>5</td>
|
||||
<td>13</td>
|
||||
<td>14</td>
|
||||
<td>10</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>11</td>
|
||||
<td>16</td>
|
||||
<td>7</td>
|
||||
<td>4</td>
|
||||
<td>18</td>
|
||||
<td>12</td>
|
||||
<td>6</td>
|
||||
<td>3</td>
|
||||
<td>0</td>
|
||||
<td>17</td>
|
||||
<td>8</td>
|
||||
<td>19</td>
|
||||
<td>9</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h4>SSD Scramble:</h4>
|
||||
<table class="code">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Output index</td>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
<td>4</td>
|
||||
<td>5</td>
|
||||
<td>6</td>
|
||||
<td>7</td>
|
||||
<td>8</td>
|
||||
<td>9</td>
|
||||
<td>10</td>
|
||||
<td>11</td>
|
||||
<td>12</td>
|
||||
<td>13</td>
|
||||
<td>14</td>
|
||||
<td>15</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Input index</td>
|
||||
<td>6</td>
|
||||
<td>8</td>
|
||||
<td>4</td>
|
||||
<td>12</td>
|
||||
<td>7</td>
|
||||
<td>13</td>
|
||||
<td>1</td>
|
||||
<td>10</td>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
<td>11</td>
|
||||
<td>14</td>
|
||||
<td>15</td>
|
||||
<td>0</td>
|
||||
<td>5</td>
|
||||
<td>9</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><b>NOTE: The following information may only be true for MaiMai FiNALE! I have not yet verified this on other
|
||||
games!</b>
|
||||
<br>
|
||||
In dev mode, the game will only ever request a single string as the challenge.
|
||||
</p>
|
||||
<p>This string is <code>2CFECBC71CF1E4</code>, and its corresponding four response pages are (not scrambled):</p>
|
||||
<ol>
|
||||
<li><code>ca6ed736401682dd4411a27d2440ac4b478bad8b</code></li>
|
||||
<li><code>4ac606302ce5ef51abb3df2dc46c863b3c06aa2c</code></li>
|
||||
<li><code>2aaf35b2aba4c6840bdb7bd40ecbce2cca934795</code></li>
|
||||
<li><code>2f6d713dabde3c43df818491ab9467ba8ba0fed4</code></li>
|
||||
</ol>
|
||||
<p>
|
||||
<b>NOTE: The following information is super un-verified!</b><br>
|
||||
Occasionally the game will make a query to the DS table that is not present in the table. In this instance, the
|
||||
keychip responds with the entry at index n, where n is a counter that increments every time a
|
||||
<code>keychip.ds.compute</code> query is performed, modulo 100.
|
||||
</p>
|
||||
<p>It should be noted that if the keychip binary imemdiatly terminates the connection, rather than sending a
|
||||
known-incorrect response (such as if the challenge matches none in the known tables), the game will re-attempt the
|
||||
query, and this query will, with a high likelyhood, be different.</p>
|
||||
|
||||
<p>It should additionally be noted that this whole process can be skipped by returning <code>code=54</code> rather than
|
||||
a valid response.</p>
|
||||
|
||||
{% endblock %}
|
@ -1,6 +1,526 @@
|
||||
{% extends "sega.html" %}
|
||||
{% block title %}Security{% endblock %}
|
||||
{% block title %}{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Security</h1>
|
||||
{{ generate_toc()|safe }}
|
||||
<h1>System Security</h1>
|
||||
<p>The Ring* series have a number of security measures in place, some easy to bypass, others requiring more work. They
|
||||
are listed here in, roughly, the order in which each layer is applied.</p>
|
||||
|
||||
<h2 id="ata">Drive ATA Password</h2>
|
||||
<p>The SSD contained within the system has an ATA password set. The system BIOS contains a password derivation function
|
||||
that derives the specific password for that drive based on its serial number.</p>
|
||||
<p>This can be bypassed either by extracting the password used, or by first powering on the Ring* system with the drive
|
||||
connected, then hotplugging the SATA data cable on the drive while keeping the drive powered.</p>
|
||||
|
||||
<details>
|
||||
<summary>Why does this work?</summary>
|
||||
<p>The following is the sequence of possible security modes for an ATA drive:</p>
|
||||
<svg viewbox="-0.5 -0.5 800 400" style="width: 800px">
|
||||
<defs>
|
||||
<marker id="arrowhead-good" markerWidth="10" markerHeight="8" refX="0" refY="4" orient="auto">
|
||||
<polygon points="0 0, 10 4, 0 8" fill="currentColor" class="ata-good" />
|
||||
</marker>
|
||||
</defs>
|
||||
<defs>
|
||||
<marker id="arrowhead-ignore" markerWidth="10" markerHeight="8" refX="0" refY="4" orient="auto">
|
||||
<polygon points="0 0, 10 4, 0 8" fill="currentColor" class="ata-ignore" />
|
||||
</marker>
|
||||
</defs>
|
||||
<defs>
|
||||
<marker id="arrowhead-bad" markerWidth="10" markerHeight="8" refX="0" refY="4" orient="auto">
|
||||
<polygon points="0 0, 10 4, 0 8" fill="currentColor" class="ata-bad" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<g class="ata-ignore">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC0
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="43" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Power: off
|
||||
</text>
|
||||
<text x="75" y="57" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: No
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g class="ata-ignore">
|
||||
<text x="200" y="30" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Power on
|
||||
</text>
|
||||
<line x1="150" y1="40" x2="240" y2="40" stroke="currentColor" marker-end="url(#arrowhead-ignore)"></line>
|
||||
|
||||
<text x="450" y="20" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Freeze
|
||||
</text>
|
||||
<line x1="400" y1="30" x2="490" y2="30" stroke="currentColor" marker-end="url(#arrowhead-ignore)"></line>
|
||||
<line x1="500" y1="50" x2="410" y2="50" stroke="currentColor" marker-end="url(#arrowhead-ignore)"></line>
|
||||
<text x="450" y="60" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
HW Reset
|
||||
</text>
|
||||
|
||||
<line x1="325" y1="80" x2="325" y2="150" stroke="currentColor"></line>
|
||||
</g>
|
||||
|
||||
<g transform="translate(250 0)" class="ata-ignore">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC1
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="43" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: No
|
||||
</text>
|
||||
<text x="75" y="57" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Frozen: No
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(500 0)" class="ata-ignore">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC2
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="43" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: No
|
||||
</text>
|
||||
<text x="75" y="57" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Frozen: Yes
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(250 150)" class="ata-good">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC5
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="36" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: Yes
|
||||
</text>
|
||||
<text x="75" y="50" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Unlocked: Yes
|
||||
</text>
|
||||
<text x="75" y="64" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Frozen: No
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(250 300)" class="ata-bad">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC4
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="36" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: Yes
|
||||
</text>
|
||||
<text x="75" y="50" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Unlocked: No
|
||||
</text>
|
||||
<text x="75" y="64" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Frozen: No
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(500 225)" class="ata-good">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC6
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="36" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: Yes
|
||||
</text>
|
||||
<text x="75" y="50" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Unlocked: Yes
|
||||
</text>
|
||||
<text x="75" y="64" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Frozen: Yes
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g class="ata-bad">
|
||||
<text x="200" y="330" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Power on
|
||||
</text>
|
||||
<line x1="150" y1="340" x2="240" y2="340" stroke="currentColor" marker-end="url(#arrowhead-bad)"></line>
|
||||
|
||||
<text x="470" y="340" fill="currentColor" dominant-baseline="middle" text-anchor="start"
|
||||
font-family="monospace">
|
||||
HW reset
|
||||
</text>
|
||||
<line x1="575" y1="305" x2="410" y2="340" stroke="currentColor" marker-end="url(#arrowhead-bad)"></line>
|
||||
</g>
|
||||
|
||||
<g class="ata-good">
|
||||
<text x="318" y="265" fill="currentColor" dominant-baseline="middle" text-anchor="end"
|
||||
font-family="monospace">
|
||||
Unlock
|
||||
</text>
|
||||
<line x1="325" y1="300" x2="325" y2="240" stroke="currentColor" marker-end="url(#arrowhead-good)"></line>
|
||||
|
||||
<line x1="400" y1="195" x2="490" y2="265" stroke="currentColor" marker-end="url(#arrowhead-good)"></line>
|
||||
<text x="435" y="210" fill="currentColor" dominant-baseline="middle" text-anchor="start"
|
||||
font-family="monospace">
|
||||
Freeze
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0 300)" class="ata-bad">
|
||||
<rect fill="transparent" x="0" y="0" width="150" height="20" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="10" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
SEC3
|
||||
</text>
|
||||
<rect fill="transparent" x="0" y="20" width="150" height="60" stroke-width="1px" stroke="currentColor">
|
||||
</rect>
|
||||
<text x="75" y="43" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Power: off
|
||||
</text>
|
||||
<text x="75" y="57" fill="currentColor" dominant-baseline="middle" text-anchor="middle"
|
||||
font-family="monospace">
|
||||
Security: Yes
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
<p>When the drive has a password set, it initially starts in SEC3. The RingEdge then transitions the drive to SEC5
|
||||
then SEC6. Importantly, however, as long as power is never lost, the drive will remain in SEC6 mode, even if we
|
||||
connect it to a different system.</p>
|
||||
<p>This allows us full read-write access to the drive without ever knowing the password!</p>
|
||||
</details>
|
||||
|
||||
<h2 id="windows">Windows Password</h2>
|
||||
<p>It seems silly to mention, but it's worth noting. <code>AppUser</code> will automatically log in, but if you need to
|
||||
log back in, or wish to login as <code>SystemUser</code>, you'll need the passwords.</p>
|
||||
<p><code>AppUser</code>'s password is <code>segahard</code>.</p>
|
||||
<p><code>SystemUser</code>'s password is <code><6/=U=#tpe!$*3!5</code>. <b>NOTE:</b> if a debugger is attached to
|
||||
<code>mxprestartup</code>, <code>Miflac=Ifme9Jfp0</code> will be attempted as the password for
|
||||
<code>SystemUser</code> instead. This is not the correct password for a production unit.
|
||||
</p>
|
||||
|
||||
<details>
|
||||
<summary>Finding SystemUser's password</summary>
|
||||
|
||||
<p>When AppUser logs in, mxprestartup is executed. This binary constructs SystemUser's password then elevates
|
||||
permissions. When no debugger is present, the following steps are performed:</p>
|
||||
|
||||
<ul>
|
||||
<li>Add <code>153815264b5839090b0d1c1a423c02241633130673071a1e38443912410b47380f213c1d</code> and
|
||||
<code>2e0247311e162b666c6640393737724157001f56045b4b4f24333457335a26381f4c3349</code> together (these are
|
||||
strings contained within the binary).
|
||||
</li>
|
||||
<ul>
|
||||
<li>Result: <code>C:\Windows\System32\wbem\wmitemp.mof</code></li>
|
||||
</ul>
|
||||
<li>Read <code>C:\Windows\System32\wbem\wmitemp.mof</code>.
|
||||
</li>
|
||||
<ul>
|
||||
<li>Result: <code>270a2a053b29042b261d1b22070d140c</code></li>
|
||||
</ul>
|
||||
<li>Add <code>152c05381a141f494a48060223260d29</code> to this value.
|
||||
</li>
|
||||
<ul>
|
||||
<li>Result: <code><6/=U=#tpe!$*3!5</code></li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<p>If a debugger is present, a similar process is performed:</p>
|
||||
|
||||
<ul>
|
||||
<li>Add <code>d0b1c034a32243340505a343659517c121f0319583d205c593a690719604846000e062a6</code> and
|
||||
<code>63f10541f0c201c0703022f332a13094b542f130c25491a1c280a65440831296e2563590</code> together, with each
|
||||
byte
|
||||
modulo 255 (not 256!).
|
||||
</li>
|
||||
<ul>
|
||||
<li>Result: <code>34a3c57594e444f47535c53797374756d63323c546279667562737c5d68796f6e2379737</code></li>
|
||||
</ul>
|
||||
<li>Flip the nibbles of each byte.</li>
|
||||
<ul>
|
||||
<li>Result: <code>C:\WINDOWS\system32\drivers\mxio.sys</code></li>
|
||||
</ul>
|
||||
<li>Read <code>C:\WINDOWS\system32\drivers\mxio.sys</code>.</li>
|
||||
<ul>
|
||||
<li>Result: <code>9160e5c22392918371e43573f2b095b1</code></li>
|
||||
</ul>
|
||||
<li>Add <code>43368004f2a34211f4f12120b1b57151</code> to this value, with each byte modulo 255.
|
||||
</li>
|
||||
<ul>
|
||||
<li>Result: <code>d49666c61636d39466d65693a4660703</code></li>
|
||||
</ul>
|
||||
<li>Flip the nibbles of each byte.</li>
|
||||
<ul>
|
||||
<li>Result: <code>Miflac=Ifme9Jfp0</code></li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<p>Why is the process for a debugged process more elaborate? I'm not sure.</p>
|
||||
</details>
|
||||
|
||||
<h2 id="sdrive">System binaries encryption</h2>
|
||||
<p>The system binaries, normally located on <code>S:</code>, are a TrueCrypt partition. The partition file can be found
|
||||
at <code>C:\System\Execute\System</code>. It has password <code>segahardpassword</code>, and used the <a
|
||||
href="https://www.sans.org/blog/alternate-data-streams-overview/" rel="noopener noreferrer">alternate data
|
||||
stream</a> loated at
|
||||
<code>C:\System\Execute\DLL:SystemKeyFile</code> as a keyfile.
|
||||
</p>
|
||||
<p><code>C:\System\Execute\DLL:UpdateKeyFile</code> is also present here, which is used for encryption of update
|
||||
data (but is not utilised in the process of mounting <code>S:</code>).</p>
|
||||
|
||||
<details>
|
||||
<summary>What's an Alternate Data Stream?</summary>
|
||||
<p>An alternate data stream, or ADS for short, is a feature of the NTFS filesystem where additional data can be
|
||||
stored alongside a file or directory. On modern Windows systems, they can be shown using <code>dir /R</code>. If
|
||||
you are performing analysis on the system using digital forensics software, which you probably should be, all
|
||||
major packages will show these streams clearly.</p>
|
||||
|
||||
<figure>
|
||||
<img class="graphic" src="{{ROOT}}/images/ftk.png">
|
||||
<figcaption>The alternate data streams, in FTK Imager</figcaption>
|
||||
</figure>
|
||||
|
||||
<p>The <a href="https://www.sans.org/blog/alternate-data-streams-overview/" rel="noopener noreferrer">SANS Institude
|
||||
website</a> is a great resource for more information and links.</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Finding this information</summary>
|
||||
|
||||
<p>The decryption of the system partition is the responsibility of <code>mxstartup</code>. This file contains pairs
|
||||
of hex strings which sum to produce the paths to the alternate data streams, and the volume password.</p>
|
||||
</details>
|
||||
|
||||
<h3 id="keyfiles">Keyfile downloads</h3>
|
||||
<ul>
|
||||
<li><a href="{{ROOT}}/static/keys/SystemKeyFile">SystemKeyFile</a></li>
|
||||
<li><a href="{{ROOT}}/static/keys/UpdateKeyFile">UpdateKeyFile</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="keychip">Keychip</h2>
|
||||
<p>This is the first point during the boot process where a physical keychip is required in order to continue the system
|
||||
boot process.</p>
|
||||
<p>With the exception of <a href="{{ROOT}}/sega/software/mx/mxkeychip.html"><code>mxkeychip</code></a>, processes do not
|
||||
directly communicate with the keychip. Instead, they communicate with <a
|
||||
href="{{ROOT}}/sega/software/mx/mxkeychip.html"><code>mxkeychip</code> via a PCP</a>. <a
|
||||
href="{{ROOT}}/sega/software/mx/mxkeychip.html"><code>mxkeychip</code></a> itself is then responsible for
|
||||
communicating with the keychip. These communications are AES encrypted with keys agreed during the initial
|
||||
handshake.</p>
|
||||
|
||||
<p>The keychip is responsible for a number of functions:</p>
|
||||
<ul>
|
||||
<li>Each keychip is assigned a region, and this region must match the region stored in the Ring*'s EEPROM.</li>
|
||||
<li>It contains configuration tables for the network setup.</li>
|
||||
<li>It is able to perform basic encryption and decryption, which is used to derive the game encryption key.</li>
|
||||
<li>It includes a large challenge-response table used to authenticate with the specific game.</li>
|
||||
<li>It contains Aime billing information regarding how many credits are allowed on this machine before
|
||||
re-authenticating with network services.</li>
|
||||
<li>It contains a small amount of writable storage used to store trace logs, such as when the system booted or
|
||||
authenticated with network services.</li>
|
||||
</ul>
|
||||
|
||||
<p>This security layer can be bypassed by replacing the <code>mxkeychip.exe</code> binary with a custom binary,
|
||||
eliminating the need to emulate the physical parallel device, and its encryption.</p>
|
||||
|
||||
<h2 id="game-data">Game data encryption</h2>
|
||||
<p>Once the system has verified it is allowed to continue booting, it proceeds to decrypt the game partition. This is
|
||||
done by performing a <code>keychip.decrypt</code> request.</p>
|
||||
<p>The specific key used varies by game. The easiest way to retrieve this key is to, well, ask the keychip for it. We
|
||||
can first start a listener on port <code>40106</code> to retrieve the value that the boot process is passing.
|
||||
Following that, we can start the real mxkeychip, and request the same decryption. It will then provide us with the
|
||||
key to be used for the game encryption!</p>
|
||||
<p>TODO: Some proper digging into the mx binaries to determine exactly where it pulls the encrypted string in the
|
||||
request</p>
|
||||
<p>The value we have now should be 16 bytes, and is the contents for a keyfile.</p>
|
||||
<p>Like the <code>S:</code> drive, game data is a TrueCrypt partition. Check the <a
|
||||
href="{{ROOT}}/sega/misc/partition.html">SEGA partition description</a> to determine which partition contains
|
||||
the game data. If in doubt, just try they all until one works :).</p>
|
||||
|
||||
<h2 id="game-handshake">Game-keychip handshake</h2>
|
||||
<p>There is one final security step present, however I believe this to only be present in some games.</p>
|
||||
<p>When the game boots, it makes requests to <code>keychip.ssd.proof</code> and <code>keychip.ds.compute</code>. These
|
||||
are challenge-response queries, which the keychip will internally look up in a large table of possible values.</p>
|
||||
<p>While we could dump the EEPROM chip located on the keychip, there is no need for this. As the game also needs a copy
|
||||
of the responses to validate against, we can just grab that file, as we have already decrypted the game partition by
|
||||
this point. The file will likely be named <code>[game ID]_Table.dat</code>.</p>
|
||||
|
||||
<p>I'll flush this out later, but for now, here's the structure of that file:</p>
|
||||
<pre>{% highlight "c" %}
|
||||
struct {
|
||||
struct {
|
||||
char challenge[7];
|
||||
char responses[4][20];
|
||||
} ds[10000];
|
||||
struct {
|
||||
char challenge[16];
|
||||
char response[16];
|
||||
} ssd[10000];
|
||||
}
|
||||
{% endhighlight %}</pre>
|
||||
|
||||
<p>The responses are scrambled as described below:</p>
|
||||
<h4>DS Scramble:</h4>
|
||||
<table class="code">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Output index</td>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
<td>4</td>
|
||||
<td>5</td>
|
||||
<td>6</td>
|
||||
<td>7</td>
|
||||
<td>8</td>
|
||||
<td>9</td>
|
||||
<td>10</td>
|
||||
<td>11</td>
|
||||
<td>12</td>
|
||||
<td>13</td>
|
||||
<td>14</td>
|
||||
<td>15</td>
|
||||
<td>16</td>
|
||||
<td>17</td>
|
||||
<td>18</td>
|
||||
<td>19</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Input index</td>
|
||||
<td>15</td>
|
||||
<td>5</td>
|
||||
<td>13</td>
|
||||
<td>14</td>
|
||||
<td>10</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>11</td>
|
||||
<td>16</td>
|
||||
<td>7</td>
|
||||
<td>4</td>
|
||||
<td>18</td>
|
||||
<td>12</td>
|
||||
<td>6</td>
|
||||
<td>3</td>
|
||||
<td>0</td>
|
||||
<td>17</td>
|
||||
<td>8</td>
|
||||
<td>19</td>
|
||||
<td>9</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h4>SSD Scramble:</h4>
|
||||
<table class="code">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>Output index</td>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
<td>4</td>
|
||||
<td>5</td>
|
||||
<td>6</td>
|
||||
<td>7</td>
|
||||
<td>8</td>
|
||||
<td>9</td>
|
||||
<td>10</td>
|
||||
<td>11</td>
|
||||
<td>12</td>
|
||||
<td>13</td>
|
||||
<td>14</td>
|
||||
<td>15</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Input index</td>
|
||||
<td>6</td>
|
||||
<td>8</td>
|
||||
<td>4</td>
|
||||
<td>12</td>
|
||||
<td>7</td>
|
||||
<td>13</td>
|
||||
<td>1</td>
|
||||
<td>10</td>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
<td>11</td>
|
||||
<td>14</td>
|
||||
<td>15</td>
|
||||
<td>0</td>
|
||||
<td>5</td>
|
||||
<td>9</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><b>NOTE: The following information may only be true for MaiMai FiNALE! I have not yet verified this on other
|
||||
games!</b>
|
||||
<br>
|
||||
In dev mode, the game will only ever request a single string as the challenge.
|
||||
</p>
|
||||
<p>This string is <code>2CFECBC71CF1E4</code>, and its corresponding four response pages are (not scrambled):</p>
|
||||
<ol>
|
||||
<li><code>ca6ed736401682dd4411a27d2440ac4b478bad8b</code></li>
|
||||
<li><code>4ac606302ce5ef51abb3df2dc46c863b3c06aa2c</code></li>
|
||||
<li><code>2aaf35b2aba4c6840bdb7bd40ecbce2cca934795</code></li>
|
||||
<li><code>2f6d713dabde3c43df818491ab9467ba8ba0fed4</code></li>
|
||||
</ol>
|
||||
<p>
|
||||
<b>NOTE: The following information is super un-verified!</b><br>
|
||||
Occasionally the game will make a query to the DS table that is not present in the table. In this instance, the
|
||||
keychip responds with the entry at index n, where n is a counter that increments every time a
|
||||
<code>keychip.ds.compute</code> query is performed, modulo 100.
|
||||
</p>
|
||||
<p>It should be noted that if the keychip binary imemdiatly terminates the connection, rather than sending a
|
||||
known-incorrect response (such as if the challenge matches none in the known tables), the game will re-attempt the
|
||||
query, and this query will, with a high likelyhood, be different.</p>
|
||||
|
||||
<p>It should additionally be noted that this whole process can be skipped by returning <code>code=54</code> rather than
|
||||
a valid response.</p>
|
||||
|
||||
{% endblock %}
|
Loading…
Reference in New Issue
Block a user