1
Fork 0
mirror of https://github.com/naehrwert/scetool.git synced 2025-04-23 13:17:46 +00:00
scetool/frontend.cpp

568 lines
14 KiB
C++
Raw Normal View History

2013-05-10 13:02:25 +02:00
/*
* Copyright (c) 2011-2013 by naehrwert
* This file is released under the GPLv2.
*/
#include <stdio.h>
#include <stdlib.h>
#include "types.h"
#include "config.h"
#include "aes.h"
#include "util.h"
#include "keys.h"
#include "sce.h"
#include "sce_inlines.h"
#include "self.h"
#include "np.h"
#include "rvk.h"
#include "spp.h"
#include "util.h"
#include "tables.h"
/*! Parameters. */
extern s8 *_template;
extern s8 *_file_type;
extern s8 *_compress_data;
extern s8 *_skip_sections;
extern s8 *_key_rev;
extern s8 *_meta_info;
extern s8 *_keyset;
extern s8 *_auth_id;
extern s8 *_vendor_id;
extern s8 *_self_type;
extern s8 *_app_version;
extern s8 *_fw_version;
extern s8 *_add_shdrs;
extern s8 *_ctrl_flags;
extern s8 *_cap_flags;
#ifdef CONFIG_CUSTOM_INDIV_SEED
extern s8 *_indiv_seed;
#endif
extern s8 *_license_type;
extern s8 *_app_type;
extern s8 *_content_id;
extern s8 *_real_fname;
extern s8 *_add_sig;
static BOOL _is_hexdigit(s8 c)
{
if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
return TRUE;
return FALSE;
}
static BOOL _is_hexnumber(const s8 *str)
{
u32 i, len = strlen(str);
for(i = 0; i < len; i++)
if(_is_hexdigit(str[i]) == FALSE)
return FALSE;
return TRUE;
}
static BOOL _fill_self_config_template(s8 *file, self_config_t *sconf)
{
u8 *buf = _read_buffer(file, NULL);
if(buf != NULL)
{
sce_buffer_ctxt_t *ctxt = sce_create_ctxt_from_buffer(buf);
if(ctxt != NULL)
{
if(sce_decrypt_header(ctxt, NULL, NULL))
{
_LOG_VERBOSE("Template header decrypted.\n");
_LOG_VERBOSE("Using:\n");
sconf->key_revision = ctxt->sceh->key_revision;
_IF_VERBOSE(printf(" Key Revision 0x%04X\n", sconf->key_revision));
sconf->auth_id = ctxt->self.ai->auth_id;
_IF_VERBOSE(printf(" Auth-ID 0x%016llX\n", sconf->auth_id));
sconf->vendor_id = ctxt->self.ai->vendor_id;
_IF_VERBOSE(printf(" Vendor-ID 0x%08X\n", sconf->vendor_id));
sconf->self_type = ctxt->self.ai->self_type;
_IF_VERBOSE(printf(" SELF-Type 0x%08X\n", sconf->self_type));
sconf->app_version = ctxt->self.ai->version;
_IF_VERBOSE(printf(" APP Version 0x%016llX\n", sconf->app_version));
control_info_t *ci = sce_get_ctrl_info(ctxt, CONTROL_INFO_TYPE_DIGEST);
ci_data_digest_40_t *cid = (ci_data_digest_40_t *)((u8 *)ci + sizeof(control_info_t));
_es_ci_data_digest_40(cid);
sconf->fw_version = cid->fw_version;
_IF_VERBOSE(printf(" FW Version 0x%016llX\n", sconf->fw_version));
ci = sce_get_ctrl_info(ctxt, CONTROL_INFO_TYPE_FLAGS);
sconf->ctrl_flags = (u8 *)_memdup(((u8 *)ci) + sizeof(control_info_t), 0x20);
_IF_VERBOSE(_hexdump(stdout, " Control Flags ", 0, sconf->ctrl_flags, 0x20, 0));
opt_header_t *oh = sce_get_opt_header(ctxt, OPT_HEADER_TYPE_CAP_FLAGS);
sconf->cap_flags = (u8 *)_memdup(((u8 *)oh) + sizeof(opt_header_t), 0x20);
_IF_VERBOSE(_hexdump(stdout, " Capability Flags", 0, sconf->cap_flags, 0x20, 0));
#ifdef CONFIG_CUSTOM_INDIV_SEED
sconf->indiv_seed = NULL;
if(ctxt->self.ai->self_type == SELF_TYPE_ISO)
{
oh = sce_get_opt_header(ctxt, OPT_HEADER_TYPE_INDIV_SEED);
sconf->indiv_seed = (u8 *)_memdup(((u8 *)oh) + sizeof(opt_header_t), oh->size - sizeof(opt_header_t));
sconf->indiv_seed_size = oh->size - sizeof(opt_header_t);
_IF_VERBOSE(_hexdump(stdout, " Individuals Seed", 0, sconf->indiv_seed, sconf->indiv_seed_size, 0));
}
#endif
sconf->add_shdrs = TRUE;
if(_add_shdrs != NULL)
if(strcmp(_add_shdrs, "FALSE") == 0)
sconf->add_shdrs = FALSE;
sconf->skip_sections = TRUE;
if(_skip_sections != NULL)
if(strcmp(_skip_sections, "FALSE") == 0)
sconf->skip_sections = FALSE;
sconf->npdrm_config = NULL;
return TRUE;
}
else
printf("[*] Warning: Could not decrypt template header.\n");
free(ctxt);
}
else
printf("[*] Error: Could not process template %s\n", file);
free(buf);
}
else
printf("[*] Error: Could not load template %s\n", file);
return FALSE;
}
static BOOL _fill_self_config(self_config_t *sconf)
{
if(_key_rev == NULL)
{
printf("[*] Error: Please specify a key revision.\n");
return FALSE;
}
if(_is_hexnumber(_key_rev) == FALSE)
{
printf("[*] Error (Key Revision): Please provide a valid hexadecimal number.\n");
return FALSE;
}
sconf->key_revision = _x_to_u64(_key_rev);
if(_auth_id == NULL)
{
printf("[*] Error: Please specify an auth ID.\n");
return FALSE;
}
sconf->auth_id = _x_to_u64(_auth_id);
if(_vendor_id == NULL)
{
printf("[*] Error: Please specify a vendor ID.\n");
return FALSE;
}
sconf->vendor_id = _x_to_u64(_vendor_id);
if(_self_type == NULL)
{
printf("[*] Error: Please specify a SELF type.\n");
return FALSE;
}
u64 type = _get_id(_self_types_params, _self_type);
if(type == (u64)(-1))
{
printf("[*] Error: Invalid SELF type.\n");
return FALSE;
}
sconf->self_type = type;
if(_app_version == NULL)
{
printf("[*] Error: Please specify an application version.\n");
return FALSE;
}
sconf->app_version = _x_to_u64(_app_version);
sconf->fw_version = 0;
if(_fw_version != NULL)
sconf->fw_version = _x_to_u64(_fw_version);
sconf->add_shdrs = TRUE;
if(_add_shdrs != NULL)
if(strcmp(_add_shdrs, "FALSE") == 0)
sconf->add_shdrs = FALSE;
sconf->skip_sections = TRUE;
if(_skip_sections != NULL)
if(strcmp(_skip_sections, "FALSE") == 0)
sconf->skip_sections = FALSE;
sconf->ctrl_flags = NULL;
if(_ctrl_flags != NULL)
{
if(strlen(_ctrl_flags) != 0x20*2)
{
printf("[*] Error: Control flags need to be 32 bytes.\n");
return FALSE;
}
sconf->ctrl_flags = _x_to_u8_buffer(_ctrl_flags);
}
sconf->cap_flags = NULL;
if(_cap_flags != NULL)
{
if(strlen(_cap_flags) != 0x20*2)
{
printf("[*] Error: Capability flags need to be 32 bytes.\n");
return FALSE;
}
sconf->cap_flags = _x_to_u8_buffer(_cap_flags);
}
#ifdef CONFIG_CUSTOM_INDIV_SEED
sconf->indiv_seed = NULL;
if(_indiv_seed != NULL)
{
u32 len = strlen(_indiv_seed);
if(len > 0x100*2)
{
printf("[*] Error: Individuals seed must be <= 0x100 bytes.\n");
return FALSE;
}
sconf->indiv_seed = _x_to_u8_buffer(_indiv_seed);
sconf->indiv_seed_size = len / 2;
}
#endif
sconf->npdrm_config = NULL;
return TRUE;
}
static BOOL _fill_npdrm_config(self_config_t *sconf)
{
if((sconf->npdrm_config = (npdrm_config_t *)malloc(sizeof(npdrm_config_t))) == NULL)
return FALSE;
if(_license_type == NULL)
{
printf("[*] Error: Please specify a license type.\n");
return FALSE;
}
//TODO!
if(strcmp(_license_type, "FREE") == 0)
sconf->npdrm_config->license_type = NP_LICENSE_FREE;
else if(strcmp(_license_type, "LOCAL") == 0)
sconf->npdrm_config->license_type = NP_LICENSE_LOCAL;
else
{
printf("[*] Error: Only supporting LOCAL and FREE license for now.\n");
return FALSE;
}
if(_app_type == NULL)
{
printf("[*] Error: Please specify an application type.\n");
return FALSE;
}
u64 type = _get_id(_np_app_types, _app_type);
if(type == (u64)(-1))
{
printf("[*] Error: Invalid application type.\n");
return FALSE;
}
sconf->npdrm_config->app_type = type;
if(_content_id == NULL)
{
printf("[*] Error: Please specify a content ID.\n");
return FALSE;
}
strncpy((s8 *)sconf->npdrm_config->content_id, _content_id, 0x30);
if(_real_fname == NULL)
{
printf("[*] Error: Please specify a real filename.\n");
return FALSE;
}
sconf->npdrm_config->real_fname = _real_fname;
return TRUE;
}
void frontend_print_infos(s8 *file)
{
u8 *buf = _read_buffer(file, NULL);
if(buf != NULL)
{
sce_buffer_ctxt_t *ctxt = sce_create_ctxt_from_buffer(buf);
if(ctxt != NULL)
{
u8 *meta_info = NULL;
if(_meta_info != NULL)
{
if(strlen(_meta_info) != 0x40*2)
{
printf("[*] Error: Metadata info needs to be 64 bytes.\n");
return;
}
meta_info = _x_to_u8_buffer(_meta_info);
}
u8 *keyset = NULL;
if(_keyset != NULL)
{
if(strlen(_keyset) != (0x20 + 0x10 + 0x15 + 0x28 + 0x01)*2)
{
printf("[*] Error: Keyset has a wrong length.\n");
return;
}
keyset = _x_to_u8_buffer(_keyset);
}
if(sce_decrypt_header(ctxt, meta_info, keyset))
{
_LOG_VERBOSE("Header decrypted.\n");
2014-07-25 11:34:35 +02:00
//Just deal with spkg_hdr.1 files now, no need to decrypt whole PKG.
if(ctxt->sceh->header_type != SCE_HEADER_TYPE_PKG)
{
if(sce_decrypt_data(ctxt))
_LOG_VERBOSE("Data decrypted.\n");
else
printf("[*] Warning: Could not decrypt data.\n");
}
2013-05-10 13:02:25 +02:00
}
else
printf("[*] Warning: Could not decrypt header.\n");
2014-07-25 11:34:35 +02:00
2013-05-10 13:02:25 +02:00
sce_print_info(stdout, ctxt);
if(ctxt->sceh->header_type == SCE_HEADER_TYPE_SELF)
self_print_info(stdout, ctxt);
else if(ctxt->sceh->header_type == SCE_HEADER_TYPE_RVK && ctxt->mdec == TRUE)
rvk_print(stdout, ctxt);
else if(ctxt->sceh->header_type == SCE_HEADER_TYPE_SPP && ctxt->mdec == TRUE)
spp_print(stdout, ctxt);
2014-07-25 11:34:35 +02:00
2013-05-10 13:02:25 +02:00
free(ctxt);
}
else
printf("[*] Error: Could not process %s\n", file);
free(buf);
}
else
printf("[*] Error: Could not load %s\n", file);
}
void frontend_decrypt(s8 *file_in, s8 *file_out)
{
u8 *buf = _read_buffer(file_in, NULL);
if(buf != NULL)
{
sce_buffer_ctxt_t *ctxt = sce_create_ctxt_from_buffer(buf);
if(ctxt != NULL)
{
u8 *meta_info = NULL;
if(_meta_info != NULL)
{
if(strlen(_meta_info) != 0x40*2)
{
printf("[*] Error: Metadata info needs to be 64 bytes.\n");
return;
}
meta_info = _x_to_u8_buffer(_meta_info);
}
u8 *keyset = NULL;
if(_keyset != NULL)
{
if(strlen(_keyset) != (0x20 + 0x10 + 0x15 + 0x28 + 0x01)*2)
{
printf("[*] Error: Keyset has a wrong length.\n");
return;
}
keyset = _x_to_u8_buffer(_keyset);
}
if(sce_decrypt_header(ctxt, meta_info, keyset))
{
_LOG_VERBOSE("Header decrypted.\n");
if(sce_decrypt_data(ctxt))
{
_LOG_VERBOSE("Data decrypted.\n");
if(ctxt->sceh->header_type == SCE_HEADER_TYPE_SELF)
{
if(self_write_to_elf(ctxt, file_out) == TRUE)
printf("[*] ELF written to %s.\n", file_out);
else
printf("[*] Error: Could not write ELF.\n");
}
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))
printf("[*] RVK written to %s.\n", file_out);
else
printf("[*] Error: Could not write RVK.\n");
}
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");*/
printf("soon...\n");
}
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))
printf("[*] SPP written to %s.\n", file_out);
else
printf("[*] Error: Could not write SPP.\n");
}
}
else
printf("[*] Error: Could not decrypt data.\n");
}
else
printf("[*] Error: Could not decrypt header.\n");
free(ctxt);
}
else
printf("[*] Error: Could not process %s\n", file_in);
free(buf);
}
else
printf("[*] Error: Could not load %s\n", file_in);
}
void frontend_encrypt(s8 *file_in, s8 *file_out)
{
BOOL can_compress = FALSE;
self_config_t sconf;
sce_buffer_ctxt_t *ctxt;
u32 file_len = 0;
u8 *file;
if(_file_type == NULL)
{
printf("[*] Error: Please specify a file type.\n");
return;
}
u8 *keyset = NULL;
if(_keyset != NULL)
{
if(strlen(_keyset) != (0x20 + 0x10 + 0x15 + 0x28 + 0x01)*2)
{
printf("[*] Error: Keyset has a wrong length.\n");
return;
}
keyset = _x_to_u8_buffer(_keyset);
}
if((file = _read_buffer(file_in, &file_len)) == NULL)
{
printf("[*] Error: Could not read %s.\n", file_in);
return;
}
if(strcmp(_file_type, "SELF") == 0)
{
if(_self_type == NULL && _template == NULL)
{
printf("[*] Error: Please specify a SELF type.\n");
return;
}
if(_template != NULL)
{
//Use a template SELF to fill the config.
if(_fill_self_config_template(_template, &sconf) == FALSE)
return;
}
else
{
//Fill the config from command line arguments.
if(_fill_self_config(&sconf) == FALSE)
return;
}
if(sconf.self_type == SELF_TYPE_NPDRM)
if(_fill_npdrm_config(&sconf) == FALSE)
return;
ctxt = sce_create_ctxt_build_self(file, file_len);
if(self_build_self(ctxt, &sconf) == TRUE)
printf("[*] SELF built.\n");
else
{
printf("[*] Error: SELF not built.\n");
return;
}
//SPU SELFs may not be compressed.
if(!(sconf.self_type == SELF_TYPE_LDR || sconf.self_type == SELF_TYPE_ISO))
can_compress = TRUE;
}
else if(strcmp(_file_type, "RVK") == 0)
{
printf("soon...\n");
return;
}
else if(strcmp(_file_type, "PKG") == 0)
{
printf("soon...\n");
return;
}
else if(strcmp(_file_type, "SPP") == 0)
{
printf("soon...\n");
return;
}
//Compress data if wanted.
if(_compress_data != NULL && strcmp(_compress_data, "TRUE") == 0)
{
if(can_compress == TRUE)
{
sce_compress_data(ctxt);
printf("[*] Data compressed.\n");
}
else
printf("[*] Warning: This type of file will not be compressed.\n");
}
//Layout and encrypt context.
sce_layout_ctxt(ctxt);
if(sce_encrypt_ctxt(ctxt, keyset) == TRUE)
printf("[*] Data encrypted.\n");
else
{
printf("[*] Error: Data not encrypted.\n");
return;
}
//Write file.
if(sce_write_ctxt(ctxt, file_out) == TRUE)
{
printf("[*] %s written.\n", file_out);
//Add NPDRM footer signature.
if(sconf.self_type == SELF_TYPE_NPDRM && _add_sig != NULL && strcmp(_add_sig, "TRUE") == 0)
{
if(np_sign_file(file_out) == TRUE)
printf("[*] Added NPDRM footer signature.\n");
else
printf("[*] Error: Could not add NPDRM footer signature.\n");
}
}
else
printf("[*] Error: %s not written.\n", file_out);
}