text_poke()函数用于在内核动态替换opcode,从而达到Inline Hook的效果。
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()找到函数入口地址。
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: 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 ffffffff818f5fc7: 48 85 c0 test %rax,%rax ffffffff818f5fca: 74 7d je ffffffff818f6049 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 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 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函数。
二、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 functionn");
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 functionn");
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#include #include #include #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 functionn"); 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 functionn"); 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 failedn"); 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 failedn"); return -1; } printk("orig_ip_rcv -> 0x%lxn", orig_ip_rcv); 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); 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); 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 failedn"); return -1; } ret = text_poke_init(); if (ret < 0) { printk("Hook vfs_read failedn"); 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



