summaryrefslogblamecommitdiff
path: root/moonshine.c
blob: 14cb1d4ceb5c18397d50ce81c171e7cd654d8bec (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                                   
                   
                   










                                                                                         


                                           





                                                      


                                           

                              



                                                                                                   






                                                                                          











                                                                                     
                                                                                                    





                                                  


                                        






                                                                                  








                                                                        


                                       
                            



                    
 
                                
                                      
                                          

                                
                                  
                                 
                                              
 

                             









                                                                                         








                                                                                  



                                                    
                                                   
                              

























                                                    




                                                                          
                                                                      
 





                                                                                                           



                                                                                  



                                                                                










                                        
/*
	Copyright (C) 2020-2021 Reiko Asakura. All Rights Reserved.

	Moonshine
*/

#include <kernel/libkernel.h>
#include <kernel/modulemgr.h>
#include <libdbg.h>

#include <taihen.h>

#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;
}