1、 2009-12-24 16:49:50 来自 : 伊拉克蜜枣甜掉牙 虚拟文件系统模块 增加一个虚拟文件系统是非常简单的 .假如你要开发一个新的文件系统或者支持现存的文件系统 ,就 需要写一个模块作为接口 .同样的 ,假如需 要调试已经存在的文件系统 ,也需要那样 一个接口 .必须确定你的内核不支持目标文件系统 . 一个虚拟文件系统的模块的结构应该象如下定义 : struct lkm_vfs MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; struct vfsconf *lkm_vfsconf; ; 和前面的例
2、子差不多 ,我们也有个模块类型 (LM_VFS),一个版本号 ,一个模块名和一个偏移值 .在这个 vfs 模块的例子中 ,offset值是用不到的 . 最后我们需要一个指向 vfsconf 结构 的指针 , 它包括了虚拟文件系统的操作向量以及一些其他信息 (vfsconf 结构在头文件/usr/include/sys/mount.h 中定义 ). 此结构通过 MOD_VFS宏来初始化 : MOD_VFS(“nullfs“, -1, extern struct vnodeopv_desc null_vnodeop_opv_desc; struct vfsconf nullfs_vfsconf =
3、 /* * 声明我们的模块结构 ,通过我们文件系统的 模块名 ,offset和初始的 vfsconf 结构 */ MOD_VFS(“nullfs“, -1, int cmd; int ver; vfs_opv_init_explicit( vfs_opv_init_default( DISPATCH(lkmtp, cmd, ver, lkm_nofunc, lkm_nofunc, lkm_nofunc) 好 ,编译安装它 : (一些其他的附加代码在 /usr/src/sys/miscfs/nullfs 里 ) e4gleopenbsd29# gcc -D_KERNEL -I/sys -c nu
4、ll_subr.c e4gleopenbsd29# gcc -D_KERNEL -I/sys -c null_vfsops.c e4gleopenbsd29# gcc -D_KERNEL -I/sys -c null_vnops.c e4gleopenbsd29# gcc -D_KERNEL -I/sys -c nullmod.c e4gleopenbsd29# ld -r -o nullfs.o null_vfsops.o null_vnops.o null_subr.o nullmod.o e4gleopenbsd29# modload -o nullfsmod -enullfsmod n
5、ullfs.o e4gleopenbsd29# modstat Type Id Off Loadaddr Size Info Rev Module Name VFS 0 -1 e0b84000 0003 e0b860d0 2 nullfs e4gleopenbsd29# ok,虚拟文件系统模块就说到这 . 其他类型的模块 这些模块被用来执行一些预定的模块类型所没有定义的操作 .在我这个例子中我们将为网络协议栈里加入控制代码 ,然后打印出我们接收到的 tcp 包的一些信息 . 当我们在书写其他类型的模 块时 ,我们必须要完整的检查一遍 ,确定没有预定的操作 .例如 ,同样的操作模块不能被加载两次
6、 .这等于我们在往内 核中去写入模块 .当然 , 我们都会在模块加载和卸载的控制函数里去控制 . 一个其他类型的模块结构象下面这样定义 : struct lkm_misc MODTYPE lkm_type; int lkm_ver; char *lkm_name; u_long lkm_offset; ; 同样 ,我们首先有一个模块的类型 (在这个例子中试 LM_MISC),然后是 lkm 的版本 ,再接着是模块名和 offset的 值 .在我的这个例子中 offset值 没有用到 ,但在 /usr/share/lkm/misc 提供的例子中 (增加一个系统调用 )offset 被用来在系统调
7、用表里面标记一个新的系统调用的位置 . 用 MOD_MISC宏来初始化该结构 : MOD_MISC(“tcpinfo“) 这里只有一个参数 ,指定了模块名 . 当我们的模块被加载后 ,该模块把 tcp_input 函数的指针改为我们制定的 new_input 函数 .新的函数会打印出mbuf里的包头的一些信息 ,然后 再调用原来的 tcp_input 函数 .在做这些 之前 ,我们一定 要确定同类的模块没有被加载 . 对于这个模块一些值得注意的地方 :首先运行这个模块时不适合传输大量的 tcp 包 ,printf()会变的很慢 .大家试一下就知道 .这个例子只是做测 试之用 ,其实大家可以想想
8、我们既然 可以改变 tcp 协议栈里的函数指针 ,我们用模块来做一个 tcp 的内核后门也应该很容易 ,就留给大家思考吧 ,呵呵 .第二 ,此代码原来是运行在 freebsd 之上的 ,稍微修改了一下而已 , bsd 系列的内核真是很相像 . 以下是该模块的完整代码 (tcpmod.c): #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 我们将改变 protosw结构中的 TCP入口
9、 . */ extern struct protosw inetsw; /* * 我们自定义的函数 */ extern int lkmexists _P(struct lkm_table *); extern char *inet_ntoa _P(struct in_addr); static void new_input _P(struct mbuf *, .); static void (*old_tcp_input) _P(struct mbuf *, .); /* * 声明我们的模块结构 */ MOD_MISC(“tcpinfo“) /* * 我们的句柄函数 ,用来加载和卸载模块 .
10、*/ int tcpmod_handler(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; int s; switch(cmd) case LKM_E_LOAD: /* * 确定此模块是第一次加载使用 */ if (lkmexists(lkmtp) return(EEXIST); /* * 阻赛网络协议进程 ,我们把 tcp_input 函数指针改成我们自己的包装函数 . */ s = splnet(); old_tcp_input = inetsw2.pr_input; inetsw2.pr_input = new_input; splx(s);
11、break; case LKM_E_UNLOAD: /* * 当模块退出时返回原来的结构 */ s = splnet(); inetsw2.pr_input = old_tcp_input; splx(s); break; return(0); /* * 我们的外部接口 ,没有做什么 ,用到了 DISPATCH宏 */ int tcpinfo(lkmtp, cmd, ver) struct lkm_table *lkmtp; int cmd; int ver; DISPATCH(lkmtp, cmd, ver, tcpmod_handler, tcpmod_handler, lkm_nofun
12、c) /* * 定义我们自己的包装的 tcp_input 函数 .假如 mbuf里有包头 ,则打印出网络接口接收到的包 * 的总长度以及包的源地址 .然后使原来的 tcp_input 函数正常运行 . */ static void new_input(struct mbuf *m, .) va_list ap; int iphlen; struct ifnet *ifnp; struct ip *ip; va_start(ap, m); iphlen = va_arg(ap, int); va_end(ap); if (m-m_flags ip = mtod(m, struct ip *);
13、printf(“incoming packet: %d bytes “, m-m_pkthdr.len); printf(“on %s from %sn“, ifnp-if_xname, inet_ntoa(ip-ip_src); (*old_tcp_input)(m, iphlen); return; 好 ,我们编 译安装它 : e4gleopenbsd29# gcc -D_KERNEL -I/sys -c tcpmod.c e4gleopenbsd29# modload -o tcpinfo.o -etcpinfo tcpmod.o 产生一些 tcp 连接 ,用 dmesg 来看看是否正常
14、工作 : e4gleopenbsd29# dmesg tail -3 incoming packet: 1500 bytes on ne3 from 129.128.5.191 incoming packet: 1205 bytes on ne3 from 129.128.5.191 incoming packet: 52 bytes on ne3 from 129.128.5.191 e4gleopenbsd29# ok,到这里结束 ,足以说明问题了 . 结束语 写这篇文章的目的还是为了让大家如们 bsd 系列的内核编程 ,驱动程序编程的入门 ,当然 ,作为一个网络安全的专业人员应该可以从这篇文章里 面看到一些东西 ,就是一些内核级别 的后门和截获技术 ,例如 ,我们可以通过增加和重定向系统调用的模块来截获系统调用 ,我们可以用刚才 的最后一种模块来做一个内核级别的 tcp 后门等等 .当然我们还可以利用 模块来制作一些内核级的安全工具 .发挥想象力 ,留给大家了 ,呵呵