5. System Security
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.
If you are here for encryption keys, they can be found at the following link. APM 2 games do not use an individual keychip and are thus not included.
If the fact I just linked those for free makes you angry, please reconsider a few things and maybe stop charging arcade operators insane prices for basic things.
5.1 Drive ATA Password
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.
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.
Why does this work?
The following is the sequence of possible security modes for an ATA drive:
<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>
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.
This allows us full read-write access to the drive without ever knowing the password!
The ATA key is derived in the BIOS during boot, based on the 40-byte model number of the drive provided by the ATA identify device data command (0xEC). The 32-byte password is then calculated based on the following algorithm. This algorithm is consistent between RingWide, RingEdge and RingEdge2 (thanks to Darksoft for some info here). Happy unlocking!
CHARSET = bytearray(b'/-AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789')
def charset_index(x: int) -> int:
if x in CHARSET:
return CHARSET.index(x)
return 0x55
def prepare_password(model: bytes) -> bytes:
assert len(model) == 40
password = bytearray(32)
for i in range(32):
a = charset_index(model[i])
b = charset_index(model[39 - i])
if i % 2 == 0:
password[i] = (((i ^ a) & 0x1f) << 3) ^ ((b & 0x2e) >> 1)
else:
password[i] = (((i ^ b) & 0x3b) << 2) ^ ((a & 0x66) >> 1)
return password
Some common disks:
GBDriver RS2
:7242525aba526a5aea726278ca42da4a2a223a2a0a221a2a6a027a0a5cce4a0a
GBDriver RS3
:7242525aba526a5aea726278ca42da4a2a223a2a0a221a2a6a027a0a5cce4a0a
5.1.1 Drive passsword generation tool
Model number: | |
ATA password: |
|
5.2 Windows Password
It seems silly to mention, but it’s worth noting. AppUser
will automatically log in, but if you need to log back in, or wish to login as SystemUser
, you’ll need the passwords:
AppUser
’s password issegahard
;SystemUser
’s password is<6/=U=#tpe!$*3!5
.
Warning
If a debugger is attached to mxprestartup
, Miflac=Ifme9Jfp0
will be attempted as the password for SystemUser
instead. This is not the correct password for a production unit.
Finding SystemUser’s password
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:
- Add
153815264b5839090b0d1c1a423c02241633130673071a1e38443912410b47380f213c1d
and2e0247311e162b666c6640393737724157001f56045b4b4f24333457335a26381f4c3349
together (these are strings contained within the binary).- Result:
C:\Windows\System32\wbem\wmitemp.mof
- Result:
- Read
C:\Windows\System32\wbem\wmitemp.mof
.- Result:
270a2a053b29042b261d1b22070d140c
- Result:
- Add
152c05381a141f494a48060223260d29
to this value.- Result:
<6/=U=#tpe!$*3!5
- Result:
If a debugger is present, a similar process is performed:
- Add
d0b1c034a32243340505a343659517c121f0319583d205c593a690719604846000e062a6
and63f10541f0c201c0703022f332a13094b542f130c25491a1c280a65440831296e2563590
together, with each byte modulo 255 (not 256!).- Result:
34a3c57594e444f47535c53797374756d63323c546279667562737c5d68796f6e2379737
- Result:
- Flip the nibbles of each byte.
- Result:
C:\WINDOWS\system32\drivers\mxio.sys
- Result:
- Read
C:\WINDOWS\system32\drivers\mxio.sys
.- Result:
9160e5c22392918371e43573f2b095b1
- Result:
- Add
43368004f2a34211f4f12120b1b57151
to this value, with each byte modulo 255.- Result:
d49666c61636d39466d65693a4660703
- Result:
- Flip the nibbles of each byte.
- Result:
Miflac=Ifme9Jfp0
- Result:
Why is the process for a debugged process more elaborate? I’m not sure.
5.3 System binaries encryption
The system binaries, normally located on S:
, are a TrueCrypt partition. The partition file can be found at C:\System\Execute\System
. It has password segahardpassword
, and used the alternate data stream loated at C:\System\Execute\DLL:SystemKeyFile
as a keyfile.
C:\System\Execute\DLL:UpdateKeyFile
is also present here, which is used for encryption of update data (but is not utilised in the process of mounting S:
).
What’s an Alternate Data Stream?
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 dir /R
. 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.
The SANS Institude website is a great resource for more information and links.
Finding this information
The decryption of the system partition is the responsibility of mxstartup
. This file contains pairs of hex strings which sum to produce the paths to the alternate data streams, and the volume password.
5.4 Keyfile downloads
5.5 Keychip
This is the first point during the boot process where a physical keychip is required in order to continue the system boot process.
With the exception of mxkeychip
, processes do not directly communicate with the keychip. Instead, they communicate with mxkeychip
via a PCP. mxkeychip
itself is then responsible for communicating with the keychip. These communications are AES encrypted with keys agreed during the initial handshake.
The keychip is responsible for a number of functions:
- Each keychip is assigned a region, and this region must match the region stored in the Ring*’s EEPROM.
- It contains configuration tables for the network setup.
- It is able to perform basic encryption and decryption, which is used to derive the game encryption key.
- It includes a large challenge-response table used to authenticate with the specific game.
- It contains Aime billing information regarding how many credits are allowed on this machine before re-authenticating with network services.
- It contains a small amount of writable storage used to store trace logs, such as when the system booted or authenticated with network services.
This security layer can be bypassed by replacing the mxkeychip.exe
binary with a custom binary, eliminating the need to emulate the physical parallel device, and its encryption.
5.6 Game data encryption
Once the system has verified it is allowed to continue booting. The game data is stored in TrueCrypt partitions. At least one, original0
, will be mounted to O:
. patch0
or patch1
may additionally be mounted to P:
, if present and matching the game being booted. The two are then merged using geminifs
to X:
.
The keyfile used by TrueCrypt is stored on the keychip, as keychip.appboot.seed
. This value is encrypted using the game key, and must first by decrypted using keychip.decrypt
.
5.7 Game-keychip handshake
During initialisation of the AmLib module amDongle
, games may (and in almost all cases, do) opt for an additional layer of security. This is enabled by passing a filename to amDongleSetAuthConfig()
, rather than the string "toolmode"
.
In this mode, AmLib will perform a series of challenge-response queries against both the system SSD and the DS28CN01 hardware SHA1 chip on the keychip. In most cases, these values are retreived from the table file, and matched against the expected responses stored in the same file. In some cases, a unique challenge string is generated.
Note
The DS28CN01 uses a slight variant on SHA1 (not documented here, currently), and combines the challenge data with a write-only key stored on the chip, provisioned from the factory. Despite having different table files, all games use the same SHA1 keys. The different tables contain different selections of possible challenge values.
We can extract the expected responses from the table files present in game data (named [game ID]_Table.dat
by convention). The file consists of the following structure:
struct {
struct {
char challenge[7];
char responses[4][20];
} ds[10000];
struct {
char challenge[16];
char response[16];
} ssd[10000];
}
The responses are scrambled as described below:
5.7.0.1 DS Scramble
Output index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Input index | 15 | 5 | 13 | 14 | 10 | 1 | 2 | 11 | 16 | 7 | 4 | 18 | 12 | 6 | 3 | 0 | 17 | 8 | 19 | 9 |
5.7.0.2 SSD Scramble
Output index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Input index | 6 | 8 | 4 | 12 | 7 | 13 | 1 | 10 | 2 | 3 | 11 | 14 | 15 | 0 | 5 | 9 |
In dev mode, the game will only ever request a single string as the challenge.
This string is 2CFECBC71CF1E4
, and its corresponding four response pages are (not scrambled):
ca6ed736401682dd4411a27d2440ac4b478bad8b
4ac606302ce5ef51abb3df2dc46c863b3c06aa2c
2aaf35b2aba4c6840bdb7bd40ecbce2cca934795
2f6d713dabde3c43df818491ab9467ba8ba0fed4
The following information is super un-verified!
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 keychip.ds.compute
query is performed, modulo 100.
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.
It should additionally be noted that this whole process can be skipped by returning code=54
rather than a valid response.