PCP and Alpha
This commit is contained in:
parent
b1544d136c
commit
c408eff8d0
BIN
images/pcp-railroad.png
Normal file
BIN
images/pcp-railroad.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
232
templates/pages/sega/software/alphadvd.html
Normal file
232
templates/pages/sega/software/alphadvd.html
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
{% extends "sega.html" %} {% block title %}AlphADVD{% endblock %} {% block body %}
|
||||||
|
<h1>AlphaDVD (aka αDVD)</h1>
|
||||||
|
<p>
|
||||||
|
αDVD is the custom copy-protection SEGA employ for update DVDs. It is handled by
|
||||||
|
<code>mxAuthDisc.exe</code> on Ring systems. Is is present on DVR-* discs, typically the first in a multi-DVD
|
||||||
|
install process.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In order to understand αDVD, it's important to first have a basic understanding of how data is stored on a
|
||||||
|
DVD. Rather than like random access storage, where the data stream can be moddeled as a large addressable series of
|
||||||
|
bytes, DVDs are more akin to HDDs in their division into sectors. Unlike HDDs, however, there is no prescribed order
|
||||||
|
for the sectors! Each sector of data on disc is prefixed by a header identifying that sector, and notably including
|
||||||
|
its sector number. When a DVD reader is asked to read a specific sector, it spins the disc until it reads the
|
||||||
|
appropriate header, then returns the data following that header. There is importantly nothing here that would stop a
|
||||||
|
disc from containing multiple sectors with the same sector number in their prefix!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
DVD readers will return the first sector that matches the requested sector number, so if we know where the different
|
||||||
|
duplicates are on disc we can seek to a known sector a short distance before the instance of the duplicate we wish
|
||||||
|
to aquire, then ask the DVD reader to read the duplicated sector. Depending on where we first seek, we will receive
|
||||||
|
different data back.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
αDVD utilises this, with 6 duplicated sectors, each with three distinct copies. When authenticating a disc,
|
||||||
|
only one of these 6 duplicates will be checked, however which is checked is random, so in practice all 6 should be
|
||||||
|
present lest the disc sporadically fails the authentication. This is similar to a copy-protection scheme called
|
||||||
|
TAGES, however more advanced. All three instances <b>must</b> be present, so it is impossible to create a single
|
||||||
|
flat image that passes authentication!
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
As well as this more hardware based authentication, there is a level of encryption applied to the disc headers too.
|
||||||
|
This is however much easeier to work with. Each disc has a header in sector 16, sector 1, or sector 17 (checked in
|
||||||
|
that order). There is no indication which sector contains the header, so in turn each sector is read and decryption
|
||||||
|
is attempted. We can then validate the header magic number.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>αDVD Encryption</h2>
|
||||||
|
<p>
|
||||||
|
αDVD encryption is a basic XOR cipher, where the text is XORed with a key, repeating the key as needed. The
|
||||||
|
key is always 32768 (8000<sub>h</sub>) bytes, and is unmodified during this process.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Keys are derrived based on a key expansion algorithm that takes as input an unsigned short (16 bit) seed. I'm not
|
||||||
|
totally sure what expansion algorithm this is, or if it's something totally custom, but for now here's a snippet of
|
||||||
|
python code that implements the expansion:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{%highlight "python"%}
|
||||||
|
def amAuthDiskInitKey(seed):
|
||||||
|
key = bytearray(0x8000)
|
||||||
|
|
||||||
|
for i in range(0x8000):
|
||||||
|
uVar1 = (seed * 2 >> 4 ^ seed * 2) >> 10 & 2 | seed << 2
|
||||||
|
|
||||||
|
uVar2 = uVar1 * 2
|
||||||
|
uVar3 = ((seed << 2) >> 4 ^ uVar1) >> 10 & 2 | uVar2
|
||||||
|
uVar1 = uVar3 * 2
|
||||||
|
uVar3 = (uVar2 >> 4 ^ uVar3) >> 10 & 2 | uVar1
|
||||||
|
|
||||||
|
uVar2 = uVar3 * 2
|
||||||
|
uVar3 = (uVar1 >> 4 ^ uVar3) >> 10 & 2 | uVar2
|
||||||
|
uVar1 = uVar3 * 2
|
||||||
|
uVar3 = (uVar2 >> 4 ^ uVar3) >> 10 & 2 | uVar1
|
||||||
|
|
||||||
|
uVar2 = uVar3 * 2
|
||||||
|
uVar3 = (uVar1 >> 4 ^ uVar3) >> 10 & 2 | uVar2
|
||||||
|
uVar1 = uVar3 * 2
|
||||||
|
uVar2 = (uVar2 >> 4 ^ uVar3) >> 10 & 2 | uVar1
|
||||||
|
|
||||||
|
seed = uVar2 | (uVar1 >> 4 ^ uVar2) >> 11 & 1
|
||||||
|
key[i] = seed & 0xff
|
||||||
|
|
||||||
|
return key
|
||||||
|
{% endhighlight %}</pre
|
||||||
|
>
|
||||||
|
|
||||||
|
<h2>αDVD Headers</h2>
|
||||||
|
<p>
|
||||||
|
Now that we know how to decrypt data on αDVDs we can search for the header. The header will always be
|
||||||
|
encrypted with a fixed key with seed <code>5369</code>. The header is a sequence of 53 bytes, located at offset 318
|
||||||
|
if it is in sector 16, and offset 508 if it is in sector 1 or 17.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td colspan="17">Header Offset</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></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>A</td>
|
||||||
|
<td>B</td>
|
||||||
|
<td>C</td>
|
||||||
|
<td>D</td>
|
||||||
|
<td>E</td>
|
||||||
|
<td>F</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><b>0</b></td>
|
||||||
|
<td colspan="4">Magic = F1FFFF1F<sub>h</sub></td>
|
||||||
|
<td colspan="2"></td>
|
||||||
|
<td colspan="2">Auth sector 1</td>
|
||||||
|
<td colspan="2">Auth sector 2</td>
|
||||||
|
<td colspan="2">Auth sector 3</td>
|
||||||
|
<td colspan="6"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><b>1</b></td>
|
||||||
|
<td colspan="2">Data Offset</td>
|
||||||
|
<td colspan="4"></td>
|
||||||
|
<td colspan="2">??</td>
|
||||||
|
<td colspan="20">DVD Name</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><b>2</b></td>
|
||||||
|
<td colspan="2">Key seed</td>
|
||||||
|
<td colspan="2">Dummy number</td>
|
||||||
|
<td colspan="3"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
To validate the decryption of a header, both the magic number and the DVD name are checked. The DVD name must start
|
||||||
|
with <code>SEGA_DVD</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The key seed present is the header is used to generate a new key that will be used to decrypt the authentication
|
||||||
|
sectors.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>The Authentication Sectors</h2>
|
||||||
|
<p>
|
||||||
|
The three sector addresses in the header are now used to perform a series of seeks and reads. We seek the drive by
|
||||||
|
requesting a read of 16 sectors, but disregarding the returned data. The first step is to choose the authentication
|
||||||
|
sector we wish to read. The six duplicates are present using the following offsets:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>140<sub>h</sub></li>
|
||||||
|
<li>150<sub>h</sub></li>
|
||||||
|
<li>160<sub>h</sub></li>
|
||||||
|
<li>170<sub>h</sub></li>
|
||||||
|
<li>180<sub>h</sub></li>
|
||||||
|
<li>190<sub>h</sub></li>
|
||||||
|
</ul>
|
||||||
|
<p>We will refer to our chosen offset, from this list, as <code>n</code>.</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Seek to <code>[Auth sector 1] - 16</code></li>
|
||||||
|
<li>Read(16) <code>([Auth sector 1] + (n - 1)) & 0xFFFFFFF0</code></li>
|
||||||
|
<li>Seek to <code>[Auth sector 1] - 16</code></li>
|
||||||
|
<li>Seek to <code>[Auth sector 1] + [Auth sector 2] - 8</code></li>
|
||||||
|
<li>Read(16) <code>([Auth sector 1] + (n - 1)) & 0xFFFFFFF0</code></li>
|
||||||
|
<li>Seek to <code>[Auth sector 1] + [Auth sector 3] + 8</code></li>
|
||||||
|
<li>Read(16) <code>([Auth sector 1] + (n - 1)) & 0xFFFFFFF0</code></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Each of the three reads are decrypted using the key we generated earlier, and are authentication block 1, 2, and 3
|
||||||
|
respectively. The actual data is at offset 31228 in these 16-sector blocks, and follows the following structure:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td colspan="17">Header Offset</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></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>A</td>
|
||||||
|
<td>B</td>
|
||||||
|
<td>C</td>
|
||||||
|
<td>D</td>
|
||||||
|
<td>E</td>
|
||||||
|
<td>F</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><b>0</b></td>
|
||||||
|
<td colspan="4">Magic = F1FFFF1F<sub>h</sub></td>
|
||||||
|
<td colspan="4">Num magic</td>
|
||||||
|
<td colspan="4"><code>n</code></td>
|
||||||
|
<td colspan="4"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
Num magic will be F1FFFF1F<sub>h</sub> in the first sector, F2FFFF2F<sub>h</sub> in the second, and F3FFFF3F<sub
|
||||||
|
>h</sub
|
||||||
|
>
|
||||||
|
in the third.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
There is, however, one extra curveball. One of these three sectors is a dummy sector that contains nonsensiacal data
|
||||||
|
(in practice this appears to just be nulls). This is the sector indicated by the lower byte of the
|
||||||
|
<code>Dummy number</code> field in the alpha header. It is essential that this header is <b>not</b> valid.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Assuming we pass these checks, αDVD authentication succeeded. The disc will now be be read as usual, applying
|
||||||
|
the data offset from the alpha header before any operations. Coindidentally if an ISO image has been made of an
|
||||||
|
alphaDVD (which will be unable to pass authentication anyway), all sectors preceeding this offset can be stripped,
|
||||||
|
and the ISO now matches that of a non-alpha disc.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -1,48 +1,71 @@
|
|||||||
{% extends "sega.html" %}
|
{% extends "sega.html" %} {% block title %}PCP{% endblock %} {% block body %}
|
||||||
{% block title %}PCP{% endblock %}
|
|
||||||
{% block body %}
|
|
||||||
<h1>PCP</h1>
|
<h1>PCP</h1>
|
||||||
<h2>What is PCP?</h2>
|
<h2>What is PCP?</h2>
|
||||||
<p>PCP is the protocol used for inter-process communication between services running on Ring* systems. I have no idea
|
<p>
|
||||||
what it stands for; head-canon it as Process Command Protocol or whatever you want really. The official
|
PCP is the protocol used for inter-process communication between services running on Ring* systems. I have no idea
|
||||||
implementation is <code>libpcp</code>, which is statically linked in to binaries that make use of the protocll (and
|
what it stands for; head-canon it as Process Communication Protocol or whatever you want really. The official
|
||||||
is itself dependent on <code>amLib</code>).</p>
|
implementation is <code>libpcp</code>, which is statically linked in to binaries that make use of the protocol (and
|
||||||
<p>On paper, there are many things the format would at first appear to
|
is itself dependent on <code>amLib</code>).
|
||||||
support, but is unable to due to the reference implementation in <code>libpcp</code>. Specification of this nature
|
</p>
|
||||||
will be <span class="mark">marked</span>. Custom implementations should be liberal in what they receive, and
|
<p>
|
||||||
conservative in what they transmit; marked specification are key areas of focus for this.</p>
|
On paper, there are many things the format would at first appear to support, but is unable to due to the reference
|
||||||
|
implementation in <code>libpcp</code>. Specification of this nature will be <span class="mark">marked</span>. Custom
|
||||||
|
implementations should be liberal in what they receive, and conservative in what they transmit; marked specification
|
||||||
|
are key areas of focus for this.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>Protocol Specification</h2>
|
<h2>Protocol Specification</h2>
|
||||||
<p>We consider two processes communicating: a server, and a consumer. A server need only be capable of processing at
|
<p>
|
||||||
least one consumer concurrently, though implementations may desire the ability to do so.</p>
|
We consider two processes communicating: a server, and a consumer. A server need only be capable of processing at
|
||||||
<p>When a server is ready to process a command, it transmits a single <code>></code> byte to its connected consumer.
|
least one consumer concurrently, though implementations may desire the ability to process multiple consumers.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
When a server is ready to process a command, it transmits a single <code>></code> byte to its connected consumer.
|
||||||
</p>
|
</p>
|
||||||
<p>The consumer responds with a CRLF-terminated payload packet, containing the command.</p>
|
<p>The consumer responds with a CRLF-terminated payload packet, containing the command.</p>
|
||||||
<p>The server then responds syncronously with a CRLF-terminated payload packet. If a data transfer is being a performed,
|
<p>
|
||||||
this packet will contain <code>port</code> and <code>size</code> as paramaters.</p>
|
The server then responds syncronously with a CRLF-terminated payload packet. If a data transfer is being a
|
||||||
<p>In a server->consumer data transfer operation, the consumer connects to the provided port, and expects to receive
|
performed, this packet will contain <code>port</code> and <code>size</code> as paramaters.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In a server->consumer data transfer operation, the consumer connects to the provided port, and expects to receive
|
||||||
<code>size</code> bytes of data. It then closes the connection to the data port and transmits a <code>$</code> byte
|
<code>size</code> bytes of data. It then closes the connection to the data port and transmits a <code>$</code> byte
|
||||||
to the server to ackgnowledge receipt. The server will only process this ackgnowledgement after it has succesfully
|
to the server to ackgnowledge receipt. The server will only process this ackgnowledgement after it has succesfully
|
||||||
transmitted its data to a consumer.
|
transmitted its data to a consumer.
|
||||||
</p>
|
</p>
|
||||||
<p>The characteristics of a consumer->server transfer as as yet undocumented. I'll get round to it!</p>
|
<p>The characteristics of a consumer->server transfer as as yet undocumented. I'll get round to it!</p>
|
||||||
|
|
||||||
<p>If the server is unable to process a request for any reason, it may respond with a single <code>?</code>. This may be
|
<p>
|
||||||
due to a non existant command being requested, or it may be due to an invalid packet.</p>
|
If the server is unable to process a request for any reason, it may respond with a single <code>?</code>. This may
|
||||||
|
be due to a non existant command being requested, or it may be due to an invalid packet.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3>Payload format</h3>
|
<h3>Payload format</h3>
|
||||||
<p>Payloads are a non-zero number of <code>&</code> delimited <code>=</code> seperated key-value pairs. i.e.
|
|
||||||
<code>key1=value2&key2=value2</code>. Both the key and the value can contain any alphanumeric character, and any of
|
<p>
|
||||||
the symbols <code>._-:@%/\{}</code>. The value may alternatively be a single <code>?</code>. Leading and trailing
|
Outside of special packets such as <code>></code> and <code>$</code>, all communication in both directions
|
||||||
ampersands are illegal, as is more than one ampersand consecuitevely. Empty strings for either the key or value is
|
strictly follows the following structure:
|
||||||
likewise illegal.
|
|
||||||
</p>
|
</p>
|
||||||
<p>Spaces (ascii 20<sub>h</sub>) and tabs (ascii 09<sub>h</sub>) are allowable whitespace. They may be present anywhere
|
|
||||||
|
<img src="{{ROOT}}/images/pcp-railroad.png" class="graphic" />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<i><code>Text</code></i> is defined as a series of one or more bytes matching <code>[a-zA-Z0-9._:@%/\{}-]</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Spaces (ascii 20<sub>h</sub>) and tabs (ascii 09<sub>h</sub>) are allowable whitespace. They may be present anywhere
|
||||||
in the packet surrounding keys, values, any delimiter, or seperator, and will be ignored. They are not valid within
|
in the packet surrounding keys, values, any delimiter, or seperator, and will be ignored. They are not valid within
|
||||||
a key or a value.</p>
|
a key or a value.
|
||||||
<p>Comments begin and end with the <code>#</code> symbol. They may appear at any point in a packet, and the packet
|
</p>
|
||||||
should be processed as if the comment is not there. <b>The content of comments observe the same restrictions as keys
|
<p>
|
||||||
and values. This notably includes no whitespace.</b></p>
|
Comments begin and end with the <code>#</code> symbol. They may appear at any point in a packet, and the packet
|
||||||
|
should be processed as if the comment is not there.
|
||||||
|
<b
|
||||||
|
>The content of comments must match <i><code>Text</code></i
|
||||||
|
>. This notably includes no whitespace.</b
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>Format restrictions</h2>
|
<h2>Format restrictions</h2>
|
||||||
<ul>
|
<ul>
|
||||||
@ -65,24 +88,31 @@
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>libpcp bugs</summary>
|
<summary>libpcp bugs</summary>
|
||||||
<p>When parsing requests, libpcp null-terminates <code>?</code> values with an off-by-one error. This means if you
|
<p>
|
||||||
|
When parsing requests, libpcp null-terminates <code>?</code> values with an off-by-one error. This means if you
|
||||||
transmit <code>test=12345</code> followed by <code>test=?</code>, it will be parsed as if you had transmitted
|
transmit <code>test=12345</code> followed by <code>test=?</code>, it will be parsed as if you had transmitted
|
||||||
<code>test=?2</code>. This could actually be an issue with how I am inspecting the internal state; I will
|
<code>test=?2</code>. This could actually be an issue with how I am inspecting the internal state; I will
|
||||||
update/remove this spoilier once I've had a chance to dig deeper.
|
update/remove this spoilier once I've had a chance to dig deeper.
|
||||||
</p>
|
</p>
|
||||||
<p>libpcp does not enforce ampersand placement, causing strange memory artifacts. Consumers <b>MUST</b> conform to
|
<p>
|
||||||
the ampersand specification.</p>
|
libpcp does not enforce ampersand placement, causing strange memory artifacts. Consumers <b>MUST</b> conform to
|
||||||
<p>libpcp allows empty keys and values. The case of an empty key causes the pair to be keyed with an empty string,
|
the ampersand specification.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
libpcp allows empty keys and values. The case of an empty key causes the pair to be keyed with an empty string,
|
||||||
however an empty value causes it to contain a random value, reading from memory where the previous packet was
|
however an empty value causes it to contain a random value, reading from memory where the previous packet was
|
||||||
decoded. In fact, the presence of a <code>=</code> is not validated either, likewise causing it to read
|
decoded. In fact, the presence of a <code>=</code> is not validated either, likewise causing it to read
|
||||||
undefined regions of memory. Consumers <b>MUST</b> always provide both the key and the value.</p>
|
undefined regions of memory. Consumers <b>MUST</b> always provide both the key and the value.
|
||||||
|
</p>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
<summary>Using libpcp</summary>
|
<summary>Using libpcp</summary>
|
||||||
<p>I have reproduced a locally functioning standalone distribution of libpcp, warts and all. Eventually I will
|
<p>
|
||||||
|
I have reproduced a locally functioning standalone distribution of libpcp, warts and all. Eventually I will
|
||||||
produce some basic docs for making use of the exported functions, and will hopefully be able to provide a
|
produce some basic docs for making use of the exported functions, and will hopefully be able to provide a
|
||||||
download for a precompiled library. I'm still unsure if the source code will ever be made available however
|
download for a precompiled library. I'm still unsure if the source code will ever be made available however
|
||||||
because it's a <em>very</em> true to the original reproduction, potentially problematically so!</p>
|
because it's a <em>very</em> true to the original reproduction, potentially problematically so!
|
||||||
|
</p>
|
||||||
<p>If rather than implementing your own pcp you wish to use <em>the</em> libpcp, watch this space.</p>
|
<p>If rather than implementing your own pcp you wish to use <em>the</em> libpcp, watch this space.</p>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user