/* Copyright (C) 2020-2021 Reiko Asakura. All Rights Reserved. Moonshine */ #include #include #include #include #include "config.h" #include "opcode.h" #include "patch.h" #define SYSTEM_TITLE_ID_PREFIX "NPXS" #define RETAIL_TITLE_ID_PREFIX "PCS" #define TITLE_INFO_PREFIX "{\"npTitleId\":\"" #define TITLE_INFO_PREFIX_LEN (sizeof(TITLE_INFO_PREFIX) - 1) #define TITLE_INFO_LEN_BOUND (TITLE_INFO_PREFIX_LEN + 4) #define STARTSWITH(s, t) (sceClibMemcmp(s, t, sizeof(t) - 1) == 0) static SceUID SceShell_uid; static uint32_t sceNpWebApiSendRequest_ofs; static SceUID set_presence_hook_id = -1; static tai_hook_ref_t set_presence_hook_ref; static SceUID sceNpWebApiSendRequest_hook_id = -1; static tai_hook_ref_t sceNpWebApiSendRequest_hook_ref; static SceUID post_status_hook_id = -1; static tai_hook_ref_t post_status_hook_ref; static MoonshineConfig config; static int32_t sceNpWebApiSendRequest_hook(int64_t requestId, const void *pData, size_t dataSize) { if (dataSize >= TITLE_INFO_LEN_BOUND && STARTSWITH(pData, TITLE_INFO_PREFIX)) { const char *title_id = ((const char *)pData) + TITLE_INFO_PREFIX_LEN; config_read(&config); if (config.block_presence == MOONSHINE_CONFIG_BLOCK_NONE || (config.block_presence == MOONSHINE_CONFIG_BLOCK_HOMEBREW && (STARTSWITH(title_id, SYSTEM_TITLE_ID_PREFIX) || STARTSWITH(title_id, RETAIL_TITLE_ID_PREFIX)))) { SCE_DBG_LOG_INFO("Request permitted: %.*s", dataSize, pData); } else { SCE_DBG_LOG_INFO("Request blocked: %.*s", dataSize, pData); return -1; } } return HOOK_NEXT(sceNpWebApiSendRequest, requestId, pData, dataSize); } static int set_presence_hook(int r0, int r1) { if (sceNpWebApiSendRequest_hook_id < 0) { HOOK_OFFSET(SceShell_uid, 0, sceNpWebApiSendRequest_ofs, 0, sceNpWebApiSendRequest); } int ret = HOOK_NEXT(set_presence, r0, r1); UNHOOK(set_presence); return ret; } static int post_status_hook(char **r0) { char *title_id = r0[21]; config_read(&config); if (config.block_status == MOONSHINE_CONFIG_BLOCK_NONE || (config.block_status == MOONSHINE_CONFIG_BLOCK_HOMEBREW && (STARTSWITH(title_id, SYSTEM_TITLE_ID_PREFIX) || STARTSWITH(title_id, RETAIL_TITLE_ID_PREFIX)))) { SCE_DBG_LOG_INFO("Post status permitted: %s", title_id); } else { SCE_DBG_LOG_INFO("Post status blocked: %s", title_id); return -1; } return HOOK_NEXT(post_status, r0); } static void cleanup(void) { UNHOOK(set_presence); UNHOOK(sceNpWebApiSendRequest); UNHOOK(post_status); } int module_start() { int ret; tai_module_info_t minfo; SceKernelModuleInfo sce_minfo; uint32_t SceShell_dbg_fingerprint; uint16_t *SceShell_seg0; uint32_t set_presence_ofs; uint32_t post_status_ofs; uint16_t *sceNpWebApiSendRequest_addr; config_init(&config); sceClibMemset(&minfo, 0, sizeof(minfo)); minfo.size = sizeof(minfo); ret = taiGetModuleInfo("SceShell", &minfo); if (ret < 0) { SCE_DBG_LOG_ERROR("Failed to get SceShell taiHEN module info %08X", ret); goto fail; } SceShell_uid = minfo.modid; SceShell_dbg_fingerprint = minfo.module_nid; sceClibMemset(&sce_minfo, 0, sizeof(sce_minfo)); sce_minfo.size = sizeof(sce_minfo); ret = sceKernelGetModuleInfo(SceShell_uid, &sce_minfo); if (ret < 0) { SCE_DBG_LOG_ERROR("Failed to get SceShell module info %08X", ret); goto fail; } SceShell_seg0 = (uint16_t *)sce_minfo.segments[0].vaddr; switch(SceShell_dbg_fingerprint) { case 0x0552F692: // 3.60 retail case 0x532155E5: // 3.61 retail set_presence_ofs = 0x3B9EEE; post_status_ofs = 0x1BDE54; break; case 0xBB4B0A3E: // 3.63 retail set_presence_ofs = 0x3B9F8E; post_status_ofs = 0x1BDF1C; break; case 0x5549BF1F: // 3.65 retail case 0x34B4D82E: // 3.67 retail case 0x12DAC0F3: // 3.68 retail set_presence_ofs = 0x3BA336; post_status_ofs = 0x1BDF1C; break; case 0x0703C828: // 3.69 retail case 0x2053B5A5: // 3.70 retail case 0xF476E785: // 3.71 retail case 0x939FFBE9: // 3.72 retail case 0x734D476A: // 3.73 retail set_presence_ofs = 0x3BA346; post_status_ofs = 0x1BDF1C; break; case 0xEAB89D5C: // 3.60 testkit set_presence_ofs = 0x3B066E; post_status_ofs = 0x1B6288; break; case 0x587F9CED: // 3.65 testkit set_presence_ofs = 0x3B0AAE; post_status_ofs = 0x1B6350; break; default: SCE_DBG_LOG_ERROR("Unsupported SceShell version"); goto fail; } SCE_DBG_LOG_INFO("set_presence at offset %p", set_presence_ofs); SCE_DBG_LOG_INFO("post_status at offset %p", post_status_ofs); if (get_addr_blx(SceShell_seg0 + set_presence_ofs/2 + 0x254/2, &sceNpWebApiSendRequest_addr) < 0) { goto fail; } sceNpWebApiSendRequest_ofs = (uint32_t)sceNpWebApiSendRequest_addr - (uint32_t)SceShell_seg0; SCE_DBG_LOG_INFO("sceNpWebApiSendRequest at offset %p", sceNpWebApiSendRequest_ofs); if (HOOK_OFFSET(SceShell_uid, 0, set_presence_ofs, 1, set_presence) < 0) { goto fail; } if (HOOK_OFFSET(SceShell_uid, 0, post_status_ofs, 1, post_status) < 0) { goto fail; } return SCE_KERNEL_START_SUCCESS; fail: cleanup(); return SCE_KERNEL_START_FAILED; } int module_stop() { cleanup(); return SCE_KERNEL_STOP_SUCCESS; }