MajorasKid
MajorasKid

Reputation: 873

ARM64 - assembly branch to function address

i've got a problem with loading an 64bit address into a register from within a LKM using arm64 inline assembly.

I'm trying to setup a function hook within the kernel memory. So every time when a specific function is called, it should branch to my function instead.

My idea was to load an address into a register, which is obtained while running using this:

unsigned long address = &hooked_do_undefinstr;

And then write the corresponding OPCode of

BLR X3

into the memory.

I tried to load the address into register X3 (because its an 64bit OS) with

__asm__ __volatile__ ( "MOV x3, %[var]" : [var] "=r" (address));

Because I have to get the address while running, i cant use the LDR command. when inserting the module i get the following error:

root@___ :~# insmod mod_init.ko 
[   70.386938] mod_init: Unknown symbol x19 (err 0)
[   70.391508] mod_init: Unknown symbol x3 (err 0)

With this command, the output when i print the X3 content is zero:

[  558.948492]          MOV x3 Register value 0x0

My question now is, is there a way to load an 64bit address into a register? Or is there a better way to implement my function hook in order to jump to my address?

Greetings and thanks for your help

Upvotes: 1

Views: 3581

Answers (2)

MajorasKid
MajorasKid

Reputation: 873

I were able to solve this problem by using @Jester's idea.

Just note, that if you're using his code, check whether or not your system runs little or big endian. This can be done by:

//Get the SCTLR_EL1 content
__asm__ __volatile__ ( "MRS %[result], SCTLR_EL1" : [result] "=r" (sctlr_el1));

//Check the 25th bit. if 1 -> big, else little
if(sctlr_el1 & (1<<25))
{
    printk(KERN_INFO "          Big Endian Found\n");
    create_hook_big(addresse);
}else
{
    printk(KERN_INFO "          Little Endian found\n");
    create_hook_little(addresse);
}

Else: this is my working code:

//Backup original entries
memcpy(original_blr, (void*)el1_sync,sizeof(original_blr));


//Set function hook, el1_sync is my used target
memcpy((void*)el1_sync,replace_jump_offset,sizeof(replace_jump_offset));
*(void (**)(void))(el1_sync + 8) = &hooked_do_undefinstr;

Upvotes: 0

Jester
Jester

Reputation: 58762

It's not entirely clear what you are doing. If you want to hook a function, you can't just stick your blr x3 in there and expect x3 to hold a value you set using inline asm elsewhere (unless you know it's not touched anywhere, but I find that unlikely). You need to put the x3 loading code also into the hooked function, something like this could work:

ldr x3, .+8
blr x3

Creating the machine code using an assembler gives: 43 00 00 58 60 00 3F D6

When patching, your code should append the target address at the end:

void patch(unsigned char* target)
{
    unsigned char code[] = { 0x43, 0x00, 0x00, 0x58, 0x60, 0x00, 0x3F, 0xD6 };
    memcpy(target, code, 8);
    *(void (**)())(target + 8) = hooked_do_undefinstr;
}

Also note that whatever you have overwritten should be compensated by your hook function. As usual, you also need to make sure the section you are patching is writable.

Upvotes: 2

Related Questions