And all the examples I've found till now in a lot of different sources, namely - PSPLink, RemoteJoy, Hookcore, etc. - weren't really up 2 date.
Nor did they offer the feature of hooking Usermode Functions to Kernel Functions, that I needed.
Below you will find Code Sections of all the important parts of my hooking code.
I figured out the NID for 5.0 Firmware sceKernelQuerySystemCall by comparing the decrypted modules of 3.0 firmware and 5.0 firmware btw. :P
hook.h
Code: Select all
#ifndef PSP_HOOK_H
#define PSP_HOOK_H
// Hook Modes
#define HOOK_SYSCALL 0
#define HOOK_JUMP 1
// Hook Function - returns 0 on success, < 0 on error.
int hookAPI(const char * module, const char * library, unsigned int nid, void * function, int mode, unsigned int * orig_loader);
#endif
Code: Select all
#include <stdio.h>
#include <psputilsforkernel.h>
#include <systemctrl.h>
#include "interruptman.h"
#include "hook.h"
#include "debug.h"
// MIPS Opcodes
#define MIPS_NOP 0x00000000
#define MIPS_SYSCALL(NUM) (((NUM)<<6)|12)
#define MIPS_J(ADDR) (0x08000000 + ((((unsigned int)(ADDR))&0x0ffffffc)>>2))
#define MIPS_STACK_SIZE(SIZE) (0x27BD0000 + (((unsigned int)(SIZE)) & 0x0000FFFF))
#define MIPS_PUSH_RA(STACKPOS) (0xAFBF0000 + (((unsigned int)(STACKPOS)) & 0x0000FFFF))
#define MIPS_POP_RA(STACKPOS) (0x8FBF0000 + (((unsigned int)(STACKPOS)) & 0x0000FFFF))
#define MIPS_RETURN 0x03E00008
int hookJump(const char * module, const char * library, unsigned int nid, void * function, unsigned int * orig_loader)
{
// Hooking Result
int result = 0;
// Check Arguments
if(module && library && function)
{
// Find Function
unsigned int * sfunc = (unsigned int*)sctrlHENFindFunction(module, library, nid);
// Found Function
if(sfunc)
{
// Backup Interrupts
int interrupt = pspSdkDisableInterrupts();
// Create Original Loader
if(orig_loader)
{
// Backup Original Instructions
orig_loader[0] = sfunc[0];
orig_loader[1] = sfunc[1];
orig_loader[2] = sfunc[2];
orig_loader[3] = sfunc[3];
orig_loader[4] = sfunc[4];
orig_loader[5] = sfunc[5];
orig_loader[6] = sfunc[6];
orig_loader[7] = sfunc[7];
// Jump Delay Slot (Just to be on the safe side...)
orig_loader[8] = MIPS_NOP;
// Jump to Original Code
orig_loader[9] = MIPS_J(&sfunc[8]);
// Jump Delay Slot
orig_loader[10] = MIPS_NOP;
}
// Patch Function with Jump
sfunc[0] = MIPS_STACK_SIZE(-4); // Allocate 4 Bytes on Stack
sfunc[1] = MIPS_PUSH_RA(0); // Backup RA on Stack
sfunc[2] = MIPS_SYSCALL(sceKernelQuerySystemCall(function)); // Syscall to our Hook
sfunc[3] = MIPS_NOP; // Delay Slot
sfunc[4] = MIPS_POP_RA(0); // Get RA from Stack
sfunc[5] = MIPS_STACK_SIZE(4); // Free 4 Bytes on Stack
sfunc[6] = MIPS_RETURN; // Return
sfunc[7] = MIPS_NOP; // Delay Slot
// Force Memory Mirroring
sceKernelDcacheWritebackInvalidateRange(sfunc, sizeof(unsigned int) * 8);
sceKernelIcacheInvalidateRange(sfunc, sizeof(unsigned int) * 8);
// Enable Interrupts
pspSdkEnableInterrupts(interrupt);
// Hooking Debug Log
char log[128];
sprintf(log, "hookJump: Set Jump Hook on %08X to %08X (Module: %s, Library: %s, NID: %08X).\n", (unsigned int)sfunc, (unsigned int)function, module, library, nid);
debuglog(log);
}
// Failed Finding Function
else
{
// Result
result = -5;
// Log Error
debuglog("hookJump: Couldn't find Function. NID might be incorrect.\n");
}
}
// Invalid Arguments
else
{
// Result
result = -4;
// Log Error
debuglog("hookJump: Invalid Arguments.\n");
}
// Return Hooking Result
return result;
}
int hookSyscall(const char * module, const char * library, unsigned int nid, void * function, unsigned int * orig_loader)
{
// Hooking Result
int result = 0;
// Check Arguments
if(module && library && function)
{
// Find Function
unsigned int * sfunc = (unsigned int*)sctrlHENFindFunction(module, library, nid);
// Found Function
if(sfunc)
{
// Create Original Loader
if(orig_loader)
{
// Jump to Original Code
orig_loader[0] = MIPS_J(sfunc);
// Jump Delay Slot
orig_loader[1] = MIPS_NOP;
}
// Patch Syscall
sctrlHENPatchSyscall((unsigned int)sfunc, function);
// Hooking Debug Log
char log[128];
sprintf(log, "hookSyscall: Set Syscall Hook on %08X to %08X (Module: %s, Library: %s, NID: %08X).\n", (unsigned int)sfunc, (unsigned int)function, module, library, nid);
debuglog(log);
}
// Failed Finding Function
else
{
// Result
result = -5;
// Log Error
debuglog("hookSyscall: Couldn't find Target Function. NID might be incorrect.\n");
}
}
// Invalid Arguments
else
{
// Result
result = -4;
// Log Error
debuglog("hookSyscall: Invalid Arguments.\n");
}
// Return Hooking Result
return result;
}
int hookAPI(const char * module, const char * library, unsigned int nid, void * function, int mode, unsigned int * orig_loader)
{
// Hooking Result
int result = 0;
// Avoid Crash
sceKernelDelayThread(10000);
// Check Arguments
if(module && library && function)
{
// Find Module in Memory
SceModule * findmodule = (SceModule*)sceKernelFindModuleByName(module);
// Found Module
if(findmodule)
{
// Hook via Syscall
if(mode == HOOK_SYSCALL) result = hookSyscall(module, library, nid, function, orig_loader);
// Hook via Jump
else if(mode == HOOK_JUMP) result = hookJump(module, library, nid, function, orig_loader);
// Invalid Hook Mode
else
{
// Result
result = -3;
// Log Error
debuglog("hookAPI: Invalid Hook Mode.\n");
}
}
// Couldn't Find Module
else
{
// Result
result = -2;
// Log Error
debuglog("hookAPI: Couldn't find Module. Might not be loaded yet.\n");
}
}
// Invalid Arguments
else
{
// Result
result = -1;
// Log Error
debuglog("hookAPI: Invalid Arguments.\n");
}
// Avoid Crash
sceKernelDelayThread(10000);
// Return Hooking Result
return result;
}
Code: Select all
#ifndef PSP_INTERRUPTMAN_H
#define PSP_INTERRUPTMAN_H
// Get Syscallnum from Function Address
int sceKernelQuerySystemCall(void * function);
#endif
Code: Select all
.set noreorder
#include "pspstub.s"
STUB_START "InterruptManagerForKernel",0x00090011,0x00010005
STUB_FUNC 0xEB988556,sceKernelQuerySystemCall
STUB_END
Code: Select all
#ifndef PSP_DEBUG_H
#define PSP_DEBUG_H
#include <string.h>
#define LOGFILE "ms0:/psphook.log"
// Debug Log
int debuglog(const char * string);
// Append Buffer to File
int appendBufferToFile(const char * path, void * buffer, int buflen);
#endif
Code: Select all
#include <pspiofilemgr.h>
#include "debug.h"
int debuglog(const char * string)
{
// Append Data
return appendBufferToFile(LOGFILE, (void*)string, strlen(string));
}
int appendBufferToFile(const char * path, void * buffer, int buflen)
{
// Written Bytes
int written = 0;
// Open File for Appending
SceUID file = sceIoOpen(path, PSP_O_APPEND | PSP_O_CREAT | PSP_O_WRONLY, 0777);
// Opened File
if(file >= 0)
{
// Write Buffer
written = sceIoWrite(file, buffer, buflen);
// Close File
sceIoClose(file);
}
// Return Written Bytes
return written;
}
1. Make sure you link your project against "-lpspsystemctrl_kernel" - else you will run into trouble with hooking, because my hooking code relies on the M33 SDK Functions.
2. Obviously - MAKE YOUR PROJECT A KERNEL ONE! :)
3. Export your Hook Replacement Functions, else hooking will fail. You've been warned. Any Export Mode should be fine, direct jumping or syscalling... as the hooking code will dynamically create a syscall one anyway...
4. Don't try and forward Kernel Memory Range Pointers in Usermode Real Functions... you know it will fail. :P
5. For hooking Usermode functions use the HOOK_JUMP flag.
6. For hooking Kernelmode functions use the HOOK_JUMP flag if you want ALL calls to jump into your function (both User & Kernel) or use the HOOK_SYSCALL flag if you only want User calls to jump into your function.
7. For Usermode Hooks, please make sure to use the pspSdkSetK1 functions to properly backup and restore K1 register... otherwise you will be very limited in what you can do inside your hooked function.
8. To keep up compatiblity with other PRX Modules that also hook stuff, please - by gods sake... use the HOOK_JUMP flag.
Unlike HOOK_SYSCALL, HOOK_JUMP is stackable...
Let's say someone hooks sceKernelThreadDelay (I know, really stupid but whatever...) and you want to hook it aswell...
HOOK_SYSCALL would overwrite the Syscall in the Syscalltable, and thus make the original function undetectable by you, making it impossible to hook.
HOOK_JUMP though... backups the original function instructions and writes your own... simple as that...
If someone uses HOOK_JUMP after another module already jumphooked the function, it will backup THEIR code, and write YOUR own...
So - it will build a chain...
First call would enter your function, you forward it to the "real function" you saved, which is the first hook from another module, which in turn forwards it to its own "real function" it saved... which is the real function.
Get what I mean? S-T-A-C-K-A-B-L-E.
So in theory, if everyone who wants to hook stuff used this code example of mine... every module could hook the same function without interfering with other modules.
Nice in theory isn't it? Hope that module developers will think about implementing my sample so we can make sure that as many modules as possible become compatible with each other.
9. As the Minimal PSPSDK Setup for Windows Operating Systems is getting more and more famous and spread on the Internet, you might run into problems with the Stubs File for sceKernelQuerySystemCall though, as MinGW doesn't know the difference between .s and .S files...
To fix this, edit your makefile with the following line.
Code: Select all
ASFLAGS = $(CFLAGS) -c -x assembler-with-cpp
10. For those not knowing where to get the M33 SDK. Google for "4.01 M33 Download" - the 4.01 firmware package from Dark_Alex comes with the M33 SDK precompiled, just copy it into your PSPSDK include / lib folders.
11. This code is FAR from being sane! Whether a hook works or not pretty much depends on luck from what I can tell...
I discovered a few functions I found pretty much - unhookable - using this code.
To name some, sceNetAdhocctlInit & sceNetAdhocctlTerm aswell as sceIfhandleIoctl. The hook - technically - works, just execution doesn't... it won't even reach your function.
So... if your PSP crashes while using this code... do some trial and error and see if a hook's causing it to.
I suppose hooking User and Kernelmode functions all in one does come at its price...
12. Confirmed Unhookable Functions:
sceNetAdhocctlInit - Hook Error, will crash entering your hook.
sceNetAdhocctlTerm - Hook Error, will crash entering your hook.
sceNetAdhocMatchingInit - Hook Error, will crash entering your hook.
sceNetAdhocMatchingTerm - Hook Error, will crash entering your hook.
sceNetAdhocMatchingStart - Hooks fine, calling the "Real Function" inside your hook ALWAYS fails though... (Return Value != 0)
sceNetAdhocMatchingGetPoolMaxAlloc - Hook Error, will crash entering your hook.
sceNetIfhandleIoctl - Hook Error, will crash entering your hook.
Sidenote: I'm trying to figure out why this problems occur... I tested this Unhookable Functions with 1.0 Firmware Version Libraries from Ridge Racer EU UMD.
If you got a idea what part of my code could be causing this, please drop me a message.
How to use it~
Code: Select all
// Original Function Pointer
int (*RealFunction)(void) = NULL;
// Dynamic Original Function Call Buffer
int orig_call[11]; // Make it smaller than that and you die.
int FakeFunction(void)
{
// Result
int result = 0;
// Capture Real Function Result
result = RealFunction();
// Get More POWAR!
int k1 = pspSdkSetK1(0);
// Do your shit here...
// Give back POWAR!
pspSdkSetK1(k1);
// Return Result
return result;
}
int main(int argc, char * argv[])
{
// Result
int result = 0;
// Hook Stuff
// ModuleName -> Module Name of the to-get-hooked Module.
// LibraryName -> Library Name of the to-get-hooked Library.
// 0x12345678 -> NID of the to-get-hooked Function
// FakeFunction -> Your replacement function.
// HOOK_JUMP -> Flag for hook mode, alternatively you can use HOOK_SYSCALL.
// orig_call -> Pointer to an integer array to hold MIPS assembly for launching the original function.
result = hookAPI("ModuleName", "LibraryName", 0x12345678, FakeFunction, HOOK_JUMP, orig_call);
// Link Real Function Call
RealFunction = (int(*)(void))orig_call;
// Return Result
return result;
}