2 实现自己Module
自己Module就是说,自己在源代码中添加hook函数接口,编译后就相当于在内核开了个后门;然后通过Module实现函数的具体功能,插入到内核中,即使具体功能有变化,只要接口不便,就无需再编译源代码,而且可以卸载Module。当然,也可以直接修改源代码添加功能,不过这需要添加一次就修改编译源代码一次,操作上不实际。
这部分,需要参考"Kernel_TCPIP"作为基础部分。
2.1 简单实例
参考Kernel_TCPIP可知, 内核软件在发送数据包的最后一个函数是/net/core/dev.c中的dev_queue_xmit(),(8.8节),先就拿这个函数动手。
2.1.1 添加后门函数
首先在这个函数前面添加一个函数指针声明,
int (*myHook_1)(struct sk_buff* ) = 0; //struct sk_buff是内核网络中的一个数据结构
然后在函数内部,添加
int dev_queue_xmit(struct sk_buff* skb)
{
//....
if( myHook_1) //判断是否加载了myHook_1
{
myHook_1(skb);
}
//...
}
最后在dev.c末尾添加导出声明,这类似于DLL的函数导出声明
//....
EXPORT_SYMBOL(myHook_1);
这三步完成后,内核中的后门函数即安装完毕,重新编译替换内核
2.1.2 编写Module
直接源代码列出myHook_1.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
extern int (*myHook_1)(struct sk_buff*);
int myHook_1_impl() //相当于真正的实现函数
{
printk("Hello myHook_1"); //printk是内核的打印语句,可以 #cat /var/log/message 查看
return 0;
}
static int init_hook( void )
{
myHook_1 = myHook_1_impl; //后门函数指向了真正的实现函数
printk("Module installed"); //表示加载成功
return 0;
}
static int exit_hook(void)
{
myHook_1 = 0; //后门函数清0
printk("Module removed/n"); //表示卸载成功
return 0;
}
MODULE_LICENSE("GPL"); //表示采用的开放协议
module_init(init_hook); //为当加载Module时,自动调用init_hook
module_exit(exit_hook); //当卸载Module时,自动调用exit_hook;
2.1.3编写Makefile
Linux下编译程序,除非自己写完整的gcc编译语句,否则一般编写Makefile来简化编译过程
编写Makefile,注意格式很重要,顶头的一定要顶头,空行开头的一定要以tab空行,而tab是几个space是没关系的
obj-m := myHook_1.o
all:
make -C /lib/modules/2.6.24.3/build /
M=$(PWD) modules
clean:
make -C /lib/modules/2.6.24.3/build /
M-$(PWD) modules
保存后,输入
#make
即可编译出 myHook_1.ko, 虽然Makefile中是myHook_1.o,但编译结果是ko,
2.1.4 加载和卸载
#insmod myHook_1.ko
#cat /var/log/message
就能看到 "Module installed",表示加载成功
#ping www.sina.com.cn
....
然后再
#cat /var/log/message
可以看到"Hello myHook_1" ,表示已经成功加载到内核中了,ok,Linux内核编程已经迈进一只脚了,不过也就刚一只脚,呵呵
#rmmod myHook_1
#cat /var/log/message
就能看到 "Module removed",表示卸载成功
再ping,就不会出现"Hello myHook_1"了。