From 0d4c515ed86d48affb65f427c8b2c7eb26859fa6 Mon Sep 17 00:00:00 2001 From: euss <7unicorn@live.nl> Date: Wed, 12 Oct 2011 15:59:40 +0200 Subject: [PATCH] np --- Makefile.am | 2 + README_npdrm | 35 +++++++++ readself.c | 22 ++---- readself2.c | 13 ++-- self.c | 78 ++++++++++++++++++- self.h | 16 +++- tools.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools.h | 12 ++- types.h | 23 ++++++ unself.c | 6 ++ unself2.c | 2 +- 11 files changed, 390 insertions(+), 26 deletions(-) create mode 100644 README_npdrm diff --git a/Makefile.am b/Makefile.am index 16dc5d0..08e9e8b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,6 +46,8 @@ bin_PROGRAMS = sceverify \ eidsplitr \ spp +bin_SCRIPTS = pupexplode + if HAVE_LIBGMP bin_PROGRAMS += scekrit endif diff --git a/README_npdrm b/README_npdrm new file mode 100644 index 0000000..e595fa1 --- /dev/null +++ b/README_npdrm @@ -0,0 +1,35 @@ +Based off of gitbrew's 215d8903bc86539ca1da53519e2ac10eeafc4c27 +ps3tools. .git folder not included to protect senstive info about the author. +Sorry about the fucked up tabs, TAB = 4 SPACES 4 LYFE!!! +Add the files in the npdrm_keystuff folder to your ps3 keys folder. Create a +file with your console's 16 byte IDPS in the 'idps' file in your ps3 keys +folder (e.g. ~/.ps3/idps). +Copy your PS3's exdata folder containing your act.dat and rif files to your +ps3 keys dir (e.g. ~/.ps3/exdata/act.dat). Compile and have fun with your +LEGALLY purchased NPDRM games! + +Also works on free games without exdata/idps. + +Apologies for any existing bugs in unself. Adding these changes to +unself2 is left as an exercise for the reader. + +v2 info: +Added npdrm magic to unself2 and readself2 +unself2 doesn't like the metadata section with type 3 in my game. +This section looks to be some kind of linking information. Here is a snippet: +crt0:p190002crt1:p190002libaudio_stub:p190002libaudio_stub:p190002 + +I haven't yet looked around to see if this is type of section is documented anywhere. + + + +Thanks: +fail0verflow for the orginal tools +JuanNadie for figuring out NPDRM +euss for his endless hours of work on the wiki + + +Why didn't you beat me to the punch, Team PS360? I didn't need to reverse a +single instruction! It isn't that hard... + + diff --git a/readself.c b/readself.c index faa4ede..ed0f68e 100644 --- a/readself.c +++ b/readself.c @@ -142,8 +142,8 @@ static void parse_self(void) static struct keylist *self_load_keys(void) { enum sce_key id; - - switch (app_type) { + + switch (app_type) { case 1: id = KEY_LV0; break; @@ -163,7 +163,7 @@ static struct keylist *self_load_keys(void) id = KEY_LDR; break; case 8: - return NULL; + id = KEY_NPDRM; break; default: fail("invalid type: %08x", app_type); @@ -180,7 +180,8 @@ static void decrypt_header(void) if (klist == NULL) return; - decrypted = sce_decrypt_header(self, klist); + sce_remove_npdrm(self, klist); + decrypted = sce_decrypt_header(self, klist); free(klist->keys); free(klist); } @@ -217,13 +218,6 @@ static void show_self_header(void) printf("\n"); } -static void print_hash(u8 *ptr, u32 len) -{ - while(len--) - printf(" %02x", *ptr++); -} - - static void show_ctrl(void) { u32 i, j; @@ -365,14 +359,14 @@ static void show_meta(void) printf("\n"); printf(" Sections\n"); - printf(" Offset Length Key IV SHA1\n"); + printf(" Offset Length Key IV SHA1 Type\n"); for (i = 0; i < meta_n_hdr; i++) { tmp = self + meta_offset + 0x80 + 0x30*i; offset = be64(tmp); size = be64(tmp + 8); - printf(" %08x_%08x %08x_%08x %03d %03d %03d\n", + printf(" %08x_%08x %08x_%08x %03d %03d %03d %4d\n", (u32)(offset >> 32), (u32)offset, (u32)(size >> 32), (u32)size, - be32(tmp + 0x24), be32(tmp + 0x28), be32(tmp + 0x1c)); + be32(tmp + 0x24), be32(tmp + 0x28), be32(tmp + 0x1c), be32(tmp + 0x10)); } printf("\n"); diff --git a/readself2.c b/readself2.c index a3bbb6e..6869cf8 100644 --- a/readself2.c +++ b/readself2.c @@ -182,7 +182,7 @@ static struct keylist *self_load_keys(void) return NULL; break; case 8: - return NULL; + id = KEY_NPDRM; break; default: fail("invalid type: %08x", app_type); @@ -196,12 +196,6 @@ static const char *get_auth_type(void) return "UnknownAuthIdType"; } -static void print_hash(u8 *ptr, u32 len) -{ - while(len--) - printf(" %02x", *ptr++); -} - static void get_flags(u32 flags, char *ptr) { memset(ptr, '-', 3); @@ -268,6 +262,7 @@ static void decrypt_header(void) if (klist == NULL) return; + sce_remove_npdrm(self, klist); decrypted = sce_decrypt_header(self, klist); free(klist->keys); free(klist); @@ -988,6 +983,10 @@ static void show_meta(void) (u16)((meta_offset + 0x80 + 0x30*i) + 0x1c), be32(tmp + 0x1c) ); + printf(" Type: %04x = %04x\n", + (u16)((meta_offset + 0x80 + 0x30*i) + 0x10), + be32(tmp + 0x10) + ); } printf("\n"); diff --git a/self.c b/self.c index 94b7b1e..a7b100a 100644 --- a/self.c +++ b/self.c @@ -13,6 +13,12 @@ static struct keylist *load_keys(APP_INFO *app_info); static int decrypt_metadata(uint8_t *metadata, uint32_t metadata_size, struct keylist *klist); +static int remove_npdrm(SELF *self, CONTROL_INFO *control_info, uint8_t *metadata, + struct keylist *klist); +static void decrypt_npdrm(uint8_t *metadata, struct keylist *klist, + struct key *klicensee); + + void @@ -166,6 +172,9 @@ self_read_headers(FILE *in, SELF *self, APP_INFO *app_info, ELF *elf, if (info[index].type == 1) info[index].control_flags.control_flags = swap64 (info[index].control_flags.control_flags); + if (info[index].type == 3) + info[index].npdrm.license_type = + swap32 (info[index].npdrm.license_type); offset += info[index].size; index++; @@ -219,7 +228,7 @@ void self_read_metadata (FILE *in, SELF *self, APP_INFO *app_info, METADATA_INFO *metadata_info, METADATA_HEADER *metadata_header, METADATA_SECTION_HEADER **section_headers, uint8_t **keys, - SIGNATURE_INFO *signature_info, SIGNATURE *signature) + SIGNATURE_INFO *signature_info, SIGNATURE *signature, CONTROL_INFO *control_info) { uint8_t *metadata = NULL; uint32_t metadata_size = self->header_len - self->metadata_offset - 0x20; @@ -240,6 +249,9 @@ self_read_metadata (FILE *in, SELF *self, APP_INFO *app_info, if (klist == NULL) ERROR(-5, "no key found"); + if (remove_npdrm (self, control_info, metadata, klist) < 0) + ERROR (-5, "Error removing NPDRM"); + if (decrypt_metadata (metadata, metadata_size, klist) < 0) ERROR (-5, "Error decrypting metadata"); } @@ -405,6 +417,9 @@ load_keys(APP_INFO *app_info) case 6: id = KEY_LDR; break; + case 8: + id = KEY_NPDRM; + break; default: fprintf (stderr, "SELF type is invalid : 0x%08X\n", app_info->self_type); exit (-4); @@ -413,6 +428,66 @@ load_keys(APP_INFO *app_info) return keys_get(id); } +static int +remove_npdrm(SELF *self, CONTROL_INFO *control_info, uint8_t *metadata, struct keylist *klist) +{ + CONTROL_INFO *info; + u32 license_type; + char content_id[0x31] = {'\0'}; + struct rif *rif; + struct actdat *actdat; + u8 enc_const[0x10]; + u8 dec_actdat[0x10]; + struct key klicensee; + int i; + u64 off; + + for (i = off = 0; off < self->controlinfo_size; i++) { + info = &control_info[i]; + if (info->type == 3) { + license_type = info->npdrm.license_type; + switch (license_type) { + case 1: + // cant decrypt network stuff + return -1; + case 2: + memcpy(content_id, info->npdrm.content_id, 0x30); + rif = rif_get(content_id); + if (rif == NULL) { + return -1; + } + aes128(klist->rif->key, rif->padding, rif->padding); + aes128_enc(klist->idps->key, klist->npdrm_const->key, enc_const); + actdat = actdat_get(); + if (actdat == NULL) { + return -1; + } + aes128(enc_const, &actdat->keyTable[swap32(rif->actDatIndex)*0x10], dec_actdat); + aes128(dec_actdat, rif->key, klicensee.key); + decrypt_npdrm(metadata, klist, &klicensee); + return 1; + case 3: + decrypt_npdrm(metadata, klist, klist->free_klicensee); + return 1; + } + } + + off += info->size; + } + return 0; +} + +static void +decrypt_npdrm(uint8_t *metadata, struct keylist *klist, struct key *klicensee) +{ + struct key d_klic; + + // iv is 0 + memset(&d_klic, 0, sizeof(struct key)); + aes128(klist->klic->key, klicensee->key, d_klic.key); + + aes128cbc(d_klic.key, d_klic.iv, metadata, 0x40, metadata); +} static int decrypt_metadata(uint8_t *metadata, uint32_t metadata_size, @@ -422,7 +497,6 @@ decrypt_metadata(uint8_t *metadata, uint32_t metadata_size, METADATA_INFO metadata_info; uint8_t zero[16] = {0}; - memset (zero, 0, 16); for (i = 0; i < klist->n; i++) { aes256cbc(klist->keys[i].key, klist->keys[i].iv, metadata, sizeof(METADATA_INFO), (uint8_t *) &metadata_info); diff --git a/self.h b/self.h index c62d890..ce706f7 100644 --- a/self.h +++ b/self.h @@ -113,6 +113,20 @@ typedef struct { uint8_t digest2[20]; uint8_t padding[8]; } file_digest; + + struct { + uint32_t unknown1; + uint32_t unknown2; + uint32_t magic; + uint32_t unknown3; + uint32_t license_type; + uint32_t type; + uint8_t content_id[0x30]; + uint8_t hash[0x10]; + uint8_t hash_iv[0x10]; + uint8_t hash_xor[0x10]; + uint8_t padding[0x10]; + } npdrm; }; } __attribute__((packed)) CONTROL_INFO; @@ -186,7 +200,7 @@ void self_read_headers(FILE *in, SELF *self, APP_INFO *app_info, ELF *elf, void self_read_metadata (FILE *in, SELF *self, APP_INFO *app_info, METADATA_INFO *metadata_info, METADATA_HEADER *metadata_header, METADATA_SECTION_HEADER **section_headers, uint8_t **keys, - SIGNATURE_INFO *signature_info, SIGNATURE *signature); + SIGNATURE_INFO *signature_info, SIGNATURE *signature, CONTROL_INFO *control_info); int self_load_sections (FILE *in, SELF *self, ELF *elf, ELF_PHDR **phdr, METADATA_HEADER *metadata_header, METADATA_SECTION_HEADER **section_headers, diff --git a/tools.c b/tools.c index 2fe3c8f..15c0708 100644 --- a/tools.c +++ b/tools.c @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef WIN32 #include "mingw_mmap.h" @@ -25,6 +26,7 @@ #include "tools.h" #include "aes.h" #include "sha1.h" +#include "common.h" static struct id2name_tbl t_key2file[] = { {KEY_LV0, "lv0"}, @@ -35,6 +37,7 @@ static struct id2name_tbl t_key2file[] = { {KEY_LDR, "ldr"}, {KEY_PKG, "pkg"}, {KEY_SPP, "spp"}, + {KEY_NPDRM, "app"}, {0, NULL} }; @@ -42,6 +45,12 @@ static struct id2name_tbl t_key2file[] = { // misc // +void print_hash(u8 *ptr, u32 len) +{ + while(len--) + printf(" %02x", *ptr++); +} + void *mmap_file(const char *path) { int fd; @@ -405,6 +414,21 @@ void aes128ctr(u8 *key, u8 *iv, u8 *in, u64 len, u8 *out) } } +void aes128(u8 *key, const u8 *in, u8 *out) { + AES_KEY k; + + assert(!AES_set_decrypt_key(key, 128, &k)); + AES_decrypt(in, out, &k); +} + +void aes128_enc(u8 *key, const u8 *in, u8 *out) { + AES_KEY k; + + assert(!AES_set_encrypt_key(key, 128, &k)); + AES_encrypt(in, out, &k); +} + + // FIXME: use a non-broken sha1.c *sigh* static void sha1_fixup(struct SHA1Context *ctx, u8 *digest) @@ -588,6 +612,48 @@ struct keylist *keys_get(enum sce_key type) } } + if (type == KEY_NPDRM) { + klist->idps = calloc(sizeof(struct key), 1); + if (klist->idps == NULL) + goto fail; + snprintf(path, sizeof path, "%s/idps", base); + if (key_read(path, 16, klist->idps->key) != 0) { + printf(" key file: %s (ERROR)\n", path); + } + + klist->klic = calloc(sizeof(struct key), 1); + if (klist->klic == NULL) + goto fail; + snprintf(path, sizeof path, "%s/klic-key", base); + if (key_read(path, 16, klist->klic->key) != 0) { + printf(" key file: %s (ERROR)\n", path); + } + + klist->rif = calloc(sizeof(struct key), 1); + if (klist->rif == NULL) + goto fail; + snprintf(path, sizeof path, "%s/rif-key", base); + if (key_read(path, 16, klist->rif->key) != 0) { + printf(" key file: %s (ERROR)\n", path); + } + + klist->npdrm_const = calloc(sizeof(struct key), 1); + if (klist->npdrm_const == NULL) + goto fail; + snprintf(path, sizeof path, "%s/npdrm-const", base); + if (key_read(path, 16, klist->npdrm_const->key) != 0) { + printf(" key file: %s (ERROR)\n", path); + } + + klist->free_klicensee = calloc(sizeof(struct key), 1); + if (klist->free_klicensee == NULL) + goto fail; + snprintf(path, sizeof path, "%s/free_klicensee-key", base); + if (key_read(path, 16, klist->free_klicensee->key) != 0) { + printf(" key file: %s (ERROR)\n", path); + } + } + return klist; fail: @@ -692,6 +758,76 @@ int key_get(enum sce_key type, const char *suffix, struct key *k) return 0; } +struct rif *rif_get(const char *content_id) { + char base[256]; + char path[256]; + struct rif *rif; + FILE *fp; + u32 read; + + rif = malloc(sizeof(struct rif)); + if (rif == NULL) + goto fail; + + if (key_build_path(base) < 0) + goto fail; + + snprintf(path, sizeof path, "%s/exdata/%s.rif", base, content_id); + + fp = fopen(path, "rb"); + if (fp == NULL) + goto fail; + + read = fread(rif, sizeof(struct rif), 1, fp); + + if (read != 1) + goto fail; + + return rif; + +fail: + if (rif != NULL) { + free(rif); + } + + return NULL; +} + +struct actdat *actdat_get(void) { + char base[256]; + char path[256]; + struct actdat *actdat; + FILE *fp; + u32 read; + + actdat = malloc(sizeof(struct actdat)); + if (actdat == NULL) + goto fail; + + if (key_build_path(base) < 0) + goto fail; + + snprintf(path, sizeof path, "%s/exdata/act.dat", base); + + fp = fopen(path, "rb"); + if (fp == NULL) + goto fail; + + read = fread(actdat, sizeof(struct actdat), 1, fp); + + if (read != 1) + goto fail; + + return actdat; + +fail: + if (actdat != NULL) { + free(actdat); + } + + return NULL; +} + static void memcpy_inv(u8 *dst, u8 *src, u32 len) { u32 j; @@ -729,6 +865,77 @@ int ecdsa_get_params(u32 type, u8 *p, u8 *a, u8 *b, u8 *N, u8 *Gx, u8 *Gy) return 0; } +int sce_remove_npdrm(u8 *ptr, struct keylist *klist) +{ + u64 ctrl_offset; + u64 ctrl_size; + u32 block_type; + u32 block_size; + u32 license_type; + char content_id[0x31] = {'\0'}; + struct rif *rif; + struct actdat *actdat; + u8 enc_const[0x10]; + u8 dec_actdat[0x10]; + struct key klicensee; + u64 i; + + ctrl_offset = be64(ptr + 0x58); + ctrl_size = be64(ptr + 0x60); + + for (i = 0; i < ctrl_size; ) { + block_type = be32(ptr + ctrl_offset + i); + block_size = be32(ptr + ctrl_offset + i + 0x4); + + if (block_type == 3) { + license_type = be32(ptr + ctrl_offset + i + 0x18); + switch (license_type) { + case 1: + // cant decrypt network stuff + return -1; + case 2: + memcpy(content_id, ptr + ctrl_offset + i + 0x20, 0x30); + rif = rif_get(content_id); + if (rif == NULL) { + return -1; + } + aes128(klist->rif->key, rif->padding, rif->padding); + aes128_enc(klist->idps->key, klist->npdrm_const->key, enc_const); + actdat = actdat_get(); + if (actdat == NULL) { + return -1; + } + aes128(enc_const, &actdat->keyTable[swap32(rif->actDatIndex)*0x10], dec_actdat); + aes128(dec_actdat, rif->key, klicensee.key); + sce_decrypt_npdrm(ptr, klist, &klicensee); + return 1; + case 3: + sce_decrypt_npdrm(ptr, klist, klist->free_klicensee); + return 1; + } + } + + i += block_size; + } + + return 0; +} + +void sce_decrypt_npdrm(u8 *ptr, struct keylist *klist, struct key *klicensee) +{ + u32 meta_offset; + struct key d_klic; + + meta_offset = be32(ptr + 0x0c); + + // iv is 0 + memset(&d_klic, 0, sizeof(struct key)); + aes128(klist->klic->key, klicensee->key, d_klic.key); + + aes128cbc(d_klic.key, d_klic.iv, ptr + meta_offset + 0x20, 0x40, ptr + meta_offset + 0x20); +} + + int sce_decrypt_header(u8 *ptr, struct keylist *klist) { u32 meta_offset; diff --git a/tools.h b/tools.h index 4eb6d30..5d99f39 100644 --- a/tools.h +++ b/tools.h @@ -17,9 +17,11 @@ enum sce_key { KEY_ISO, KEY_LDR, KEY_PKG, - KEY_SPP + KEY_SPP, + KEY_NPDRM }; +void print_hash(u8 *ptr, u32 len); void *mmap_file(const char *path); void memcpy_to_file(const char *fname, u8 *ptr, u64 size); const char *id2name(u32 id, struct id2name_tbl *t, const char *unk); @@ -37,6 +39,8 @@ void aes256cbc_enc(u8 *key, u8 *iv, u8 *in, u64 len, u8 *out); void aes128ctr(u8 *key, u8 *iv, u8 *in, u64 len, u8 *out); void aes128cbc(u8 *key, u8 *iv_in, u8 *in, u64 len, u8 *out); void aes128cbc_enc(u8 *key, u8 *iv, u8 *in, u64 len, u8 *out); +void aes128(u8 *key, const u8 *in, u8 *out); +void aes128_enc(u8 *key, const u8 *in, u8 *out); void sha1(u8 *data, u32 len, u8 *digest); void sha1_hmac(u8 *key, u8 *data, u32 len, u8 *digest); @@ -45,6 +49,12 @@ int key_get(enum sce_key type, const char *suffix, struct key *k); int key_get_simple(const char *name, u8 *bfr, u32 len); struct keylist *keys_get(enum sce_key type); +struct rif *rif_get(const char *content_id); +struct actdat *actdat_get(void); + +int sce_remove_npdrm(u8 *ptr, struct keylist *klist); +void sce_decrypt_npdrm(u8 *ptr, struct keylist *klist, struct key *klicensee); + int sce_decrypt_header(u8 *ptr, struct keylist *klist); int sce_encrypt_header(u8 *ptr, struct key *k); int sce_decrypt_data(u8 *ptr); diff --git a/types.h b/types.h index 5fbe8bf..40b495e 100644 --- a/types.h +++ b/types.h @@ -84,8 +84,31 @@ struct key { struct keylist { u32 n; struct key *keys; + struct key *idps; + struct key *klic; + struct key *rif; + struct key *npdrm_const; + struct key *free_klicensee; }; +struct rif { + u8 unk1[0x10]; //version, license type and user number + u8 titleid[0x30]; //Content ID + u8 padding[0xC]; //Padding for randomness + u32 actDatIndex; //Key index on act.dat between 0x00 and 0x7F + u8 key[0x10]; //encrypted klicensee + u64 unk2; //timestamp?? + u64 unk3; //Always 0 + u8 rs[0x28]; +} __attribute__ ((packed)); + +struct actdat { + u8 unk1[0x10]; //Version, User number + u8 keyTable[0x800]; //Key Table + u8 unk2[0x800]; + u8 signature[0x28]; +} __attribute__ ((packed)); + static inline u8 be8(u8 *p) { return *p; diff --git a/unself.c b/unself.c index 8172686..470b988 100644 --- a/unself.c +++ b/unself.c @@ -325,6 +325,9 @@ static struct keylist *self_load_keys(void) case 6: id = KEY_LDR; break; + case 8: + id = KEY_NPDRM; + break; default: fail("invalid type: %08x", app_type); } @@ -340,6 +343,9 @@ static void self_decrypt(void) if (klist == NULL) fail("no key found"); + if (sce_remove_npdrm(self, klist) < 0) + fail("self_remove_npdrm failed"); + if (sce_decrypt_header(self, klist) < 0) fail("self_decrypt_header failed"); diff --git a/unself2.c b/unself2.c index 6adb683..52f13c3 100644 --- a/unself2.c +++ b/unself2.c @@ -49,7 +49,7 @@ int main(int argc, char *argv[]) self_read_metadata (in, &self, &app_info, &metadata_info, &metadata_header, §ion_headers, &keys, - &signature_info, &signature); + &signature_info, &signature, control_info); num_sections = self_load_sections (in, &self, &elf, &phdr, &metadata_header, §ion_headers, &keys, §ions);