hostile-takeover/drmutil/main.cpp
2014-07-06 17:47:28 -07:00

499 lines
9.6 KiB
C++

// DRM utility
// Spiffcode Confidential
// Copyright 2003 Spiffcode, Inc.
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <winsock2.h>
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;
// The code is derived from the Owner name. The Owner name is backed up and is the same
// after a cold boot; that's why we use it. The Code is simply a non-obvious binary
// translation of the name into a form that can be validated.
struct Code // code
{
byte ab[4];
byte bXor;
byte bSum;
byte abHash[2];
};
// ESD keys gets generated via one way translation from the code
// Retail keys are self-validating and don't require a code
struct Key // key
{
byte ab[9];
};
dword HashBytes(byte *pb, int cb)
{
dword dwHash = 0;
while (cb-- != 0)
dwHash = (dwHash << 5) + dwHash + *pb++;
return dwHash;
}
// Returns a pseudo-random number 0 through 32767.
long glSeed;
int GetRandom()
{
return ((glSeed = glSeed * 214013L + 2531011L) >> 16) & 0x7fff;
}
void SetRandomSeed(unsigned long nSeed)
{
glSeed = (long)nSeed;
}
bool GetCode(char *szName, Code *pcode)
{
if (szName == NULL || strlen(szName) == 0)
return false;
SetRandomSeed((long)HashBytes((byte *)szName, (int)strlen(szName)));
// Serialize in endian independent way
Code code;
memset(&code, 0, sizeof(code));
int n;
for (n = 0; n < 4; n++) {
code.ab[n] = (byte)GetRandom();
code.bXor ^= code.ab[n];
code.bSum += code.ab[n];
}
// Hash what we have so far - more validation
word wHash = (word)(HashBytes(code.ab, 6) >> 16);
code.abHash[0] = (byte)wHash;
code.abHash[1] = (byte)(wHash >> 8);
// Now xor the first 6 with abHash[0]
byte *pb = (byte *)&code;
for (n = 0; n < 6; n++)
*pb++ ^= code.abHash[0];
// Now xor the first 7 with abHash[1]
pb = (byte *)&code;
for (n = 0; n < 7; n++)
*pb++ ^= code.abHash[1];
// Done
*pcode = code;
return true;
}
bool ValidateCode(Code *pcode)
{
Code code = *pcode;
// Now xor the first 7 with abHash[1]
byte *pb = (byte *)&code;
int n;
for (n = 0; n < 7; n++)
*pb++ ^= code.abHash[1];
// Now xor the first 6 with abHash[0]
pb = (byte *)&code;
for (n = 0; n < 6; n++)
*pb++ ^= code.abHash[0];
// Hash the first 6 and compare to our hash
word wHash = (word)(HashBytes(code.ab, 6) >> 16);
if (code.abHash[0] != (byte)wHash)
return false;
if (code.abHash[1] != (byte)(wHash >> 8))
return false;
// Xor and add the first 4
byte bXor = 0;
byte bSum = 0;
for (n = 0; n < 4; n++) {
bXor ^= code.ab[n];
bSum += code.ab[n];
}
if (bXor != code.bXor)
return false;
if (bSum != code.bSum)
return false;
return true;
}
void GetKeyFromCode(Code *pcode, Key *pkey)
{
byte *abCode = (byte *)pcode;
int cbCode = sizeof(*pcode);
// Generate a seed
long lSeed = 0x29a;
int n;
for (n = 0; n < cbCode; n++)
lSeed = lSeed * abCode[n] + n;
// Initialize rotors
byte abR1[256];
byte abR2[256];
byte abR3[256];
for (n = 0; n < 256; n++)
abR1[n] = n;
memset(abR2, 0, sizeof(abR2));
memset(abR3, 0, sizeof(abR3));
for (n = 0; n < 256; n++) {
lSeed = 3 * lSeed + abCode[n & 7];
long lRandom = lSeed % 65521;
int ibR1 = (lRandom & 255) % (256 - n);
lRandom >>= 8;
byte bTemp = abR1[255 - n];
abR1[255 - n] = abR1[ibR1];
abR1[ibR1] = bTemp;
if (abR3[255 - n] == 0) {
int nT = (lRandom & 255) % (255 - n);
while (abR3[nT] != 0)
nT = (nT + 1) % (255 - n);
abR3[255 - n] = nT & 255;
abR3[nT] = 255 - n;
}
}
for (n = 0; n < 256; n++)
abR2[abR1[n]] = n;
// Encrypt the code into a key
int nT = 0;
byte *abKey = (byte *)pkey;
int cbKey = sizeof(*pkey);
for (n = 0; n < cbKey; n++) {
int nSlotR1 = abR1[(n + nT) & 255];
int nSlotR3 = abR3[nSlotR1 & 255];
int nSlotR2 = abR2[nSlotR3 & 255];
abKey[n] = abR2[nSlotR2 & 255] - nT;
nT = (nT + 1) & 255;
}
}
void FillTemplate(char *psz, char *pszFormat, byte *pb)
{
char *pchSrc = pszFormat;
char *pchDst = psz;
bool fMSB = true;
byte bT = 0;
int cb = (int)strlen(pszFormat) + 1;
while (cb-- != 0) {
if (*pchSrc != '?') {
*pchDst++ = *pchSrc++;
continue;
}
int nNibble = 0;
if (fMSB) {
fMSB = false;
bT = *pb++;
nNibble = (bT >> 4) & 0x0f;
} else {
fMSB = true;
nNibble = (bT & 0x0f);
}
char chT;
if (nNibble < 0x0a) {
chT = '0' + nNibble;
} else {
chT = 'A' + nNibble - 0x0a;
}
*pchDst++ = chT;
pchSrc++;
}
}
bool ParseCode(char *psz, Code *pcode)
{
int an[4];
int c = sscanf(psz, "C-%04x-%04x-%04x-%04x", &an[0], &an[1], &an[2], &an[3]);
if (c != 4)
return false;
byte *pb = (byte *)pcode;
for (int i = 0; i < 4; i++) {
for (int j = 1; j >= 0; j--) {
*pb++ = (byte)(an[i] >> (j * 8)) & 0xff;
}
}
return true;
}
bool ParseKey(char *psz, Key *pkey)
{
int an[3];
int c = sscanf(psz, "K-%06x-%06x-%06x", &an[0], &an[1], &an[2]);
if (c != 3)
return false;
byte *pb = (byte *)pkey;
for (int i = 0; i < 3; i++) {
for (int j = 2; j >= 0; j--) {
*pb++ = (byte)(an[i] >> (j * 8)) & 0xff;
}
}
return true;
}
void Usage()
{
printf("\n");
printf("Usage:\n");
printf("\n");
printf("drmutil key code\t\tGenerate a key given a code\n");
printf("drmutil name \"name\"\t\tGenerate code and key given a name\n");
printf("drmutil valid code\t\tCheck if a code is valid\n");
printf("drmutil retailkey number\tGenerate retail key. Number range 101-16777215\n");
printf("drmutil retailkeyvalid key\tCheck if a retail key is valid\n");
printf("drmutil pc\t\t\tPrint codes for this desktop PC\n");
printf("\n");
printf("Code format:\tC-XXXX-XXXX-XXXX-XXXX\n");
printf("Key format:\tK-XXXXXX-XXXXXX-XXXXXX\n");
printf("'X' is a digit 0-9,A-F\n");
exit(1);
}
void OutputCodeAndKey(char *sz)
{
// Gen / output code
Code code;
if (!GetCode(sz, &code)) {
printf("Can't generate code; invalid name!\n");
Usage();
}
printf("Code and key for \"%s\":\n", sz);
FillTemplate(sz, "C-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?", (byte *)&code);
printf("%s\n", sz);
// Gen / output key
Key key;
GetKeyFromCode(&code, &key);
FillTemplate(sz, "K-\?\?\?\?\?\?-\?\?\?\?\?\?-\?\?\?\?\?\?", (byte *)&key);
printf("%s\n", sz);
}
void GetRetailKey(int nNumber, Key *pkey)
{
SetRandomSeed(nNumber);
char szT[4];
szT[0] = (byte)GetRandom();
szT[1] = (byte)GetRandom();
szT[2] = (byte)GetRandom();
szT[3] = 0;
// Ensure none of these have zeros in them
for (int n = 0; n < sizeof(szT) - 1; n++) {
while (szT[n] == 0)
szT[n] = (byte)GetRandom();
}
// Get code and key
Code code;
GetCode(szT, &code);
Key key;
GetKeyFromCode(&code, &key);
// Now fill in the first 3 bytes with the encoded "name"
key.ab[0] = szT[0];
SetRandomSeed((dword)szT[0]);
key.ab[1] = szT[1] ^ (byte)GetRandom();
key.ab[2] = szT[2] ^ (byte)GetRandom();
*pkey = key;
}
void OutputRetailKey(char *sz)
{
// Number can be between 101 and 2^24-1 (3 bytes)
int nNumber = atoi(sz);
if (nNumber < 101 || nNumber > 16777215) {
printf("Seed number is invalid\n");
Usage();
}
// Generate retail key
Key key;
GetRetailKey(nNumber, &key);
// Output
printf("Seed: %d\n", nNumber);
char szKey[64];
FillTemplate(szKey, "K-\?\?\?\?\?\?-\?\?\?\?\?\?-\?\?\?\?\?\?", (byte *)&key);
printf("%s\n", szKey);
}
bool CompareKeys(Key *pkeyA, Key *pkeyB)
{
byte *pbA = (byte *)pkeyA;
byte *pbB = (byte *)pkeyB;
int cb = sizeof(*pkeyA);
while (cb-- != 0) {
if (*pbA++ != *pbB++)
return false;
}
return true;
}
bool ValidateRetailKey(Key *pkey)
{
char szT[4];
// Derive the "name" from the key
szT[0] = pkey->ab[0];
SetRandomSeed((dword)szT[0]);
szT[1] = pkey->ab[1] ^ (byte)GetRandom();
szT[2] = pkey->ab[2] ^ (byte)GetRandom();
szT[3] = 0;
// Fill in zeros
int n;
for (n = 0; n < sizeof(szT) - 1; n++) {
while (szT[n] == 0)
szT[n] = (byte)GetRandom();
}
// Get code
Code code;
GetCode(szT, &code);
// Gen full key from this
Key keyT;
GetKeyFromCode(&code, &keyT);
// Compare keys - compares the last 6 bytes because we copy over the first 3
Key keyReconstructed = *pkey;
for (n = 0; n < sizeof(szT) - 1; n++)
keyReconstructed.ab[n] = keyT.ab[n];
// Now compare these two keys
return CompareKeys(&keyT, &keyReconstructed);
}
int main(int argc, char **argv)
{
char *pszCommand = argv[1];
char *pszArg = argv[2];
if (argc == 2) {
if (strcmp(pszCommand, "pc") == 0) {
char sz[64];
sz[0] = 0;
// Get host name
WSADATA wsad;
WSAStartup(MAKEWORD(1, 1), &wsad);
gethostname(sz, sizeof(sz) - 1);
WSACleanup();
OutputCodeAndKey(sz);
return 0;
}
Usage();
}
if (argc == 3) {
if (strcmp(pszCommand, "key") == 0) {
Code code;
if (!ParseCode(pszArg, &code)) {
printf("Code entered in incorrect format!\n");
Usage();
}
if (!ValidateCode(&code)) {
printf("This code is invalid. It fails internal consistency check\n");
Usage();
}
Key key;
GetKeyFromCode(&code, &key);
char szKey[64];
FillTemplate(szKey, "K-\?\?\?\?\?\?-\?\?\?\?\?\?-\?\?\?\?\?\?", (byte *)&key);
printf("%s\n", szKey);
return 0;
}
if (strcmp(pszCommand, "name") == 0) {
OutputCodeAndKey(pszArg);
return 0;
}
if (strcmp(pszCommand, "valid") == 0) {
Code code;
if (!ParseCode(pszArg, &code)) {
printf("Code entered in incorrect format!\n");
Usage();
}
if (!ValidateCode(&code)) {
printf("Invalid code\n");
return 1;
}
printf("Valid code\n");
return 0;
}
if (strcmp(pszCommand, "retailkey") == 0) {
OutputRetailKey(pszArg);
return 0;
}
if (strcmp(pszCommand, "retailkeyvalid") == 0) {
Key key;
if (!ParseKey(pszArg, &key)) {
printf("Key entered in incorrect format!\n");
Usage();
}
if (!ValidateRetailKey(&key)) {
printf("Retail key invalid.\n");
} else {
printf("Retail key valid.\n");
}
return 0;
}
}
Usage();
}