Linux x86架构内核Hook实现
linux-dash
A beautiful web dashboard for Linux
项目地址:https://gitcode.com/gh_mirrors/li/linux-dash
免费下载资源
·
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);
}
//该函数返回函数的入口地址。
编译出的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函数。
二、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
GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:1 个月前 )
186a802e
added ecosystem file for PM2 4 年前
5def40a3
Add host customization support for the NodeJS version 4 年前
更多推荐
已为社区贡献7条内容
所有评论(0)