diff --git a/CMakeLists.txt b/CMakeLists.txt index 44c00a3c..e0c3d09a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,7 @@ add_executable(VitaShell network_download.c context_menu.c archive.c + pbp.c psarc.c photo.c audioplayer.c @@ -124,6 +125,7 @@ add_executable(VitaShell utils.c elf.c sha1.c + sha256.c minizip/zip.c minizip/ioapi.c bm.c @@ -148,10 +150,10 @@ add_executable(VitaShell ) add_dependencies(VitaShell vitashell_user_stubs) -add_dependencies(VitaShell kernel.skprx) -add_dependencies(VitaShell user.suprx) -add_dependencies(VitaShell patch.skprx) -add_dependencies(VitaShell usbdevice.skprx) +add_dependencies(VitaShell kernel.skprx-self) +add_dependencies(VitaShell user.suprx-self) +add_dependencies(VitaShell patch.skprx-self) +add_dependencies(VitaShell usbdevice.skprx-self) target_link_libraries(VitaShell ${CMAKE_CURRENT_BINARY_DIR}/modules/user/libVitaShellUser_stub_weak.a diff --git a/main.h b/main.h index b080e657..280e6568 100644 --- a/main.h +++ b/main.h @@ -54,7 +54,7 @@ // VitaShell version major.minor #define VITASHELL_VERSION_MAJOR 0x02 -#define VITASHELL_VERSION_MINOR 0x00 +#define VITASHELL_VERSION_MINOR 0x02 #define VITASHELL_VERSION ((VITASHELL_VERSION_MAJOR << 0x18) | (VITASHELL_VERSION_MINOR << 0x10)) diff --git a/modules/kernel/main.c b/modules/kernel/main.c index 69fbb181..1e7d029a 100644 --- a/modules/kernel/main.c +++ b/modules/kernel/main.c @@ -20,10 +20,10 @@ #include #include #include +#include #include #include -#include #include diff --git a/package_installer.c b/package_installer.c index 20cf50a2..7fdee589 100644 --- a/package_installer.c +++ b/package_installer.c @@ -48,14 +48,14 @@ static int unloadScePaf() { return sceSysmoduleUnloadModuleInternalWithArg(SCE_SYSMODULE_INTERNAL_PAF, 0, NULL, &buf); } -int promotePsm(const char *path, const char *titleid) { +int promoteCma(const char *path, const char *titleid, int type) { int res; ScePromoterUtilityImportParams promoteArgs; memset(&promoteArgs,0x00,sizeof(ScePromoterUtilityImportParams)); strncpy(promoteArgs.path,path,0x7F); strncpy(promoteArgs.titleid,titleid,0xB); - promoteArgs.type = SCE_PKG_TYPE_PSM; + promoteArgs.type = type; promoteArgs.attribute = 0x1; res = loadScePaf(); @@ -243,7 +243,7 @@ int makeHeadBin() { getSfoString(sfo_buffer, "TITLE_ID", titleid, sizeof(titleid)); // Enforce TITLE_ID format - if ((strlen(titleid) != 9) || (strncmp(titleid, strupr(titleid), 9) != 0)) + if (TITLEID_FMT_CHECK(titleid)) return VITASHELL_ERROR_INVALID_TITLEID; // Get content id @@ -291,6 +291,7 @@ int makeHeadBin() { return 0; } + int installPackage(const char *file) { int res; diff --git a/package_installer.h b/package_installer.h index 2a56e029..342855fe 100644 --- a/package_installer.h +++ b/package_installer.h @@ -24,12 +24,16 @@ #define PACKAGE_DIR "ux0:data/pkg" #define HEAD_BIN PACKAGE_DIR "/sce_sys/package/head.bin" +#define TITLEID_FMT_CHECK(x) (x == NULL) || (strlen(x) != 9) || (strncmp(x, strupr(x), 9) != 0) + typedef struct { char *file; } InstallArguments; int promoteApp(const char *path); -int promotePsm(const char *path, const char *titleid); +int promoteCma(const char *path, const char *titleid, int type); +int promotePsp(const char *path); + int deleteApp(const char *titleid); int checkAppExist(const char *titleid); diff --git a/pbp.c b/pbp.c new file mode 100644 index 00000000..44278af1 --- /dev/null +++ b/pbp.c @@ -0,0 +1,319 @@ +/* + VitaShell + Copyright (C) 2015-2018, 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 . +*/ +#include +#include "sha256.h" +#include "main.h" +#include "pbp.h" +#include "sfo.h" + +int read_content_id_data_psp(SceUID pbp_fd, char* content_id) { + DataPspHeader data_psp_header; + + // read data.psp header + int read_sz = sceIoRead(pbp_fd, &data_psp_header, sizeof(DataPspHeader)); + if(read_sz < sizeof(DataPspHeader)) return 0; + + // copy the content id from data.psp header to this content_id buffer + strncpy(content_id, data_psp_header.content_id, 0x30); + + return (strlen(content_id) == 36); +} + +int read_content_id_npumdimg(SceUID pbp_fd, char* content_id) { + NpUmdImgHeader npumdimg_header; + + // read npumd header + int read_sz = sceIoRead(pbp_fd, &npumdimg_header, sizeof(NpUmdImgHeader)); + if(read_sz < sizeof(NpUmdImgHeader)) return 0; + + // copy the content id from npumdimg_header to this content_id buffer + strncpy(content_id, npumdimg_header.content_id, 0x30); + + return (strlen(content_id) == 36); +} + +int is_psx_signed(SceUID pbp_fd, int data_psar_offset, int pbp_type) { // check psx eboot is signed. + char pgd_magic[0x4]; // this is to filter out psx2psp eboots, and other such tools. + // which could never work from the vita's LiveSpace. + // seek to iso header + if(pbp_type == PBP_TYPE_PSISOIMG) + sceIoLseek(pbp_fd, data_psar_offset+0x400, SCE_SEEK_SET); + else + sceIoLseek(pbp_fd, data_psar_offset+0x200, SCE_SEEK_SET); + + // read magic + int read_len = sceIoRead(pbp_fd, pgd_magic, sizeof(pgd_magic)); + if(read_len < sizeof(pgd_magic)) + return PBP_TYPE_UNKNOWN; + + // if is not PGD, then it is not a signed PSX PBP. + if(memcmp(pgd_magic, "\0PGD", sizeof(pgd_magic)) == 0) + return pbp_type; + else + return PBP_TYPE_UNKNOWN; +} + +int determine_pbp_type(SceUID pbp_fd, PbpHeader* pbp_header) { + char data_psar_magic[0x8]; + + int read_sz = sceIoRead(pbp_fd, pbp_header, sizeof(PbpHeader)); + if(read_sz < sizeof(PbpHeader)) return 0; + + // seek to data.psar + sceIoLseek(pbp_fd, pbp_header->data_psar_ptr, SCE_SEEK_SET); + + // read magic value to determine pbp type + read_sz = sceIoRead(pbp_fd, data_psar_magic, sizeof(data_psar_magic)); + if(read_sz < sizeof(data_psar_magic)) return 0; + + if(memcmp(data_psar_magic, "NPUMDIMG", 0x8) == 0) { // psp + return PBP_TYPE_NPUMDIMG; + } + else if(memcmp(data_psar_magic, "PSISOIMG", 0x8) == 0) { // ps1 single disc + return is_psx_signed(pbp_fd, pbp_header->data_psar_ptr, PBP_TYPE_PSISOIMG); + } + else if(memcmp(data_psar_magic, "PSTITLEI", 0x8) == 0) { // ps1 multi disc + return is_psx_signed(pbp_fd, pbp_header->data_psar_ptr, PBP_TYPE_PSTITLEIMG); + } + else{ // update package, homebrew, etc, + return PBP_TYPE_UNKNOWN; + } + + if(read_sz < sizeof(PbpHeader)) return PBP_TYPE_UNKNOWN; +} + +int read_data_psar_header(SceUID pbp_fd, char* content_id) { + PbpHeader pbp_header; + int pbp_type = determine_pbp_type(pbp_fd, &pbp_header); + if(pbp_type == PBP_TYPE_NPUMDIMG) { + // seek to start of npumdimg + sceIoLseek(pbp_fd, pbp_header.data_psar_ptr, SCE_SEEK_SET); + + // read content_id from npumdimg + return read_content_id_npumdimg(pbp_fd, content_id); + } + else if(pbp_type == PBP_TYPE_PSISOIMG || pbp_type == PBP_TYPE_PSTITLEIMG) { + // seek to start of data.psp + sceIoLseek(pbp_fd, pbp_header.data_psp_ptr, SCE_SEEK_SET); + + // read content_id from data.psp + return read_content_id_data_psp(pbp_fd, content_id); + } + else { + return 0; + } +} + +void get_sce_discinfo_sig(char* sce_discinfo, char* disc_id) { + memset(sce_discinfo, 0x00, 0x100); + SceUID discinfo_fd = sceIoOpen("vs0:app/NPXS10028/__sce_discinfo", SCE_O_RDONLY, 0); + + if(discinfo_fd < 0) + return; + + int read_size = 0; + do{ + read_size = sceIoRead(discinfo_fd, sce_discinfo, 0x100); + if(strncmp(sce_discinfo, disc_id, 0x9) == 0) { + break; + } + } while(read_size >= 0x100); + + sceIoClose(discinfo_fd); +} + +int read_sfo(SceUID pbp_fd, void** param_sfo_buffer){ + PbpHeader pbp_header; + // read pbp header + int read_sz = sceIoRead(pbp_fd, &pbp_header, sizeof(PbpHeader)); + if(read_sz < sizeof(PbpHeader)) return 0; + + // get sfo size + int param_sfo_size = pbp_header.icon0_png_ptr - pbp_header.param_sfo_ptr; + if(param_sfo_size <= 0) return 0; + + // allocate a buffer for the param.sfo file + *param_sfo_buffer = malloc(param_sfo_size); + if(*param_sfo_buffer == NULL) return 0; + + // seek to the start of param.sfo + sceIoLseek(pbp_fd, pbp_header.param_sfo_ptr, SCE_SEEK_SET); + + // read the param.sfo file + read_sz = sceIoRead(pbp_fd, *param_sfo_buffer, param_sfo_size); + if(read_sz < param_sfo_size) { + free(*param_sfo_buffer); + *param_sfo_buffer = NULL; + return 0; + } + + return param_sfo_size; +} + +int hash_pbp(SceUID pbp_fd, unsigned char* out_hash) { + unsigned char wbuf[0x7c0]; + + // seek to the start of the eboot.pbp + sceIoLseek(pbp_fd, 0x00, SCE_SEEK_SET); + + // inital read + int read_sz = sceIoRead(pbp_fd, wbuf, sizeof(wbuf)); + if(read_sz < sizeof(PbpHeader)) return read_sz; + + // calculate data hash size + size_t hash_sz = (((PbpHeader*)wbuf)->data_psar_ptr + 0x1C0000); + + // initalize hash + SHA256_CTX ctx; + sha256_init(&ctx); + + // first hash + sha256_update(&ctx, wbuf, read_sz); + size_t total_hashed = read_sz; + + do { + read_sz = sceIoRead(pbp_fd, wbuf, sizeof(wbuf)); + + if((total_hashed + read_sz) > hash_sz) + read_sz = (hash_sz - total_hashed); // calculate remaining + + sha256_update(&ctx, wbuf, read_sz); + total_hashed += read_sz; + + if(read_sz < sizeof(wbuf)) // treat EOF as complete + total_hashed = hash_sz; + + } while(total_hashed < hash_sz); + + sha256_final(&ctx, out_hash); + + return 1; +} + + +int get_pbp_sfo(const char* pbp_file, void** param_sfo_buffer) { + PbpHeader pbp_header; + + if(param_sfo_buffer == NULL) return 0; + *param_sfo_buffer = NULL; + + int res = 0; + + if(pbp_file != NULL) { + SceUID pbp_fd = sceIoOpen(pbp_file, SCE_O_RDONLY, 0); + if(pbp_fd < 0) return 0; + + // read param.sfo from pbp + res = read_sfo(pbp_fd, param_sfo_buffer); + + sceIoClose(pbp_fd); + } + + return res; +} + +int get_pbp_type(const char* pbp_file) { + int res = 0; + PbpHeader pbp_header; + if(pbp_file != NULL) { + SceUID pbp_fd = sceIoOpen(pbp_file, SCE_O_RDONLY, 0); + if(pbp_fd < 0) + return 0; + + res = determine_pbp_type(pbp_fd, &pbp_header); + + sceIoClose(pbp_fd); + } + + return res; +} + +int get_pbp_content_id(const char* pbp_file, char* content_id) { + int res = 0; + + if(pbp_file != NULL && content_id != NULL) { + SceUID pbp_fd = sceIoOpen(pbp_file, SCE_O_RDONLY, 0); + if(pbp_fd < 0) + return 0; + res = read_data_psar_header(pbp_fd, content_id); + + // check the content id is valid + if(res) { + int content_id_len = strnlen(content_id, 0x30); + if(content_id_len != 0x24) res = 0; + } + + sceIoClose(pbp_fd); + } + + return res; + +} + +int gen_sce_ebootpbp(const char* psp_game_folder, char* disc_id) { + int res = 0; + + unsigned char pbp_hash[0x20]; + char sce_ebootpbp[0x200]; + char sce_discinfo[0x100]; + + int sw_version = 0; + PbpHeader pbp_header; + + if(psp_game_folder != NULL) { + char ebootpbp_path[MAX_PATH_LENGTH]; + char sce_ebootpbp_path[MAX_PATH_LENGTH]; + + snprintf(ebootpbp_path, MAX_PATH_LENGTH, "%s/EBOOT.PBP", psp_game_folder); + snprintf(sce_ebootpbp_path, MAX_PATH_LENGTH, "%s/__sce_ebootpbp", psp_game_folder); + + memset(pbp_hash, 0x00, sizeof(pbp_hash)); + memset(sce_ebootpbp, 0x00, sizeof(pbp_hash)); + + SceUID pbp_fd = sceIoOpen(ebootpbp_path, SCE_O_RDONLY, 0); + + if(pbp_fd < 0) + return res; + + int pbp_type = determine_pbp_type(pbp_fd, &pbp_header); // determine pbp header + + if(pbp_type == PBP_TYPE_PSISOIMG || pbp_type == PBP_TYPE_NPUMDIMG) + res = hash_pbp(pbp_fd, pbp_hash); // hash eboot.pbp + if(pbp_type == PBP_TYPE_PSTITLEIMG) + get_sce_discinfo_sig(sce_discinfo, disc_id); // read sce_discinfo + + sceIoClose(pbp_fd); + + if(pbp_type == PBP_TYPE_UNKNOWN) + return res; + + + // actually generate the __sce_ebootpbp + if(pbp_type == PBP_TYPE_NPUMDIMG) + res = _vshNpDrmEbootSigGenPsp(ebootpbp_path, pbp_hash, sce_ebootpbp, &sw_version); + else if(pbp_type == PBP_TYPE_PSISOIMG) + res = _vshNpDrmEbootSigGenPs1(ebootpbp_path, pbp_hash, sce_ebootpbp, &sw_version); + else if(pbp_type == PBP_TYPE_PSTITLEIMG) + res = _vshNpDrmEbootSigGenMultiDisc(ebootpbp_path, sce_discinfo, sce_ebootpbp, &sw_version); + + if(res >= 0) { // write __sce_ebootpbp + res = WriteFile(sce_ebootpbp_path, sce_ebootpbp, 0x200); + } + } + return res; +} \ No newline at end of file diff --git a/pbp.h b/pbp.h new file mode 100644 index 00000000..1f02a501 --- /dev/null +++ b/pbp.h @@ -0,0 +1,105 @@ +/* + VitaShell + Copyright (C) 2015-2018, 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 . +*/ + +#ifndef __PBP_H__ +#define __PBP_H__ + + +typedef enum PbpType{ + PBP_TYPE_NPUMDIMG = 0, + PBP_TYPE_PSISOIMG = 1, + PBP_TYPE_PSTITLEIMG = 2, + PBP_TYPE_UNKNOWN = 3 +} PbpType; + +// for PSISOIMG and PSTITLEIMG contents + +typedef struct DataPspHeader{ + SceUInt8 magic[0x4]; + SceUInt8 unk[0x7C]; + SceUInt8 unk2[0x32]; + SceUInt8 unk3[0xE]; + SceUInt8 hash[0x14]; + SceUInt8 reserved[0x58]; + SceUInt8 unk4[0x434]; + char content_id[0x30]; +} DataPspHeader; + +// for NPUMDIMG content +typedef struct NpUmdImgBody { + SceUInt16 sector_size; // 0x0800 + SceUInt16 unk_2; // 0xE000 + SceUInt32 unk_4; + SceUInt32 unk_8; + SceUInt32 unk_12; + SceUInt32 unk_16; + SceUInt32 lba_start; + SceUInt32 unk_24; + SceUInt32 nsectors; + SceUInt32 unk_32; + SceUInt32 lba_end; + SceUInt32 unk_40; + SceUInt32 block_entry_offset; + char disc_id[0x10]; + SceUInt32 header_start_offset; + SceUInt32 unk_68; + SceUInt8 unk_72; + SceUInt8 bbmac_param; + SceUInt8 unk_74; + SceUInt8 unk_75; + SceUInt32 unk_76; + SceUInt32 unk_80; + SceUInt32 unk_84; + SceUInt32 unk_88; + SceUInt32 unk_92; +} NpUmdImgBody; + +typedef struct NpUmdImgHeader{ + SceUInt8 magic[0x08]; // "NPUMDIMG" + SceUInt32 key_index; // usually 2, or 3. + SceUInt32 block_basis; + char content_id[0x30]; + NpUmdImgBody body; + SceUInt8 header_key[0x10]; + SceUInt8 data_key[0x10]; + SceUInt8 header_hash[0x10]; + SceUInt8 padding[0x8]; + SceUInt8 ecdsa_sig[0x28]; +} NpUmdImgHeader; + +// generic eboot.pbp header + +typedef struct PbpHeader +{ + char magic[0x4]; + SceUInt32 version; + SceUInt32 param_sfo_ptr; + SceUInt32 icon0_png_ptr; + SceUInt32 icon1_pmf_ptr; + SceUInt32 pic0_png_ptr; + SceUInt32 pic1_png_ptr; + SceUInt32 snd0_at3_ptr; + SceUInt32 data_psp_ptr; + SceUInt32 data_psar_ptr; +} PbpHeader; + +int get_pbp_type(const char* pbp_file); +int get_pbp_sfo(const char* pbp_file, void** param_sfo_buffer); +int get_pbp_content_id(const char* pbp_file, char* content_id); +int gen_sce_ebootpbp(const char* psp_game_folder, char* disc_id); +#endif \ No newline at end of file diff --git a/refresh.c b/refresh.c index 13100f1e..f9b39d96 100644 --- a/refresh.c +++ b/refresh.c @@ -19,6 +19,7 @@ along with this program. If not, see . */ +#include #include "main.h" #include "init.h" #include "io_process.h" @@ -31,6 +32,7 @@ #include "utils.h" #include "rif.h" #include "pfs.h" +#include "pbp.h" // Note: The promotion process is *VERY* sensitive to the directories used below // Don't change them unless you know what you are doing! @@ -39,6 +41,7 @@ #define PATCH_TEMP "ux0:temp/patch" #define PSM_TEMP "ux0:temp/game" #define THEME_TEMP "ux0:temp/theme" +#define PSP_TEMP "ux0:pspemu/temp/game" #define MAX_DLC_PER_TITLE 1024 @@ -55,14 +58,13 @@ int isCustomHomebrew(const char* path) { return 1; } - - int refreshNeeded(const char *app_path, const char* content_type) { char appmeta_path[MAX_PATH_LENGTH]; char appmeta_param[MAX_PATH_LENGTH]; char sfo_path[MAX_PATH_LENGTH]; int mounted_appmeta; char titleid[12], contentid[50], appver[8]; + if(strcmp(content_type,"psm") == 0) { char contentid_path[MAX_PATH_LENGTH]; @@ -87,6 +89,58 @@ int refreshNeeded(const char *app_path, const char* content_type) { free(cidFile); } + else if(strcmp(content_type, "psp") == 0) { + // read eboot.pbp + char ebootpbp_path[MAX_PATH_LENGTH]; + + // Initalize buffers + memset(titleid,0,12); + memset(contentid,0,50); + memset(appver,0,8); + + snprintf(ebootpbp_path, MAX_PATH_LENGTH, "%s/EBOOT.PBP", app_path); + + // the vita actually uses the folder name as the title id in PSP case + // this is also important for e.g cloning trick + char* app_directory = getFilename(app_path); + if(TITLEID_FMT_CHECK(app_directory)){ + if(app_directory != NULL) free(app_directory); + return 0; + } + strncpy(titleid, app_directory, 11); + free(app_directory); + snprintf(ebootpbp_path, MAX_PATH_LENGTH, "%s/EBOOT.PBP", app_path); + + int pbp_type = get_pbp_type(ebootpbp_path); + if(pbp_type == PBP_TYPE_UNKNOWN) + return 0; + + // Get content_id + if(!get_pbp_content_id(ebootpbp_path, contentid)) + return 0; + + // Get param.sfo + void *sfo_buffer = NULL; + int sfo_size = get_pbp_sfo(ebootpbp_path, &sfo_buffer); + if(sfo_size <= 0) + return 0; + + // always use real disc id from param.sfo for PS1 titles because + // if a ps1 game is installed to the wrong directory, will give + // "Failed to open the memory card" error message. + if(pbp_type == PBP_TYPE_PSISOIMG || pbp_type == PBP_TYPE_PSTITLEIMG) + getSfoString(sfo_buffer, "DISC_ID", titleid, sizeof(titleid)); + + getSfoString(sfo_buffer, "APP_VER", appver, sizeof(appver)); + + // ps1 do not have APP_VER + if(strcmp(appver, "") == 0) + strcpy(appver, "01.00"); + + // free sfo_buffer + if(sfo_buffer != NULL) + free(sfo_buffer); + } else { // Read param.sfo snprintf(sfo_path, MAX_PATH_LENGTH, "%s/sce_sys/param.sfo", app_path); @@ -108,7 +162,7 @@ int refreshNeeded(const char *app_path, const char* content_type) { // Check if app or dlc exists - if (((strcmp(content_type, "app") == 0)||(strcmp(content_type, "dlc") == 0)||(strcmp(content_type,"psm") == 0))&&(checkAppExist(titleid))) { + if(((strcmp(content_type, "app") == 0) || (strcmp(content_type, "dlc") == 0)) && (checkAppExist(titleid))) { char rif_name[48]; char rif_path[MAX_PATH_LENGTH]; @@ -133,10 +187,7 @@ int refreshNeeded(const char *app_path, const char* content_type) { if (checkFileExist(rif_path)) return 0; - if(strcmp(content_type,"psm") == 0) - return 0; } - // Check if patch for installed app exists else if (strcmp(content_type, "patch") == 0) { if (!checkAppExist(titleid)) @@ -160,6 +211,43 @@ int refreshNeeded(const char *app_path, const char* content_type) { return 0; } } + // license not needed to promote psp or psm contents + else if((strcmp(content_type, "psm") == 0 || strcmp(content_type, "psp") == 0) && checkAppExist(titleid)) { + if(strcmp(content_type, "psp") == 0) { + char eboot_signature[0x200]; + + // get path to eboot.pbp + char ebootpbp_path[MAX_PATH_LENGTH]; + snprintf(ebootpbp_path, MAX_PATH_LENGTH, "%s/EBOOT.PBP", app_path); + + // get path to __sce_ebootpbp + char sce_ebootpbp[MAX_PATH_LENGTH]; + snprintf(sce_ebootpbp, MAX_PATH_LENGTH, "%s/__sce_ebootpbp", app_path); + + // check EBOOT.PBP exists + if(getFileSize(ebootpbp_path) < 0) + return 0; + + int sce_ebootpbp_exist = (getFileSize(sce_ebootpbp) >= 0); + + // verify __sce_ebootpbp + if(sce_ebootpbp_exist) { + int read_sz = ReadFile(sce_ebootpbp, eboot_signature, 0x200); + + long unk0; + int verify = _vshNpDrmEbootSigVerify(ebootpbp_path, eboot_signature, &unk0); + + if(verify < 0) // if signature is invalid, then needs refresh + return 1; + + return 0; + } + else { + return 1; + } + } + return 0; + } return 1; } @@ -248,6 +336,7 @@ typedef struct { uint8_t* rif; } license_data_t; + void app_callback(void* data, const char* dir, const char* subdir) { refresh_data_t *refresh_data = (refresh_data_t*)data; char path[MAX_PATH_LENGTH]; @@ -354,50 +443,180 @@ void patch_callback(void* data, const char* dir, const char* subdir) { } } -void psm_callback(void* data, const char* dir, const char* subdir) { +void psp_callback(void* data, const char* dir, const char* subdir) { refresh_data_t *refresh_data = (refresh_data_t*)data; char path[MAX_PATH_LENGTH]; - if (strcasecmp(subdir, vitashell_titleid) == 0) - return; + if (refresh_data->refresh_pass) { + snprintf(path, MAX_PATH_LENGTH, "%s/%s", dir, subdir); + if (refreshNeeded(path, "psp")) { + char contentid[0x30]; + + char sce_ebootpbp[MAX_PATH_LENGTH]; + char eboot_pbp[MAX_PATH_LENGTH]; + char license_rif[MAX_PATH_LENGTH]; + + snprintf(eboot_pbp, MAX_PATH_LENGTH, "%s/EBOOT.PBP", path); + snprintf(sce_ebootpbp, MAX_PATH_LENGTH, "%s/__sce_ebootpbp", path); + + // get pbp type + int pbp_type = get_pbp_type(eboot_pbp); + if(pbp_type != PBP_TYPE_UNKNOWN) { + + // cache current __sce_ebootpbp signature file + void* sce_ebootpbp_sig_data = NULL; + int sce_ebootpbp_sz = allocateReadFile(sce_ebootpbp, &sce_ebootpbp_sig_data); + + + if(get_pbp_content_id(eboot_pbp, contentid)) { + // create directories + char promote_psp_folder[MAX_PATH_LENGTH]; + char promote_psp_game_folder[MAX_PATH_LENGTH]; + char promote_psp_license_folder[MAX_PATH_LENGTH]; + + char promote_license_rif[MAX_PATH_LENGTH]; + char promote_game_folder[MAX_PATH_LENGTH]; + + snprintf(promote_psp_folder, MAX_PATH_LENGTH, "%s/PSP", PSP_TEMP); + snprintf(promote_psp_game_folder, MAX_PATH_LENGTH, "%s/PSP/GAME", PSP_TEMP); + snprintf(promote_psp_license_folder, MAX_PATH_LENGTH, "%s/PSP/LICENSE", PSP_TEMP); + + snprintf(promote_license_rif, MAX_PATH_LENGTH, "%s/PSP/LICENSE/%s.rif", PSP_TEMP, contentid); + + void *sfo_buffer = NULL; + int sfo_size = get_pbp_sfo(eboot_pbp, &sfo_buffer); + + if(sfo_size >= 0) { + + char discid[12]; + + getSfoString(sfo_buffer, "DISC_ID", discid, sizeof(discid)); + + // maintain compatiblity with psp bubble cloning, and other tricks + // use folder name as disc id, *only* on npumdimg + if(pbp_type == PBP_TYPE_NPUMDIMG) + strncpy(discid, subdir, sizeof(discid)-1); + + // ensure its installing PS1 to the correct folder .. + // if ps1 installed to incorrect folder, will give + // 'cannot open the memory card' error message + snprintf(promote_game_folder, MAX_PATH_LENGTH, "%s/PSP/GAME/%s", PSP_TEMP, discid); + sceClibPrintf("promote_game_folder: %s\n", promote_game_folder); + sceClibPrintf("game_folder: %s\n", path); + + // get current rif location + snprintf(license_rif, MAX_PATH_LENGTH, "ux0:/pspemu/PSP/LICENSE/%s.rif", contentid); + + // create the promote directories + + sceIoMkdir("ux0:pspemu", 0006); + sceIoMkdir("ux0:pspemu/temp", 0006); + sceIoMkdir(PSP_TEMP, 0006); + sceIoMkdir(promote_psp_folder, 0006); + sceIoMkdir(promote_psp_game_folder, 0006); + sceIoMkdir(promote_psp_license_folder, 0006); + + // copy the rif to the promote location + int res = copyFile(license_rif, promote_license_rif, NULL); + + if(res < 0) { // no rif found? + // generate fake psp license + SceNpDrmLicense license; + memset(&license, 0x00, sizeof(SceNpDrmLicense)); + license.account_id = 0x0123456789ABCDEFLL; + memset(license.ecdsa_signature, 0xFF, 0x28); + strncpy(license.content_id, contentid, 0x30); + WriteFile(promote_license_rif, &license, offsetof(SceNpDrmLicense, flags)); + } + + // promote will fail if __sce_ebootpbp signature file is invalid (or for another account) + // so we have to generate a new one .. + sceIoRemove(sce_ebootpbp); + + int eboot_gen = gen_sce_ebootpbp(path, discid); + + // move path to promote folder + sceIoRename(path, promote_game_folder); + + int promote = promoteCma(PSP_TEMP, discid, SCE_PKG_TYPE_PSP); + + sceClibPrintf("eboot_gen: %x, promote %x\n", eboot_gen, promote); + + if (promote == 0) { + refresh_data->refreshed++; + } + else { + sceIoRename(promote_game_folder, path); // Restore folder on error + removePath(PSP_TEMP, NULL); // delete what was created + } + + // if eboot signature generation was unsuccessful, write original signature back + if(eboot_gen < 0) { + if(sce_ebootpbp_sz > 0) + WriteFile(sce_ebootpbp, sce_ebootpbp_sig_data, sce_ebootpbp_sz); // Restore __sce_ebootpbp on error + } + } + + if(sfo_buffer != NULL) + free(sfo_buffer); + + } + + if(sce_ebootpbp_sig_data != NULL) + free(sce_ebootpbp_sig_data); + + } + } + SetProgress(++refresh_data->processed, refresh_data->count); + } else { + refresh_data->count++; + } + +} + +void psm_callback(void* data, const char* dir, const char* subdir) { + refresh_data_t *refresh_data = (refresh_data_t*)data; + char path[MAX_PATH_LENGTH]; if (refresh_data->refresh_pass) { - snprintf(path, MAX_PATH_LENGTH, "%s/%s", dir, subdir); + snprintf(path, MAX_PATH_LENGTH, "%s/%s", dir, subdir); if (refreshNeeded(path, "psm")) { - char contentid_path[MAX_PATH_LENGTH]; - snprintf(contentid_path, MAX_PATH_LENGTH, "%s/RW/System/content_id", path); - - char titleid[12]; - void *cidFile = NULL; - - // Initalize Bufer - memset(titleid,0,12); - - // Get content id - allocateReadFile(contentid_path, &cidFile); + char contentid_path[MAX_PATH_LENGTH]; + snprintf(contentid_path, MAX_PATH_LENGTH, "%s/RW/System/content_id", path); + + char titleid[12]; + void *cidFile = NULL; + + // Initalize Bufer + memset(titleid,0,12); - // Get title id from content id - strncpy(titleid,cidFile+7,9); - - //free buffers - free(cidFile); - - - // Get promote path - char promote_path[MAX_PATH_LENGTH]; - snprintf(promote_path,MAX_PATH_LENGTH,"%s/%s",PSM_TEMP,titleid); - - // Move the directory to temp for installation - removePath(promote_path, NULL); - sceIoRename(path, promote_path); - - // Finally call promote - if (promotePsm(PSM_TEMP,titleid) == 0) - refresh_data->refreshed++; - else - sceIoRename(promote_path, path); // Restore folder on error + // Get content id + allocateReadFile(contentid_path, &cidFile); + + // Get title id from content id + strncpy(titleid,cidFile+7,9); + + //free buffers + free(cidFile); + + + // Get promote path + char promote_path[MAX_PATH_LENGTH]; + snprintf(promote_path,MAX_PATH_LENGTH,"%s/%s",PSM_TEMP, titleid); + + // Move the directory to temp for installation + removePath(promote_path, NULL); + sceIoRename(path, promote_path); + + // Finally call promote + if (promoteCma(PSM_TEMP, titleid, SCE_PKG_TYPE_PSM) == 0) { + refresh_data->refreshed++; + } + else{ + sceIoRename(promote_path, path); // Restore folder on error + } + SetProgress(++refresh_data->processed, refresh_data->count); } - SetProgress(++refresh_data->processed, refresh_data->count); } else { refresh_data->count++; } @@ -406,7 +625,7 @@ void psm_callback(void* data, const char* dir, const char* subdir) { int refresh_thread(SceSize args, void *argp) { SceUID thid = -1; refresh_data_t refresh_data = { 0, 0, 0, 0 }; - + // Lock power timers powerLock(); @@ -429,15 +648,22 @@ int refresh_thread(SceSize args, void *argp) { // Get the psm count if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:psm", psm_callback, &refresh_data) < 0) goto EXIT; + + // Get the psp count + if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:pspemu/PSP/GAME", psp_callback, &refresh_data) < 0) + goto EXIT; // Update thread thid = createStartUpdateThread(refresh_data.count, 0); // Make sure we have the temp directories we need sceIoMkdir("ux0:temp", 0006); + sceIoMkdir("ux0:pspemu", 0006); + sceIoMkdir("ux0:pspemu/temp", 0006); sceIoMkdir(DLC_TEMP, 0006); sceIoMkdir(PATCH_TEMP, 0006); sceIoMkdir(PSM_TEMP, 0006); + sceIoMkdir(PSP_TEMP, 0006); refresh_data.refresh_pass = 1; // Refresh apps @@ -456,10 +682,15 @@ int refresh_thread(SceSize args, void *argp) { if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:psm", psm_callback, &refresh_data) < 0) goto EXIT; + // Refresh psp + if (parse_dir_with_callback(SCE_S_IFDIR, "ux0:pspemu/PSP/GAME", psp_callback, &refresh_data) < 0) + goto EXIT; + sceIoRmdir(DLC_TEMP); sceIoRmdir(PATCH_TEMP); sceIoRmdir(PSM_TEMP); - + sceIoRmdir(PSP_TEMP); + // Set progress to 100% sceMsgDialogProgressBarSetValue(SCE_MSG_DIALOG_PROGRESSBAR_TARGET_BAR_DEFAULT, 100); sceKernelDelayThread(COUNTUP_WAIT); @@ -475,7 +706,8 @@ int refresh_thread(SceSize args, void *argp) { // Unlock power timers powerUnlock(); - + + return sceKernelExitDeleteThread(0); } diff --git a/sha256.c b/sha256.c new file mode 100644 index 00000000..eb9c5c07 --- /dev/null +++ b/sha256.c @@ -0,0 +1,158 @@ +/********************************************************************* +* Filename: sha256.c +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Implementation of the SHA-256 hashing algorithm. + SHA-256 is one of the three algorithms in the SHA2 + specification. The others, SHA-384 and SHA-512, are not + offered in this implementation. + Algorithm specification can be found here: + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf + This implementation uses little endian byte order. +*********************************************************************/ + +/*************************** HEADER FILES ***************************/ +#include +#include +#include "sha256.h" + +/****************************** MACROS ******************************/ +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) + +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +/**************************** VARIABLES *****************************/ +static const WORD k[64] = { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) +{ + WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + for ( ; i < 64; ++i) + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +void sha256_init(SHA256_CTX *ctx) +{ + ctx->datalen = 0; + ctx->bitlen = 0; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) +{ + WORD i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + sha256_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void sha256_final(SHA256_CTX *ctx, BYTE hash[]) +{ + WORD i; + + i = ctx->datalen; + + // Pad whatever data is left in the buffer. + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) + ctx->data[i++] = 0x00; + } + else { + ctx->data[i++] = 0x80; + while (i < 64) + ctx->data[i++] = 0x00; + sha256_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + + // Append to the padding the total message's length in bits and transform. + ctx->bitlen += ctx->datalen * 8; + ctx->data[63] = ctx->bitlen; + ctx->data[62] = ctx->bitlen >> 8; + ctx->data[61] = ctx->bitlen >> 16; + ctx->data[60] = ctx->bitlen >> 24; + ctx->data[59] = ctx->bitlen >> 32; + ctx->data[58] = ctx->bitlen >> 40; + ctx->data[57] = ctx->bitlen >> 48; + ctx->data[56] = ctx->bitlen >> 56; + sha256_transform(ctx, ctx->data); + + // Since this implementation uses little endian byte ordering and SHA uses big endian, + // reverse all the bytes when copying the final state to the output hash. + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } +} diff --git a/sha256.h b/sha256.h new file mode 100644 index 00000000..7123a30d --- /dev/null +++ b/sha256.h @@ -0,0 +1,34 @@ +/********************************************************************* +* Filename: sha256.h +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Defines the API for the corresponding SHA1 implementation. +*********************************************************************/ + +#ifndef SHA256_H +#define SHA256_H + +/*************************** HEADER FILES ***************************/ +#include + +/****************************** MACROS ******************************/ +#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest + +/**************************** DATA TYPES ****************************/ +typedef unsigned char BYTE; // 8-bit byte +typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines + +typedef struct { + BYTE data[64]; + WORD datalen; + unsigned long long bitlen; + WORD state[8]; +} SHA256_CTX; + +/*********************** FUNCTION DECLARATIONS **********************/ +void sha256_init(SHA256_CTX *ctx); +void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); +void sha256_final(SHA256_CTX *ctx, BYTE hash[]); + +#endif // SHA256_H