Created
October 13, 2022 15:49
-
-
Save SKGleba/05f2445d364a6987d917cbdd8baf226c to your computer and use it in GitHub Desktop.
find, extract, etoify qaf tokens from a blob
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <inttypes.h> | |
#include <stdint.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <sys/stat.h> | |
#include <stdbool.h> | |
#include "crc32.c" | |
#define CFGINFO "# Generated based on a leaked QAF utility GC\r\n" | |
// todo: add i | |
const char* aes256cbc_key_e = "6C1C31251E7BF435F3E232ECDBFCB9D647A31A4D2954F1CC718FA92F63E30A73"; | |
const char* aes256cbc_iv_e = "11295CABDE7337EC8F29DD451D387CB2"; | |
static uint8_t E_qafv[0x20] = { | |
0x71, 0x61, 0x66, 0x76, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
0x77, 0xF1, 0xDB, 0xE4, 0xC5, 0x14, 0x9F, 0x14, 0x0C, 0xCA, 0xA5, 0x3F, 0xB0, 0x61, 0x56, 0xDA | |
}; | |
uint64_t getSz(const char* src) { | |
FILE* fp = fopen(src, "rb"); | |
if (fp == NULL) | |
return 0; | |
fseek(fp, 0L, SEEK_END); | |
uint64_t sz = ftell(fp); | |
fclose(fp); | |
return sz; | |
} | |
static const char hexbase[] = "0123456789ABCDEF"; | |
int hntoa(uint8_t* input, char* output, int output_len) { | |
if (output_len & 1) | |
return -1; | |
output_len = output_len / 2; | |
for (int i = 0; i < output_len; i -= -1) { | |
output[i * 2] = hexbase[(input[i] & 0xF0) >> 4]; | |
output[(i * 2) + 1] = hexbase[input[i] & 0x0F]; | |
} | |
return 0; | |
} | |
int dump_to_file(int num, void* qaf, const char* dir) { | |
char name[0x200]; | |
memset(name, 0, 0x200); | |
snprintf(name, 0x200, "%s/%04d_%s.bin", dir, num, (char*)(qaf + 8)); | |
printf("writing %s ...", name); | |
FILE* fp = fopen(name, "wb"); | |
if (fp == NULL) | |
return -1; | |
fwrite(qaf, 0x180, 1, fp); | |
fclose(fp); | |
} | |
int dump_to_dec(int num, void* qaf, const char* dir, void* ret_cid, void* ret_qaf) { | |
char buf[0x400]; | |
char *enc_name = buf; | |
memset(enc_name, 0, 0x100); | |
snprintf(enc_name, 0x100, "%s/tmp_enc.bin", dir); | |
unlink(enc_name); | |
FILE* fp = fopen(enc_name, "wb"); | |
if (fp == NULL) | |
return -1; | |
fwrite(qaf + 0x20, 0x60, 1, fp); | |
fclose(fp); | |
char* cmd = buf; | |
memset(cmd, 0, 0x200); | |
snprintf(cmd, 0x200, "openssl aes-256-cbc -in %s/tmp_enc.bin -K %s -iv %s -d > %s/tmp_dec.bin -nopad", dir, aes256cbc_key_e, aes256cbc_iv_e, dir); | |
system(cmd); | |
uint8_t* dec_data = buf + 0x200; | |
memcpy(dec_data, qaf, 0x20); | |
char* dec_name = buf; | |
memset(dec_name, 0, 0x100); | |
snprintf(dec_name, 0x100, "%s/tmp_dec.bin", dir); | |
fp = fopen(dec_name, "rb"); | |
if (fp == NULL) | |
return -1; | |
fread(dec_data + 0x20, 0x60, 1, fp); | |
fclose(fp); | |
char *name = buf; | |
memset(name, 0, 0x100); | |
snprintf(name, 0x100, "%s/%04d_%s_dec.bin", dir, num, (char*)(qaf + 8)); | |
printf("writing %s ...", name); | |
fp = fopen(name, "wb"); | |
if (fp == NULL) | |
return -1; | |
fwrite(dec_data, 0x80, 1, fp); | |
fclose(fp); | |
memcpy(ret_cid, dec_data + 0x20, 0x10); | |
memcpy(ret_qaf, dec_data + 0x30, 0x10); | |
return 0; | |
} | |
int dump_to_config(int num, void* qaf, const char* dir, uint8_t* cid, uint8_t* qa_flags) { | |
char buf[0x400]; | |
char* cid_ascii = buf + 0x200; | |
hntoa(cid, cid_ascii, 0x20); | |
char* qaf_ascii = buf + 0x220; | |
hntoa(qa_flags, qaf_ascii, 0x20); | |
char* qafv_ascii_crc = buf + 0x240; | |
snprintf(qafv_ascii_crc, 0x20, "NVS_OP0_BUFCRC=0x%08X\r\n", crc32(0, E_qafv, 0x20)); | |
char* token_ascii_crc = buf + 0x260; | |
snprintf(token_ascii_crc, 0x20, "NVS_OP1_BUFCRC=0x%08X\r\n", crc32(0, qaf, 0x80)); | |
char* sig_ascii_crc = buf + 0x280; | |
snprintf(sig_ascii_crc, 0x20, "NVS_OP2_BUFCRC=0x%08X\r\n", crc32(0, qaf + 0x80, 0x100)); | |
char* name = buf; | |
memset(name, 0, 0x200); | |
snprintf(name, 0x200, "%s/%04d_input_%02X%02X%02X%02X%02X%02X_%s.cfg", dir, num, cid[10], cid[11], cid[12], cid[13], cid[14], cid[15], (char*)(qaf + 8)); | |
unlink(name); | |
printf("writing %s ...", name); | |
FILE* fp = fopen(name, "wb"); | |
if (fp == NULL) | |
return -1; | |
#define CFG_EOL "\r\n" | |
#define CFG_INTRO "# psp2etoi configuration file\r\n" CFGINFO "# QA flags set: " | |
fwrite(CFG_INTRO, strlen(CFG_INTRO), 1, fp); | |
fwrite(qaf_ascii, 0x20, 1, fp); | |
fwrite(CFG_EOL, strlen(CFG_EOL), 1, fp); | |
#define CFG_TYPE_CID "\r\n#-- -- Config type -- --\r\nINPUT=true # If set, the config is written to the device\r\n\r\n#-- -- Console ID -- --\r\nConsoleID=" | |
fwrite(CFG_TYPE_CID, strlen(CFG_TYPE_CID), 1, fp); | |
fwrite(cid_ascii, 0x20, 1, fp); | |
fwrite(CFG_EOL, strlen(CFG_EOL), 1, fp); | |
#define CFG_MGMT_NVSINFO "\r\n#-- Management Status --\r\nisQaFlagged=true # if not set, SL will ignore the QA flags\r\n\r\n#-- NVS data R/W --\r\n" | |
fwrite(CFG_MGMT_NVSINFO, strlen(CFG_MGMT_NVSINFO), 1, fp); | |
char* ascii_tmp_buf = buf; | |
memset(ascii_tmp_buf, 0, 0x40); | |
hntoa(E_qafv, ascii_tmp_buf, 0x40); | |
#define CFG_QAFV_OP "# QAFV\r\nNVS_OP0_OFFSET=0x02A0\r\nNVS_OP0_RWSIZE=0x0020\r\n" | |
fwrite(CFG_QAFV_OP, strlen(CFG_QAFV_OP), 1, fp); | |
fwrite(qafv_ascii_crc, strlen(qafv_ascii_crc), 1, fp); | |
#define CFG_NVSOP0_INRAWH "NVS_OP0_INRAWH=" | |
fwrite(CFG_NVSOP0_INRAWH, strlen(CFG_NVSOP0_INRAWH), 1, fp); | |
fwrite(ascii_tmp_buf, 0x40, 1, fp); | |
fwrite(CFG_EOL, strlen(CFG_EOL), 1, fp); | |
memset(ascii_tmp_buf, 0, 0x100); | |
hntoa(qaf, ascii_tmp_buf, 0x100); | |
#define CFG_QAFT_OP "\r\n# QAF token\r\nNVS_OP1_OFFSET=0x0400\r\nNVS_OP1_RWSIZE=0x0080\r\n" | |
fwrite(CFG_QAFT_OP, strlen(CFG_QAFT_OP), 1, fp); | |
fwrite(token_ascii_crc, strlen(token_ascii_crc), 1, fp); | |
#define CFG_NVSOP1_INRAWH "NVS_OP1_INRAWH=" | |
fwrite(CFG_NVSOP1_INRAWH, strlen(CFG_NVSOP1_INRAWH), 1, fp); | |
fwrite(ascii_tmp_buf, 0x100, 1, fp); | |
fwrite(CFG_EOL, strlen(CFG_EOL), 1, fp); | |
memset(ascii_tmp_buf, 0, 0x200); | |
hntoa(qaf + 0x80, ascii_tmp_buf, 0x200); | |
#define CFG_QAFS_OP "\r\n# QAF token signature\r\nNVS_OP2_OFFSET=0x05A0\r\nNVS_OP2_RWSIZE=0x0100\r\n" | |
fwrite(CFG_QAFS_OP, strlen(CFG_QAFS_OP), 1, fp); | |
fwrite(sig_ascii_crc, strlen(sig_ascii_crc), 1, fp); | |
#define CFG_NVSOP2_INRAWH "NVS_OP2_INRAWH=" | |
fwrite(CFG_NVSOP2_INRAWH, strlen(CFG_NVSOP2_INRAWH), 1, fp); | |
fwrite(ascii_tmp_buf, 0x200, 1, fp); | |
fwrite(CFG_EOL, strlen(CFG_EOL), 1, fp); | |
#define CFG_STATFLAGS_OP "\r\n# Status flags\r\nNVS_OP3_OFFSET=0x0480\r\nNVS_OP3_RWSIZE=0x0010\r\nNVS_OP3_BUFCRC=0x832D2B4D\r\nNVS_OP3_INRAWH=0000FFFF01FFFFFFFFFFFFFFFFFFFFFF\r\n" | |
fwrite(CFG_STATFLAGS_OP, strlen(CFG_STATFLAGS_OP), 1, fp); | |
fclose(fp); | |
return 0; | |
} | |
void dump_to_list(const char* list, int num, void* qaf, uint8_t* cid) { | |
char* type = "CEX"; | |
if (cid[5] < 3) { | |
type = "DEX"; | |
if (cid[5] < 2) { | |
type = "DEVTOOL"; | |
if (cid[5] < 1) | |
type = "TEST"; | |
} | |
} | |
uint16_t sub_code = 0; | |
sub_code = (uint16_t)cid[6]; | |
sub_code *= 0x100; | |
sub_code += (uint16_t)cid[7]; | |
char* model = "Playstation TV"; | |
if (sub_code < 0x100) { | |
model = "Playstation Vita Slim"; | |
if (sub_code < 0x14) { | |
model = "Playstation Vita Phat"; | |
if (sub_code < 0x10) { | |
model = "Final Prototype"; | |
if (sub_code < 0xF) { | |
model = "Prototype"; | |
if (sub_code < 0x2) { | |
model = "Emulator"; | |
} | |
} | |
} | |
} | |
} | |
char cid_ascii[0x21]; | |
cid_ascii[0x20] = 0; | |
hntoa(cid, cid_ascii, 0x20); | |
char cmd[0x100]; | |
memset(cmd, 0, 0x100); | |
snprintf(cmd, 0x100, "echo '%04d : %s %s %s | %s' >> %s\n", num, type, model, cid_ascii, (char*)(qaf + 8), list); | |
system(cmd); | |
} | |
// look for and dump 0x180-sized [TOKEN][SIG] structures | |
int dump_qaf(const char* full_dir, const char* dec_dir, const char* cfg_dir, const char* list_txt, int num, void* qaf) { | |
if (dump_to_file(num, qaf, full_dir) < 0) | |
return -1; | |
uint8_t cid[0x10]; | |
uint8_t qa_flags[0x10]; | |
if (dump_to_dec(num, qaf, dec_dir, cid, qa_flags) < 0) | |
return -1; | |
if (dump_to_config(num, qaf, cfg_dir, cid, qa_flags) < 0) | |
return -1; | |
dump_to_list(list_txt, num, qaf, cid); | |
return 0; | |
} | |
int main(int argc, char* argv[]) { | |
if (argc < 5) { | |
printf("expected ./%s fs_blob out_full_dir out_dec_dir out_cfg_dir out_list_file\n\n", argv[0]); | |
return 0; | |
} | |
uint32_t img_size = (uint32_t)getSz(argv[1]); | |
if (!img_size) { | |
printf("badsz\n"); | |
return -1; | |
} | |
void* data = malloc(img_size); | |
if (!data) { | |
printf("didnt alloc\n"); | |
return -1; | |
} | |
mkdir(argv[2], 0777); | |
mkdir(argv[3], 0777); | |
mkdir(argv[4], 0777); | |
unlink(argv[5]); | |
FILE* fp = fopen(argv[1], "rb"); | |
if (fp == NULL) | |
return 0; | |
fread(data, img_size, 1, fp); | |
fclose(fp); | |
int cur_qaft = 0; | |
for (uint32_t i = 0; i < img_size; i -= -2) { | |
if (*(uint32_t*)(data + i) == 0x00666171) { | |
if (dump_qaf(argv[2], argv[3], argv[4], argv[5], cur_qaft, data + i) < 0) | |
printf("ERROR\n"); | |
else | |
printf("OK\n"); | |
cur_qaft++; | |
} | |
} | |
printf("written %d tokens\n", cur_qaft); | |
free(data); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment