- 论坛徽章:
- 0
|
参照相关文档,写了(或者说是验证了)一个用kld修改函数指针,从而替换内核函数的例子,以icmp输入函数icmp_input()为例。这个简单的例子将内核中对icmp_input()的函数指针调用替换为对自己的new_icmp_input()的调用。我们可以在new_icmp_input()函数中对收到的报文进行自定义的解析处理。但是在本例中,我们不做任何处理,仅打印出一行调试信息,随后继续调用我们保存下来的系统原来的icmp_input()函数,从而完成正常的icmp输入处理。
工作目录下有两个文件,一个是Makefile,一个是我们的源文件icmphook.c。
Makefile文件的内容如下:
- 1 KMOD= icmphook
- 2 SRCS= icmphook.c
- 3
- 4 .include <bsd.kmod.mk>
复制代码
icmphook.c文件的内容如下:
- 1 #include <sys/types.h>
- 2 #include <sys/param.h>
- 3 #include <sys/module.h>
- 4 #include <sys/kernel.h>
- 5 #include <sys/conf.h>
- 6 #include <sys/protosw.h>
- 7 #include <sys/mbuf.h>
- 8 #include <netinet/in.h>
- 9
- 10 extern struct protosw inetsw[];
- 11 extern u_char ip_protox[];
- 12 void new_icmp_input(struct mbuf *, int);
- 13 void *old_icmp_input;
- 14
- 15 void new_icmp_input(struct mbuf * m, int off)
- 16 {
- 17 printf("new_icmp_input recv packet!\n");
- 18 ((void (*)(struct mbuf *, int))old_icmp_input)(m, off);
- 19 }
- 20
- 21 static int
- 22 load(struct module *module, int cmd, void *arg)
- 23 {
- 24 int error = 0;
- 25
- 26 switch(cmd) {
- 27 case MOD_LOAD:
- 28 uprintf("Replacing ICMP Input\n");
- 29 old_icmp_input = inetsw[ip_protox[IPPROTO_ICMP]].pr_input;
- 30 inetsw[ip_protox[IPPROTO_ICMP]].pr_input = new_icmp_input;
- 31 break;
- 32
- 33 case MOD_UNLOAD:
- 34 uprintf("Restoring icmp_input\n");
- 35 inetsw[ip_protox[IPPROTO_ICMP]].pr_input = old_icmp_input;
- 36 break;
- 37
- 38 default:
- 39 error = EINVAL;
- 40 break;
- 41 }
- 42 return(error);
- 43
- 44 }
- 45
- 46 DEV_MODULE(icmphook, load, NULL);
复制代码
在工作目录下make我们的模块:
- $make
- Warning: Object directory not changed from original /usr/home/xxx
- @ -> /usr/src/sys
- machine -> /usr/src/sys/i386/include
- cc -O2 -fno-strict-aliasing -pipe -Werror -D_KERNEL -DKLD_MODULE -nostdinc -I- -I. -I@ -I@/contrib/altq -I@/../include -I/usr/include -finline-limit=8000 -fno-common -mno-align-long-strings -mpreferred-stack-boundary=2 -mno-mmx -mno-3dnow -mno-sse -mno-sse2 -ffreestanding -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual -fformat-extensions -std=c99 -c icmphook.c
- ld -d -warn-common -r -d -o icmphook.kld icmphook.o
- touch export_syms
- awk -f /sys/conf/kmod_syms.awk icmphook.kld export_syms | xargs -J% objcopy % icmphook.kld
- ld -Bshareable -d -warn-common -o icmphook.ko icmphook.kld
- objcopy --strip-debug icmphook.ko
- $
复制代码
此时,就会在工作目录中生成icmphook.ko文件,这就是我们的模块。以root权限加载它:
- #
- # kldload ./icmphook.ko
- Replacing ICMP Input
- #
复制代码
通过调试信息我们可以看到,这个模块已经成功加载了。现在我们就来测试它是否能正确工作。我做测试的FreeBSD是一台远程机器,现在我在本地ping它(用x.x.x.x隐去了真实的ip地址):
- C:\Documents and Settings\freebsd>ping x.x.x.x
- Pinging x.x.x.x with 32 bytes of data:
- Reply from x.x.x.x: bytes=32 time=14ms TTL=63
- Reply from x.x.x.x: bytes=32 time<10ms TTL=63
- Reply from x.x.x.x: bytes=32 time<10ms TTL=63
- Reply from x.x.x.x: bytes=32 time<10ms TTL=63
- Ping statistics for x.x.x.x:
- Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
- Approximate round trip times in milli-seconds:
- Minimum = 0ms, Maximum = 14ms, Average = 3ms
- C:\Documents and Settings\freebsd>
复制代码
我们可以看到,FreeBSD机器仍然可以ping通,这是因为我们在new_icmp_input()函数中调用了原来的icmp_input()函数,使得正常的icmp输入处理没有受到影响。由于我们测试的是远程机器,new_icmp_input()函数中的printf无法打印到本地屏幕上,我们可以使用dmesg来查看:
- #
- # dmesg
- ......
- new_icmp_input recv packet!
- new_icmp_input recv packet!
- new_icmp_input recv packet!
- new_icmp_input recv packet!
- #
复制代码
我们可以看到,dmesg正确的记录了我们在模块中添加的调试信息,说明我们的new_icmp_input()函数已经成功的“插入”了系统的icmp处理流程中。 |
|