PSX Xploder Encryption Schemes

Lazy Bastard (schemes cracked by misfire)
Having experienced firsthand that these routines can get a bit confusing, particularly for someone not-so-versed in C, I'll give a brief explanation of them here. I've also included misfire's original C source, for completeness. I'll give you the entire thing, then break it down and explain it as best I can. A big thanks to misfire for cracking the 4k, 5k, and 6k schemes, and to both misfire and Parasyte for cracking the 7k scheme.

A note: codes encrypted with the 4k scheme will always have a second digit of "4"; those encrypted with the 5k scheme will always have a second digit of "5"; and so forth.

In misfire's source, 4k decryption looks like this:
___________________________

code[1] ^= 0x25;
code[2] ^= (code[1] & 0x11) + 0xFA;
code[3] ^= (code[2] & 0x11) + (code[1] ^ 0x12) - 0x40;
code[4] ^= (code[3] & 0x11) + (code[2] ^ 0x12) - 0x82 + code[1];
code[5] ^= (code[4] & 0x11) + (code[3] ^ 0x12) - 0xDA + code[2] + code[1];
___________________________

Without going into much detail about what's directly before this, I'll just say that code[0], if there had been one explicitly, would be expressed as such:

code[0] ^= code[0] ^ 0x04

...this would be ^ 0x05 for a 5k code, and so on.

Anyway, let me explain what we're looking at.

Obviously, the numbers we're dealing with will be in hexadecimal. If you didn't know this, or don't know how hexadecimal works, drop by the GSHI Library ( http://gshi.org/?s=faqs ), and read the FAQ on hex.

First of all, code[1] refers to the second set of two digits in the code, code[2] refers to the third set of two digits, and so forth. This is because code[0] refers to the first set of two digits.

^, in C, means bitwise XOR, or eXclusive OR. In other instances, it can be used as an exponentiation symbol, so if you've used it in this way, don't be confused. &, in C (and most other languages) means bitwise AND. Bitwise operators in detail are a topic for another day, but for now, to XOR or AND something with something else, all you'll need is the built-in calculator that comes with Windows (or a scientific calculator, if you're not using Windows). For example, to perform "86 XOR 35" in hex, simply open the Windows calc, choose Scientific mode, choose Hex, enter 86, click XOR, enter 35, and click the Equals button.

0x simply means, "in hex". You can pretty much ignore 0x, as long as you remember that we're working in hex here.

code[x] ^= means, "code[x] now equals what code[x] used to equal, XORed by whatever follows". I know this seems like a complicated way of writing that, but it actually saves a lot of programming time, and simplifies code.

Without further ado, let's rewrite this 4k decryption in simpler terms. "00" is code[0], "11" is code[1], and so forth:


00112233 4455 ("00", "11", "22", "33", "44", "55")

"00" = "00" XOR 04
"11" = "11" XOR 25
"22" = "22" XOR (("11" & 11) + FA)
"33" = "33" XOR (("22" & 11) + ("11" XOR 12) - 40)
"44" = "44" XOR (("33" & 11) + ("22" XOR 12) - 82 + "11")
"55" = "55" XOR (("44" & 11) + ("33" XOR 12) - DA + "22" + "11")


Again, "00" would be XORed by 05 if we were decrypting a code encrypted with the 5k scheme, and so forth.

Something important to note is that these operations are dependent on the results of the ones before them (well, except for "00", but a mistake would still leave you with an incorrect code). If you make a mistake XORing "11" with 25, you'll end up with an incorrect "22", "33", "44", and "55", and a completely incorrect code. This is because, for example, once "11" has been XORed with 25, the result is the new value of "11". Everything that references "11" from then on will be referencing the new value.

So, that said, let's look at an example (this just happens to be the one I was asked about, which caused me to dig up misfire's source and implement this decryption utility, heh):


342289E9 E5D9

In this case, 34 is "00", 22 is "11", and so forth. The decryption would look like this (I've added the results of each operation to the far left side; these aren't part of the operation, but are required to understand the operations after them):


30   "00" = 34 XOR 04
07   "11" = 22 XOR 25
72   "22" = 89 XOR ((07 & 11) + FA)
0C   "33" = E9 XOR ((72 & 11) + (07 XOR 12) - 40)
00   "44" = E5 XOR ((0C & 11) + (72 XOR 12) - 82 + 07)
64   "55" = D9 XOR ((00 & 11) + (0C XOR 12) - DA + 72 + 07)

Before we continue, let me add an important note. These operations are to be performed on unsigned, 8-bit numbers. This means, 0-FF, with no such thing as a negative number. What this effectively means is that, if the result of any operation (including sub-operations of each line here) is either more than FF, or less than 0, the result rolls over or under. This means, though you needn't necessarily understand why, if you use the Windows calculator, you should be operating in Byte mode (8 bits make 1 byte). Anyway, if the result of an operation were 104, the proper result would be 04. If it were -A, the proper result would be 09. An easy way to achieve these results, if you're using a calculator that doesn't have Byte mode, would be to XOR your result with FF if it's a negative number, and AND the result with FF if it's over FF, but if you're operating in Byte mode, regardless of your calculator, you don't need, or want, to do that.

OK, moving on, let's perform these operations step by step.

"00" = 34 XOR 04
"00" = 30

30_ _ _ _ _ _    _ _ _ _



"11" = 22 XOR 25
"11" = 07

3007_ _ _ _    _ _ _ _



"22" = 89 XOR ((07 & 11) + FA)
"22" = 89 XOR (01 + FA)
"22" = 89 XOR FB
"22" = 72

300772_ _    _ _ _ _



"33" = E9 XOR ((72 & 11) + (07 XOR 12) - 40)
"33" = E9 XOR (10 + 15 - 40)
"33" = E9 XOR E5
"33" = 0C

3007720C    _ _ _ _



"44" = E5 XOR ((0C & 11) + (72 XOR 12) - 82 + 07)
"44" = E5 XOR (00 + 60 - 82 + 07)
"44" = E5 XOR E5
"44" = 00

3007720C 00_ _



"55" = D9 XOR ((00 & 11) + (0C XOR 12) - DA + 72 + 07)
"55" = D9 XOR (00 + 1E - DA + 72 + 07)
"55" = D9 XOR BD
"55" = 64

3007720C 0064


As you can see, "33", "44", and "55" all involve roll-over or under operations. A calc that can handle Byte mode really comes in handy here, but if you don't have one, at least you know how to perform these operations without one.

The other three schemes are all fairly easy, and this should serve as an effective example, though you'll be roughly inverting the process with the 7k scheme. If you have any questions, feel free to email me at [email protected], drop by GSHI forums or chat, or hell...ask misfire or Parasyte :)

- Lazy Bastard


________________________________________________________________


/*
 * Xploder PSX crypto routines
 *
 * All keys cracked by misfire
 * Some help on 7K encryption by Parasyte
 */

void XPEncrypt(u8 *code, u8 key)
{
	code[0] ^= key;

	switch (key) {
		case 4:
			code[5] ^= (code[4] & 0x11) + (code[3] ^ 0x12) - 0xDA + code[2] + code[1];
			code[4] ^= (code[3] & 0x11) + (code[2] ^ 0x12) - 0x82 + code[1];
			code[3] ^= (code[2] & 0x11) + (code[1] ^ 0x12) - 0x40;
			code[2] ^= (code[1] & 0x11) + 0xFA;
			code[1] ^= 0x25;
			break;
		case 5:
			code[1] -= 0x57;
			code[2] -= 0x42;
			code[3] -= 0x31;
			code[4] -= 0x32;
			code[5] -= 0x33;
			break;
		case 6:
			code[1] = (code[1] ^ 0x01) - 0xAB;
			code[2] = (code[2] ^ 0x02) - 0xAB;
			code[3] = (code[3] ^ 0x03) - 0xAB;
			code[4] = (code[4] ^ 0x04) - 0xAB;
			code[5] = (code[5] ^ 0x05) - 0xAB;
			break;
		case 7:
			code[1] -= (code[2] & 0x73) - (code[3] ^ 0x90) + 0xF5 + code[4] + code[5];
			code[2] -= (code[3] & 0x73) - (code[4] ^ 0x90) + 0x16 + code[5];
			code[3] -= (code[4] & 0x73) - (code[5] ^ 0x90) + 0x5A;
			code[4] -= (code[5] & 0x73) - 0x35;
			code[5] += 0x35;
			break;
	}
}

void XPDecrypt(u8 *code, u8 key)
{
	if (!key) key = code[0] & 0x0F; // Auto process

	code[0] ^= key;

	switch (key) {
		case 4:
			code[1] ^= 0x25;
			code[2] ^= (code[1] & 0x11) + 0xFA;
			code[3] ^= (code[2] & 0x11) + (code[1] ^ 0x12) - 0x40;
			code[4] ^= (code[3] & 0x11) + (code[2] ^ 0x12) - 0x82 + code[1];
			code[5] ^= (code[4] & 0x11) + (code[3] ^ 0x12) - 0xDA + code[2] + code[1];
			break;
		case 5:
			code[1] += 0x57;
			code[2] += 0x42;
			code[3] += 0x31;
			code[4] += 0x32;
			code[5] += 0x33;
			break;
		case 6:
			code[1] = (code[1] + 0xAB) ^ 0x01;
			code[2] = (code[2] + 0xAB) ^ 0x02;
			code[3] = (code[3] + 0xAB) ^ 0x03;
			code[4] = (code[4] + 0xAB) ^ 0x04;
			code[5] = (code[5] + 0xAB) ^ 0x05;
			break;
		case 7:
			code[5] -= 0x35;
			code[4] += (code[5] & 0x73) - 0x35;
			code[3] += (code[4] & 0x73) - (code[5] ^ 0x90) + 0x5A;
			code[2] += (code[3] & 0x73) - (code[4] ^ 0x90) + 0x16 + code[5];
			code[1] += (code[2] & 0x73) - (code[3] ^ 0x90) + 0xF5 + code[4] + code[5];
			break;
	}
}




This text was brought to you by GSHI.org, unless someone else gave it to you, in which case it was only written by someone at GSHI.org. Heheh.