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);