mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-25 12:12:50 +01:00
f1b420eb3b
SELF decrypter improved:
The files 'scetool.exe' and 'zlib1.dll' are no longer needed. Everything
needed is now included in the rpsc3 project. So the only thing you need
in order to load SELF files are the keys. More information about this
matter in my last commit: c1565e55
Warning for devs! There is a lot of spaghetti code in /scetool/. I
fucked up things a bit while trying to include scetool in rpcs3. There
is a lot of unused code there and I need to make sure that everything is
working properly. In any case, the code seems to work stable so
end-users shouldn't be worried about this warning. ;-)
'About...' dialog added:
Well, I have nothing more to say here. I wish you all a nice day!
1564 lines
34 KiB
C++
1564 lines
34 KiB
C++
/*
|
|
* Copyright (c) 2011-2013 by naehrwert
|
|
* This file is released under the GPLv2.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#include "getopt.h"
|
|
#else
|
|
#include <unistd.h>
|
|
#include <getopt.h>
|
|
#endif
|
|
|
|
#include "types.h"
|
|
#include "config.h"
|
|
#include "aes.c"
|
|
#include "getopt.c"
|
|
#include "sha1.c"
|
|
#include "zlib.h"
|
|
#include "util.h"
|
|
#include "keys.h"
|
|
#include "sce.h"
|
|
#include "np.h"
|
|
#include "self.h"
|
|
#include "rvk.h"
|
|
|
|
|
|
/*! Verbose mode. */
|
|
BOOL _verbose = FALSE;
|
|
/*! Raw mode. */
|
|
BOOL _raw = FALSE;
|
|
|
|
|
|
/*! List keys. */
|
|
static BOOL _list_keys = FALSE;
|
|
|
|
/*! Parameters. */
|
|
scetool::s8 *_template = NULL;
|
|
scetool::s8 *_file_type = NULL;
|
|
scetool::s8 *_compress_data = NULL;
|
|
scetool::s8 *_skip_sections = NULL;
|
|
scetool::s8 *_key_rev = NULL;
|
|
scetool::s8 *_meta_info = NULL;
|
|
scetool::s8 *_keyset = NULL;
|
|
scetool::s8 *_auth_id = NULL;
|
|
scetool::s8 *_vendor_id = NULL;
|
|
scetool::s8 *_self_type = NULL;
|
|
scetool::s8 *_app_version = NULL;
|
|
scetool::s8 *_fw_version = NULL;
|
|
scetool::s8 *_add_shdrs = NULL;
|
|
scetool::s8 *_ctrl_flags = NULL;
|
|
scetool::s8 *_cap_flags = NULL;
|
|
#ifdef CONFIG_CUSTOM_INDIV_SEED
|
|
scetool::s8 *_indiv_seed = NULL;
|
|
#endif
|
|
scetool::s8 *_license_type = NULL;
|
|
scetool::s8 *_app_type = NULL;
|
|
scetool::s8 *_content_id = NULL;
|
|
scetool::s8 *_klicensee = NULL;
|
|
scetool::s8 *_real_fname = NULL;
|
|
scetool::s8 *_add_sig = NULL;
|
|
|
|
#include <time.h>
|
|
|
|
|
|
//FILE: LIST.CPP
|
|
list_t *list_create()
|
|
{
|
|
list_t *res;
|
|
|
|
if((res = (list_t *)malloc(sizeof(list_t))) == NULL)
|
|
return NULL;
|
|
|
|
res->head = NULL;
|
|
res->count = 0;
|
|
|
|
return res;
|
|
}
|
|
|
|
void list_destroy(list_t *l)
|
|
{
|
|
if(l == NULL)
|
|
return;
|
|
|
|
lnode_t *iter = l->head, *tmp;
|
|
|
|
while(iter != NULL)
|
|
{
|
|
tmp = iter;
|
|
iter = iter->next;
|
|
free(tmp);
|
|
}
|
|
|
|
free(l);
|
|
}
|
|
|
|
BOOL list_push(list_t *l, void *value)
|
|
{
|
|
if(l == NULL)
|
|
return FALSE;
|
|
|
|
lnode_t *_new;
|
|
|
|
//Allocate new node.
|
|
if((_new = (lnode_t *)malloc(sizeof(lnode_t))) == NULL)
|
|
return FALSE;
|
|
|
|
//Insert.
|
|
_new->value = value;
|
|
_new->next = l->head;
|
|
l->head = _new;
|
|
l->count++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL list_add_back(list_t *l, void *value)
|
|
{
|
|
if(l == NULL)
|
|
return FALSE;
|
|
|
|
lnode_t *n, *_new;
|
|
|
|
//Allocate new node.
|
|
if((_new = (lnode_t *)malloc(sizeof(lnode_t))) == NULL)
|
|
return FALSE;
|
|
|
|
_new->value = value;
|
|
_new->next = NULL;
|
|
|
|
if(l->head == NULL)
|
|
l->head = _new;
|
|
else
|
|
{
|
|
//Move to the list end.
|
|
for(n = l->head; n->next != NULL; n = n->next);
|
|
|
|
//Add.
|
|
n->next = _new;
|
|
l->count++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL list_remove_node(list_t *l, lnode_t *node)
|
|
{
|
|
if(l == NULL)
|
|
return FALSE;
|
|
|
|
lnode_t *iter;
|
|
|
|
if(l->head == node)
|
|
{
|
|
l->head = l->head->next;
|
|
free(node);
|
|
l->count--;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
iter = l->head;
|
|
while(iter->next != NULL)
|
|
{
|
|
if(iter->next == node)
|
|
{
|
|
iter->next = iter->next->next;
|
|
free(node);
|
|
l->count--;
|
|
|
|
return TRUE;
|
|
}
|
|
iter = iter->next;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
////
|
|
|
|
//FILE: UTIL.CPP
|
|
scetool::u64 _x_to_u64(const scetool::s8 *hex)
|
|
{
|
|
scetool::u64 t = 0, res = 0;
|
|
scetool::u32 len = strlen(hex);
|
|
char c;
|
|
|
|
while(len--)
|
|
{
|
|
c = *hex++;
|
|
if(c >= '0' && c <= '9')
|
|
t = c - '0';
|
|
else if(c >= 'a' && c <= 'f')
|
|
t = c - 'a' + 10;
|
|
else if(c >= 'A' && c <= 'F')
|
|
t = c - 'A' + 10;
|
|
else
|
|
t = 0;
|
|
res |= t << (len * 4);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
scetool::u8 *_x_to_u8_buffer(const scetool::s8 *hex)
|
|
{
|
|
scetool::u32 len = strlen(hex);
|
|
scetool::s8 xtmp[3] = {0, 0, 0};
|
|
|
|
//Must be aligned to 2.
|
|
if(len % 2 != 0)
|
|
return NULL;
|
|
|
|
scetool::u8 *res = (scetool::u8 *)malloc(sizeof(scetool::u8) * len);
|
|
scetool::u8 *ptr = res;
|
|
|
|
while(len--)
|
|
{
|
|
xtmp[0] = *hex++;
|
|
xtmp[1] = *hex++;
|
|
|
|
*ptr++ = (scetool::u8)_x_to_u64(xtmp);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
scetool::u8 *_read_buffer(const scetool::s8 *file, scetool::u32 *length)
|
|
{
|
|
FILE *fp;
|
|
scetool::u32 size;
|
|
|
|
if((fp = fopen(file, "rb")) == NULL)
|
|
return NULL;
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
size = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
scetool::u8 *buffer = (scetool::u8 *)malloc(sizeof(scetool::u8) * size);
|
|
fread(buffer, sizeof(scetool::u8), size, fp);
|
|
|
|
if(length != NULL)
|
|
*length = size;
|
|
|
|
fclose(fp);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
int _write_buffer(const scetool::s8 *file, scetool::u8 *buffer, scetool::u32 length)
|
|
{
|
|
FILE *fp;
|
|
|
|
if((fp = fopen(file, "wb")) == NULL)
|
|
return 0;
|
|
|
|
/**/
|
|
while(length > 0)
|
|
{
|
|
scetool::u32 wrlen = 1024;
|
|
if(length < 1024)
|
|
wrlen = length;
|
|
fwrite(buffer, sizeof(scetool::u8), wrlen, fp);
|
|
length -= wrlen;
|
|
buffer += 1024;
|
|
}
|
|
/**/
|
|
|
|
//fwrite(buffer, sizeof(scetool::u8), length, fp);
|
|
|
|
fclose(fp);
|
|
|
|
return 1;
|
|
}
|
|
|
|
void _zlib_inflate(scetool::u8 *in, scetool::u64 len_in, scetool::u8 *out, scetool::u64 len_out)
|
|
{
|
|
z_stream s;
|
|
memset(&s, 0, sizeof(z_stream));
|
|
|
|
s.zalloc = Z_NULL;
|
|
s.zfree = Z_NULL;
|
|
s.opaque = Z_NULL;
|
|
|
|
inflateInit(&s);
|
|
|
|
s.avail_in = len_in;
|
|
s.next_in = in;
|
|
s.avail_out = len_out;
|
|
s.next_out = out;
|
|
|
|
inflate(&s, Z_FINISH);
|
|
|
|
inflateEnd(&s);
|
|
}
|
|
|
|
void *_memdup(void *ptr, scetool::u32 size)
|
|
{
|
|
void *res = malloc(size);
|
|
|
|
if(res != NULL)
|
|
memcpy(res, ptr, size);
|
|
|
|
return res;
|
|
}
|
|
|
|
void _print_align(FILE *fp, const scetool::s8 *str, scetool::s32 align, scetool::s32 len)
|
|
{
|
|
scetool::s32 i, tmp;
|
|
tmp = align - len;
|
|
if(tmp < 0)
|
|
tmp = 0;
|
|
for(i = 0; i < tmp; i++)
|
|
fputs(str, fp);
|
|
}
|
|
|
|
const scetool::s8 *_get_name(id_to_name_t *tab, scetool::u64 id)
|
|
{
|
|
scetool::u32 i = 0;
|
|
|
|
while(!(tab[i].name == NULL && tab[i].id == 0))
|
|
{
|
|
if(tab[i].id == id)
|
|
return tab[i].name;
|
|
i++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//FILE: SCE.CPP
|
|
#include "sce_inlines.h"
|
|
|
|
sce_buffer_ctxt_t *sce_create_ctxt_from_buffer(scetool::u8 *scebuffer)
|
|
{
|
|
sce_buffer_ctxt_t *res;
|
|
|
|
if((res = (sce_buffer_ctxt_t *)malloc(sizeof(sce_buffer_ctxt_t))) == NULL)
|
|
return NULL;
|
|
|
|
memset(res, 0, sizeof(sce_buffer_ctxt_t));
|
|
|
|
res->scebuffer = scebuffer;
|
|
res->mdec = FALSE;
|
|
|
|
//Set pointer to SCE header.
|
|
res->sceh = (sce_header_t *)scebuffer;
|
|
_es_sce_header(res->sceh);
|
|
|
|
//Set pointers to file type specific headers.
|
|
switch(res->sceh->header_type)
|
|
{
|
|
case SCE_HEADER_TYPE_SELF:
|
|
{
|
|
//SELF header.
|
|
res->self.selfh = (self_header_t *)(res->scebuffer + sizeof(sce_header_t));
|
|
_es_self_header(res->self.selfh);
|
|
|
|
//Application info.
|
|
res->self.ai = (app_info_t *)(res->scebuffer + res->self.selfh->app_info_offset);
|
|
_es_app_info(res->self.ai);
|
|
|
|
//Section infos.
|
|
res->self.si = (section_info_t *)(res->scebuffer + res->self.selfh->section_info_offset);
|
|
|
|
//SCE version.
|
|
if(res->self.selfh->sce_version_offset != NULL)
|
|
{
|
|
res->self.sv = (sce_version_t *)(res->scebuffer + res->self.selfh->sce_version_offset);
|
|
_es_sce_version(res->self.sv);
|
|
}
|
|
else
|
|
res->self.sv = 0;
|
|
|
|
//Get pointers to all control infos.
|
|
scetool::u32 len = (scetool::u32)res->self.selfh->control_info_size;
|
|
if(len > 0)
|
|
{
|
|
scetool::u8 *ptr = res->scebuffer + res->self.selfh->control_info_offset;
|
|
res->self.cis = list_create();
|
|
|
|
while(len > 0)
|
|
{
|
|
control_info_t *tci = (control_info_t *)ptr;
|
|
_es_control_info(tci);
|
|
ptr += tci->size;
|
|
len -= tci->size;
|
|
list_add_back(res->self.cis, tci);
|
|
}
|
|
}
|
|
else
|
|
res->self.cis = NULL;
|
|
}
|
|
break;
|
|
case SCE_HEADER_TYPE_RVK:
|
|
//TODO
|
|
break;
|
|
case SCE_HEADER_TYPE_PKG:
|
|
//TODO
|
|
break;
|
|
case SCE_HEADER_TYPE_SPP:
|
|
//TODO
|
|
break;
|
|
default:
|
|
free(res);
|
|
return NULL;
|
|
break;
|
|
}
|
|
|
|
//Set pointers to metadata headers.
|
|
res->metai = (metadata_info_t *)(scebuffer + sizeof(sce_header_t) + res->sceh->metadata_offset);
|
|
res->metah = (metadata_header_t *)((scetool::u8 *)res->metai + sizeof(metadata_info_t));
|
|
res->metash = (metadata_section_header_t *)((scetool::u8 *)res->metah + sizeof(metadata_header_t));
|
|
|
|
return res;
|
|
}
|
|
|
|
static scetool::s8 _sce_tmp_vstr[16];
|
|
scetool::s8 *sce_version_to_str(scetool::u64 version)
|
|
{
|
|
scetool::u32 v = version >> 32;
|
|
sprintf(_sce_tmp_vstr, "%02X.%02X", (v & 0xFFFF0000) >> 16, v & 0x0000FFFF);
|
|
return _sce_tmp_vstr;
|
|
}
|
|
|
|
BOOL sce_decrypt_header(sce_buffer_ctxt_t *ctxt, scetool::u8 *metadata_info, scetool::u8 *keyset)
|
|
{
|
|
scetool::u32 i;
|
|
size_t nc_off;
|
|
scetool::u8 sblk[0x10], iv[0x10];
|
|
keyset_t *ks;
|
|
aes_context aes_ctxt;
|
|
|
|
//Check if provided metadata info should be used.
|
|
if(metadata_info == NULL)
|
|
{
|
|
//Check if a keyset is provided.
|
|
if(keyset == NULL)
|
|
{
|
|
//Try to find keyset.
|
|
if((ks = keyset_find(ctxt)) == NULL)
|
|
return FALSE;
|
|
|
|
_LOG_VERBOSE("Using keyset [%s 0x%04X %s]\n", ks->name, ks->key_revision, sce_version_to_str(ks->version));
|
|
}
|
|
else
|
|
{
|
|
//Use the provided keyset.
|
|
ks = keyset_from_buffer(keyset);
|
|
}
|
|
|
|
//Remove NPDRM layer.
|
|
if(ctxt->sceh->header_type == SCE_HEADER_TYPE_SELF && ctxt->self.ai->self_type == SELF_TYPE_NPDRM)
|
|
if(np_decrypt_npdrm(ctxt) == FALSE)
|
|
return FALSE;
|
|
|
|
//Decrypt metadata info.
|
|
aes_setkey_dec(&aes_ctxt, ks->erk, KEYBITS(ks->erklen));
|
|
memcpy(iv, ks->riv, 0x10); //!!!
|
|
aes_crypt_cbc(&aes_ctxt, AES_DECRYPT, sizeof(metadata_info_t), iv, (scetool::u8 *)ctxt->metai, (scetool::u8 *)ctxt->metai);
|
|
}
|
|
else
|
|
{
|
|
//Copy provided metadata info over SELF metadata.
|
|
memcpy((scetool::u8 *)ctxt->metai, metadata_info, sizeof(metadata_info));
|
|
}
|
|
|
|
if(ctxt->metai->key_pad[0] != 0x00 || ctxt->metai->iv_pad[0] != 0x00)
|
|
return FALSE;
|
|
|
|
//Decrypt metadata header, metadata section headers and keys.
|
|
nc_off = 0;
|
|
aes_setkey_enc(&aes_ctxt, ctxt->metai->key, METADATA_INFO_KEYBITS);
|
|
aes_crypt_ctr(&aes_ctxt,
|
|
ctxt->sceh->header_len - (sizeof(sce_header_t) + ctxt->sceh->metadata_offset + sizeof(metadata_info_t)),
|
|
&nc_off, ctxt->metai->iv, sblk, (scetool::u8 *)ctxt->metah, (scetool::u8 *)ctxt->metah);
|
|
|
|
//Fixup headers.
|
|
_es_metadata_header(ctxt->metah);
|
|
for(i = 0; i < ctxt->metah->section_count; i++)
|
|
_es_metadata_section_header(&ctxt->metash[i]);
|
|
|
|
//Metadata decrypted.
|
|
ctxt->mdec = TRUE;
|
|
|
|
//Set start of SCE file keys.
|
|
ctxt->keys = (scetool::u8 *)ctxt->metash + sizeof(metadata_section_header_t) * ctxt->metah->section_count;
|
|
ctxt->keys_len = ctxt->metah->key_count * 0x10;
|
|
|
|
//Set SELF only headers.
|
|
if(ctxt->sceh->header_type == SCE_HEADER_TYPE_SELF)
|
|
{
|
|
//Get pointers to all optional headers.
|
|
ctxt->self.ohs = list_create();
|
|
opt_header_t *oh = (opt_header_t *)(ctxt->keys + ctxt->metah->key_count * 0x10);
|
|
_es_opt_header(oh);
|
|
list_add_back(ctxt->self.ohs, oh);
|
|
while(oh->next != 0)
|
|
{
|
|
oh = (opt_header_t *)((scetool::u8 *)oh + oh->size);
|
|
_es_opt_header(oh);
|
|
list_add_back(ctxt->self.ohs, oh);
|
|
}
|
|
|
|
//Signature.
|
|
ctxt->sig = (signature_t *)((scetool::u8 *)oh + oh->size);
|
|
}
|
|
else
|
|
ctxt->sig = (signature_t *)(ctxt->keys + ctxt->metah->key_count * 0x10);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL sce_decrypt_data(sce_buffer_ctxt_t *ctxt)
|
|
{
|
|
scetool::u32 i;
|
|
aes_context aes_ctxt;
|
|
|
|
//Decrypt sections.
|
|
for(i = 0; i < ctxt->metah->section_count; i++)
|
|
{
|
|
size_t nc_off = 0;
|
|
scetool::u8 buf[16];
|
|
scetool::u8 iv[16];
|
|
|
|
//Only decrypt encrypted sections.
|
|
if(ctxt->metash[i].encrypted == METADATA_SECTION_ENCRYPTED)
|
|
{
|
|
if(ctxt->metash[i].key_index > ctxt->metah->key_count - 1 || ctxt->metash[i].iv_index > ctxt->metah->key_count)
|
|
printf("[*] Warning: Skipped decryption of section %03d (marked encrypted but key/iv index out of range)\n", i);
|
|
else
|
|
{
|
|
memcpy(iv, ctxt->keys + ctxt->metash[i].iv_index * 0x10, 0x10);
|
|
aes_setkey_enc(&aes_ctxt, ctxt->keys + ctxt->metash[i].key_index * 0x10, 128);
|
|
scetool::u8 *ptr = ctxt->scebuffer + ctxt->metash[i].data_offset;
|
|
aes_crypt_ctr(&aes_ctxt, ctxt->metash[i].data_size, &nc_off, iv, buf, ptr, ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//FILE: SELF.CPP
|
|
#include "elf_inlines.h"
|
|
|
|
//TODO: maybe implement better.
|
|
BOOL self_write_to_elf(sce_buffer_ctxt_t *ctxt, const scetool::s8 *elf_out)
|
|
{
|
|
FILE *fp;
|
|
scetool::u32 i, self_type;
|
|
|
|
const scetool::u8 *eident;
|
|
|
|
//Check for SELF.
|
|
if(ctxt->sceh->header_type != SCE_HEADER_TYPE_SELF)
|
|
return FALSE;
|
|
|
|
if((fp = fopen(elf_out, "wb")) == NULL)
|
|
return FALSE;
|
|
|
|
self_type = ctxt->self.ai->self_type;
|
|
eident = ctxt->scebuffer + ctxt->self.selfh->elf_offset;
|
|
|
|
//SPU is 32 bit.
|
|
if(self_type == SELF_TYPE_LDR || self_type == SELF_TYPE_ISO || eident[EI_CLASS] == ELFCLASs32)
|
|
{
|
|
#ifdef CONFIG_DUMP_INDIV_SEED
|
|
/*
|
|
//Print individuals seed.
|
|
if(self_type == SELF_TYPE_ISO)
|
|
{
|
|
scetool::u8 *indiv_seed = (scetool::u8 *)ctxt->self.ish + sizeof(iseed_header_t);
|
|
scetool::s8 ifile[256];
|
|
sprintf(ifile, "%s.indiv_seed.bin", elf_out);
|
|
FILE *ifp = fopen(ifile, "wb");
|
|
fwrite(indiv_seed, sizeof(scetool::u8), ctxt->self.ish->size - sizeof(iseed_header_t), ifp);
|
|
fclose(ifp);
|
|
}
|
|
*/
|
|
#endif
|
|
|
|
//32 bit ELF.
|
|
Elf32_Ehdr ceh, *eh = (Elf32_Ehdr *)(ctxt->scebuffer + ctxt->self.selfh->elf_offset);
|
|
_copy_es_elf32_ehdr(&ceh, eh);
|
|
|
|
//Write ELF header.
|
|
fwrite(eh, sizeof(Elf32_Ehdr), 1, fp);
|
|
|
|
//Write program headers.
|
|
Elf32_Phdr *ph = (Elf32_Phdr *)(ctxt->scebuffer + ctxt->self.selfh->phdr_offset);
|
|
fwrite(ph, sizeof(Elf32_Phdr), ceh.e_phnum, fp);
|
|
|
|
//Write program data.
|
|
metadata_section_header_t *msh = ctxt->metash;
|
|
for(i = 0; i < ctxt->metah->section_count; i++)
|
|
{
|
|
if(msh[i].type == METADATA_SECTION_TYPE_PHDR)
|
|
{
|
|
_es_elf32_phdr(&ph[msh[i].index]);
|
|
fseek(fp, ph[msh[i].index].p_offset, SEEK_SET);
|
|
fwrite(ctxt->scebuffer + msh[i].data_offset, sizeof(scetool::u8), msh[i].data_size, fp);
|
|
}
|
|
}
|
|
|
|
//Write section headers.
|
|
if(ctxt->self.selfh->shdr_offset != 0)
|
|
{
|
|
Elf32_Shdr *sh = (Elf32_Shdr *)(ctxt->scebuffer + ctxt->self.selfh->shdr_offset);
|
|
fseek(fp, ceh.e_shoff, SEEK_SET);
|
|
fwrite(sh, sizeof(Elf32_Shdr), ceh.e_shnum, fp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//64 bit ELF.
|
|
Elf64_Ehdr ceh, *eh = (Elf64_Ehdr *)(ctxt->scebuffer + ctxt->self.selfh->elf_offset);
|
|
_copy_es_elf64_ehdr(&ceh, eh);
|
|
|
|
//Write ELF header.
|
|
fwrite(eh, sizeof(Elf64_Ehdr), 1, fp);
|
|
|
|
//Write program headers.
|
|
Elf64_Phdr *ph = (Elf64_Phdr *)(ctxt->scebuffer + ctxt->self.selfh->phdr_offset);
|
|
fwrite(ph, sizeof(Elf64_Phdr), ceh.e_phnum, fp);
|
|
|
|
//Write program data.
|
|
metadata_section_header_t *msh = ctxt->metash;
|
|
for(i = 0; i < ctxt->metah->section_count; i++)
|
|
{
|
|
if(msh[i].type == METADATA_SECTION_TYPE_PHDR)
|
|
{
|
|
if(msh[i].compressed == METADATA_SECTION_COMPRESSED)
|
|
{
|
|
_es_elf64_phdr(&ph[msh[i].index]);
|
|
scetool::u8 *data = (scetool::u8 *)malloc(ph[msh[i].index].p_filesz);
|
|
|
|
_zlib_inflate(ctxt->scebuffer + msh[i].data_offset, msh[i].data_size, data, ph[msh[i].index].p_filesz);
|
|
fseek(fp, ph[msh[i].index].p_offset, SEEK_SET);
|
|
fwrite(data, sizeof(scetool::u8), ph[msh[i].index].p_filesz, fp);
|
|
|
|
free(data);
|
|
}
|
|
else
|
|
{
|
|
_es_elf64_phdr(&ph[msh[i].index]);
|
|
fseek(fp, ph[msh[i].index].p_offset, SEEK_SET);
|
|
fwrite(ctxt->scebuffer + msh[i].data_offset, sizeof(scetool::u8), msh[i].data_size, fp);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Write section headers.
|
|
if(ctxt->self.selfh->shdr_offset != 0)
|
|
{
|
|
Elf64_Shdr *sh = (Elf64_Shdr *)(ctxt->scebuffer + ctxt->self.selfh->shdr_offset);
|
|
fseek(fp, ceh.e_shoff, SEEK_SET);
|
|
fwrite(sh, sizeof(Elf64_Shdr), ceh.e_shnum, fp);
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//FILE: NP.CPP
|
|
/*! klicensee key. */
|
|
static scetool::u8 *_klicensee_key;
|
|
|
|
static ci_data_npdrm_t *_sce_find_ci_npdrm(sce_buffer_ctxt_t *ctxt)
|
|
{
|
|
if(ctxt->self.cis != NULL)
|
|
{
|
|
LIST_FOREACH(iter, ctxt->self.cis)
|
|
{
|
|
control_info_t *ci = (control_info_t *)iter->value;
|
|
|
|
if(ci->type == CONTROL_INFO_TYPE_NPDRM)
|
|
{
|
|
ci_data_npdrm_t *np = (ci_data_npdrm_t *)((scetool::u8 *)ci + sizeof(control_info_t));
|
|
//Fixup.
|
|
_es_ci_data_npdrm(np);
|
|
return np;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void np_set_klicensee(scetool::u8 *klicensee)
|
|
{
|
|
_klicensee_key = klicensee;
|
|
}
|
|
|
|
BOOL np_decrypt_npdrm(sce_buffer_ctxt_t *ctxt)
|
|
{
|
|
aes_context aes_ctxt;
|
|
keyset_t *ks_np_klic_free, *ks_klic_key;
|
|
scetool::u8 npdrm_key[0x10];
|
|
scetool::u8 npdrm_iv[0x10];
|
|
ci_data_npdrm_t *np;
|
|
|
|
if((np = _sce_find_ci_npdrm(ctxt)) == NULL)
|
|
return FALSE;
|
|
|
|
//Try to find keysets.
|
|
ks_klic_key = keyset_find_by_name(CONFIG_NP_KLIC_KEY_KNAME);
|
|
if(ks_klic_key == NULL)
|
|
return FALSE;
|
|
if(_klicensee_key != NULL)
|
|
memcpy(npdrm_key, _klicensee_key, 0x10);
|
|
else if(np->license_type == NP_LICENSE_FREE)
|
|
{
|
|
ks_np_klic_free = keyset_find_by_name(CONFIG_NP_KLIC_FREE_KNAME);
|
|
if(ks_np_klic_free == NULL)
|
|
return FALSE;
|
|
memcpy(npdrm_key, ks_np_klic_free->erk, 0x10);
|
|
}
|
|
else if(np->license_type == NP_LICENSE_LOCAL)
|
|
{
|
|
if ((klicensee_by_content_id((scetool::s8 *)np->content_id, npdrm_key)) == FALSE)
|
|
return FALSE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
|
|
aes_setkey_dec(&aes_ctxt, ks_klic_key->erk, METADATA_INFO_KEYBITS);
|
|
aes_crypt_ecb(&aes_ctxt, AES_DECRYPT, npdrm_key, npdrm_key);
|
|
|
|
memset(npdrm_iv, 0, 0x10);
|
|
aes_setkey_dec(&aes_ctxt, npdrm_key, METADATA_INFO_KEYBITS);
|
|
aes_crypt_cbc(&aes_ctxt, AES_DECRYPT, sizeof(metadata_info_t), npdrm_iv, (scetool::u8 *)ctxt->metai, (scetool::u8 *)ctxt->metai);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//FILE: TABLES.CPP
|
|
/*! SCE header types. */
|
|
id_to_name_t _sce_header_types[] =
|
|
{
|
|
{SCE_HEADER_TYPE_SELF, "SELF"},
|
|
{SCE_HEADER_TYPE_RVK, "RVK"},
|
|
{SCE_HEADER_TYPE_PKG, "PKG"},
|
|
{SCE_HEADER_TYPE_SPP, "SPP"},
|
|
{0, NULL}
|
|
};
|
|
|
|
/*! SELF types. */
|
|
id_to_name_t _self_types[] =
|
|
{
|
|
{SELF_TYPE_LV0, "lv0"},
|
|
{SELF_TYPE_LV1, "lv1"},
|
|
{SELF_TYPE_LV2, "lv2"},
|
|
{SELF_TYPE_APP, "Application"},
|
|
{SELF_TYPE_ISO, "Isolated SPU Module"},
|
|
{SELF_TYPE_LDR, "Secure Loader"},
|
|
{SELF_TYPE_UNK_7, "Unknown 7"},
|
|
{SELF_TYPE_NPDRM, "NPDRM Application"},
|
|
{0, NULL}
|
|
};
|
|
|
|
/*! Key types. */
|
|
id_to_name_t _key_types[] =
|
|
{
|
|
{KEYTYPE_SELF, "SELF"},
|
|
{KEYTYPE_RVK, "RVK"},
|
|
{KEYTYPE_PKG, "PKG"},
|
|
{KEYTYPE_SPP, "SPP"},
|
|
{KEYTYPE_OTHER, "OTHER"},
|
|
{0, NULL}
|
|
};
|
|
|
|
//FILE: KEYS.CPP
|
|
/*! Loaded keysets. */
|
|
list_t *_keysets;
|
|
/*! Loaded curves. */
|
|
curve_t *_curves;
|
|
/*! Loaded VSH curves. */
|
|
vsh_curve_t *_vsh_curves;
|
|
|
|
static scetool::u8 rap_init_key[0x10] =
|
|
{
|
|
0x86, 0x9F, 0x77, 0x45, 0xC1, 0x3F, 0xD8, 0x90, 0xCC, 0xF2, 0x91, 0x88, 0xE3, 0xCC, 0x3E, 0xDF
|
|
};
|
|
|
|
static scetool::u8 rap_pbox[0x10] =
|
|
{
|
|
0x0C, 0x03, 0x06, 0x04, 0x01, 0x0B, 0x0F, 0x08, 0x02, 0x07, 0x00, 0x05, 0x0A, 0x0E, 0x0D, 0x09
|
|
};
|
|
|
|
static scetool::u8 rap_e1[0x10] =
|
|
{
|
|
0xA9, 0x3E, 0x1F, 0xD6, 0x7C, 0x55, 0xA3, 0x29, 0xB7, 0x5F, 0xDD, 0xA6, 0x2A, 0x95, 0xC7, 0xA5
|
|
};
|
|
|
|
static scetool::u8 rap_e2[0x10] =
|
|
{
|
|
0x67, 0xD4, 0x5D, 0xA3, 0x29, 0x6D, 0x00, 0x6A, 0x4E, 0x7C, 0x53, 0x7B, 0xF5, 0x53, 0x8C, 0x74
|
|
};
|
|
|
|
static act_dat_t *act_dat_load()
|
|
{
|
|
scetool::s8 *ps3 = NULL, path[256];
|
|
act_dat_t *act_dat;
|
|
scetool::u32 len = 0;
|
|
|
|
if((ps3 = getenv(CONFIG_ENV_PS3)) != NULL)
|
|
if(_access(ps3, 0) != 0)
|
|
ps3 = NULL;
|
|
|
|
if(ps3 != NULL)
|
|
{
|
|
sprintf(path, "%s/%s", ps3, CONFIG_ACT_DAT_FILE);
|
|
if(_access(path, 0) != 0)
|
|
sprintf(path, "%s/%s", CONFIG_ACT_DAT_PATH, CONFIG_ACT_DAT_FILE);
|
|
}
|
|
else
|
|
sprintf(path, "%s/%s", CONFIG_ACT_DAT_PATH, CONFIG_ACT_DAT_FILE);
|
|
|
|
act_dat = (act_dat_t *)_read_buffer(path, &len);
|
|
|
|
if(act_dat == NULL)
|
|
return NULL;
|
|
|
|
if(len != ACT_DAT_LENGTH)
|
|
{
|
|
free(act_dat);
|
|
return NULL;
|
|
}
|
|
|
|
return act_dat;
|
|
}
|
|
|
|
static rif_t *rif_load(const scetool::s8 *content_id)
|
|
{
|
|
scetool::s8 *ps3 = NULL, path[256];
|
|
rif_t *rif;
|
|
scetool::u32 len = 0;
|
|
|
|
if((ps3 = getenv(CONFIG_ENV_PS3)) != NULL)
|
|
if(_access(ps3, 0) != 0)
|
|
ps3 = NULL;
|
|
|
|
if(ps3 != NULL)
|
|
{
|
|
sprintf(path, "%s/%s%s", ps3, content_id, CONFIG_RIF_FILE_EXT);
|
|
if(_access(path, 0) != 0)
|
|
sprintf(path, "%s/%s%s", CONFIG_RIF_PATH, content_id, CONFIG_RIF_FILE_EXT);
|
|
}
|
|
else
|
|
sprintf(path, "%s/%s%s", CONFIG_RIF_PATH, content_id, CONFIG_RIF_FILE_EXT);
|
|
|
|
rif = (rif_t *)_read_buffer(path, &len);
|
|
if(rif == NULL)
|
|
return NULL;
|
|
|
|
if(len < RIF_LENGTH)
|
|
{
|
|
free(rif);
|
|
return NULL;
|
|
}
|
|
|
|
return rif;
|
|
}
|
|
|
|
static scetool::u8 *rap_load(const scetool::s8 *content_id)
|
|
{
|
|
scetool::s8 *ps3 = NULL, path[256];
|
|
scetool::u8 *rap;
|
|
scetool::u32 len = 0;
|
|
|
|
if((ps3 = getenv(CONFIG_ENV_PS3)) != NULL)
|
|
if(_access(ps3, 0) != 0)
|
|
ps3 = NULL;
|
|
|
|
if(ps3 != NULL)
|
|
{
|
|
sprintf(path, "%s/%s%s", ps3, content_id, CONFIG_RAP_FILE_EXT);
|
|
if(_access(path, 0) != 0)
|
|
sprintf(path, "%s/%s%s", CONFIG_RAP_PATH, content_id, CONFIG_RAP_FILE_EXT);
|
|
}
|
|
else
|
|
sprintf(path, "%s/%s%s", CONFIG_RAP_PATH, content_id, CONFIG_RAP_FILE_EXT);
|
|
|
|
rap = (scetool::u8 *)_read_buffer(path, &len);
|
|
|
|
if(rap == NULL)
|
|
return NULL;
|
|
|
|
if(len != RAP_LENGTH)
|
|
{
|
|
free(rap);
|
|
return NULL;
|
|
}
|
|
|
|
return rap;
|
|
}
|
|
|
|
static BOOL rap_to_klicensee(const scetool::s8 *content_id, scetool::u8 *klicensee)
|
|
{
|
|
scetool::u8 *rap;
|
|
aes_context aes_ctxt;
|
|
int round_num;
|
|
int i;
|
|
|
|
rap = rap_load(content_id);
|
|
if(rap == NULL)
|
|
return FALSE;
|
|
|
|
aes_setkey_dec(&aes_ctxt, rap_init_key, RAP_KEYBITS);
|
|
aes_crypt_ecb(&aes_ctxt, AES_DECRYPT, rap, rap);
|
|
|
|
for (round_num = 0; round_num < 5; ++round_num)
|
|
{
|
|
for (i = 0; i < 16; ++i)
|
|
{
|
|
int p = rap_pbox[i];
|
|
rap[p] ^= rap_e1[p];
|
|
}
|
|
for (i = 15; i >= 1; --i)
|
|
{
|
|
int p = rap_pbox[i];
|
|
int pp = rap_pbox[i - 1];
|
|
rap[p] ^= rap[pp];
|
|
}
|
|
int o = 0;
|
|
for (i = 0; i < 16; ++i)
|
|
{
|
|
int p = rap_pbox[i];
|
|
scetool::u8 kc = rap[p] - o;
|
|
scetool::u8 ec2 = rap_e2[p];
|
|
if (o != 1 || kc != 0xFF)
|
|
{
|
|
o = kc < ec2 ? 1 : 0;
|
|
rap[p] = kc - ec2;
|
|
}
|
|
else if (kc == 0xFF)
|
|
rap[p] = kc - ec2;
|
|
else
|
|
rap[p] = kc;
|
|
}
|
|
}
|
|
|
|
memcpy(klicensee, rap, RAP_LENGTH);
|
|
free(rap);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static scetool::u8 *idps_load()
|
|
{
|
|
scetool::s8 *ps3 = NULL, path[256];
|
|
scetool::u8 *idps;
|
|
scetool::u32 len = 0;
|
|
|
|
if((ps3 = getenv(CONFIG_ENV_PS3)) != NULL)
|
|
if(_access(ps3, 0) != 0)
|
|
ps3 = NULL;
|
|
|
|
if(ps3 != NULL)
|
|
{
|
|
sprintf(path, "%s/%s", ps3, CONFIG_IDPS_FILE);
|
|
if(_access(path, 0) != 0)
|
|
sprintf(path, "%s/%s", CONFIG_IDPS_PATH, CONFIG_IDPS_FILE);
|
|
}
|
|
else
|
|
sprintf(path, "%s/%s", CONFIG_IDPS_PATH, CONFIG_IDPS_FILE);
|
|
|
|
idps = (scetool::u8 *)_read_buffer(path, &len);
|
|
|
|
if(idps == NULL)
|
|
return NULL;
|
|
|
|
if(len != IDPS_LENGTH)
|
|
{
|
|
free(idps);
|
|
return NULL;
|
|
}
|
|
|
|
return idps;
|
|
}
|
|
|
|
BOOL klicensee_by_content_id(const scetool::s8 *content_id, scetool::u8 *klicensee)
|
|
{
|
|
aes_context aes_ctxt;
|
|
|
|
if(rap_to_klicensee(content_id, klicensee) == FALSE)
|
|
{
|
|
keyset_t *ks_np_idps_const, *ks_np_rif_key;
|
|
rif_t *rif;
|
|
scetool::u8 idps_const[0x10];
|
|
scetool::u8 act_dat_key[0x10];
|
|
scetool::u32 act_dat_key_index;
|
|
scetool::u8 *idps;
|
|
act_dat_t *act_dat;
|
|
|
|
if((idps = idps_load()) == NULL)
|
|
{
|
|
printf("[*] Error: Could not load IDPS.\n");
|
|
return FALSE;
|
|
}
|
|
else
|
|
_LOG_VERBOSE("IDPS loaded.\n");
|
|
|
|
if((act_dat = act_dat_load()) == NULL)
|
|
{
|
|
printf("[*] Error: Could not load act.dat.\n");
|
|
return FALSE;
|
|
}
|
|
else
|
|
_LOG_VERBOSE("act.dat loaded.\n");
|
|
|
|
ks_np_idps_const = keyset_find_by_name(CONFIG_NP_IDPS_CONST_KNAME);
|
|
if(ks_np_idps_const == NULL)
|
|
return FALSE;
|
|
memcpy(idps_const, ks_np_idps_const->erk, 0x10);
|
|
|
|
ks_np_rif_key = keyset_find_by_name(CONFIG_NP_RIF_KEY_KNAME);
|
|
if(ks_np_rif_key == NULL)
|
|
return FALSE;
|
|
|
|
rif = rif_load(content_id);
|
|
if(rif == NULL)
|
|
{
|
|
printf("[*] Error: Could not obtain klicensee for '%s'.\n", content_id);
|
|
return FALSE;
|
|
}
|
|
|
|
aes_setkey_dec(&aes_ctxt, ks_np_rif_key->erk, RIF_KEYBITS);
|
|
aes_crypt_ecb(&aes_ctxt, AES_DECRYPT, rif->act_key_index, rif->act_key_index);
|
|
|
|
act_dat_key_index = _Es32(*(scetool::u32 *)(rif->act_key_index + 12));
|
|
if(act_dat_key_index > 127)
|
|
{
|
|
printf("[*] Error: act.dat key index out of bounds.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy(act_dat_key, act_dat->primary_key_table + act_dat_key_index * BITS2BYTES(ACT_DAT_KEYBITS), BITS2BYTES(ACT_DAT_KEYBITS));
|
|
|
|
aes_setkey_enc(&aes_ctxt, idps, IDPS_KEYBITS);
|
|
aes_crypt_ecb(&aes_ctxt, AES_ENCRYPT, idps_const, idps_const);
|
|
|
|
aes_setkey_dec(&aes_ctxt, idps_const, IDPS_KEYBITS);
|
|
aes_crypt_ecb(&aes_ctxt, AES_DECRYPT, act_dat_key, act_dat_key);
|
|
|
|
aes_setkey_dec(&aes_ctxt, act_dat_key, ACT_DAT_KEYBITS);
|
|
aes_crypt_ecb(&aes_ctxt, AES_DECRYPT, rif->klicensee, klicensee);
|
|
|
|
free(rif);
|
|
|
|
_LOG_VERBOSE("klicensee decrypted.\n");
|
|
}
|
|
else
|
|
_LOG_VERBOSE("klicensee converted from %s.rap.\n", content_id);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
keyset_t *keyset_from_buffer(scetool::u8 *keyset)
|
|
{
|
|
keyset_t *ks;
|
|
|
|
if((ks = (keyset_t *)malloc(sizeof(keyset_t))) == NULL)
|
|
return NULL;
|
|
|
|
ks->erk = (scetool::u8 *)_memdup(keyset, 0x20);
|
|
ks->erklen = 0x20;
|
|
ks->riv = (scetool::u8 *)_memdup(keyset + 0x20, 0x10);
|
|
ks->rivlen = 0x10;
|
|
ks->pub = (scetool::u8 *)_memdup(keyset + 0x20 + 0x10, 0x28);
|
|
ks->priv = (scetool::u8 *)_memdup(keyset + 0x20 + 0x10 + 0x28, 0x15);
|
|
ks->ctype = (scetool::u8)*(keyset + 0x20 + 0x10 + 0x28 + 0x15);
|
|
|
|
return ks;
|
|
}
|
|
|
|
keyset_t *keyset_find_by_name(const scetool::s8 *name)
|
|
{
|
|
LIST_FOREACH(iter, _keysets)
|
|
{
|
|
keyset_t *ks = (keyset_t *)iter->value;
|
|
if(strcmp(ks->name, name) == 0)
|
|
return ks;
|
|
}
|
|
|
|
printf("[*] Error: Could not find keyset '%s'.\n", name);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static keyset_t *_keyset_find_for_self(scetool::u32 self_type, scetool::u16 key_revision, scetool::u64 version)
|
|
{
|
|
LIST_FOREACH(iter, _keysets)
|
|
{
|
|
keyset_t *ks = (keyset_t *)iter->value;
|
|
|
|
if(ks->self_type == self_type)
|
|
{
|
|
switch(self_type)
|
|
{
|
|
case SELF_TYPE_LV0:
|
|
return ks;
|
|
break;
|
|
case SELF_TYPE_LV1:
|
|
if(version <= ks->version)
|
|
return ks;
|
|
break;
|
|
case SELF_TYPE_LV2:
|
|
if(version <= ks->version)
|
|
return ks;
|
|
break;
|
|
case SELF_TYPE_APP:
|
|
if(key_revision == ks->key_revision)
|
|
return ks;
|
|
break;
|
|
case SELF_TYPE_ISO:
|
|
if(version <= ks->version && key_revision == ks->key_revision)
|
|
return ks;
|
|
break;
|
|
case SELF_TYPE_LDR:
|
|
return ks;
|
|
break;
|
|
case SELF_TYPE_NPDRM:
|
|
if(key_revision == ks->key_revision)
|
|
return ks;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static keyset_t *_keyset_find_for_rvk(scetool::u32 key_revision)
|
|
{
|
|
LIST_FOREACH(iter, _keysets)
|
|
{
|
|
keyset_t *ks = (keyset_t *)iter->value;
|
|
|
|
if(ks->type == KEYTYPE_RVK && key_revision <= ks->key_revision)
|
|
return ks;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static keyset_t *_keyset_find_for_pkg(scetool::u16 key_revision)
|
|
{
|
|
LIST_FOREACH(iter, _keysets)
|
|
{
|
|
keyset_t *ks = (keyset_t *)iter->value;
|
|
|
|
if(ks->type == KEYTYPE_PKG && key_revision <= ks->key_revision)
|
|
return ks;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static keyset_t *_keyset_find_for_spp(scetool::u16 key_revision)
|
|
{
|
|
LIST_FOREACH(iter, _keysets)
|
|
{
|
|
keyset_t *ks = (keyset_t *)iter->value;
|
|
|
|
if(ks->type == KEYTYPE_SPP && key_revision <= ks->key_revision)
|
|
return ks;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
keyset_t *keyset_find(sce_buffer_ctxt_t *ctxt)
|
|
{
|
|
keyset_t *res = NULL;
|
|
|
|
switch(ctxt->sceh->header_type)
|
|
{
|
|
case SCE_HEADER_TYPE_SELF:
|
|
res = _keyset_find_for_self(ctxt->self.ai->self_type, ctxt->sceh->key_revision, ctxt->self.ai->version);
|
|
break;
|
|
case SCE_HEADER_TYPE_RVK:
|
|
res = _keyset_find_for_rvk(ctxt->sceh->key_revision);
|
|
break;
|
|
case SCE_HEADER_TYPE_PKG:
|
|
res = _keyset_find_for_pkg(ctxt->sceh->key_revision);
|
|
break;
|
|
case SCE_HEADER_TYPE_SPP:
|
|
res = _keyset_find_for_spp(ctxt->sceh->key_revision);
|
|
break;
|
|
}
|
|
|
|
if(res == NULL)
|
|
printf("[*] Error: Could not find keyset for %s.\n", _get_name(_sce_header_types, ctxt->sceh->header_type));
|
|
|
|
return res;
|
|
}
|
|
|
|
static void _fill_property(keyset_t *ks, scetool::s8 *prop, scetool::s8 *value)
|
|
{
|
|
if(strcmp(prop, "type") == 0)
|
|
{
|
|
if(strcmp(value, "SELF") == 0)
|
|
ks->type = KEYTYPE_SELF;
|
|
else if(strcmp(value, "RVK") == 0)
|
|
ks->type = KEYTYPE_RVK;
|
|
else if(strcmp(value, "PKG") == 0)
|
|
ks->type = KEYTYPE_PKG;
|
|
else if(strcmp(value, "SPP") == 0)
|
|
ks->type = KEYTYPE_SPP;
|
|
else if(strcmp(value, "OTHER") == 0)
|
|
ks->type = KEYTYPE_OTHER;
|
|
else
|
|
printf("[*] Error: Unknown type '%s'.\n", value);
|
|
}
|
|
else if(strcmp(prop, "revision") == 0)
|
|
ks->key_revision = (scetool::u16)_x_to_u64(value);
|
|
else if(strcmp(prop, "version") == 0)
|
|
ks->version = _x_to_u64(value);
|
|
else if(strcmp(prop, "self_type") == 0)
|
|
{
|
|
if(strcmp(value, "LV0") == 0)
|
|
ks->self_type = SELF_TYPE_LV0;
|
|
else if(strcmp(value, "LV1") == 0)
|
|
ks->self_type = SELF_TYPE_LV1;
|
|
else if(strcmp(value, "LV2") == 0)
|
|
ks->self_type = SELF_TYPE_LV2;
|
|
else if(strcmp(value, "APP") == 0)
|
|
ks->self_type = SELF_TYPE_APP;
|
|
else if(strcmp(value, "ISO") == 0)
|
|
ks->self_type = SELF_TYPE_ISO;
|
|
else if(strcmp(value, "LDR") == 0)
|
|
ks->self_type = SELF_TYPE_LDR;
|
|
else if(strcmp(value, "UNK_7") == 0)
|
|
ks->self_type = SELF_TYPE_UNK_7;
|
|
else if(strcmp(value, "NPDRM") == 0)
|
|
ks->self_type = SELF_TYPE_NPDRM;
|
|
else
|
|
printf("[*] Error: unknown SELF type '%s'.\n", value);
|
|
}
|
|
else if(strcmp(prop, "erk") == 0 || strcmp(prop, "key") == 0)
|
|
{
|
|
ks->erk = _x_to_u8_buffer(value);
|
|
ks->erklen = strlen(value) / 2;
|
|
}
|
|
else if(strcmp(prop, "riv") == 0)
|
|
{
|
|
ks->riv = _x_to_u8_buffer(value);
|
|
ks->rivlen = strlen(value) / 2;
|
|
}
|
|
else if(strcmp(prop, "pub") == 0)
|
|
ks->pub = _x_to_u8_buffer(value);
|
|
else if(strcmp(prop, "priv") == 0)
|
|
ks->priv = _x_to_u8_buffer(value);
|
|
else if(strcmp(prop, "ctype") == 0)
|
|
ks->ctype = (scetool::u8)_x_to_u64(value);
|
|
else
|
|
printf("[*] Error: Unknown keyfile property '%s'.\n", prop);
|
|
}
|
|
|
|
BOOL curves_load(const scetool::s8 *cfile)
|
|
{
|
|
scetool::u32 len = 0;
|
|
|
|
_curves = (curve_t *)_read_buffer(cfile, &len);
|
|
|
|
if(_curves == NULL)
|
|
return FALSE;
|
|
|
|
if(len != CURVES_LENGTH)
|
|
{
|
|
free(_curves);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL vsh_curves_load(const scetool::s8 *cfile)
|
|
{
|
|
scetool::u32 len = 0;
|
|
|
|
_vsh_curves = (vsh_curve_t *)_read_buffer(cfile, &len);
|
|
|
|
if(_vsh_curves == NULL)
|
|
return FALSE;
|
|
|
|
if(len != VSH_CURVES_LENGTH)
|
|
{
|
|
free(_vsh_curves);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static scetool::s64 _compare_keysets(keyset_t *ks1, keyset_t *ks2)
|
|
{
|
|
scetool::s64 res;
|
|
|
|
if((res = (scetool::s64)ks1->version - (scetool::s64)ks2->version) == 0)
|
|
res = (scetool::s64)ks1->key_revision - (scetool::s64)ks2->key_revision;
|
|
|
|
return res;
|
|
}
|
|
|
|
static void _sort_keysets()
|
|
{
|
|
scetool::u32 i, to = _keysets->count;
|
|
lnode_t *max;
|
|
|
|
list_t *tmp = list_create();
|
|
|
|
for(i = 0; i < to; i++)
|
|
{
|
|
max = _keysets->head;
|
|
LIST_FOREACH(iter, _keysets)
|
|
{
|
|
if(_compare_keysets((keyset_t *)max->value, (keyset_t *)iter->value) < 0)
|
|
max = iter;
|
|
}
|
|
list_push(tmp, max->value);
|
|
list_remove_node(_keysets, max);
|
|
}
|
|
|
|
list_destroy(_keysets);
|
|
_keysets = tmp;
|
|
}
|
|
|
|
#define LINEBUFSIZE 512
|
|
BOOL keys_load(const scetool::s8 *kfile)
|
|
{
|
|
scetool::u32 i = 0, lblen;
|
|
FILE *fp;
|
|
scetool::s8 lbuf[LINEBUFSIZE];
|
|
keyset_t *cks = NULL;
|
|
|
|
if((_keysets = list_create()) == NULL)
|
|
return FALSE;
|
|
|
|
if((fp = fopen(kfile, "r")) == NULL)
|
|
{
|
|
list_destroy(_keysets);
|
|
return FALSE;
|
|
}
|
|
|
|
do
|
|
{
|
|
//Get next line.
|
|
lbuf[0] = 0;
|
|
fgets(lbuf, LINEBUFSIZE, fp);
|
|
lblen = strlen(lbuf);
|
|
|
|
//Don't parse empty lines (ignore '\n') and comment lines (starting with '#').
|
|
if(lblen > 1 && lbuf[0] != '#')
|
|
{
|
|
//Remove '\n'.
|
|
lbuf[lblen-1] = 0;
|
|
|
|
//Check for keyset entry.
|
|
if(lblen > 2 && lbuf[0] == '[')
|
|
{
|
|
if(cks != NULL)
|
|
{
|
|
//Add to keyset list.
|
|
list_push(_keysets, cks);
|
|
cks = NULL;
|
|
}
|
|
|
|
//Find name end.
|
|
for(i = 0; lbuf[i] != ']' && lbuf[i] != '\n' && i < lblen; i++);
|
|
lbuf[i] = 0;
|
|
|
|
//Allocate keyset and fill name.
|
|
cks = (keyset_t *)malloc(sizeof(keyset_t));
|
|
memset(cks, 0, sizeof(keyset_t));
|
|
cks->name = _strdup(&lbuf[1]);
|
|
}
|
|
else if(cks != NULL)
|
|
{
|
|
//Find property name end.
|
|
for(i = 0; lbuf[i] != '=' && lbuf[i] != '\n' && i < lblen; i++);
|
|
lbuf[i] = 0;
|
|
|
|
//Fill property.
|
|
_fill_property(cks, &lbuf[0], &lbuf[i+1]);
|
|
}
|
|
}
|
|
} while(!feof(fp));
|
|
|
|
//Add last keyset to keyset list.
|
|
if(cks != NULL)
|
|
list_push(_keysets, cks);
|
|
|
|
//Sort keysets.
|
|
_sort_keysets();
|
|
|
|
return TRUE;
|
|
}
|
|
#undef LINEBUFSIZE
|
|
|
|
bool frontend_decrypt(scetool::s8 *file_in, scetool::s8 *file_out)
|
|
{
|
|
scetool::u8 *buf = _read_buffer(file_in, NULL);
|
|
if(buf != NULL)
|
|
{
|
|
sce_buffer_ctxt_t *ctxt = sce_create_ctxt_from_buffer(buf);
|
|
if(ctxt != NULL)
|
|
{
|
|
scetool::u8 *meta_info = NULL;
|
|
if(_meta_info != NULL)
|
|
{
|
|
if(strlen(_meta_info) != 0x40*2)
|
|
{
|
|
ConLog.Error("scetool: Metadata info needs to be 64 bytes.\n");
|
|
return false;
|
|
}
|
|
meta_info = _x_to_u8_buffer(_meta_info);
|
|
}
|
|
|
|
scetool::u8 *keyset = NULL;
|
|
if(_keyset != NULL)
|
|
{
|
|
if(strlen(_keyset) != (0x20 + 0x10 + 0x15 + 0x28 + 0x01)*2)
|
|
{
|
|
ConLog.Error("scetool: Keyset has a wrong length");
|
|
return false;
|
|
}
|
|
keyset = _x_to_u8_buffer(_keyset);
|
|
}
|
|
|
|
if(sce_decrypt_header(ctxt, meta_info, keyset))
|
|
{
|
|
if(sce_decrypt_data(ctxt))
|
|
{
|
|
if(ctxt->sceh->header_type == SCE_HEADER_TYPE_SELF)
|
|
{
|
|
if(self_write_to_elf(ctxt, file_out) == TRUE)
|
|
ConLog.Write("scetool: ELF written to %s", file_out);
|
|
else
|
|
ConLog.Error("scetool: Could not write ELF");
|
|
}
|
|
else if(ctxt->sceh->header_type == SCE_HEADER_TYPE_RVK)
|
|
{
|
|
if(_write_buffer(file_out, ctxt->scebuffer + ctxt->metash[0].data_offset,
|
|
ctxt->metash[0].data_size + ctxt->metash[1].data_size))
|
|
ConLog.Write("scetool: RVK written to %s", file_out);
|
|
else
|
|
ConLog.Error("scetool: Could not write RVK");
|
|
}
|
|
else if(ctxt->sceh->header_type == SCE_HEADER_TYPE_PKG)
|
|
{
|
|
/*if(_write_buffer(file_out, ctxt->scebuffer + ctxt->metash[0].data_offset,
|
|
ctxt->metash[0].data_size + ctxt->metash[1].data_size + ctxt->metash[2].data_size))
|
|
printf("[*] PKG written to %s.\n", file_out);
|
|
else
|
|
printf("[*] Error: Could not write PKG.\n");*/
|
|
ConLog.Warning("scetool: Not yet supported");
|
|
}
|
|
else if(ctxt->sceh->header_type == SCE_HEADER_TYPE_SPP)
|
|
{
|
|
if(_write_buffer(file_out, ctxt->scebuffer + ctxt->metash[0].data_offset,
|
|
ctxt->metash[0].data_size + ctxt->metash[1].data_size))
|
|
ConLog.Write("scetool: SPP written to %s", file_out);
|
|
else
|
|
ConLog.Error("scetool: Could not write SPP");
|
|
}
|
|
}
|
|
else
|
|
ConLog.Error("scetool: Could not decrypt data");
|
|
}
|
|
else
|
|
ConLog.Error("scetool: Could not decrypt header");
|
|
free(ctxt);
|
|
}
|
|
else
|
|
ConLog.Error("scetool: Could not process %s", file_in);
|
|
free(buf);
|
|
}
|
|
else
|
|
ConLog.Error("scetool: Could not load %s", file_in);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool scetool_decrypt(scetool::s8 *_file_in, scetool::s8 *_file_out)
|
|
{
|
|
scetool::s8 *ps3 = NULL, path[256];
|
|
|
|
//Try to get path from env:PS3.
|
|
if((ps3 = getenv(CONFIG_ENV_PS3)) != NULL)
|
|
if(_access(ps3, 0) != 0)
|
|
ps3 = NULL;
|
|
|
|
//Load keysets.
|
|
if(ps3 != NULL)
|
|
{
|
|
sprintf(path, "%s/%s", ps3, CONFIG_KEYS_FILE);
|
|
if(_access(path, 0) != 0)
|
|
sprintf(path, "%s/%s", CONFIG_KEYS_PATH, CONFIG_KEYS_FILE);
|
|
}
|
|
else
|
|
sprintf(path, "%s/%s", CONFIG_KEYS_PATH, CONFIG_KEYS_FILE);
|
|
if(!keys_load(path))
|
|
{
|
|
ConLog.Error("scetool: Could not load keys");
|
|
return false;
|
|
}
|
|
|
|
//Load curves.
|
|
if(ps3 != NULL)
|
|
{
|
|
sprintf(path, "%s/%s", ps3, CONFIG_CURVES_FILE);
|
|
if(_access(path, 0) != 0)
|
|
sprintf(path, "%s/%s", CONFIG_CURVES_PATH, CONFIG_CURVES_FILE);
|
|
}
|
|
else
|
|
sprintf(path, "%s/%s", CONFIG_CURVES_PATH, CONFIG_CURVES_FILE);
|
|
if(!curves_load(path))
|
|
{
|
|
ConLog.Error("scetool: Could not load loader curves");
|
|
return false;
|
|
}
|
|
|
|
//Load curves.
|
|
if(ps3 != NULL)
|
|
{
|
|
sprintf(path, "%s/%s", ps3, CONFIG_VSH_CURVES_FILE);
|
|
if(_access(path, 0) != 0)
|
|
sprintf(path, "%s/%s", CONFIG_VSH_CURVES_PATH, CONFIG_VSH_CURVES_FILE);
|
|
}
|
|
else
|
|
sprintf(path, "%s/%s", CONFIG_VSH_CURVES_PATH, CONFIG_VSH_CURVES_FILE);
|
|
if(!vsh_curves_load(path))
|
|
{
|
|
ConLog.Error("scetool: Could not load vsh curves");
|
|
return false;
|
|
}
|
|
|
|
//Set klicensee.
|
|
if(_klicensee != NULL)
|
|
{
|
|
if(strlen(_klicensee) != 0x10*2)
|
|
{
|
|
ConLog.Error("scetool: klicensee needs to be 16 bytes.");
|
|
return false;
|
|
}
|
|
np_set_klicensee(_x_to_u8_buffer(_klicensee));
|
|
}
|
|
|
|
|
|
return frontend_decrypt(_file_in, _file_out);;
|
|
}
|