Skip to content

Commit

Permalink
Improved brute-force mode:
Browse files Browse the repository at this point in the history
- Added option to change binary data step;
- Added option to handle data as text or binary.

Fixed hash validation and forging.
  • Loading branch information
Hykem committed May 4, 2014
1 parent 25c42dd commit f496f05
Show file tree
Hide file tree
Showing 6 changed files with 370 additions and 52 deletions.
148 changes: 122 additions & 26 deletions Linux/make_npdata.c
Original file line number Diff line number Diff line change
Expand Up @@ -535,12 +535,26 @@ void validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HE

int file_name_length = strlen(file_name);
unsigned char *buf = (unsigned char *) malloc (0x30 + file_name_length);
unsigned char dev[0x60];
unsigned char key[0x10];
memset(dev, 0, 0x60);
memset(key, 0, 0x10);

// Build the buffer (content_id + file_name).
// Build the title buffer (content_id + file_name).
memcpy(buf, npd->content_id, 0x30);
memcpy(buf + 0x30, file_name, file_name_length);

// Build the dev buffer (first 0x60 bytes of NPD header in big-endian).
memcpy(dev, npd, 0x60);

// Fix endianness.
int version = se32(npd->version);
int license = se32(npd->license);
int type = se32(npd->type);
memcpy(dev + 0x4, &version, 4);
memcpy(dev + 0x8, &license, 4);
memcpy(dev + 0xC, &type, 4);

// Hash with NPDRM_OMAC_KEY_3 and compare with title_hash.
title_hash_result = cmac_hash_compare(NPDRM_OMAC_KEY_3, 0x10, buf, 0x30 + file_name_length, npd->title_hash);

Expand Down Expand Up @@ -575,7 +589,7 @@ void validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HE
xor(key, klicensee, NPDRM_OMAC_KEY_2, 0x10);

// Hash with generated key and compare with dev_hash.
dev_hash_result = cmac_hash_compare(key, 0x10, (unsigned char *)npd, 0x60, npd->dev_hash);
dev_hash_result = cmac_hash_compare(key, 0x10, dev, 0x60, npd->dev_hash);

if (verbose)
{
Expand Down Expand Up @@ -937,7 +951,7 @@ void forge_npd_title_hash(const char* file_name, NPD_HEADER *npd)
unsigned char title_hash[0x10];
memset(title_hash, 0, 0x10);

// Build the buffer (content_id + file_name).
// Build the title buffer (content_id + file_name).
memcpy(buf, npd->content_id, 0x30);
memcpy(buf + 0x30, file_name, file_name_length);

Expand All @@ -953,15 +967,28 @@ void forge_npd_title_hash(const char* file_name, NPD_HEADER *npd)
void forge_npd_dev_hash(unsigned char *klicensee, NPD_HEADER *npd)
{
unsigned char key[0x10];
unsigned char dev[0x60];
unsigned char dev_hash[0x10];
memset(key, 0, 0x10);
memset(dev, 0, 0x60);
memset(dev_hash, 0, 0x10);

// Build the dev buffer (first 0x60 bytes of NPD header in big-endian).
memcpy(dev, npd, 0x60);

// Fix endianness.
int version = se32(npd->version);
int license = se32(npd->license);
int type = se32(npd->type);
memcpy(dev + 0x4, &version, 4);
memcpy(dev + 0x8, &license, 4);
memcpy(dev + 0xC, &type, 4);

// Generate klicensee xor key.
xor(key, klicensee, NPDRM_OMAC_KEY_2, 0x10);

// Forge with the generated key and create the dev hash.
cmac_hash_forge(key, 0x10, (unsigned char *)npd, 0x60, dev_hash);
cmac_hash_forge(key, 0x10, dev, 0x60, dev_hash);

// Write the key in the NPD header.
memcpy(npd->dev_hash, dev_hash, 0x10);
Expand Down Expand Up @@ -1077,27 +1104,29 @@ bool pack_data(FILE *input, FILE *output, const char* input_file_name, unsigned
}

// Write forged NPD header.
int version_le = se32(NPD->version);
int license_le = se32(NPD->license);
int type_le = se32(NPD->type);
int version_be = se32(NPD->version);
int license_be = se32(NPD->license);
int type_be = se32(NPD->type);
u64 unk1_be = se64(NPD->unk1);
u64 unk2_be = se64(NPD->unk2);
fwrite(NPD->magic, sizeof(NPD->magic), 1, output);
fwrite(&version_le, sizeof(version_le), 1, output);
fwrite(&license_le, sizeof(license_le), 1, output);
fwrite(&type_le, sizeof(type_le), 1, output);
fwrite(&version_be, sizeof(version_be), 1, output);
fwrite(&license_be, sizeof(license_be), 1, output);
fwrite(&type_be, sizeof(type_be), 1, output);
fwrite(NPD->content_id, sizeof(NPD->content_id), 1, output);
fwrite(NPD->digest, sizeof(NPD->digest), 1, output);
fwrite(NPD->title_hash, sizeof(NPD->title_hash), 1, output);
fwrite(NPD->dev_hash, sizeof(NPD->dev_hash), 1, output);
fwrite(&NPD->unk1, sizeof(NPD->unk1), 1, output);
fwrite(&NPD->unk2, sizeof(NPD->unk2), 1, output);
fwrite(&unk1_be, sizeof(unk1_be), 1, output);
fwrite(&unk2_be, sizeof(unk2_be), 1, output);

// Write forged EDAT/SDAT header.
int flags_le = se32(EDAT->flags);
int block_size_le = se32(EDAT->block_size);
u64 file_size_le = se64(EDAT->file_size);
fwrite(&flags_le, sizeof(flags_le), 1, output);
fwrite(&block_size_le, sizeof(block_size_le), 1, output);
fwrite(&file_size_le, sizeof(file_size_le), 1, output);
int flags_be = se32(EDAT->flags);
int block_size_be = se32(EDAT->block_size);
u64 file_size_be = se64(EDAT->file_size);
fwrite(&flags_be, sizeof(flags_be), 1, output);
fwrite(&block_size_be, sizeof(block_size_be), 1, output);
fwrite(&file_size_be, sizeof(file_size_be), 1, output);

printf("Encrypting data...\n");
if (encrypt_data(input, output, EDAT, NPD, key, verbose))
Expand Down Expand Up @@ -1128,7 +1157,7 @@ void print_usage()
printf("Usage: make_npdata [-v] -e <input> <output> <version> <license> <type>\n");
printf(" <format> <cID> <klic> <rap>\n");
printf(" make_npdata [-v] -d <input> <output> <klic> <rap>\n");
printf(" make_npdata [-v] -b <input> <source>\n\n");
printf(" make_npdata [-v] -b <input> <source> <step> <mode>\n\n");
printf("- Modes:\n");
printf("[-v]: Verbose mode\n");
printf("[-e]: Encryption mode\n");
Expand All @@ -1143,7 +1172,7 @@ void print_usage()
printf("<license>: 0 - Debug license (SDAT)\n");
printf(" 1 - Network license (not supported)\n");
printf(" 2 - Local license (uses RAP file as key)\n");
printf(" 3 - Free license (uses klic as key\n");
printf(" 3 - Free license (uses klic as key)\n");
printf("<type>: 00 - Common\n");
printf(" 01 - PS2 EDAT\n");
printf(" 20 - PSP Remasters\n");
Expand All @@ -1167,6 +1196,11 @@ void print_usage()
printf("\n");
printf("- Bruteforce mode:\n");
printf("<source>: ELF file source for klic\n");
printf("<step>: 1 - Byte\n");
printf(" 2 - Halfword\n");
printf(" 4 - Word\n");
printf("<mode>: 0 - Binary\n");
printf(" 1 - Text\n");
}

int main(int argc, char **argv)
Expand All @@ -1189,7 +1223,7 @@ int main(int argc, char **argv)
}

// Check which mode we're using (encryption/decryption/bruteforce).
if ((!strcmp(argv[arg_offset + 1], "-e")) && (argc > 9))
if ((!strcmp(argv[arg_offset + 1], "-e")) && (argc > (arg_offset + 9)))
{
// Skip the mode argument.
arg_offset++;
Expand Down Expand Up @@ -1306,7 +1340,7 @@ int main(int argc, char **argv)
fclose(output);
return 0;
}
else if (!strcmp(argv[arg_offset + 1], "-d") && (argc > 5))
else if (!strcmp(argv[arg_offset + 1], "-d") && (argc > (arg_offset + 4)))
{
// Skip the mode argument.
arg_offset++;
Expand Down Expand Up @@ -1403,7 +1437,7 @@ int main(int argc, char **argv)
fclose(output);
return 0;
}
else if (!strcmp(argv[arg_offset + 1], "-b") && (argc > 2))
else if (!strcmp(argv[arg_offset + 1], "-b") && (argc > (arg_offset + 3)))
{
// Skip the mode argument.
arg_offset++;
Expand All @@ -1414,6 +1448,30 @@ int main(int argc, char **argv)
FILE* input = fopen(input_file_name, "rb");
FILE* source = fopen(source_file_name, "rb");

// Use user defined data length (1, 2 or 4).
int step = 4;
if ((argc > (arg_offset + 3)) && (argv[arg_offset + 3] != NULL))
{
step = atoi(argv[arg_offset + 3]);
if ((step != 1) && (step != 2) && (step != 4))
{
printf("ERROR: Invalid parameters!\n");
return 0;
}
}

// Read data as plain binary or as text (hexadecimal).
int mode = 0;
if ((argc > (arg_offset + 4)) && (argv[arg_offset + 4] != NULL))
{
mode = atoi(argv[arg_offset + 4]);
if ((mode != 0) && (mode != 1))
{
printf("ERROR: Invalid parameters!\n");
return 0;
}
}

// Get the source file size.
fseek(source, 0, SEEK_END);
int source_file_size = ftell(source);
Expand All @@ -1427,6 +1485,34 @@ int main(int argc, char **argv)
memset(test_klicensee, 0, 0x10);
memset(test_dev_hash, 0, 0x10);

// Buffer to handle klicensee as text.
char test_klicensee_text[0x21];
memset(test_klicensee_text, 0, 0x21);

// Read the file's header magic and seek back.
unsigned char magic[0x4];
fread(magic, 0x4, 1, input);
fseek(input, 0, SEEK_SET);

// If header starts with SCE, the file is a SELF or SPRX.
// If not, assume regular EDAT/SDAT (NPD).
unsigned char sce_magic[4] = {0x53, 0x43, 0x45, 0x00}; //SCE0
if(!memcmp(magic, sce_magic, 4))
{
// File is SCE, read the NPD dev_hash offset from the
// first 0x10 bytes of the SCE header and seek to the NPD area.
unsigned char sce_header[0x10];
fread(sce_header, 0x10, 1, input);
short npd_offset = se16(*(short*)&sce_header[0xE]) - 0x60;
fseek(input, npd_offset, SEEK_SET);

if (verbose)
{
printf("SCE file detected!\n");
printf("NPD offset inside SCE: 0x%08x\n", npd_offset);
}
}

// Read the first 0x60 bytes of the NPD header.
unsigned char npd_buf[0x60];
fread(npd_buf, 0x60, 1, input);
Expand All @@ -1444,14 +1530,24 @@ int main(int argc, char **argv)
}

printf("Bruteforcing klic...\n");

int i;
bool found = false;
for (i = 0; i < source_file_size; i += 4)
for (i = 0; i < source_file_size; i += step)
{
// Iterate the source file and generate klicensee xor key.
fseek(source, i, SEEK_SET);
fread(test_klicensee, 0x10, 1, source);

// If reading in text mode, convert the hexadecimal string to binary data.
if (mode)
{
fread(test_klicensee_text, 0x21, 1, source);
hex_to_bytes(test_klicensee, test_klicensee_text);
memset(test_klicensee_text, 0, 0x21);
}
else
fread(test_klicensee, 0x10, 1, source);

xor(test_key, test_klicensee, NPDRM_OMAC_KEY_2, 0x10);

if (cmac_hash_compare(test_key, 0x10, npd_buf, 0x60, test_dev_hash))
Expand Down
55 changes: 55 additions & 0 deletions Linux/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
#include "utils.h"

// Auxiliary functions (endian swap and xor).
short se16(short i)
{
return (((i & 0xFF00) >> 8) | ((i & 0xFF) << 8));
}

int se32(int i)
{
return ((i & 0xFF000000) >> 24) | ((i & 0xFF0000) >> 8) | ((i & 0xFF00) << 8) | ((i & 0xFF) << 24);
Expand All @@ -27,6 +32,56 @@ void xor(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size
}
}

// Hex string conversion auxiliary functions.
u64 hex_to_u64(const char* hex_str)
{
u32 length = strlen(hex_str);
u64 tmp = 0;
u64 result = 0;
char c;

while (length--)
{
c = *hex_str++;
if((c >= '0') && (c <= '9'))
tmp = c - '0';
else if((c >= 'a') && (c <= 'f'))
tmp = c - 'a' + 10;
else if((c >= 'A') && (c <= 'F'))
tmp = c - 'A' + 10;
else
tmp = 0;
result |= (tmp << (length * 4));
}

return result;
}

void hex_to_bytes(unsigned char *data, const char *hex_str)
{
u32 str_length = strlen(hex_str);
u32 data_length = str_length / 2;
char tmp_buf[3] = {0, 0, 0};

// Don't convert if the string length is odd.
if (!(str_length % 2))
{
u8 *out = (u8 *) malloc (str_length * sizeof(u8));
u8 *pos = out;

while (str_length--)
{
tmp_buf[0] = *hex_str++;
tmp_buf[1] = *hex_str++;

*pos++ = (u8)(hex_to_u64(tmp_buf) & 0xFF);
}

// Copy back to our array.
memcpy(data, out, data_length);
}
}

// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, int len)
{
Expand Down
8 changes: 8 additions & 0 deletions Linux/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,20 @@
#include "lz.h"

typedef unsigned long long u64;
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;

// Auxiliary functions (endian swap and xor).
short se16(short i);
int se32(int i);
u64 se64(u64 i);
void xor(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size);

// Hex string conversion auxiliary functions.
u64 hex_to_u64(const char* hex_str);
void hex_to_bytes(unsigned char *data, const char *hex_str);

// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, int len);
void aescbc128_encrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, int len);
Expand Down
Loading

0 comments on commit f496f05

Please sign in to comment.