当前位置:   article > 正文

Linux x86架构内核Hook实现_text_poke

text_poke

Linux x86架构内核Hook实现

一、内核函数

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);
}

//该函数返回函数的入口地址。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

编译出的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
.....
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

从上面反汇编的结果可以看出函数调用了函数__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)
{
    .....
}
  • 1
  • 2
  • 3
  • 4
  • 5

构造跳转函数,如果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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

三、实现源码

使用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
  • 1
  • 2
  • 3
  • 4
  • 5
#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");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127

驱动安装之后内核打印:

[ 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号