Easy Hooking Example - User & Kernel

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
User avatar
Coldbird
Posts: 97
Joined: Thu Feb 08, 2007 7:22 am

Easy Hooking Example - User & Kernel

Post by Coldbird »

First of, I'm posting this here because hooking has been bothering me for quite some time now.

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&#40;const char * module, const char * library, unsigned int nid, void * function, int mode, unsigned int * orig_loader&#41;;

#endif
hook.c

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&#40;NUM&#41; &#40;&#40;&#40;NUM&#41;<<6&#41;|12&#41;
#define MIPS_J&#40;ADDR&#41; &#40;0x08000000 + &#40;&#40;&#40;&#40;unsigned int&#41;&#40;ADDR&#41;&#41;&0x0ffffffc&#41;>>2&#41;&#41;
#define MIPS_STACK_SIZE&#40;SIZE&#41; &#40;0x27BD0000 + &#40;&#40;&#40;unsigned int&#41;&#40;SIZE&#41;&#41; & 0x0000FFFF&#41;&#41;
#define MIPS_PUSH_RA&#40;STACKPOS&#41; &#40;0xAFBF0000 + &#40;&#40;&#40;unsigned int&#41;&#40;STACKPOS&#41;&#41; & 0x0000FFFF&#41;&#41;
#define MIPS_POP_RA&#40;STACKPOS&#41; &#40;0x8FBF0000 + &#40;&#40;&#40;unsigned int&#41;&#40;STACKPOS&#41;&#41; & 0x0000FFFF&#41;&#41;
#define MIPS_RETURN 0x03E00008

int hookJump&#40;const char * module, const char * library, unsigned int nid, void * function, unsigned int * orig_loader&#41;
&#123;
  // Hooking Result
  int result = 0;
  
  // Check Arguments
  if&#40;module && library && function&#41;
  &#123;
    // Find Function
    unsigned int * sfunc = &#40;unsigned int*&#41;sctrlHENFindFunction&#40;module, library, nid&#41;;
    
    // Found Function
    if&#40;sfunc&#41;
    &#123;
      // Backup Interrupts
      int interrupt = pspSdkDisableInterrupts&#40;&#41;;
      
      // Create Original Loader
      if&#40;orig_loader&#41;
      &#123;
        // Backup Original Instructions
        orig_loader&#91;0&#93; = sfunc&#91;0&#93;;
        orig_loader&#91;1&#93; = sfunc&#91;1&#93;;
        orig_loader&#91;2&#93; = sfunc&#91;2&#93;;
        orig_loader&#91;3&#93; = sfunc&#91;3&#93;;
        orig_loader&#91;4&#93; = sfunc&#91;4&#93;;
        orig_loader&#91;5&#93; = sfunc&#91;5&#93;;
        orig_loader&#91;6&#93; = sfunc&#91;6&#93;;
        orig_loader&#91;7&#93; = sfunc&#91;7&#93;;
        
        // Jump Delay Slot &#40;Just to be on the safe side...&#41;
        orig_loader&#91;8&#93; = MIPS_NOP;
        
        // Jump to Original Code
        orig_loader&#91;9&#93; = MIPS_J&#40;&sfunc&#91;8&#93;&#41;;
        
        // Jump Delay Slot
        orig_loader&#91;10&#93; = MIPS_NOP;
      &#125;
      
      // Patch Function with Jump
      sfunc&#91;0&#93; = MIPS_STACK_SIZE&#40;-4&#41;; // Allocate 4 Bytes on Stack
      sfunc&#91;1&#93; = MIPS_PUSH_RA&#40;0&#41;; // Backup RA on Stack
      sfunc&#91;2&#93; = MIPS_SYSCALL&#40;sceKernelQuerySystemCall&#40;function&#41;&#41;; // Syscall to our Hook
      sfunc&#91;3&#93; = MIPS_NOP; // Delay Slot
      sfunc&#91;4&#93; = MIPS_POP_RA&#40;0&#41;; // Get RA from Stack
      sfunc&#91;5&#93; = MIPS_STACK_SIZE&#40;4&#41;; // Free 4 Bytes on Stack
      sfunc&#91;6&#93; = MIPS_RETURN; // Return
      sfunc&#91;7&#93; = MIPS_NOP; // Delay Slot
      
      // Force Memory Mirroring
      sceKernelDcacheWritebackInvalidateRange&#40;sfunc, sizeof&#40;unsigned int&#41; * 8&#41;;
      sceKernelIcacheInvalidateRange&#40;sfunc, sizeof&#40;unsigned int&#41; * 8&#41;;
      
      // Enable Interrupts
      pspSdkEnableInterrupts&#40;interrupt&#41;;
      
      // Hooking Debug Log
      char log&#91;128&#93;;
      sprintf&#40;log, "hookJump&#58; Set Jump Hook on %08X to %08X &#40;Module&#58; %s, Library&#58; %s, NID&#58; %08X&#41;.\n", &#40;unsigned int&#41;sfunc, &#40;unsigned int&#41;function, module, library, nid&#41;;
      debuglog&#40;log&#41;;
    &#125;
    
    // Failed Finding Function
    else
    &#123;
      // Result
      result = -5;
      
      // Log Error
      debuglog&#40;"hookJump&#58; Couldn't find Function. NID might be incorrect.\n"&#41;;
    &#125;
  &#125;
  
  // Invalid Arguments
  else
  &#123;
    // Result
    result = -4;
    
    // Log Error
    debuglog&#40;"hookJump&#58; Invalid Arguments.\n"&#41;;
  &#125;
  
  // Return Hooking Result
  return result;
&#125;

int hookSyscall&#40;const char * module, const char * library, unsigned int nid, void * function, unsigned int * orig_loader&#41;
&#123;
  // Hooking Result
  int result = 0;
  
  // Check Arguments
  if&#40;module && library && function&#41;
  &#123;
    // Find Function
    unsigned int * sfunc = &#40;unsigned int*&#41;sctrlHENFindFunction&#40;module, library, nid&#41;;
    
    // Found Function
    if&#40;sfunc&#41;
    &#123;
      // Create Original Loader
      if&#40;orig_loader&#41;
      &#123;
        // Jump to Original Code
        orig_loader&#91;0&#93; = MIPS_J&#40;sfunc&#41;;
        
        // Jump Delay Slot
        orig_loader&#91;1&#93; = MIPS_NOP;
      &#125;
      
      // Patch Syscall
      sctrlHENPatchSyscall&#40;&#40;unsigned int&#41;sfunc, function&#41;;
      
      // Hooking Debug Log
      char log&#91;128&#93;;
      sprintf&#40;log, "hookSyscall&#58; Set Syscall Hook on %08X to %08X &#40;Module&#58; %s, Library&#58; %s, NID&#58; %08X&#41;.\n", &#40;unsigned int&#41;sfunc, &#40;unsigned int&#41;function, module, library, nid&#41;;
      debuglog&#40;log&#41;;
    &#125;
    
    // Failed Finding Function
    else
    &#123;
      // Result
      result = -5;
      
      // Log Error
      debuglog&#40;"hookSyscall&#58; Couldn't find Target Function. NID might be incorrect.\n"&#41;;
    &#125;
  &#125;
  
  // Invalid Arguments
  else
  &#123;
    // Result
    result = -4;
    
    // Log Error
    debuglog&#40;"hookSyscall&#58; Invalid Arguments.\n"&#41;;
  &#125;
  
  // Return Hooking Result
  return result;
&#125;

int hookAPI&#40;const char * module, const char * library, unsigned int nid, void * function, int mode, unsigned int * orig_loader&#41;
&#123;
  // Hooking Result
  int result = 0;
  
  // Avoid Crash
  sceKernelDelayThread&#40;10000&#41;;
  
  // Check Arguments
  if&#40;module && library && function&#41;
  &#123;
    // Find Module in Memory
    SceModule * findmodule = &#40;SceModule*&#41;sceKernelFindModuleByName&#40;module&#41;;
    
    // Found Module
    if&#40;findmodule&#41;
    &#123;
      // Hook via Syscall
      if&#40;mode == HOOK_SYSCALL&#41; result = hookSyscall&#40;module, library, nid, function, orig_loader&#41;;
      
      // Hook via Jump
      else if&#40;mode == HOOK_JUMP&#41; result = hookJump&#40;module, library, nid, function, orig_loader&#41;;
      
      // Invalid Hook Mode
      else
      &#123;
        // Result
        result = -3;
        
        // Log Error
        debuglog&#40;"hookAPI&#58; Invalid Hook Mode.\n"&#41;;
      &#125;
    &#125;
    
    // Couldn't Find Module
    else
    &#123;
      // Result
      result = -2;
      
      // Log Error
      debuglog&#40;"hookAPI&#58; Couldn't find Module. Might not be loaded yet.\n"&#41;;
    &#125;
  &#125;
  
  // Invalid Arguments
  else
  &#123;
    // Result
    result = -1;
    
    // Log Error
    debuglog&#40;"hookAPI&#58; Invalid Arguments.\n"&#41;;
  &#125;
  
  // Avoid Crash
  sceKernelDelayThread&#40;10000&#41;;
  
  // Return Hooking Result
  return result;
&#125;
interruptman.h

Code: Select all

#ifndef PSP_INTERRUPTMAN_H
#define PSP_INTERRUPTMAN_H

// Get Syscallnum from Function Address
int sceKernelQuerySystemCall&#40;void * function&#41;;

#endif
InterruptManagerForKernel.S

Code: Select all

	.set noreorder

#include "pspstub.s"

	STUB_START "InterruptManagerForKernel",0x00090011,0x00010005
	STUB_FUNC  0xEB988556,sceKernelQuerySystemCall
	STUB_END
debug.h

Code: Select all

#ifndef PSP_DEBUG_H
#define PSP_DEBUG_H

#include <string.h>

#define LOGFILE "ms0&#58;/psphook.log"

// Debug Log
int debuglog&#40;const char * string&#41;;

// Append Buffer to File
int appendBufferToFile&#40;const char * path, void * buffer, int buflen&#41;;

#endif
debug.c

Code: Select all

#include <pspiofilemgr.h>
#include "debug.h"

int debuglog&#40;const char * string&#41;
&#123;
  // Append Data
  return appendBufferToFile&#40;LOGFILE, &#40;void*&#41;string, strlen&#40;string&#41;&#41;;
&#125;

int appendBufferToFile&#40;const char * path, void * buffer, int buflen&#41;
&#123;
  // Written Bytes
  int written = 0;
  
  // Open File for Appending
  SceUID file = sceIoOpen&#40;path, PSP_O_APPEND | PSP_O_CREAT | PSP_O_WRONLY, 0777&#41;;
  
  // Opened File
  if&#40;file >= 0&#41;
  &#123;
    // Write Buffer
    written = sceIoWrite&#40;file, buffer, buflen&#41;;
    
    // Close File
    sceIoClose&#40;file&#41;;
  &#125;
  
  // Return Written Bytes
  return written;
&#125;
Things to keep in mind while using~

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 = $&#40;CFLAGS&#41; -c -x assembler-with-cpp
This practically forces all Stubs files to be handled like .S files. Not a nice workaround, but it works.

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 &#40;*RealFunction&#41;&#40;void&#41; = NULL;

// Dynamic Original Function Call Buffer
int orig_call&#91;11&#93;; // Make it smaller than that and you die.

int FakeFunction&#40;void&#41;
&#123;
  // Result
  int result = 0;
  
  // Capture Real Function Result
  result = RealFunction&#40;&#41;;
  
  // Get More POWAR!
  int k1 = pspSdkSetK1&#40;0&#41;;
  
  // Do your shit here...
  
  // Give back POWAR!
  pspSdkSetK1&#40;k1&#41;;
  
  // Return Result
  return result;
&#125;

int main&#40;int argc, char * argv&#91;&#93;&#41;
&#123;
  // 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&#40;"ModuleName", "LibraryName", 0x12345678, FakeFunction, HOOK_JUMP, orig_call&#41;;
  
  // Link Real Function Call
  RealFunction = &#40;int&#40;*&#41;&#40;void&#41;&#41;orig_call;
  
  // Return Result
  return result;
&#125;
Last edited by Coldbird on Fri Apr 17, 2009 6:33 am, edited 12 times in total.
Been gone for some time. Now I'm back. Someone mind getting me up-2-date?
User avatar
jean
Posts: 489
Joined: Sat Jan 05, 2008 2:44 am

Post by jean »

Very nice for those (like me!) too lazy to dig in sources and do error-and-retry tests a million times. Thanks for sharing.
User avatar
Coldbird
Posts: 97
Joined: Thu Feb 08, 2007 7:22 am

Post by Coldbird »

jean wrote:Very nice for those (like me!) too lazy to dig in sources and do error-and-retry tests a million times. Thanks for sharing.
I'm glad it's of use to you. Took me some time to figure and work out a proper solution to this problem.

Especially because all I could find in previous sources was this apihook function thing which totally didn't do what I wanted it to do.
Been gone for some time. Now I'm back. Someone mind getting me up-2-date?
Pirata Nervo
Posts: 409
Joined: Tue Oct 09, 2007 4:22 am

Post by Pirata Nervo »

Thanks a lot :)
Image
Upgrade your PSP
User avatar
Coldbird
Posts: 97
Joined: Thu Feb 08, 2007 7:22 am

Post by Coldbird »

No problem. I'm still trying to figure out why some functions won't hook properly with HOOK_JUMP, like the three ones I mentioned in the warnings.

I disassembled the affected functions but couldn't find a reason why they aren't hooking properly.

If you guys figure out what might be causing those few selected functions to not hook, please drop me a message.

Edit found a bug in my code, please check out hook.c for the fixed code.
Edit 2 found a crash related to too fast repeative hooking, placed a few thread delays in the hooking function to avoid this.
Edit 3 found a bug in my code that MIGHT have caused a few of the "broken hooks". I didn't invalidate all the memory I modified for the hooks...
Been gone for some time. Now I'm back. Someone mind getting me up-2-date?
Smong
Posts: 82
Joined: Tue Sep 04, 2007 4:44 am

Post by Smong »

Are you still having trouble with this? You may want to try making your stack allocations a multiple of 16 bytes instead of just 4 bytes.
(+[__]%)
User avatar
Coldbird
Posts: 97
Joined: Thu Feb 08, 2007 7:22 am

Post by Coldbird »

Well - yes I do.
Some of the functions Im hooking with this example code still deal me trouble.

Now that I think of it, I've never seen allocation of as small memory regions as 4 bytes in the asm code...

You think that could be it? I will try that later on today.
Thanks for the tip.
Been gone for some time. Now I'm back. Someone mind getting me up-2-date?
kralyk
Posts: 114
Joined: Sun Apr 06, 2008 8:18 pm
Location: Czech Republic, central EU

Post by kralyk »

The problem seems to be in the syscall query.
When I substitute the MIPS_SYSCALL with just NOP, it's ok.
I mean, it's not ok, it doesn't work of course, but it doesn't crash
the psp so that should show that the problem is in the syscall...

dunno whats wrong with the syscall though, any ideas?
...sorry for my english...
Torky
Posts: 1
Joined: Thu Jul 30, 2009 3:49 am

Post by Torky »

help please :)

the function seems to be hooked.
hookJump: Set Jump Hook on 880F5848 to 8822E600 (Module: scePower_Service, Library: scePower_driver, NID: 69958B65).
but i never get the "test" line in the psphook.log file. whats wrong with my code?

Code: Select all

#include <stdio.h>
#include <pspkernel.h>
#include <psputilsforkernel.h>
#include <pspsdk.h>
#include <pspctrl.h>
#include <string.h>
#include <pspdebug.h>
#include <pspsuspend.h>
#include <psppower.h>
#include <pspreg.h>
#include <psprtc.h>
#include <psputils.h>
#include <stdlib.h>
#include <stdarg.h>
#include <malloc.h>
#include "hook.h"
#include "debug.h"

PSP_MODULE_INFO&#40;"test", 0x1000, 1, 1&#41;;
PSP_MAIN_THREAD_ATTR&#40;0&#41;;
// Original Function Pointer
int &#40;*scePowerBatteryDisableUsbCharging&#41;&#40;void&#41; = NULL;

// Dynamic Original Function Call Buffer
int orig_call&#91;11&#93;; // Make it smaller than that and you die.

int scePowerBatteryDisableUsbCharging_fake&#40;void&#41;
&#123;
  // Result
  int result = 0;

      debuglog&#40;"test\n"&#41;;
  result = scePowerBatteryDisableUsbCharging&#40;&#41;;

 
  // Return Result
  return result;
&#125;

int module_start&#40;SceSize args, void *argp&#41;
&#123;
  // Result
  int result = 0;
 
  result = hookAPI&#40;"scePower_Service", "scePower_driver", 0x69958B65, scePowerBatteryDisableUsbCharging_fake, HOOK_JUMP, orig_call&#41;;
 
  // Link Real Function Call
  scePowerBatteryDisableUsbCharging = &#40;int&#40;*&#41;&#40;void&#41;&#41;orig_call;

  // Return Result
  return result;
&#125;

Code: Select all

TARGET = test
OBJS = test.o InterruptManagerForKernel.o debug.o hook.o exports.o
BUILD_PRX=1
PRX_EXPORTS=exports.exp
# Use the kernel's small inbuilt libc
USE_KERNEL_LIBC = 1
# Use only kernel libraries
USE_KERNEL_LIBS = 1

INCDIR = 
CFLAGS = -O2 -G0
CXXFLAGS = $&#40;CFLAGS&#41; -fno-exceptions -fno-rtti
ASFLAGS = $&#40;CFLAGS&#41;

LIBDIR =

LIBS = -lpspsystemctrl_kernel

PSPSDK=$&#40;shell psp-config --pspsdk-path&#41;
include $&#40;PSPSDK&#41;/lib/build_prx.mak

Code: Select all

PSP_BEGIN_EXPORTS

# These four lines are mandatory &#40;although you can add other functions like module_stop&#41;
# syslib is a psynonym for the single mandatory export.
PSP_EXPORT_START&#40;syslib, 0, 0x8000&#41;
PSP_EXPORT_FUNC_HASH&#40;module_start&#41;
PSP_EXPORT_END

PSP_EXPORT_START&#40;MyLib, 0, 0x0001&#41;
PSP_EXPORT_FUNC_HASH&#40;scePowerBatteryDisableUsbCharging_fake&#41;
PSP_EXPORT_END

PSP_END_EXPORTS
Post Reply