mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 18:53:28 +01:00
1566 lines
35 KiB
C++
1566 lines
35 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>
|
|
#define _access access
|
|
#define _strdup strdup
|
|
#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);;
|
|
}
|