diff --git a/images/pcp-railroad.png b/images/pcp-railroad.png new file mode 100644 index 0000000..5b96dab Binary files /dev/null and b/images/pcp-railroad.png differ diff --git a/templates/pages/sega/software/alphadvd.html b/templates/pages/sega/software/alphadvd.html new file mode 100644 index 0000000..ee38919 --- /dev/null +++ b/templates/pages/sega/software/alphadvd.html @@ -0,0 +1,232 @@ +{% extends "sega.html" %} {% block title %}AlphADVD{% endblock %} {% block body %} +
+ αDVD is the custom copy-protection SEGA employ for update DVDs. It is handled by
+ mxAuthDisc.exe
on Ring systems. Is is present on DVR-* discs, typically the first in a multi-DVD
+ install process.
+
+ 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! +
++ 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. +
++ α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 must be present, so it is impossible to create a single + flat image that passes authentication! +
+ ++ 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. +
+ ++ α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 (8000h) bytes, and is unmodified during this process. +
++ 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: +
+ ++{%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 %}+ +
+ 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 5369
. 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.
+
Header Offset | +||||||||||||||||||||||||||||
+ | 0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +A | +B | +C | +D | +E | +F | +||||||||||||
0 | +Magic = F1FFFF1Fh | ++ | Auth sector 1 | +Auth sector 2 | +Auth sector 3 | ++ | ||||||||||||||||||||||
1 | +Data Offset | ++ | ?? | +DVD Name | +||||||||||||||||||||||||
2 | +Key seed | +Dummy number | ++ |
+ To validate the decryption of a header, both the magic number and the DVD name are checked. The DVD name must start
+ with SEGA_DVD
.
+
+ The key seed present is the header is used to generate a new key that will be used to decrypt the authentication + sectors. +
+ ++ 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: +
+We will refer to our chosen offset, from this list, as n
.
[Auth sector 1] - 16
([Auth sector 1] + (n - 1)) & 0xFFFFFFF0
[Auth sector 1] - 16
[Auth sector 1] + [Auth sector 2] - 8
([Auth sector 1] + (n - 1)) & 0xFFFFFFF0
[Auth sector 1] + [Auth sector 3] + 8
([Auth sector 1] + (n - 1)) & 0xFFFFFFF0
+ 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: +
+ +Header Offset | +||||||||||||||||
+ | 0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 | +A | +B | +C | +D | +E | +F | +
0 | +Magic = F1FFFF1Fh | +Num magic | +n |
+ + |
+ Num magic will be F1FFFF1Fh in the first sector, F2FFFF2Fh in the second, and F3FFFF3Fh + in the third. +
+
+ 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
+ Dummy number
field in the alpha header. It is essential that this header is not valid.
+
+ 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. +
+ +{% endblock %} diff --git a/templates/pages/sega/software/pcp/index.html b/templates/pages/sega/software/pcp/index.html index f851e36..fc22f77 100644 --- a/templates/pages/sega/software/pcp/index.html +++ b/templates/pages/sega/software/pcp/index.html @@ -1,48 +1,71 @@ -{% extends "sega.html" %} -{% block title %}PCP{% endblock %} -{% block body %} +{% extends "sega.html" %} {% block title %}PCP{% endblock %} {% block body %}PCP is the protocol used for inter-process communication between services running on Ring* systems. I have no idea
- what it stands for; head-canon it as Process Command Protocol or whatever you want really. The official
- implementation is libpcp
, which is statically linked in to binaries that make use of the protocll (and
- is itself dependent on amLib
).
On paper, there are many things the format would at first appear to
- support, but is unable to due to the reference implementation in libpcp
. Specification of this nature
- will be marked. Custom implementations should be liberal in what they receive, and
- conservative in what they transmit; marked specification are key areas of focus for this.
+ PCP is the protocol used for inter-process communication between services running on Ring* systems. I have no idea
+ what it stands for; head-canon it as Process Communication Protocol or whatever you want really. The official
+ implementation is libpcp
, which is statically linked in to binaries that make use of the protocol (and
+ is itself dependent on amLib
).
+
+ On paper, there are many things the format would at first appear to support, but is unable to due to the reference
+ implementation in libpcp
. Specification of this nature will be marked. Custom
+ implementations should be liberal in what they receive, and conservative in what they transmit; marked specification
+ are key areas of focus for this.
+
We consider two processes communicating: a server, and a consumer. A server need only be capable of processing at - least one consumer concurrently, though implementations may desire the ability to do so.
-When a server is ready to process a command, it transmits a single >
byte to its connected consumer.
+
+ We consider two processes communicating: a server, and a consumer. A server need only be capable of processing at + least one consumer concurrently, though implementations may desire the ability to process multiple consumers. +
+
+ When a server is ready to process a command, it transmits a single >
byte to its connected consumer.
The consumer responds with a CRLF-terminated payload packet, containing the command.
-The server then responds syncronously with a CRLF-terminated payload packet. If a data transfer is being a performed,
- this packet will contain port
and size
as paramaters.
In a server->consumer data transfer operation, the consumer connects to the provided port, and expects to receive +
+ The server then responds syncronously with a CRLF-terminated payload packet. If a data transfer is being a
+ performed, this packet will contain port
and size
as paramaters.
+
+ In a server->consumer data transfer operation, the consumer connects to the provided port, and expects to receive
size
bytes of data. It then closes the connection to the data port and transmits a $
byte
to the server to ackgnowledge receipt. The server will only process this ackgnowledgement after it has succesfully
transmitted its data to a consumer.
The characteristics of a consumer->server transfer as as yet undocumented. I'll get round to it!
-If the server is unable to process a request for any reason, it may respond with a single ?
. This may be
- due to a non existant command being requested, or it may be due to an invalid packet.
+ If the server is unable to process a request for any reason, it may respond with a single ?
. This may
+ be due to a non existant command being requested, or it may be due to an invalid packet.
+
Payloads are a non-zero number of &
delimited =
seperated key-value pairs. i.e.
- key1=value2&key2=value2
. Both the key and the value can contain any alphanumeric character, and any of
- the symbols ._-:@%/\{}
. The value may alternatively be a single ?
. Leading and trailing
- ampersands are illegal, as is more than one ampersand consecuitevely. Empty strings for either the key or value is
- likewise illegal.
+
+
+ Outside of special packets such as >
and $
, all communication in both directions
+ strictly follows the following structure:
Spaces (ascii 20h) and tabs (ascii 09h) are allowable whitespace. They may be present anywhere + + + +
+ Text
is defined as a series of one or more bytes matching [a-zA-Z0-9._:@%/\{}-]
.
+
+ Spaces (ascii 20h) and tabs (ascii 09h) 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 - a key or a value.
-Comments begin and end with the #
symbol. They may appear at any point in a packet, and the packet
- should be processed as if the comment is not there. The content of comments observe the same restrictions as keys
- and values. This notably includes no whitespace.
+ Comments begin and end with the #
symbol. They may appear at any point in a packet, and the packet
+ should be processed as if the comment is not there.
+ The content of comments must match Text
. This notably includes no whitespace.
+
When parsing requests, libpcp null-terminates ?
values with an off-by-one error. This means if you
+
+ When parsing requests, libpcp null-terminates ?
values with an off-by-one error. This means if you
transmit test=12345
followed by test=?
, it will be parsed as if you had transmitted
test=?2
. 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.
libpcp does not enforce ampersand placement, causing strange memory artifacts. Consumers MUST conform to - the ampersand specification.
-libpcp allows empty keys and values. The case of an empty key causes the pair to be keyed with an empty string, +
+ libpcp does not enforce ampersand placement, causing strange memory artifacts. Consumers MUST conform to + the ampersand specification. +
+
+ 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
decoded. In fact, the presence of a =
is not validated either, likewise causing it to read
- undefined regions of memory. Consumers MUST always provide both the key and the value.
I have reproduced a locally functioning standalone distribution of libpcp, warts and all. Eventually I will +
+ 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 download for a precompiled library. I'm still unsure if the source code will ever be made available however - because it's a very true to the original reproduction, potentially problematically so!
+ because it's a very true to the original reproduction, potentially problematically so! +If rather than implementing your own pcp you wish to use the libpcp, watch this space.