赞
踩
text_poke()函数用于在内核动态替换opcode,从而达到Inline Hook的效果。
/** * text_poke - Update instructions on a live kernel * @addr: address to modify * @opcode: source of the copy * @len: length to copy * * Only atomic text poke/set should be allowed when not doing early patching. * It means the size must be writable atomically and the address must be aligned * in a way that permits an atomic write. It also makes sure we fit on a single * page. * * Note that the caller must ensure that if the modified code is part of a * module, the module would not be removed during poking. This can be achieved * by registering a module notifier, and ordering module removal and patching * trough a mutex. */ void *text_poke(void *addr, const void *opcode, size_t len) { lockdep_assert_held(&text_mutex); return __text_poke(addr, opcode, len); } //5.11.x内核这个函数没有,需要自己使用kallsyms_lookup_name()找到函数入口地址。 /* Lookup the address for this symbol. Returns 0 if not found. */ unsigned long kallsyms_lookup_name(const char *name) { char namebuf[KSYM_NAME_LEN]; unsigned long i; unsigned int off; for (i = 0, off = 0; i < kallsyms_num_syms; i++) { off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); if (strcmp(namebuf, name) == 0) return kallsyms_sym_address(i); if (cleanup_symbol_name(namebuf) && strcmp(namebuf, name) == 0) return kallsyms_sym_address(i); } return module_kallsyms_lookup_name(name); } //该函数返回函数的入口地址。
编译出的vmlinux使用objdump进行返汇编操作,查看ip_rcv()反汇编之后的汇编代码。
ffffffff818f5f90 <ip_rcv>: ffffffff818f5f90: e8 ab b8 30 00 callq ffffffff81c01840 <__fentry__> ffffffff818f5f95: 55 push %rbp ffffffff818f5f96: 48 89 e5 mov %rsp,%rbp ffffffff818f5f99: 41 55 push %r13 ffffffff818f5f9b: 41 54 push %r12 ffffffff818f5f9d: 53 push %rbx ffffffff818f5f9e: 48 89 f3 mov %rsi,%rbx ffffffff818f5fa1: 48 83 ec 38 sub $0x38,%rsp ffffffff818f5fa5: 4c 8b ae 28 05 00 00 mov 0x528(%rsi),%r13 ffffffff818f5fac: 65 48 8b 04 25 28 00 mov %gs:0x28,%rax ffffffff818f5fb3: 00 00 ffffffff818f5fb5: 48 89 45 e0 mov %rax,-0x20(%rbp) ffffffff818f5fb9: 31 c0 xor %eax,%eax ffffffff818f5fbb: 49 8d b5 90 01 00 00 lea 0x190(%r13),%rsi ffffffff818f5fc2: e8 e9 fa ff ff callq ffffffff818f5ab0 <ip_rcv_core.isra.0> ffffffff818f5fc7: 48 85 c0 test %rax,%rax ffffffff818f5fca: 74 7d je ffffffff818f6049 <ip_rcv+0xb9> ffffffff818f5fcc: 49 89 c4 mov %rax,%r12 ffffffff818f5fcf: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) ffffffff818f5fd4: 4c 89 e2 mov %r12,%rdx ffffffff818f5fd7: 31 f6 xor %esi,%esi ffffffff818f5fd9: 4c 89 ef mov %r13,%rdi ffffffff818f5fdc: e8 af f7 ff ff callq ffffffff818f5790 <ip_rcv_finish> ffffffff818f5fe1: 48 8b 4d e0 mov -0x20(%rbp),%rcx ffffffff818f5fe5: 65 48 33 0c 25 28 00 xor %gs:0x28,%rcx ffffffff818f5fec: 00 00 ffffffff818f5fee: 75 60 jne ffffffff818f6050 <ip_rcv+0xc0> ffffffff818f5ff0: 48 83 c4 38 add $0x38,%rsp ffffffff818f5ff4: 5b pop %rbx ffffffff818f5ff5: 41 5c pop %r12 ffffffff818f5ff7: 41 5d pop %r13 ffffffff818f5ff9: 5d pop %rbp .....
从上面反汇编的结果可以看出函数调用了函数__fentry__,我们的目的是当内核调用ip_rcv()函数走进我们自定义函数,x86架构跳转指令jump opcode为e9,把这条call指令替换成无条件跳转指令,jmp offset,这样就可以进入自定义函数hook_ip_rcv函数。
ip_rcv()函数原型
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
struct net_device *orig_dev)
{
.....
}
构造跳转函数,如果hook_ip_rcv()直接跳转回orig_ip_rcv()将造成死循环,所以需要一中间跳转的桩,这个函数可以由我们自己定义。
stub_ip_rcv()
static int stub_ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
struct net_device *orig_dev)
{
printk("this is stub function\n");
asm("nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop");
return 0;
}
hook_ip_rcv()
static int hook_ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
struct net_device *orig_dev)
{
printk(KERN_ERR"This is hook ip_rcv function\n");
return stub_ip_rcv(skb, dev, pt, orig_dev);
}
使用print_hex_dump()函数来打印opcode,高内核版本kallsyms_lookup_name()函数没有导出,需要在代码中指定该函数的入口地址,查找方法:
root@rlk:/home/rlk/code/text_poke# cat /proc/kallsyms | grep kallsyms_lookup_name
ffffffffa4b56bd0 T module_kallsyms_lookup_name
ffffffffa4b576c0 T kallsyms_lookup_name
ffffffffa5e93594 r __ksymtab_kallsyms_lookup_name
ffffffffa5ea1446 r __kstrtab_kallsyms_lookup_name
#include <linux/printk.h> #include <linux/cpu.h> #include <linux/net.h> #include <net/tcp.h> #define HOOK_SIZE 5 char save_opcode[HOOK_SIZE]; char jump_opcode[HOOK_SIZE]; char stub_opcode[HOOK_SIZE]; static unsigned long (*kallsyms_lookup_name_ptr)(const char *name); static void * (*text_poke_ptr)(void *addr, const void *opcode, size_t len); static int (*orig_ip_rcv)(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) ; static int stub_ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { printk("this is stub function\n"); asm("nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop"); return 0; } static int hook_ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { printk(KERN_ERR"This is hook ip_rcv function\n"); return stub_ip_rcv(skb, dev, pt, orig_dev); } static int hook_framework_init(void) { //代码编译前需修改此函数入口地址 kallsyms_lookup_name_ptr = (unsigned long (*)(const char *))0xffffffffa4b576c0; text_poke_ptr = (void * (*)(void *, const void *, size_t)) kallsyms_lookup_name_ptr("text_poke"); if (text_poke_ptr == NULL) { printk("Get text_poke function address failed\n"); return -1; } return 0; } static int text_poke_init(void) { int32_t hook_offset = 0; int32_t stub_offset = 0; orig_ip_rcv = (int (*)(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *))kallsyms_lookup_name_ptr("ip_rcv"); if (!orig_ip_rcv) { printk("Get vfs_read_fn failed\n"); return -1; } printk("orig_ip_rcv -> 0x%lx\n", orig_ip_rcv); /* Backup raw vfs_read opcode */ memcpy(save_opcode, (char *)orig_ip_rcv, HOOK_SIZE); print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS, 16, 1, save_opcode, HOOK_SIZE, true); /* Get hook_ip_rcv opcode */ jump_opcode[0] = 0xe9; hook_offset = (int32_t)((long)hook_ip_rcv - (long)orig_ip_rcv - HOOK_SIZE); (*(int32_t *)&jump_opcode[1]) = hook_offset; print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS, 16, 1, jump_opcode, HOOK_SIZE, true); /* Get stub ip_rcv opcode */ stub_opcode[0] = 0xe9; stub_offset = (int32_t)(((long)orig_ip_rcv + HOOK_SIZE) - ((long)stub_ip_rcv + HOOK_SIZE)); (*(int32_t *)&stub_opcode[1]) = stub_offset; get_online_cpus(); text_poke_ptr((void *)stub_ip_rcv, stub_opcode, HOOK_SIZE); barrier(); text_poke_ptr((void *)orig_ip_rcv, jump_opcode, HOOK_SIZE); put_online_cpus(); print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS, 16, 1, stub_opcode, HOOK_SIZE, true); print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS, 16, 1, stub_ip_rcv, HOOK_SIZE, true); return 0; } static int __init poke_init(void) { int ret = 0; ret = hook_framework_init(); if (ret < 0) { printk("Init inline hook failed\n"); return -1; } ret = text_poke_init(); if (ret < 0) { printk("Hook vfs_read failed\n"); return -1; } return 0; } static void __exit poke_exit(void) { printk("Text_poke exit ...\n"); text_poke_ptr(orig_ip_rcv, save_opcode, HOOK_SIZE); } module_init(poke_init); module_exit(poke_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("curtis"); MODULE_DESCRIPTION("inline hook kernel function");
驱动安装之后内核打印:
[ 2291.314300] orig_ip_rcv -> 0xffffffffa53ae8c0
[ 2291.314302] raw data: 00000000115abb16: 0f 1f 44 00 00 ..D..
[ 2291.314302] raw data: 000000004d249135: e9 6b a7 29 1b .k.).
[ 2291.314305] raw data: 00000000c7f290e8: e9 c0 58 d6 e4 ..X..
[ 2291.314306] raw data: 00000000b27b0427: e9 c0 58 d6 e4 ..X..
[ 2318.487262] This is hook ip_rcv function
[ 2318.492536] This is hook ip_rcv function
[ 2318.494959] This is hook ip_rcv function
[ 2318.495728] This is hook ip_rcv function
[ 2318.495807] This is hook ip_rcv function
[ 2318.496516] This is hook ip_rcv function
[ 2318.506860] This is hook ip_rcv function
[ 2318.506903] This is hook ip_rcv function
[ 2318.508956] This is hook ip_rcv function
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。