2013-05-10 13:02:25 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2011-2013 by naehrwert
|
|
|
|
* Copyright (c) 2011-2012 by Youness Alaoui <kakaroto@kakaroto.homelinux.net>
|
|
|
|
* This file is released under the GPLv2.
|
|
|
|
*/
|
2018-01-23 23:22:15 +04:00
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include "types.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "elf.h"
|
|
|
|
#include "sce.h"
|
|
|
|
#include "sce_inlines.h"
|
|
|
|
#include "keys.h"
|
|
|
|
#include "aes.h"
|
|
|
|
#include "sha1.h"
|
|
|
|
#include "ecdsa.h"
|
|
|
|
#include "tables.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "zlib.h"
|
|
|
|
#include "np.h"
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
void _print_cert_file_header(FILE *fp, cert_file_header_t *h)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
const s8 *name;
|
|
|
|
const s8 *key_revision;
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
fprintf(fp, "[*] Certified File Header:\n");
|
|
|
|
fprintf(fp, " Magic 0x%08X [%s]\n", _ES32(h->magic), (_ES32(h->magic) == CF_MAGIC ? "OK" : "ERROR"));
|
|
|
|
fprintf(fp, " Version 0x%08X\n", _ES32(h->version));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
if(_ES16(h->key_revision) == KEY_REVISION_DEBUG)
|
2018-01-23 23:22:15 +04:00
|
|
|
fprintf(fp, " Key Revision [DEBUG]\n");
|
2013-05-10 13:02:25 +02:00
|
|
|
else
|
2018-01-23 23:22:15 +04:00
|
|
|
fprintf(fp, " Key Revision 0x%04X\n", _ES16(h->key_revision));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
name = _get_name(_cert_file_categories, _ES16(h->category));
|
2013-05-10 13:02:25 +02:00
|
|
|
if(name != NULL)
|
2018-01-23 23:22:15 +04:00
|
|
|
{
|
|
|
|
fprintf(fp, " Category ");
|
|
|
|
_PRINT_RAW(fp, "0x%04X ", _ES16(h->category));
|
|
|
|
fprintf(fp, "[%s]\n", name);
|
|
|
|
}
|
2013-05-10 13:02:25 +02:00
|
|
|
else
|
2018-01-23 23:22:15 +04:00
|
|
|
fprintf(fp, " Category 0x%04X\n", _ES16(h->category));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
fprintf(fp, " Ext Header Size 0x%08X\n", _ES32(h->ext_header_size));
|
|
|
|
fprintf(fp, " File Offset 0x%016llX\n", _ES64(h->file_offset));
|
|
|
|
fprintf(fp, " File Size 0x%016llX\n", _ES64(h->file_size));
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void _print_metadata_info(FILE *fp, metadata_info_t *mi)
|
|
|
|
{
|
2018-01-23 23:22:15 +04:00
|
|
|
fprintf(fp, "[*] Encryption Root Header:\n");
|
2013-05-10 13:02:25 +02:00
|
|
|
_hexdump(fp, " Key", 0, mi->key, METADATA_INFO_KEY_LEN, FALSE);
|
2018-03-25 21:12:36 +04:00
|
|
|
_hexdump(fp, " ", 0, mi->key_pad, METADATA_INFO_KEY_LEN, FALSE);
|
2013-05-10 13:02:25 +02:00
|
|
|
_hexdump(fp, " IV ", 0, mi->iv, METADATA_INFO_IV_LEN, FALSE);
|
2018-03-25 21:12:36 +04:00
|
|
|
_hexdump(fp, " ", 0, mi->iv_pad, METADATA_INFO_IV_LEN, FALSE);
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void _print_metadata_header(FILE *fp, metadata_header_t *mh)
|
|
|
|
{
|
2018-01-23 23:22:15 +04:00
|
|
|
const s8 *sig_algo;
|
|
|
|
sig_algo = _get_name(_sig_algorithms, _ES32(mh->sig_algorithm));
|
|
|
|
|
|
|
|
|
2013-05-10 13:02:25 +02:00
|
|
|
fprintf(fp, "[*] Metadata Header:\n");
|
2014-10-30 01:22:20 +03:00
|
|
|
fprintf(fp, " Signature Input Length 0x%016llX\n", _ES64(mh->sig_input_length));
|
2018-01-23 23:22:15 +04:00
|
|
|
if(sig_algo != NULL)
|
|
|
|
{
|
|
|
|
fprintf(fp, " Signature Algorithm ");
|
|
|
|
_PRINT_RAW(fp, "0x%08X ", _ES32(mh->sig_algorithm));
|
|
|
|
fprintf(fp, "[%s]\n", sig_algo);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fprintf(fp, " Signature Algorithm 0x%08X\n", _ES32(mh->sig_algorithm));
|
2014-10-30 01:22:20 +03:00
|
|
|
fprintf(fp, " Section Count 0x%08X\n", _ES32(mh->section_count));
|
|
|
|
fprintf(fp, " Key Count 0x%08X\n", _ES32(mh->key_count));
|
|
|
|
fprintf(fp, " Optional Header Size 0x%08X\n", _ES32(mh->opt_header_size));
|
|
|
|
fprintf(fp, " unknown_1 0x%08X\n", _ES32(mh->unknown_1));
|
|
|
|
fprintf(fp, " unknown_2 0x%08X\n", _ES32(mh->unknown_2));
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void _print_metadata_section_header_header(FILE *fp)
|
|
|
|
{
|
|
|
|
fprintf(fp, "[*] Metadata Section Headers:\n");
|
|
|
|
fprintf(fp, " Idx Offset Size Type Index Hashed SHA1 Encrypted Key IV Compressed\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void _print_metadata_section_header(FILE *fp, metadata_section_header_t *msh, u32 idx)
|
|
|
|
{
|
2018-05-24 05:05:30 +04:00
|
|
|
const s8 *name;
|
|
|
|
name = _get_name(_msh_types, _ES32(msh->type));
|
|
|
|
|
|
|
|
fprintf(fp, " %03d %08llX %08llX %s %02X ",
|
|
|
|
idx, _ES64(msh->data_offset), _ES64(msh->data_size), name, _ES32(msh->index));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
if(_ES32(msh->hashed) == METADATA_SECTION_HASHED)
|
|
|
|
fprintf(fp, "[YES] %02X ", _ES32(msh->sha1_index));
|
2013-05-10 13:02:25 +02:00
|
|
|
else
|
|
|
|
fprintf(fp, "[NO ] -- ");
|
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
if(_ES32(msh->encrypted) == METADATA_SECTION_ENCRYPTED)
|
|
|
|
fprintf(fp, "[YES] %02X %02X ", _ES32(msh->key_index), _ES32(msh->iv_index));
|
2013-05-10 13:02:25 +02:00
|
|
|
else
|
|
|
|
fprintf(fp, "[NO ] -- -- ");
|
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
if(_ES32(msh->compressed) == METADATA_SECTION_COMPRESSED)
|
2013-05-10 13:02:25 +02:00
|
|
|
fprintf(fp, "[YES]\n");
|
|
|
|
else
|
|
|
|
fprintf(fp, "[NO ]\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void _print_sce_file_keys(FILE *fp, sce_buffer_ctxt_t *ctxt)
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
//Get start of keys.
|
2014-10-30 01:22:20 +03:00
|
|
|
u8 *keys = (u8 *)ctxt->metash + sizeof(metadata_section_header_t) * _ES32(ctxt->metah->section_count);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
fprintf(fp, "[*] SCE File Keys:\n");
|
2014-10-30 01:22:20 +03:00
|
|
|
for(i = 0; i < _ES32(ctxt->metah->key_count); i++)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
fprintf(fp, " %02X:", i);
|
|
|
|
_hexdump(fp, "", i, keys+i*0x10, 0x10, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
void _print_sce_signature(FILE *fp, signature_t *sig)
|
|
|
|
{
|
2018-05-24 05:05:30 +04:00
|
|
|
fprintf(fp, "[*] Signature Info:\n");
|
2014-10-30 01:22:20 +03:00
|
|
|
_hexdump(fp, " R", 0, sig->r, SIGNATURE_R_SIZE, FALSE);
|
|
|
|
_hexdump(fp, " S", 0, sig->s, SIGNATURE_S_SIZE, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _print_sce_signature_status(FILE *fp, sce_buffer_ctxt_t *ctxt, u8 *keyset)
|
|
|
|
{
|
|
|
|
u8 hash[0x14];
|
2015-04-06 14:04:40 +03:00
|
|
|
u8 Q[0x28];
|
2018-05-24 05:05:30 +04:00
|
|
|
u8 K[0x14];
|
2015-04-06 14:04:40 +03:00
|
|
|
u8 zero_buf[0x14];
|
2014-10-30 01:22:20 +03:00
|
|
|
keyset_t *ks;
|
|
|
|
|
|
|
|
//Check if a keyset is provided.
|
|
|
|
if(keyset == NULL)
|
|
|
|
{
|
2018-01-23 23:22:15 +04:00
|
|
|
//Get previously used keyset
|
|
|
|
ks = get_used_keyset();
|
2014-10-30 01:22:20 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Use the provided keyset.
|
|
|
|
ks = keyset_from_buffer(keyset);
|
|
|
|
}
|
2018-01-23 23:22:15 +04:00
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
//Generate header hash.
|
|
|
|
sha1(ctxt->scebuffer, _ES64(ctxt->metah->sig_input_length), hash);
|
2018-05-24 05:05:30 +04:00
|
|
|
_hexdump(fp, " H", 0, hash, 0x14, FALSE);
|
|
|
|
|
|
|
|
//get curve params
|
|
|
|
u8 *ec_p = (u8 *)malloc(sizeof(u8) * 20);
|
|
|
|
u8 *ec_a = (u8 *)malloc(sizeof(u8) * 20);
|
|
|
|
u8 *ec_b = (u8 *)malloc(sizeof(u8) * 20);
|
|
|
|
u8 *ec_N = (u8 *)malloc(sizeof(u8) * 21);
|
|
|
|
u8 *ec_Gx = (u8 *)malloc(sizeof(u8) * 20);
|
|
|
|
u8 *ec_Gy = (u8 *)malloc(sizeof(u8) * 20);
|
|
|
|
memset(ec_p, 0, 20);
|
|
|
|
memset(ec_a, 0, 20);
|
|
|
|
memset(ec_b, 0, 20);
|
|
|
|
memset(ec_N, 0, 21);
|
|
|
|
memset(ec_Gx, 0, 20);
|
|
|
|
memset(ec_Gy, 0, 20);
|
|
|
|
//Print curve order N
|
|
|
|
if (ecdsa_get_params(ks->ctype, ec_p, ec_a, ec_b, ec_N, ec_Gx, ec_Gy) == 0)
|
|
|
|
_hexdump (fp, " N", 0, ec_N + 1, 20, FALSE);
|
|
|
|
|
|
|
|
//Set ecdsa params
|
2014-10-30 01:22:20 +03:00
|
|
|
ecdsa_set_curve(ks->ctype);
|
|
|
|
ecdsa_set_pub(ks->pub);
|
2018-01-23 23:22:15 +04:00
|
|
|
|
2018-05-24 05:05:30 +04:00
|
|
|
//Validate private key and calculate K
|
2015-04-06 14:04:40 +03:00
|
|
|
ec_priv_to_pub(ks->priv, Q);
|
2018-05-24 05:05:30 +04:00
|
|
|
get_m(ctxt->sig->r, ctxt->sig->s, hash, ks->priv, K);
|
2015-04-06 14:04:40 +03:00
|
|
|
if (memcmp(ks->pub, Q, sizeof(Q)) == 0)
|
2018-05-24 05:05:30 +04:00
|
|
|
_hexdump (fp, " K", 0, K, 0x14, FALSE);
|
2018-01-23 23:22:15 +04:00
|
|
|
|
2015-04-06 14:04:40 +03:00
|
|
|
//Validate the signature.
|
|
|
|
memset(zero_buf, 0, sizeof(zero_buf));
|
|
|
|
if ((memcmp(ctxt->sig->r, zero_buf, sizeof(zero_buf)) == 0) || (memcmp(ctxt->sig->s, zero_buf, sizeof(zero_buf)) == 0))
|
|
|
|
fprintf(fp, "[*] Signature status: FAIL\n");
|
|
|
|
else
|
|
|
|
fprintf(fp, "[*] Signature status: %s\n", (ecdsa_verify(hash, ctxt->sig->r, ctxt->sig->s) == TRUE ? "OK" : "FAIL"));
|
2014-10-30 01:22:20 +03:00
|
|
|
}
|
|
|
|
|
2013-05-10 13:02:25 +02:00
|
|
|
static sce_buffer_ctxt_t *_sce_create_ctxt()
|
|
|
|
{
|
|
|
|
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 = NULL;
|
|
|
|
res->mdec = TRUE;
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
//Allocate Cert file header.
|
|
|
|
res->cfh = (cert_file_header_t *)malloc(sizeof(cert_file_header_t));
|
|
|
|
memset(res->cfh, 0, sizeof(cert_file_header_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Allocate metadata info (with random key/iv).
|
|
|
|
res->metai = (metadata_info_t *)malloc(sizeof(metadata_info_t));
|
|
|
|
_fill_rand_bytes(res->metai->key, 0x10);
|
|
|
|
memset(res->metai->key_pad, 0, 0x10);
|
|
|
|
_fill_rand_bytes(res->metai->iv, 0x10);
|
|
|
|
memset(res->metai->iv_pad, 0, 0x10);
|
|
|
|
//Allocate metadata header.
|
|
|
|
res->metah = (metadata_header_t *)malloc(sizeof(metadata_header_t));
|
|
|
|
//memset(res->metah, 0, sizeof(metadata_header_t));
|
|
|
|
//Allocate signature.
|
|
|
|
res->sig = (signature_t *)malloc(sizeof(signature_t));
|
|
|
|
|
|
|
|
res->makeself = NULL;
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
sce_buffer_ctxt_t *sce_create_ctxt_from_buffer(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;
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
//Set pointer to Cert file header.
|
|
|
|
res->cfh = (cert_file_header_t *)scebuffer;
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Set pointers to file type specific headers.
|
2018-01-23 23:22:15 +04:00
|
|
|
switch(_ES16(res->cfh->category))
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_SELF:
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
//SELF header.
|
2018-01-23 23:22:15 +04:00
|
|
|
res->self.selfh = (self_header_t *)(res->scebuffer + sizeof(cert_file_header_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
//Program info.
|
2014-10-30 01:22:20 +03:00
|
|
|
res->self.ai = (app_info_t *)(res->scebuffer + _ES64(res->self.selfh->app_info_offset));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Section infos.
|
2018-03-25 18:13:30 +04:00
|
|
|
if (_ES64(res->self.selfh->segment_info_offset) != NULL)
|
2018-01-23 23:22:15 +04:00
|
|
|
{
|
2018-03-25 18:13:30 +04:00
|
|
|
res->self.si = (segment_info_t *)(res->scebuffer + _ES64(res->self.selfh->segment_info_offset));
|
2018-01-23 23:22:15 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
res->self.si = 0;
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//SCE version.
|
2014-10-30 01:22:20 +03:00
|
|
|
if(_ES64(res->self.selfh->sce_version_offset) != NULL)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2014-10-30 01:22:20 +03:00
|
|
|
res->self.sv = (sce_version_t *)(res->scebuffer + _ES64(res->self.selfh->sce_version_offset));
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
res->self.sv = 0;
|
|
|
|
|
|
|
|
//Get pointers to all control infos.
|
2018-01-23 23:22:15 +04:00
|
|
|
if ((_ES64(res->self.selfh->control_info_offset)) != 0)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2018-01-23 23:22:15 +04:00
|
|
|
u32 len = (u32)(_ES64(res->self.selfh->control_info_size));
|
|
|
|
if(len > 0)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2018-01-23 23:22:15 +04:00
|
|
|
u8 *ptr = res->scebuffer + _ES64(res->self.selfh->control_info_offset);
|
|
|
|
res->self.cis = list_create();
|
|
|
|
|
|
|
|
while(len > 0)
|
|
|
|
{
|
|
|
|
control_info_t *tci = (control_info_t *)ptr;
|
|
|
|
ptr += _ES32(tci->size);
|
|
|
|
len -= _ES32(tci->size);
|
|
|
|
list_add_back(res->self.cis, tci);
|
|
|
|
}
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
res->self.cis = NULL;
|
|
|
|
}
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_RVK:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_PKG:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_SPP:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
free(res);
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Set pointers to metadata headers.
|
2018-01-23 23:22:15 +04:00
|
|
|
res->metai = (metadata_info_t *)(scebuffer + sizeof(cert_file_header_t) + _ES32(res->cfh->ext_header_size));
|
2013-05-10 13:02:25 +02:00
|
|
|
res->metah = (metadata_header_t *)((u8 *)res->metai + sizeof(metadata_info_t));
|
|
|
|
res->metash = (metadata_section_header_t *)((u8 *)res->metah + sizeof(metadata_header_t));
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
sce_buffer_ctxt_t *sce_create_ctxt_build_self(u8 *elf, u32 elf_len)
|
|
|
|
{
|
|
|
|
sce_buffer_ctxt_t *res;
|
|
|
|
|
|
|
|
if((res = _sce_create_ctxt()) == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
res->cfh->magic = _ES32(CF_MAGIC);
|
|
|
|
res->cfh->version = _ES32(CF_VERSION_2);
|
|
|
|
res->cfh->category = _ES16(CF_CATEGORY_SELF);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Allocate SELF header.
|
|
|
|
res->self.selfh = (self_header_t *)malloc(sizeof(self_header_t));
|
|
|
|
memset(res->self.selfh, 0, sizeof(self_header_t));
|
2014-10-30 01:22:20 +03:00
|
|
|
res->self.selfh->header_type = _ES64(SUB_HEADER_TYPE_SELF);
|
2018-01-23 23:22:15 +04:00
|
|
|
//Allocate program info.
|
2013-05-10 13:02:25 +02:00
|
|
|
res->self.ai = (app_info_t *)malloc(sizeof(app_info_t));
|
|
|
|
memset(res->self.ai, 0, sizeof(app_info_t));
|
|
|
|
//SCE version.
|
|
|
|
res->self.sv = (sce_version_t *)malloc(sizeof(sce_version_t));
|
|
|
|
//Create control info list.
|
|
|
|
res->self.cis = list_create();
|
|
|
|
//Create optional headers list.
|
|
|
|
res->self.ohs = list_create();
|
|
|
|
|
|
|
|
//Makeself context.
|
|
|
|
res->makeself = (makeself_ctxt_t *)malloc(sizeof(makeself_ctxt_t));
|
|
|
|
memset(res->makeself, 0, sizeof(makeself_ctxt_t));
|
|
|
|
//ELF buffer.
|
|
|
|
res->makeself->elf = elf;
|
|
|
|
res->makeself->elf_len = elf_len;
|
|
|
|
|
|
|
|
//Section list.
|
|
|
|
res->secs = list_create();
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
void sce_add_data_section(sce_buffer_ctxt_t *ctxt, void *buffer, u32 size, bool may_compr)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
sce_section_ctxt_t *sctxt = (sce_section_ctxt_t *)malloc(sizeof(sce_section_ctxt_t));
|
|
|
|
sctxt->buffer = buffer;
|
|
|
|
sctxt->size = size;
|
|
|
|
sctxt->may_compr = may_compr;
|
|
|
|
list_add_back(ctxt->secs, sctxt);
|
|
|
|
}
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
void sce_set_metash(sce_buffer_ctxt_t *ctxt, u32 type, bool encrypted, u32 idx)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->metash[idx].type = _ES32(type);
|
|
|
|
ctxt->metash[idx].index = _ES32(_ES32(type) == METADATA_SECTION_TYPE_PHDR ? idx : _ES32(type) == METADATA_SECTION_TYPE_SHDR ? idx + 1 : idx);
|
|
|
|
ctxt->metash[idx].hashed = _ES32(METADATA_SECTION_HASHED);
|
|
|
|
ctxt->metash[idx].encrypted = _ES32(encrypted == TRUE ? METADATA_SECTION_ENCRYPTED : METADATA_SECTION_NOT_ENCRYPTED);
|
|
|
|
ctxt->metash[idx].compressed = _ES32(METADATA_SECTION_NOT_COMPRESSED);
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void sce_compress_data(sce_buffer_ctxt_t *ctxt)
|
|
|
|
{
|
|
|
|
u32 i = 0;
|
|
|
|
uLongf size_comp, size_bound;
|
|
|
|
|
|
|
|
LIST_FOREACH(iter, ctxt->secs)
|
|
|
|
{
|
|
|
|
sce_section_ctxt_t *sec = (sce_section_ctxt_t *)iter->value;
|
|
|
|
|
|
|
|
//Check if the section may be compressed.
|
|
|
|
if(sec->may_compr == TRUE)
|
|
|
|
{
|
|
|
|
if(sec->size > 0)
|
|
|
|
{
|
|
|
|
size_comp = size_bound = compressBound(sec->size);
|
|
|
|
u8 *buf = (u8 *)malloc(sizeof(u8) * size_bound);
|
|
|
|
compress(buf, &size_comp, (const u8 *)sec->buffer, sec->size);
|
|
|
|
|
|
|
|
if(size_comp < sec->size)
|
|
|
|
{
|
|
|
|
//Set compressed buffer and size.
|
|
|
|
sec->buffer = buf;
|
|
|
|
sec->size = size_comp;
|
|
|
|
|
|
|
|
//Set compression in section info.
|
2018-01-23 23:22:15 +04:00
|
|
|
if(_ES16(ctxt->cfh->category) == CF_CATEGORY_SELF && i < ctxt->makeself->si_sec_cnt)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2018-03-25 18:13:30 +04:00
|
|
|
ctxt->self.si[i].compressed = SEGMENT_INFO_COMPRESSED;
|
2013-05-10 13:02:25 +02:00
|
|
|
//Update size too.
|
|
|
|
ctxt->self.si[i].size = size_comp;
|
|
|
|
}
|
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
//Set compression in metadata section header.
|
|
|
|
ctxt->metash[i].compressed = _ES32(METADATA_SECTION_COMPRESSED);
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
free(buf);
|
|
|
|
_LOG_VERBOSE("Skipped compression of section %03d (0x%08X >= 0x%08X)\n", i, size_comp, sec->size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
_LOG_VERBOSE("Skipped compression of section %03d (size is zero)\n", i);
|
|
|
|
}
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 _sce_get_ci_len(sce_buffer_ctxt_t *ctxt)
|
|
|
|
{
|
|
|
|
u32 res = 0;
|
|
|
|
|
|
|
|
LIST_FOREACH(iter, ctxt->self.cis)
|
2014-10-30 01:22:20 +03:00
|
|
|
res += _ES32(((control_info_t *)iter->value)->size);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 _sce_get_oh_len(sce_buffer_ctxt_t *ctxt)
|
|
|
|
{
|
|
|
|
u32 res = 0;
|
|
|
|
|
|
|
|
LIST_FOREACH(iter, ctxt->self.ohs)
|
2014-10-30 01:22:20 +03:00
|
|
|
res += _ES32(((opt_header_t *)iter->value)->size);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
void _sce_fixup_ctxt(sce_buffer_ctxt_t *ctxt)
|
|
|
|
{
|
|
|
|
u32 i = 0, base_off, last_off;
|
|
|
|
|
|
|
|
//Set section info data.
|
2018-01-23 23:22:15 +04:00
|
|
|
base_off = _ES64(ctxt->cfh->file_offset);
|
2013-05-10 13:02:25 +02:00
|
|
|
LIST_FOREACH(iter, ctxt->secs)
|
|
|
|
{
|
|
|
|
//Save last offset.
|
|
|
|
last_off = base_off;
|
|
|
|
|
|
|
|
//Section offsets.
|
|
|
|
sce_section_ctxt_t *sec = (sce_section_ctxt_t *)iter->value;
|
|
|
|
sec->offset = base_off;
|
|
|
|
|
|
|
|
//Section infos for SELF (that are present as data sections).
|
2018-01-23 23:22:15 +04:00
|
|
|
if(_ES16(ctxt->cfh->category) == CF_CATEGORY_SELF && i < ctxt->makeself->si_sec_cnt)
|
2013-05-10 13:02:25 +02:00
|
|
|
//{
|
|
|
|
ctxt->self.si[i].offset = base_off;
|
|
|
|
// ctxt->self.si[i].size = sec->size;
|
|
|
|
//}
|
|
|
|
|
|
|
|
//Metadata section headers.
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->metash[i].data_offset = _ES64(base_off);
|
|
|
|
ctxt->metash[i].data_size = _ES64(sec->size);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Update offset and data length.
|
|
|
|
base_off += sec->size;
|
2018-01-23 23:22:15 +04:00
|
|
|
ctxt->cfh->file_size = _ES64(base_off - _ES64(ctxt->cfh->file_offset));
|
2013-05-10 13:02:25 +02:00
|
|
|
base_off = ALIGN(base_off, SCE_ALIGN);
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
//Set metadata offset (counted from after Cert file header).
|
|
|
|
ctxt->cfh->ext_header_size = _ES32(ctxt->off_metai - sizeof(cert_file_header_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Set metadata header values.
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->metah->sig_input_length = _ES64(ctxt->off_sig);
|
2018-01-23 23:22:15 +04:00
|
|
|
ctxt->metah->sig_algorithm = _ES32(SIGNATURE_ALGORITHM_ECDSA);
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->metah->opt_header_size = _ES32(_sce_get_oh_len(ctxt));
|
|
|
|
ctxt->metah->unknown_1 = _ES32(0);
|
|
|
|
ctxt->metah->unknown_2 = _ES32(0);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
switch(_ES16(ctxt->cfh->category))
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_SELF:
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
//Set header offsets.
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->self.selfh->app_info_offset = _ES64(ctxt->off_self.off_ai);
|
|
|
|
ctxt->self.selfh->elf_offset = _ES64(ctxt->off_self.off_ehdr);
|
|
|
|
ctxt->self.selfh->phdr_offset = _ES64(ctxt->off_self.off_phdr);
|
2018-03-25 18:13:30 +04:00
|
|
|
ctxt->self.selfh->segment_info_offset = _ES64(ctxt->off_self.off_si);
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->self.selfh->sce_version_offset = _ES64(ctxt->off_self.off_sv);
|
|
|
|
ctxt->self.selfh->control_info_offset = _ES64(ctxt->off_self.off_cis);
|
|
|
|
ctxt->self.selfh->control_info_size = _ES64(_sce_get_ci_len(ctxt));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Set section headers offset in SELF header (last data section) if available.
|
|
|
|
if(ctxt->makeself->shdrs != NULL)
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->self.selfh->shdr_offset = _ES64(last_off);
|
2013-05-10 13:02:25 +02:00
|
|
|
else
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->self.selfh->shdr_offset = _ES64(0);
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_RVK:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_PKG:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_SPP:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//TODO
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _sce_fixup_keys(sce_buffer_ctxt_t *ctxt)
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
//Build keys array.
|
|
|
|
ctxt->keys_len = 0;
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->metah->key_count = _ES32(0);
|
|
|
|
for(i = 0; i < _ES32(ctxt->metah->section_count); i++)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2014-10-30 01:22:20 +03:00
|
|
|
if(_ES32(ctxt->metash[i].encrypted) == METADATA_SECTION_ENCRYPTED)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
ctxt->keys_len += 0x80; //0x60 HMAC, 0x20 key/iv
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->metah->key_count += _ES32(8);
|
|
|
|
ctxt->metash[i].sha1_index = _ES32(_ES32(ctxt->metah->key_count) - 8);
|
|
|
|
ctxt->metash[i].key_index = _ES32(_ES32(ctxt->metah->key_count) - 2);
|
|
|
|
ctxt->metash[i].iv_index = _ES32(_ES32(ctxt->metah->key_count) - 1);
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ctxt->keys_len += 0x60; //0x60 HMAC
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->metah->key_count += _ES32(6);
|
|
|
|
ctxt->metash[i].sha1_index = _ES32(_ES32(ctxt->metah->key_count) - 6);
|
|
|
|
ctxt->metash[i].key_index = _ES32(0xFFFFFFFF);
|
|
|
|
ctxt->metash[i].iv_index = _ES32(0xFFFFFFFF);
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Allocate and fill keys array.
|
|
|
|
ctxt->keys = (u8 *)malloc(sizeof(u8) * ctxt->keys_len);
|
|
|
|
_fill_rand_bytes(ctxt->keys, ctxt->keys_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Increase offset and align it. */
|
|
|
|
#define _INC_OFF_TYPE(off, type) off; \
|
|
|
|
off += sizeof(type); \
|
|
|
|
off = ALIGN(off, SCE_ALIGN)
|
|
|
|
#define _INC_OFF_SIZE(off, size) off; \
|
|
|
|
off += (size); \
|
|
|
|
off = ALIGN(off, SCE_ALIGN)
|
|
|
|
|
|
|
|
void sce_layout_ctxt(sce_buffer_ctxt_t *ctxt)
|
|
|
|
{
|
|
|
|
u32 coff = 0;
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
//Cert file header.
|
|
|
|
ctxt->off_cfh = _INC_OFF_TYPE(coff, cert_file_header_t);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
switch(_ES16(ctxt->cfh->category))
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_SELF:
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
//SELF header.
|
|
|
|
ctxt->off_self.off_selfh = _INC_OFF_TYPE(coff, self_header_t);
|
2018-01-23 23:22:15 +04:00
|
|
|
//Program info.
|
2013-05-10 13:02:25 +02:00
|
|
|
ctxt->off_self.off_ai = _INC_OFF_TYPE(coff, app_info_t);
|
|
|
|
//ELF header.
|
|
|
|
ctxt->off_self.off_ehdr = _INC_OFF_SIZE(coff, ctxt->makeself->ehsize);
|
|
|
|
//ELF Program headers.
|
|
|
|
ctxt->off_self.off_phdr = _INC_OFF_SIZE(coff, ctxt->makeself->phsize);
|
|
|
|
//Section info.
|
2018-03-25 18:13:30 +04:00
|
|
|
ctxt->off_self.off_si = _INC_OFF_SIZE(coff, sizeof(segment_info_t) * ctxt->makeself->si_cnt);
|
2013-05-10 13:02:25 +02:00
|
|
|
//SCE version.
|
|
|
|
ctxt->off_self.off_sv = _INC_OFF_TYPE(coff, sce_version_t);
|
|
|
|
//Control infos.
|
|
|
|
ctxt->off_self.off_cis = _INC_OFF_SIZE(coff, _sce_get_ci_len(ctxt));
|
|
|
|
}
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_RVK:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_PKG:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_SPP:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//TODO
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Metadata info.
|
|
|
|
ctxt->off_metai = _INC_OFF_TYPE(coff, metadata_info_t);
|
|
|
|
//Metadata header.
|
|
|
|
ctxt->off_metah = _INC_OFF_TYPE(coff, metadata_header_t);
|
|
|
|
//Metadata section headers.
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->off_metash = _INC_OFF_SIZE(coff, _ES32(ctxt->metah->section_count) * sizeof(metadata_section_header_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
//Keys.
|
|
|
|
_sce_fixup_keys(ctxt);
|
|
|
|
ctxt->off_keys = _INC_OFF_SIZE(coff, ctxt->keys_len);
|
|
|
|
|
|
|
|
//SELF only headers.
|
2018-01-23 23:22:15 +04:00
|
|
|
if(_ES16(ctxt->cfh->category) == CF_CATEGORY_SELF)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
//Optional headers.
|
|
|
|
ctxt->off_self.off_ohs = _INC_OFF_SIZE(coff, _sce_get_oh_len(ctxt));
|
|
|
|
}
|
|
|
|
|
|
|
|
//Signature.
|
|
|
|
ctxt->off_sig = _INC_OFF_TYPE(coff, signature_t);
|
|
|
|
|
|
|
|
//Header padding.
|
|
|
|
ctxt->off_hdrpad = coff;
|
|
|
|
coff = ALIGN(coff, HEADER_ALIGN);
|
|
|
|
|
|
|
|
//Set header length.
|
2018-01-23 23:22:15 +04:00
|
|
|
ctxt->cfh->file_offset = _ES64(coff);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Set missing values, etc.
|
|
|
|
_sce_fixup_ctxt(ctxt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _sce_build_header(sce_buffer_ctxt_t *ctxt)
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
//Allocate header buffer.
|
2018-01-23 23:22:15 +04:00
|
|
|
ctxt->scebuffer = (u8*)malloc(sizeof(u8) * _ES64(ctxt->cfh->file_offset));
|
|
|
|
memset(ctxt->scebuffer, 0, sizeof(u8) * _ES64(ctxt->cfh->file_offset));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
//Cert file header.
|
|
|
|
memcpy((cert_file_header_t *)(ctxt->scebuffer + ctxt->off_cfh), ctxt->cfh, sizeof(cert_file_header_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
//File category dependent headers.
|
|
|
|
switch(_ES16(ctxt->cfh->category))
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_SELF:
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
//SELF header.
|
2014-10-30 01:22:20 +03:00
|
|
|
memcpy((self_header_t *)(ctxt->scebuffer + ctxt->off_self.off_selfh), ctxt->self.selfh, sizeof(self_header_t));
|
2018-01-23 23:22:15 +04:00
|
|
|
//Program info.
|
2014-10-30 01:22:20 +03:00
|
|
|
memcpy((app_info_t *)(ctxt->scebuffer + ctxt->off_self.off_ai), ctxt->self.ai, sizeof(app_info_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
//ELF header.
|
|
|
|
memcpy(ctxt->scebuffer + ctxt->off_self.off_ehdr, ctxt->makeself->ehdr, ctxt->makeself->ehsize);
|
|
|
|
//ELF program headers.
|
|
|
|
memcpy(ctxt->scebuffer + ctxt->off_self.off_phdr, ctxt->makeself->phdrs, ctxt->makeself->phsize);
|
|
|
|
|
|
|
|
//Section info.
|
|
|
|
u32 i;
|
|
|
|
for(i = 0; i < ctxt->makeself->si_cnt; i++)
|
2018-03-25 18:13:30 +04:00
|
|
|
_copy_es_segment_info((segment_info_t *)(ctxt->scebuffer + ctxt->off_self.off_si + sizeof(segment_info_t) * i), &ctxt->self.si[i]);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//SCE version.
|
2014-10-30 01:22:20 +03:00
|
|
|
memcpy((sce_version_t *)(ctxt->scebuffer + ctxt->off_self.off_sv), ctxt->self.sv, sizeof(sce_version_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Control infos.
|
|
|
|
u32 ci_base = ctxt->off_self.off_cis;
|
|
|
|
LIST_FOREACH(iter, ctxt->self.cis)
|
|
|
|
{
|
|
|
|
control_info_t *ci = (control_info_t *)iter->value;
|
|
|
|
|
|
|
|
//Copy control info header.
|
2014-10-30 01:22:20 +03:00
|
|
|
memcpy((control_info_t *)(ctxt->scebuffer + ci_base), ci, sizeof(control_info_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
//Copy data.
|
2014-10-30 01:22:20 +03:00
|
|
|
memcpy(ctxt->scebuffer + ci_base + sizeof(control_info_t), ((u8 *)ci) + sizeof(control_info_t), _ES32(ci->size) - sizeof(control_info_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
ci_base += _ES32(ci->size);
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_RVK:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_PKG:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
2018-01-23 23:22:15 +04:00
|
|
|
case CF_CATEGORY_SPP:
|
2013-05-10 13:02:25 +02:00
|
|
|
//TODO
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//TODO
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Metadata info.
|
|
|
|
memcpy(ctxt->scebuffer + ctxt->off_metai, ctxt->metai, sizeof(metadata_info_t));
|
|
|
|
//Metadata header.
|
2014-10-30 01:22:20 +03:00
|
|
|
memcpy((metadata_header_t *)(ctxt->scebuffer + ctxt->off_metah), ctxt->metah, sizeof(metadata_header_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
//Metadata section headers.
|
2014-10-30 01:22:20 +03:00
|
|
|
for(i = 0; i < _ES32(ctxt->metah->section_count); i++)
|
|
|
|
memcpy((metadata_section_header_t *)(ctxt->scebuffer + ctxt->off_metash + sizeof(metadata_section_header_t) * i), &ctxt->metash[i], sizeof(metadata_section_header_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Keys.
|
|
|
|
//memcpy(ctxt->scebuffer + ctxt->off_keys, ctxt->keys, ctxt->keys_len);
|
|
|
|
|
|
|
|
//SELF only headers.
|
2018-01-23 23:22:15 +04:00
|
|
|
if(_ES16(ctxt->cfh->category) == CF_CATEGORY_SELF)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
//Optional headers.
|
|
|
|
u32 oh_base = ctxt->off_self.off_ohs;
|
|
|
|
LIST_FOREACH(iter, ctxt->self.ohs)
|
|
|
|
{
|
|
|
|
opt_header_t *oh = (opt_header_t *)iter->value;
|
|
|
|
|
|
|
|
//Copy optional header.
|
2014-10-30 01:22:20 +03:00
|
|
|
memcpy((opt_header_t *)(ctxt->scebuffer + oh_base), oh, sizeof(opt_header_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
//Copy data.
|
2014-10-30 01:22:20 +03:00
|
|
|
memcpy(ctxt->scebuffer + oh_base + sizeof(opt_header_t), ((u8 *)oh) + sizeof(opt_header_t), _ES32(oh->size) - sizeof(opt_header_t));
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
oh_base += _ES32(oh->size);
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
static bool _sce_sign_header(sce_buffer_ctxt_t *ctxt, keyset_t *ks)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
u8 hash[0x14];
|
|
|
|
|
|
|
|
//Well...
|
|
|
|
if(ks->priv == NULL || ks->pub == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
//Generate header hash.
|
2014-10-30 01:22:20 +03:00
|
|
|
sha1(ctxt->scebuffer, _ES64(ctxt->metah->sig_input_length), hash);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Generate signature.
|
|
|
|
ecdsa_set_curve(ks->ctype);
|
|
|
|
ecdsa_set_pub(ks->pub);
|
|
|
|
ecdsa_set_priv(ks->priv);
|
|
|
|
ecdsa_sign(hash, ctxt->sig->r, ctxt->sig->s);
|
|
|
|
|
|
|
|
//Copy Signature.
|
|
|
|
memcpy(ctxt->scebuffer + ctxt->off_sig, ctxt->sig, sizeof(signature_t));
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _sce_calculate_hashes(sce_buffer_ctxt_t *ctxt)
|
|
|
|
{
|
|
|
|
u32 i = 0, sha1_idx;
|
|
|
|
|
|
|
|
LIST_FOREACH(iter, ctxt->secs)
|
|
|
|
{
|
|
|
|
sce_section_ctxt_t *sec = (sce_section_ctxt_t *)iter->value;
|
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
sha1_idx = _ES32(ctxt->metash[i].sha1_index);
|
2013-05-10 13:02:25 +02:00
|
|
|
memset(ctxt->keys + sha1_idx * 0x10, 0, 0x20);
|
|
|
|
sha1_hmac(ctxt->keys + (sha1_idx + 2) * 0x10, 0x40, (u8 *)sec->buffer, sec->size, ctxt->keys + sha1_idx * 0x10);
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
static bool _sce_encrypt_header(sce_buffer_ctxt_t *ctxt, u8 *keyset)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
u8 *ptr;
|
|
|
|
size_t nc_off;
|
|
|
|
u8 sblk[0x10], iv[0x10];
|
|
|
|
keyset_t *ks;
|
|
|
|
aes_context aes_ctxt;
|
|
|
|
|
|
|
|
//Check if a keyset is provided.
|
|
|
|
if(keyset == NULL)
|
|
|
|
{
|
|
|
|
//Try to find keyset.
|
|
|
|
if((ks = keyset_find(ctxt)) == NULL)
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Use the provided keyset.
|
|
|
|
ks = keyset_from_buffer(keyset);
|
|
|
|
}
|
|
|
|
|
|
|
|
//Calculate hashes.
|
|
|
|
_sce_calculate_hashes(ctxt);
|
|
|
|
|
|
|
|
//Copy keys.
|
|
|
|
memcpy(ctxt->scebuffer + ctxt->off_keys, ctxt->keys, ctxt->keys_len);
|
|
|
|
|
|
|
|
//Sign header.
|
|
|
|
_sce_sign_header(ctxt, ks);
|
|
|
|
|
|
|
|
//Encrypt metadata header, metadata section headers and keys.
|
|
|
|
nc_off = 0;
|
|
|
|
ptr = ctxt->scebuffer + ctxt->off_metah;
|
|
|
|
aes_setkey_enc(&aes_ctxt, ctxt->metai->key, METADATA_INFO_KEYBITS);
|
|
|
|
memcpy(iv, ctxt->metai->iv, 0x10);
|
|
|
|
aes_crypt_ctr(&aes_ctxt,
|
2018-01-23 23:22:15 +04:00
|
|
|
_ES64(ctxt->cfh->file_offset) - (sizeof(cert_file_header_t) + _ES32(ctxt->cfh->ext_header_size) + sizeof(metadata_info_t)),
|
2013-05-10 13:02:25 +02:00
|
|
|
&nc_off, iv, sblk, ptr, ptr);
|
|
|
|
|
|
|
|
//Encrypt metadata info.
|
|
|
|
aes_setkey_enc(&aes_ctxt, ks->erk, KEYBITS(ks->erklen));
|
|
|
|
ptr = ctxt->scebuffer + ctxt->off_metai;
|
|
|
|
aes_crypt_cbc(&aes_ctxt, AES_ENCRYPT, sizeof(metadata_info_t), ks->riv, ptr, ptr);
|
|
|
|
|
|
|
|
//Add NPDRM layer.
|
2018-01-23 23:22:15 +04:00
|
|
|
if(_ES16(ctxt->cfh->category) == CF_CATEGORY_SELF && _ES32(ctxt->self.ai->program_type) == PROGRAM_TYPE_NPDRM)
|
2013-05-10 13:02:25 +02:00
|
|
|
if(np_encrypt_npdrm(ctxt) == FALSE)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _sce_encrypt_data(sce_buffer_ctxt_t *ctxt)
|
|
|
|
{
|
|
|
|
u32 i = 0;
|
|
|
|
aes_context aes_ctxt;
|
|
|
|
|
|
|
|
LIST_FOREACH(iter, ctxt->secs)
|
|
|
|
{
|
|
|
|
sce_section_ctxt_t *sec = (sce_section_ctxt_t *)iter->value;
|
|
|
|
|
|
|
|
size_t nc_off = 0;
|
|
|
|
u8 buf[16];
|
|
|
|
u8 iv[16];
|
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
if(_ES32(ctxt->metash[i].encrypted) == METADATA_SECTION_ENCRYPTED)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2014-10-30 01:22:20 +03:00
|
|
|
memcpy(iv, ctxt->keys + _ES32(ctxt->metash[i].iv_index) * 0x10, 0x10);
|
|
|
|
aes_setkey_enc(&aes_ctxt, ctxt->keys + _ES32(ctxt->metash[i].key_index) * 0x10, 128);
|
2013-05-10 13:02:25 +02:00
|
|
|
aes_crypt_ctr(&aes_ctxt, sec->size, &nc_off, iv, buf, (u8 *)sec->buffer, (u8 *)sec->buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
bool sce_encrypt_ctxt(sce_buffer_ctxt_t *ctxt, u8 *keyset)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
//Build SCE file header.
|
|
|
|
_sce_build_header(ctxt);
|
|
|
|
|
|
|
|
//Encrypt header.
|
|
|
|
if(_sce_encrypt_header(ctxt, keyset) == FALSE)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
//Encrypt data.
|
|
|
|
_sce_encrypt_data(ctxt);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
bool sce_write_ctxt(sce_buffer_ctxt_t *ctxt, s8 *fname)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
if((fp = fopen(fname, "wb")) == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
//Write SCE file header.
|
2018-01-23 23:22:15 +04:00
|
|
|
fwrite(ctxt->scebuffer, sizeof(u8), _ES64(ctxt->cfh->file_offset), fp);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Write SCE file sections.
|
|
|
|
LIST_FOREACH(iter, ctxt->secs)
|
|
|
|
{
|
|
|
|
sce_section_ctxt_t *sec = (sce_section_ctxt_t *)iter->value;
|
|
|
|
fseek(fp, sec->offset, SEEK_SET);
|
|
|
|
fwrite(sec->buffer, sizeof(u8), sec->size, fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2018-01-23 23:22:15 +04:00
|
|
|
//refactoring needed
|
|
|
|
static bool check_for_old_algorithm(sce_buffer_ctxt_t *ctxt, keyset_t *ks)
|
|
|
|
{
|
|
|
|
u8 *test_buf = (u8 *)malloc(sizeof(u8) * 0x50);
|
|
|
|
u8 *test_buf2 = (u8 *)malloc(sizeof(u8) * 0x50);
|
|
|
|
u8 *iv = (u8 *)malloc(sizeof(u8) * 0x10);
|
|
|
|
u8 *sblk = (u8 *)malloc(sizeof(u8) * 0x10);
|
|
|
|
u8 *ctr_iv = (u8 *)malloc(sizeof(u8) * 0x10);
|
|
|
|
aes_context aes_ctxt;
|
|
|
|
size_t nc_off;
|
|
|
|
u64 sig_input_length;
|
|
|
|
u32 sig_algo, section_count;
|
|
|
|
|
|
|
|
memcpy(test_buf, ctxt->metai, 0x50);
|
|
|
|
|
|
|
|
memcpy(test_buf2, test_buf, 0x50);
|
|
|
|
nc_off = 0;
|
|
|
|
|
|
|
|
memcpy(test_buf2, test_buf, 0x50);
|
|
|
|
memcpy(iv, ks->riv, 0x10);
|
|
|
|
aes_setkey_enc(&aes_ctxt, ks->erk, KEYBITS(ks->erklen));
|
|
|
|
aes_crypt_ctr(&aes_ctxt, 0x40, &nc_off, iv, sblk, test_buf2, test_buf2);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
nc_off = 0;
|
|
|
|
memcpy (ctr_iv, (test_buf2 + 0x20) ,0x10);
|
|
|
|
aes_setkey_enc(&aes_ctxt, test_buf2, METADATA_INFO_KEYBITS);
|
|
|
|
aes_crypt_ctr(&aes_ctxt, 0x10, &nc_off, ctr_iv, sblk, (test_buf2 + 0x40), (test_buf2 + 0x40));
|
|
|
|
|
|
|
|
sig_input_length = _ES64(*(u64*)&test_buf2[0x40]);
|
|
|
|
sig_algo = _ES32(*(u32*)&test_buf2[0x48]);
|
|
|
|
section_count = _ES32(*(u32*)&test_buf2[0x4C]);
|
|
|
|
|
|
|
|
if((sig_input_length < _ES64(ctxt->cfh->file_offset)) && sig_algo == 1 && section_count < 0xFF)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
//refactoring needed
|
|
|
|
bool sce_decrypt_header(sce_buffer_ctxt_t *ctxt, u8 *metadata_info, u8 *keyset)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
size_t nc_off;
|
2014-10-30 01:22:20 +03:00
|
|
|
u8 sblk[0x10], iv[0x10], ctr_iv[0x10];
|
2013-05-10 13:02:25 +02:00
|
|
|
keyset_t *ks;
|
|
|
|
aes_context aes_ctxt;
|
|
|
|
|
|
|
|
//Check if provided metadata info should be used.
|
|
|
|
if(metadata_info == NULL)
|
|
|
|
{
|
2018-01-23 23:22:15 +04:00
|
|
|
//Remove NPDRM layer.
|
|
|
|
if(_ES16(ctxt->cfh->category) == CF_CATEGORY_SELF && _ES32(ctxt->self.ai->program_type) == PROGRAM_TYPE_NPDRM)
|
|
|
|
if(np_decrypt_npdrm(ctxt) == FALSE)
|
|
|
|
return FALSE;
|
|
|
|
|
2013-05-10 13:02:25 +02:00
|
|
|
//Check if a keyset is provided.
|
|
|
|
if(keyset == NULL)
|
|
|
|
{
|
|
|
|
//Try to find keyset.
|
2018-01-23 23:22:15 +04:00
|
|
|
if((ks = keyset_bruteforce(ctxt)) == NULL)
|
2013-05-10 13:02:25 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
//Decrypt metadata info.
|
2018-01-23 23:22:15 +04:00
|
|
|
|
|
|
|
nc_off = 0;
|
|
|
|
|
2013-05-10 13:02:25 +02:00
|
|
|
memcpy(iv, ks->riv, 0x10); //!!!
|
2018-01-23 23:22:15 +04:00
|
|
|
if (check_for_old_algorithm(ctxt, ks) == false)
|
|
|
|
{
|
|
|
|
aes_setkey_dec(&aes_ctxt, ks->erk, KEYBITS(ks->erklen));
|
|
|
|
aes_crypt_cbc(&aes_ctxt, AES_DECRYPT, sizeof(metadata_info_t), iv, (u8 *)ctxt->metai, (u8 *)ctxt->metai);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aes_setkey_enc(&aes_ctxt, ks->erk, KEYBITS(ks->erklen));
|
|
|
|
aes_crypt_ctr(&aes_ctxt, sizeof(metadata_info_t), &nc_off, iv, sblk, (u8 *)ctxt->metai, (u8 *)ctxt->metai);
|
|
|
|
}
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Copy provided metadata info over SELF metadata.
|
|
|
|
memcpy((u8 *)ctxt->metai, metadata_info, sizeof(metadata_info));
|
|
|
|
}
|
|
|
|
|
|
|
|
//Decrypt metadata header, metadata section headers and keys.
|
|
|
|
nc_off = 0;
|
2014-10-30 01:22:20 +03:00
|
|
|
memcpy (ctr_iv, ctxt->metai->iv ,0x10);
|
2013-05-10 13:02:25 +02:00
|
|
|
aes_setkey_enc(&aes_ctxt, ctxt->metai->key, METADATA_INFO_KEYBITS);
|
|
|
|
aes_crypt_ctr(&aes_ctxt,
|
2018-01-23 23:22:15 +04:00
|
|
|
_ES64(ctxt->cfh->file_offset) - (sizeof(cert_file_header_t) + _ES32(ctxt->cfh->ext_header_size) + sizeof(metadata_info_t)),
|
2014-10-30 01:22:20 +03:00
|
|
|
&nc_off, ctr_iv, sblk, (u8 *)ctxt->metah, (u8 *)ctxt->metah);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
2015-04-23 23:08:46 +03:00
|
|
|
//Check if the metadata was decrypted properly.
|
2018-01-23 23:22:15 +04:00
|
|
|
if (_ES64(ctxt->metah->sig_input_length) > _ES64(ctxt->cfh->file_offset))
|
2015-04-23 23:08:46 +03:00
|
|
|
return FALSE;
|
|
|
|
|
2013-05-10 13:02:25 +02:00
|
|
|
//Metadata decrypted.
|
|
|
|
ctxt->mdec = TRUE;
|
2018-01-23 23:22:15 +04:00
|
|
|
|
2013-05-10 13:02:25 +02:00
|
|
|
//Set start of SCE file keys.
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->keys = (u8 *)ctxt->metash + sizeof(metadata_section_header_t) * _ES32(ctxt->metah->section_count);
|
|
|
|
ctxt->keys_len = _ES32(ctxt->metah->key_count) * 0x10;
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
//Set SELF only headers.
|
2018-01-23 23:22:15 +04:00
|
|
|
if((_ES16(ctxt->cfh->category) == CF_CATEGORY_SELF) && (_ES64(ctxt->metah->opt_header_size) > 0))
|
|
|
|
{
|
2013-05-10 13:02:25 +02:00
|
|
|
//Get pointers to all optional headers.
|
|
|
|
ctxt->self.ohs = list_create();
|
2014-10-30 01:22:20 +03:00
|
|
|
opt_header_t *oh = (opt_header_t *)(ctxt->keys + _ES32(ctxt->metah->key_count) * 0x10);
|
2013-05-10 13:02:25 +02:00
|
|
|
list_add_back(ctxt->self.ohs, oh);
|
2014-10-30 01:22:20 +03:00
|
|
|
while(_ES64(oh->next) != 0)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2014-10-30 01:22:20 +03:00
|
|
|
oh = (opt_header_t *)((u8 *)oh + _ES32(oh->size));
|
2013-05-10 13:02:25 +02:00
|
|
|
list_add_back(ctxt->self.ohs, oh);
|
|
|
|
}
|
|
|
|
|
|
|
|
//Signature.
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->sig = (signature_t *)((u8 *)oh + _ES32(oh->size));
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
else
|
2014-10-30 01:22:20 +03:00
|
|
|
ctxt->sig = (signature_t *)(ctxt->keys + _ES32(ctxt->metah->key_count) * 0x10);
|
2013-05-10 13:02:25 +02:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
bool sce_decrypt_data(sce_buffer_ctxt_t *ctxt)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
aes_context aes_ctxt;
|
|
|
|
|
|
|
|
//Decrypt sections.
|
2014-10-30 01:22:20 +03:00
|
|
|
for(i = 0; i < _ES32(ctxt->metah->section_count); i++)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
size_t nc_off = 0;
|
|
|
|
u8 buf[16];
|
|
|
|
u8 iv[16];
|
|
|
|
|
|
|
|
//Only decrypt encrypted sections.
|
2014-10-30 01:22:20 +03:00
|
|
|
if(_ES32(ctxt->metash[i].encrypted) == METADATA_SECTION_ENCRYPTED)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
2014-10-30 01:22:20 +03:00
|
|
|
if(_ES32(ctxt->metash[i].key_index) > _ES32(ctxt->metah->key_count) - 1 || _ES32(ctxt->metash[i].iv_index) > _ES32(ctxt->metah->key_count))
|
2013-05-10 13:02:25 +02:00
|
|
|
printf("[*] Warning: Skipped decryption of section %03d (marked encrypted but key/iv index out of range)\n", i);
|
|
|
|
else
|
|
|
|
{
|
2014-10-30 01:22:20 +03:00
|
|
|
memcpy(iv, ctxt->keys + _ES32(ctxt->metash[i].iv_index) * 0x10, 0x10);
|
|
|
|
aes_setkey_enc(&aes_ctxt, ctxt->keys + _ES32(ctxt->metash[i].key_index) * 0x10, 128);
|
|
|
|
u8 *ptr = ctxt->scebuffer + _ES64(ctxt->metash[i].data_offset);
|
|
|
|
aes_crypt_ctr(&aes_ctxt, _ES64(ctxt->metash[i].data_size), &nc_off, iv, buf, ptr, ptr);
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2018-01-23 23:22:15 +04:00
|
|
|
void cf_print_info(FILE *fp, sce_buffer_ctxt_t *ctxt)
|
|
|
|
{
|
|
|
|
//Print Cert file header.
|
|
|
|
_print_cert_file_header(fp, ctxt->cfh);
|
|
|
|
}
|
|
|
|
|
2014-10-30 01:22:20 +03:00
|
|
|
void sce_print_info(FILE *fp, sce_buffer_ctxt_t *ctxt, u8 *keyset)
|
2013-05-10 13:02:25 +02:00
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
//Check if the metadata was decrypted.
|
|
|
|
if(ctxt->mdec == FALSE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
//Print metadata infos.
|
|
|
|
_print_metadata_info(fp, ctxt->metai);
|
|
|
|
_print_metadata_header(fp, ctxt->metah);
|
|
|
|
|
|
|
|
//Print section infos.
|
|
|
|
_print_metadata_section_header_header(fp);
|
2014-10-30 01:22:20 +03:00
|
|
|
for(i = 0; i < _ES32(ctxt->metah->section_count); i++)
|
2013-05-10 13:02:25 +02:00
|
|
|
_print_metadata_section_header(fp, &ctxt->metash[i], i);
|
|
|
|
|
|
|
|
//Print keys.
|
|
|
|
_print_sce_file_keys(fp, ctxt);
|
2018-01-23 23:22:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void print_sce_signature_info(FILE *fp, sce_buffer_ctxt_t *ctxt, u8 *keyset)
|
|
|
|
{
|
2014-10-30 01:22:20 +03:00
|
|
|
_print_sce_signature(fp, ctxt->sig);
|
|
|
|
_print_sce_signature_status(fp, ctxt, keyset);
|
2013-05-10 13:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static s8 _sce_tmp_vstr[16];
|
|
|
|
s8 *sce_version_to_str(u64 version)
|
|
|
|
{
|
|
|
|
u32 v = version >> 32;
|
|
|
|
sprintf(_sce_tmp_vstr, "%02X.%02X", (v & 0xFFFF0000) >> 16, v & 0x0000FFFF);
|
|
|
|
return _sce_tmp_vstr;
|
|
|
|
}
|
|
|
|
|
|
|
|
u64 sce_str_to_version(s8 *version)
|
|
|
|
{
|
|
|
|
u16 h, l;
|
|
|
|
sscanf(version, "%02X.%02X", &h, &l);
|
|
|
|
return ((u64)(h << 16 | l)) << 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
u64 sce_hexver_to_decver(u64 version)
|
|
|
|
{
|
|
|
|
//TODO: hackity hack.
|
|
|
|
s8 tmp[16];
|
|
|
|
u32 v = version >> 32;
|
|
|
|
u64 res;
|
|
|
|
|
|
|
|
sprintf(tmp, "%02X%02X", (v & 0xFFFF0000) >> 16, v & 0x0000FFFF);
|
|
|
|
sscanf(tmp, "%d", &v);
|
|
|
|
res = v*100;
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
control_info_t *sce_get_ctrl_info(sce_buffer_ctxt_t *ctxt, u32 type)
|
|
|
|
{
|
|
|
|
LIST_FOREACH(iter, ctxt->self.cis)
|
|
|
|
{
|
|
|
|
control_info_t *ci = (control_info_t *)iter->value;
|
2014-10-30 01:22:20 +03:00
|
|
|
if(_ES32(ci->type) == type)
|
2013-05-10 13:02:25 +02:00
|
|
|
return ci;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt_header_t *sce_get_opt_header(sce_buffer_ctxt_t *ctxt, u32 type)
|
|
|
|
{
|
|
|
|
LIST_FOREACH(iter, ctxt->self.ohs)
|
|
|
|
{
|
|
|
|
opt_header_t *oh = (opt_header_t *)iter->value;
|
2014-10-30 01:22:20 +03:00
|
|
|
if(_ES32(oh->type) == type)
|
2013-05-10 13:02:25 +02:00
|
|
|
return oh;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
2015-04-06 14:04:40 +03:00
|
|
|
}
|