mirror of
https://github.com/joel16/VitaShell.git
synced 2024-12-13 14:26:01 +00:00
444 lines
11 KiB
C
444 lines
11 KiB
C
/*
|
|
VitaShell
|
|
Copyright (C) 2015-2016, TheFloW
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "../main.h"
|
|
#include "pboot.h"
|
|
|
|
#include "../elf_types.h"
|
|
#include "libkirk/kirk_engine.h"
|
|
|
|
#include "headers.h"
|
|
#include "binary.h"
|
|
#include "icon0.h"
|
|
|
|
#define HIJACKED_ADDRESS 0x174
|
|
|
|
#define BIG_BUFFER_SIZE 16 * 1024 * 1024
|
|
|
|
static uint8_t *big_buffer = NULL;
|
|
|
|
static char disc_id[128], psp_system_ver[128], title[128];
|
|
static int parental_level = 0, region = 0;
|
|
static int val_0 = 0, val_1 = 1;
|
|
|
|
static int argp_fix = 0;
|
|
|
|
typedef struct {
|
|
char *name;
|
|
int type;
|
|
void *value;
|
|
int exist;
|
|
} ParamEntry;
|
|
|
|
ParamEntry param_entries[] = {
|
|
{ "APP_VER", PSF_TYPE_STR, "99.99", 1 },
|
|
{ "ATTRIBUTE", PSF_TYPE_VAL, &val_1, 1 },
|
|
{ "BOOTABLE", PSF_TYPE_VAL, &val_1, 1 },
|
|
{ "CATEGORY", PSF_TYPE_STR, "PG", 1 },
|
|
{ "DISC_ID", PSF_TYPE_STR, disc_id, 1 },
|
|
{ "DISC_VER", PSF_TYPE_STR, "1.00", 1 },
|
|
{ "PARENTAL_LEVEL", PSF_TYPE_VAL, &parental_level, 0 },
|
|
{ "PBOOT_TITLE", PSF_TYPE_STR, title, 0 },
|
|
{ "PSP_SYSTEM_VER", PSF_TYPE_STR, psp_system_ver, 0 },
|
|
{ "REGION", PSF_TYPE_VAL, ®ion, 0 },
|
|
{ "TITLE", PSF_TYPE_STR, title, 0 },
|
|
{ "USE_USB", PSF_TYPE_VAL, &val_0, 1 },
|
|
};
|
|
|
|
#define N_PARAM_ENTRIES (sizeof(param_entries) / sizeof(ParamEntry))
|
|
|
|
typedef struct {
|
|
uint8_t aes[16];
|
|
uint8_t cmac[16];
|
|
} HeaderKeys;
|
|
|
|
int getKirkSize(uint8_t *key_hdr) {
|
|
int size = *(uint32_t *)(key_hdr + 0x70);
|
|
|
|
if (size % 0x10) {
|
|
size += 0x10 - (size % 0x10); // 16 bytes aligned
|
|
}
|
|
|
|
size += KIRK_HEADER_SIZE;
|
|
|
|
return size;
|
|
}
|
|
|
|
HeaderList *getHeaderList(int size) {
|
|
int i;
|
|
for (i = 0; i < sizeof(header_list) / sizeof(HeaderList); i++) {
|
|
if ((getKirkSize(header_list[i].kirkHeader) - PSP_HEADER_SIZE) >= size) {
|
|
return &header_list[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int checkPRX(void *buffer) {
|
|
Elf32_Ehdr *header = (Elf32_Ehdr *)buffer;
|
|
|
|
// Is PRX
|
|
if (header->e_magic == ELF_MAGIC && header->e_type == ELF_PRX_TYPE)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fixRelocations(void *buffer) {
|
|
int new_reloc = 0;
|
|
|
|
Elf32_Ehdr *header = (Elf32_Ehdr *)buffer;
|
|
Elf32_Shdr *section = (Elf32_Shdr *)((uint32_t)header + header->e_shoff);
|
|
|
|
int i;
|
|
for (i = 0; i < header->e_shnum; i++) {
|
|
if (section[i].sh_type == SHT_PRXRELOC) {
|
|
Elf32_Rel *reloc = (Elf32_Rel *)((uint32_t)header + section[i].sh_offset);
|
|
|
|
int j;
|
|
for (j = 0; j < section[i].sh_size / sizeof(Elf32_Rel); j++) {
|
|
// Remove all relocations in the hijacked function and add new relocations
|
|
if (argp_fix) {
|
|
if ((uint32_t)reloc[j].r_offset >= HIJACKED_ADDRESS && (uint32_t)reloc[j].r_offset < (HIJACKED_ADDRESS + sizeof(binary))) {
|
|
reloc[j].r_info = 0;
|
|
|
|
if (new_reloc < 2) {
|
|
if (new_reloc == 0) {
|
|
reloc[j].r_offset = HIJACKED_ADDRESS + 0x44;
|
|
reloc[j].r_info = R_MIPS_26;
|
|
} else if (new_reloc == 1) {
|
|
reloc[j].r_offset = HIJACKED_ADDRESS + 0x54;
|
|
reloc[j].r_info = R_MIPS_26;
|
|
}
|
|
|
|
new_reloc++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Relocation type 7 -> type 0
|
|
if (ELF32_R_TYPE(reloc[j].r_info) == R_MIPS_GPREL16) {
|
|
reloc[j].r_info &= ~R_MIPS_GPREL16;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void fixMainArgp(void *buffer) {
|
|
uint32_t text_addr = (uint32_t)buffer + 0x60;
|
|
|
|
// Remove atexit
|
|
*(uint32_t *)(text_addr + 0x90) = 0;
|
|
|
|
// Redirect function
|
|
*(uint32_t *)(text_addr + 0xD0) = 0x0C000000 | (((uint32_t)(HIJACKED_ADDRESS) >> 2) & 0x03FFFFFF);
|
|
*(uint32_t *)(text_addr + 0x144) = 0x3C050000 | (HIJACKED_ADDRESS >> 16);
|
|
*(uint32_t *)(text_addr + 0x148) = 0x24A50000 | (HIJACKED_ADDRESS & 0xFFFF);
|
|
|
|
// Hijack function
|
|
memcpy((void *)text_addr + HIJACKED_ADDRESS, binary, sizeof(binary));
|
|
}
|
|
|
|
void fixPrx(void *buffer) {
|
|
uint32_t text_addr = (uint32_t)buffer + 0x60;
|
|
|
|
if (*(uint32_t *)(text_addr + 0xD0) == 0x0C000000) {
|
|
argp_fix = 1;
|
|
} else {
|
|
argp_fix = 0;
|
|
}
|
|
|
|
// Fix main argp
|
|
if (argp_fix)
|
|
fixMainArgp(buffer);
|
|
|
|
// Fix relocations
|
|
fixRelocations(buffer);
|
|
}
|
|
|
|
int writeDataPsp(SceUID fdin, SceUID fdout, uint32_t offset, int size) {
|
|
HeaderKeys keys;
|
|
uint8_t kirk_header_backup[0x90];
|
|
|
|
// Init kirk
|
|
kirk_init();
|
|
|
|
// Find header
|
|
HeaderList *header = getHeaderList(size);
|
|
if (!header)
|
|
return -1;
|
|
|
|
uint8_t *kirk_header = header->kirkHeader;
|
|
uint8_t *psp_header = header->pspHeader;
|
|
int kirk_size = getKirkSize(kirk_header);
|
|
|
|
memcpy(big_buffer, kirk_header, KIRK_HEADER_SIZE);
|
|
memcpy(kirk_header_backup, big_buffer, sizeof(kirk_header_backup));
|
|
|
|
kirk_decrypt_keys((uint8_t *)&keys, big_buffer);
|
|
memcpy(big_buffer, &keys, sizeof(HeaderKeys));
|
|
|
|
// Read DATA.PSP
|
|
sceIoLseek(fdin, offset, SCE_SEEK_SET);
|
|
sceIoRead(fdin, big_buffer + KIRK_HEADER_SIZE, size);
|
|
|
|
// Check PRX
|
|
if (!checkPRX(big_buffer + KIRK_HEADER_SIZE))
|
|
return -2;
|
|
|
|
// Fix PRX
|
|
fixPrx(big_buffer + KIRK_HEADER_SIZE);
|
|
|
|
// Encrypt
|
|
if (kirk_CMD0(big_buffer, big_buffer, sizeof(big_buffer)) != 0)
|
|
return -3;
|
|
|
|
// Forge cmac block
|
|
memcpy(big_buffer, kirk_header_backup, sizeof(kirk_header_backup));
|
|
if (kirk_forge(big_buffer, sizeof(big_buffer)) != 0)
|
|
return -4;
|
|
|
|
// Write DATA.PSP
|
|
sceIoWrite(fdout, psp_header, PSP_HEADER_SIZE);
|
|
sceIoWrite(fdout, big_buffer + KIRK_HEADER_SIZE, kirk_size - KIRK_HEADER_SIZE);
|
|
|
|
return kirk_size + (PSP_HEADER_SIZE - KIRK_HEADER_SIZE);
|
|
}
|
|
|
|
void setDiscId(char *id) {
|
|
memset(disc_id, 0, sizeof(disc_id));
|
|
strncpy(disc_id, id, sizeof(disc_id));
|
|
}
|
|
|
|
void adjustParamSfo(void *buf) {
|
|
SFOHeader *h = (SFOHeader *)buf;
|
|
SFOEntry *e = (SFOEntry *)((uint32_t)h + sizeof(SFOHeader));
|
|
|
|
int i;
|
|
for (i = 0; i < h->count; i++) {
|
|
int j;
|
|
for (j = 0; j < N_PARAM_ENTRIES; j++) {
|
|
if (!param_entries[j].exist) {
|
|
if (strstr((char *)((uint32_t)h + h->keyofs + e[i].nameofs), param_entries[j].name)) {
|
|
memcpy(param_entries[j].value, (void *)((uint32_t)h + h->valofs + e[i].dataofs), e[i].valsize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int writeParamSfo(SceUID fdin, SceUID fdout, uint32_t offset, int size) {
|
|
char head[0x2000], keys[0x2000], data[0x2000];
|
|
|
|
memset(head, 0, sizeof(head));
|
|
memset(keys, 0, sizeof(keys));
|
|
memset(data, 0, sizeof(data));
|
|
|
|
char *k = keys;
|
|
char *d = data;
|
|
|
|
// Read PARAM.SFO
|
|
sceIoLseek(fdin, offset, SCE_SEEK_SET);
|
|
sceIoRead(fdin, big_buffer, size);
|
|
|
|
// Adjust PARAM.SFO
|
|
adjustParamSfo(big_buffer);
|
|
|
|
// Header info
|
|
SFOHeader *h = (SFOHeader *)head;
|
|
SFOEntry *e = (SFOEntry *)((uint32_t)h + sizeof(SFOHeader));
|
|
|
|
h->magic = PSF_MAGIC;
|
|
h->version = PSF_VERSION;
|
|
|
|
uint32_t count = 0;
|
|
|
|
int i;
|
|
for (i = 0; i < N_PARAM_ENTRIES; i++) {
|
|
// Entry info
|
|
h->count = ++count;
|
|
e->nameofs = k - keys;
|
|
e->dataofs = d - data;
|
|
e->alignment = 4;
|
|
e->type = param_entries[i].type;
|
|
|
|
// Add name
|
|
strcpy(k, param_entries[i].name);
|
|
k += strlen(k) + 1;
|
|
|
|
// Add value
|
|
if (e->type == PSF_TYPE_VAL) {
|
|
e->valsize = sizeof(int);
|
|
e->totalsize = sizeof(int);
|
|
*(uint32_t *)d = *(uint32_t *)param_entries[i].value;
|
|
d += sizeof(int);
|
|
} else {
|
|
e->valsize = strlen(param_entries[i].value) + 1;
|
|
e->totalsize = (e->valsize + 3) & ~3;
|
|
|
|
memset(d, 0, e->totalsize);
|
|
memcpy(d, param_entries[i].value, e->valsize);
|
|
d += e->totalsize;
|
|
}
|
|
|
|
e++;
|
|
}
|
|
|
|
// Adjust keysofs and valofs
|
|
uint32_t keyofs = (char *)e - head;
|
|
h->keyofs = keyofs;
|
|
|
|
uint32_t align = 3 - ((uint32_t)(k - keys) & 3);
|
|
while (align < 3) {
|
|
k++;
|
|
align--;
|
|
}
|
|
|
|
h->valofs = keyofs + (k - keys);
|
|
|
|
// Write PARAM.SFO
|
|
sceIoWrite(fdout, head, (char *)e - head);
|
|
sceIoWrite(fdout, keys, k - keys);
|
|
sceIoWrite(fdout, data, d - data);
|
|
|
|
return ((char *)e - head) + (k - keys) + (d - data);
|
|
}
|
|
|
|
int writeIcon0(SceUID fdin, SceUID fdout, uint32_t offset, int size) {
|
|
sceIoLseek(fdin, offset, SCE_SEEK_SET);
|
|
sceIoRead(fdin, big_buffer, size);
|
|
|
|
// Check
|
|
if (size == 0 || ((uint32_t *)big_buffer)[0] != 0x474E5089 || ((uint32_t *)big_buffer)[1] != 0x0A1A0A0D || ((uint32_t *)big_buffer)[3] != 0x52444849 ||
|
|
((uint32_t *)big_buffer)[4] > 0x90000000 || ((uint32_t *)big_buffer)[5] > 0x90000000) {
|
|
memcpy(big_buffer, icon0, sizeof(icon0));
|
|
size = sizeof(icon0);
|
|
}
|
|
|
|
sceIoWrite(fdout, big_buffer, size);
|
|
|
|
return size;
|
|
}
|
|
|
|
int copyByOffset(SceUID fdin, SceUID fdout, uint32_t offset, int size) {
|
|
if (size) {
|
|
sceIoLseek(fdin, offset, SCE_SEEK_SET);
|
|
sceIoRead(fdin, big_buffer, size);
|
|
sceIoWrite(fdout, big_buffer, size);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
int writePboot(char *filein, char *fileout) {
|
|
int res = 0;
|
|
|
|
big_buffer = malloc(BIG_BUFFER_SIZE);
|
|
if (!big_buffer)
|
|
return -1;
|
|
|
|
memset(big_buffer, 0, BIG_BUFFER_SIZE);
|
|
|
|
// Reset buffers
|
|
memset(psp_system_ver, 0, sizeof(psp_system_ver));
|
|
memset(title, 0, sizeof(title));
|
|
|
|
// Open input file
|
|
SceUID fdin = sceIoOpen(filein, SCE_O_RDONLY, 0);
|
|
if (fdin < 0)
|
|
goto ERROR_FREE_BUFFER_EXIT;
|
|
|
|
// Open output file
|
|
SceUID fdout = sceIoOpen(fileout, SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0777);
|
|
if (fdout < 0)
|
|
goto ERROR_FREE_BUFFER_EXIT;
|
|
|
|
// Get size of input file
|
|
uint32_t file_size = sceIoLseek(fdin, 0, SCE_SEEK_END);
|
|
sceIoLseek(fdin, 0, SCE_SEEK_SET);
|
|
|
|
uint32_t offset = 0;
|
|
|
|
// Read header
|
|
PBPHeader h_in;
|
|
sceIoRead(fdin, &h_in, sizeof(PBPHeader));
|
|
|
|
// Write header
|
|
PBPHeader h_out;
|
|
h_out.magic = PBP_MAGIC;
|
|
h_out.version = PBP_VERSION;
|
|
|
|
sceIoWrite(fdout, &h_out, sizeof(PBPHeader));
|
|
offset += sizeof(PBPHeader);
|
|
|
|
// Write PARAM.SFO
|
|
h_out.param_offset = offset;
|
|
res = writeParamSfo(fdin, fdout, h_in.param_offset, h_in.icon0_offset - h_in.param_offset);
|
|
if (res < 0)
|
|
goto ERROR_CLOSE_DESCRIPTORS_EXIT;
|
|
|
|
offset += res;
|
|
|
|
// Write ICON0.PNG, ICON1.PMF, PIC0.PNG, PIC1.PNG, SND0.AT3
|
|
h_out.icon0_offset = offset;
|
|
offset += writeIcon0(fdin, fdout, h_in.icon0_offset, h_in.icon1_offset - h_in.icon0_offset);
|
|
|
|
h_out.icon1_offset = offset;
|
|
offset += copyByOffset(fdin, fdout, h_in.icon1_offset, h_in.pic0_offset - h_in.icon1_offset);
|
|
|
|
h_out.pic0_offset = offset;
|
|
offset += copyByOffset(fdin, fdout, h_in.pic0_offset, h_in.pic1_offset - h_in.pic0_offset);
|
|
|
|
h_out.pic1_offset = offset;
|
|
offset += copyByOffset(fdin, fdout, h_in.pic1_offset, h_in.snd0_offset - h_in.pic1_offset);
|
|
|
|
h_out.snd0_offset = offset;
|
|
offset += copyByOffset(fdin, fdout, h_in.snd0_offset, h_in.elf_offset - h_in.snd0_offset);
|
|
|
|
// Write DATA.PSP
|
|
h_out.elf_offset = offset;
|
|
res = writeDataPsp(fdin, fdout, h_in.elf_offset, h_in.psar_offset - h_in.elf_offset);
|
|
if (res < 0)
|
|
goto ERROR_CLOSE_DESCRIPTORS_EXIT;
|
|
|
|
offset += res;
|
|
|
|
// Write DATA.PSAR
|
|
h_out.psar_offset = offset;
|
|
offset += copyByOffset(fdin, fdout, h_in.psar_offset, file_size - h_in.psar_offset);
|
|
|
|
// Update header
|
|
sceIoLseek(fdout, 0, SCE_SEEK_SET);
|
|
sceIoWrite(fdout, &h_out, sizeof(PBPHeader));
|
|
|
|
ERROR_CLOSE_DESCRIPTORS_EXIT:
|
|
sceIoClose(fdout);
|
|
sceIoClose(fdin);
|
|
|
|
ERROR_FREE_BUFFER_EXIT:
|
|
free(big_buffer);
|
|
|
|
if (res < 0)
|
|
sceIoRemove(fileout);
|
|
|
|
return res;
|
|
} |