开发者

Intercepting 64-bit Linux kernel function: Length of a function pointer at 32/64-bit?

开发者 https://www.devze.com 2023-02-15 13:12 出处:网络
I\'m trying to re-implement old-as-behemoth kernel intercept (described at this Phrack issue). The code to replac开发者_运维技巧e 32-bit function call is like:

I'm trying to re-implement old-as-behemoth kernel intercept (described at this Phrack issue).

The code to replac开发者_运维技巧e 32-bit function call is like:

#define SYSMAPADDR 0x12345678
#define CODESIZE 7
static char acct_code[7] = "\xb8\x00\x00\x00\x00"/*movl $0, %eax*/
"\xff\xe0";/*jmp *%eax*/
*(long*)&acct_code[1] = (long)my_hijacking_function;
// here, use either set_pages_rw or trick CR0 to do this:
memcpy(SYSMAPADDR, acct_code, CODESIZE);

But 64-bit address of original function is 0xffffffff12345678 (kernel is located in low-memory).

So will the (long) new function pointer fit just 4 \x00 bytes of the movl instruction?

Btw, please link this to Can I replace a Linux kernel function with a module? and Overriding functionality with modules in Linux kernel, the hacky method described above is more flexible (can intercept non-extern functions => no need to recompile the kernel).


There is no way to make a direct and unconditional jump to an address with a displacement greater than 2GB on any x86 (32 or 64 bit).

When I wrote a detouring library some time ago, the best options I could come up with to redirect program flow (for x86-64) involved backing up the target function's prologue by M bytes and overwriting the target function's prologue with two instructions.

I use the %r11 register instead of the accumulator. According to the AMD64 ABI Draft 0.99.5, %r11 is a temporary register that is not preserved across function calls.

The first instruction, movq $addr, %r11, does exactly what it looks like: it loads the specified address into a register. The second instruction, jmp *%r11, forces an unconditional indirect jump to the address stored in %r11.

Appended to the end of the backed-up instructions should be another unconditional indirect jump back to the original target's function, to an address immediately after the overwritten instructions. Then, when you want to call to original, you can invoke the address of the backed up function prologue and program flow continues as usual.

Remember that the number of bytes to backup, M, must be the sum of the size of the store/jump instructions and the remainder of the overwritten instruction. You don't want to leave any partial instructions behind after doing this voodoo.


Note: I am assuming this is for x86_64.

Function pointers are 64 bits, and the movl instruction zero-extends into 64-bit registers, so you'll have to rewrite the machine code. The instruction you want is probably 48 B8 (imm64) (i.e. movq ..., %rax), and I think the jump instruction can be left alone, but I don't know much about this. You should probably add the 'x86-64' and 'assembly' tags to your question.


You can use the JMP rel32 (0xE9) operation to perform a 32-bit relative jump from the current address. This will allow you to make a jump to anywhere within 2GB of the source address in five bytes. It also has the advantage that it does not clobber %eax (this may or may not be important in your case).

That said, I would recommend looking into the kprobes API instead. This handles all the hard work of runtime patching for you. It also deals with multiple markers being applied to the same function and other such nastiness, and is portable to multiple platforms. In particular, if your monkey-patching approach was in use, it could conflict with the markers API if compiled in, resulting in crashes. It would also result in crashes if dynamically patchable code was located in the first few bytes of a function (LOCK prefixes, etc).

You might also want to look into how ftrace works - depending on kernel configuration, it might be somewhat faster to hook into ftrace instead.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号