Hooks?

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

Moderators: cheriff, TyRaNiD

Post Reply
roby65
Posts: 52
Joined: Sun Jun 01, 2008 9:12 pm
Location: Mid Italy
Contact:

Hooks?

Post by roby65 »

Can someone explain me how to hook a function?
i know on windows how hook works, is that similar to this or there are some differences?
coolkehon
Posts: 355
Joined: Mon Oct 20, 2008 5:44 am

Post by coolkehon »

i would like to add to this question what is a hook i keep seeing it everyonce in a while libconfig, SDL, etc and also how do i use / create a hook
Pirata Nervo
Posts: 409
Joined: Tue Oct 09, 2007 4:22 am

Post by Pirata Nervo »

Image
Upgrade your PSP
NoEffex
Posts: 106
Joined: Thu Nov 27, 2008 6:48 am

Post by NoEffex »

In the CFW systemctrl_kernel library(probably others too, but that's the one I know off the top of my head), you can hook syscalls like

int func_addr = sctrlHENFindFunction(modname, libname, nid);
sctrlHENPatchSyscall(func_addr, &function_name);

That's one of the easier ways to do it.
Programming with:
Geany + Latest PSPSDK from svn
User avatar
Torch
Posts: 825
Joined: Wed May 28, 2008 2:50 am

Post by Torch »

For hooking user to kernel syscall functions the above method is used. This will not effect a call to the function from a kernel module, it only effects user mode to kernel syscalls.

For hooking kernel-kernel or user-user functions, you need to go the address of the function, and replace the first instructions to jump to the address of your hook function. Then in your hook function, you can either process the data, or jump back into the original function.
Pirata Nervo
Posts: 409
Joined: Tue Oct 09, 2007 4:22 am

Post by Pirata Nervo »

@Torch, is assembly the only way to do it?
Image
Upgrade your PSP
User avatar
Torch
Posts: 825
Joined: Wed May 28, 2008 2:50 am

Post by Torch »

Pirata Nervo wrote:@Torch, is assembly the only way to do it?
Yes. Because the user-user or kernel-kernel jumps are directly in code from one module to a function address in another module. The return address is stored in $ra register to go back to the original code which called it.
slasher2661996
Posts: 91
Joined: Sun Feb 22, 2009 8:32 am
Location: Melbourne Australia ZOMG

Post by slasher2661996 »

Torch could you post an example?
User avatar
Torch
Posts: 825
Joined: Wed May 28, 2008 2:50 am

Post by Torch »

Code: Select all

#define JAL_OPCODE      0x0C000000
#define J_OPCODE        0x08000000
#define MAKE_JUMP(a, f) _sw(J_OPCODE | (((u32)(f) & 0x0ffffffc) >> 2), a);
#define MAKE_CALL(a, f) _sw(JAL_OPCODE | (((u32)(f) >> 2)  & 0x03ffffff), a);

...
...

int orig_jump(int var1, int var2...)
{
    asm("1st instruction from original function");
    asm("2nd instruction from original function");
    asm("j original_function_address+8"); //will return back to 'hooked' after original function is finished
    asm("nop");
    return 0; //should not reach here
}

int hooked(int var1, int var2...)
{
    //do stuff
    return orig_jump(var1, var2...) //calls original function
}

//To do the patch
MAKE_JUMP(address of original, hooked)
//you need to add a nop after the jump too
Last edited by Torch on Sun Mar 08, 2009 8:59 pm, edited 2 times in total.
User avatar
Torch
Posts: 825
Joined: Wed May 28, 2008 2:50 am

Post by Torch »

There was a small error, I edited the post.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

branch control instructions are : J, JAL, JR, JALR, Bxx, etc.

NOTE: if one of the first two instructions in the original function is a relative branch transfer, you'll have an issue because you need to recompute the target offset so you need a stub like it if insn2 is a relative branch instruction :

Code: Select all

original_function_stub:
    insn1
    beq ..., 0f // insn2 modified (was beq ..., original_function_exit)
    insn3
    j original_function_fallback
    nop 
0:  j original_function_exit // ok
    nop
    ...

and 
/* existing function to patch in a module */
original_function:
    insn1 ----> J hooked_function
    insn2 ----> nop
    insn3
original_function_fallback: // = ((int)&original_function_1) + 3
    ...

examples of auto-generated stub functions :

Code: Select all

original_function_1_stub:
    insn1
    j original_function_1_fallback
    insn2 

original_function_2_stub:
    insn1 // insn1 is a branch control instruction
    insn2 // only insn2 can fit a branch delay slot here
    j original_function_2_fallback
    nop

original_function_3_stub:
    insn1
    insn2 // insn2 is a branch control instruction
    nop // necessary because the next J cannot fit a branch delay slot
    j original_function_3_fallback
    nop 
...
examples of patched functions :

Code: Select all

/* existing function 1 to patch in a module */
original_function_1:
    insn1 ----> J hooked_function_1
    insn2 ----> nop
original_function_1_fallback: // = ((int)&original_function_1) + 2
    ...

/* existing function 2 to patch in a module */
original_function_2:
    insn1 ----> J hooked_function_2
    insn2 ----> nop
original_function_2_fallback: // = ((int)&original_function_2) + 2
    ...

/* existing function 3 to patch in a module */
original_function_3:
    insn1 ----> J hooked_function_3
    insn2 ----> nop
original_function_3_fallback: // = ((int)&original_function_3) + 2
    ...
your hooked functions :

Code: Select all

... hooked_function_1(...)
{
    ... your code ...
    ... original_function_1_stub(...); 
    [... your code ...]
}


... hooked_function_2(...)
{
    ... your code ...
    ... original_function_2_stub(...); 
    [... your code ...]
}


... hooked_function_3(...)
{
    ... your code ...
    ... original_function_3_stub(...); 
    [... your code ...]
}

...
Here is an example of an untested function to do all the stuff :

Code: Select all

void *patch_jump(int *original, int *hooked, int *stub)
{
    if (is_branch_control_instruction(original[0]))
    {
        if (is_relative_branch_control_instruction(original[0]))
        {
            stub[0] =  (original[0]&0xFFFF0000)|4; // insn1 modified
            stub[1] =  original[1]; // insn2
            MAKE_JUMP(&stub[2], (int)(original + 2)); // J original_function_fallback
            stub[3] =  0; // nop
            MAKE_JUMP(&stub[4], (int)(original + 0 + (original[0]&0xFFFF)));
            stub[5] =  0; // nop

            writeback_dcache_and_invalidate_icache_range(stub, stub + 6);
        }
        else
        {
            stub[0] =  original[0]; // insn1
            stub[1] =  original[1]; // insn2
            MAKE_JUMP(&stub[2], (int)(original + 2)); // J original_function_fallback
            stub[3] =  0; // nop

            writeback_dcache_and_invalidate_icache_range(stub, stub + 4);
        }
    }
    else if (is_branch_control_instruction(original[1]))
    {
        if (is_relative_branch_control_instruction(original[1]))
        {
            stub[0] =  original[0]; // insn1
            stub[1] =  (original[1]&0xFFFF0000)|4; // insn2 modified
            stub[2] =  original[2]; // insn3
            MAKE_JUMP(&stub[3], (int)(original + 3)); // J original_function_fallback
            stub[4] =  0; // nop
            MAKE_JUMP(&stub[5], (int)(original + 1 + (original[1]&0xFFFF)));
            stub[6] =  0; // nop

            writeback_dcache_and_invalidate_icache_range(stub, stub + 7);
        }
        else
        {
            stub[0] =  original[0]; // insn1
            stub[1] =  original[1]; // insn2
            stub[2] =  0; // nop
            MAKE_JUMP(&stub[3], (int)(original + 2)); // J original_function_fallback
            stub[4] =  0; // nop

            writeback_dcache_and_invalidate_icache_range(stub, stub + 5);
        }

    }
    else
    {
        stub[0] =  original[0]; // insn1
        MAKE_JUMP(&stub[1], (int)(original + 2)); // J original_function_fallback
        stub[2] =  original[1]; // insn2

        writeback_dcache_and_invalidate_icache_range(stub, stub + 3);
    }
    MAKE_JUMP(&original[0], hooked); // insn1 ---> J hooked_function
    original[1] = 0; // insn2 ---> nop

    writeback_dcache_and_invalidate_icache_range(original, original + 2);

    return stub;
}
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

I edited heavily the previous post so the stub generator can handle the case when one of first two instructions in the original function contains a relative branch instruction. You need a .S file to contain your stub to overwrite (for each original function to patch, you need a stub function of 8 instructions) :

Code: Select all

.global your_stub_function
.enter your_stub_function
your_stub_function:
    jr $ra
    nop
    nop
    nop
    nop
    nop
    nop
    nop
.end your_stub_function
...
when you want to patch an original function with your hooked function, you also pass the stub function address :

Code: Select all

    extern int stub_function(int);
    int hooked_function(int a0)
    {
        int result;
        // do your stuff here
        result = stub_function(a0);
        // do your other stuff here
        return result;
    }

    ...

    patch_jump((int *)&original_function, (int *)&hooked_function, (int *)&stub_function);
    ...
Pirata Nervo
Posts: 409
Joined: Tue Oct 09, 2007 4:22 am

Post by Pirata Nervo »

Thank you very much hlide and Torch :)
Image
Upgrade your PSP
Post Reply