在2.6内核中, netlink相关的接口函数随着版本的变化很大,现在网上流传的多数代码是以较老的版本(2.6.24以下)作为依托。这里,我将一段基于2.6.18的简单的代码移植到了2.6.27中,希望可以帮助大家理解其中的变化。
这个程序主要分为内核模块和用户模块。主要功能是监控一个指定的文件,如果他被打开了,记录打开的方式、程序、时间等信息到指定的log文件中。请到源码中理解代码具体的含义和作用:
(1) 内核部分:
文件 netlinkp.c(不同内核版本代码主要不同在这个文件)
char tmp_path[MAX_LENGTH];
char local_path[MAX_LENGTH];
memset(tmp_path,0,MAX_LENGTH);
memset(local_path,0,MAX_LENGTH);
if (*pathname == '/') {
strcpy(fullname,pathname);
return;
}
while (tmp_dentry != NULL)
{
if (!strcmp(tmp_dentry->d_iname,"/"))
break;
strcpy(tmp_path,"/");
strcat(tmp_path,tmp_dentry->d_iname);
strcat(tmp_path,local_path);
strcpy(local_path,tmp_path);
tmp_dentry = tmp_dentry->d_parent;
}
strcpy(fullname,local_path);
strcat(fullname,"/");
strcat(fullname,pathname);
return;
}
/*
* 重定义open系统调用后会在原来open系统调用的基础上调用此函数
* @ 如果不是指定的监控文件,会直接返回
* @ 如果是指定的监控文件,则将相应的信息送到用户程序
*/
int AuditOpen(const char *pathname,int flags, int ret)
{
char commandname[TASK_COMM_LEN];
char fullname[256];
unsigned int size; // = strlen(pathname) + 32 + TASK_COMM_LEN;
void * buffer; // = kmalloc(size, 0);
memset(fullname, 0, 256);
get_fullname(pathname, fullname);
if (strncmp(fullname,AUDITPATH,15) != 0) return 1;
strncpy(commandname,get_current()->comm,TASK_COMM_LEN);
size = strlen(fullname) + 16 + TASK_COMM_LEN + 1;
buffer = kmalloc(size, 0);
memset(buffer, 0, size);
*((int*)buffer) = current->uid; ;
*((int*)buffer + 1) = current->pid;
*((int*)buffer + 2) = flags;
*((int*)buffer + 3) = ret;
strcpy( (char*)( 4 + (int*)buffer ), commandname);
strcpy( (char*)( 4 + TASK_COMM_LEN/4 +(int*)buffer ), fullname);
//将相应的信息通过netlink 发送
netlink_sendmsg(buffer, size);
return 0;
}
/*
* 这个函数是netlink_kernel_create中需要的回调函数
* @ 在不同版本中,这个函数的原型是不同的
* */
#ifdef VER_2_6_27
void nl_data_ready (struct sk_buff *__skb)
{
struct nlmsghdr *nlh;
struct sk_buff *skb;
skb = skb_get(__skb);
if (skb->len >= NLMSG_SPACE(0)) {
nlh = (struct nlmsghdr *)skb->data;
if( pid != 0 ) printk("Pid != 0 /n ");
pid = nlh->nlmsg_pid; //获取应用程序的pid
printk("net_link: pid is %d, data %s:/n", pid, (char *)NLMSG_DATA(nlh));
kfree_skb(skb);
}
return;
}
#elif VER_2_6_18
void nl_data_ready (struct sock *sk, int len)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
skb = skb_dequeue(&(sk->sk_receive_queue));
nlh = (struct nlmsghdr *)skb->data;
if (skb->len >= NLMSG_SPACE(0)) {
nlh = (struct nlmsghdr *)skb->data;
if( pid != 0 ) printk("Pid != 0 /n ");
pid = nlh->nlmsg_pid; //获取应用程序的pid
printk("net_link: pid is %d, data %s:/n", pid, (char *)NLMSG_DATA(nlh));
kfree_skb(skb);
}
return;
}
#endif
/*
* 初始化 netlink,在sdthook.c中的模块初始化函数中调用
* */
void netlink_init(void) {
#ifdef VER_2_6_27
struct net netlink;
nl_sk = netlink_kernel_create(&netlink, NETLINK_TEST, 0, nl_data_ready, NULL, THIS_MODULE);
#elif VER_2_6_18
nl_sk = netlink_kernel_create(NETLINK_TEST, 0, nl_data_ready, THIS_MODULE);
#endif
// NETLINK_TEST, 0, nl_data_ready, NULL, THIS_MODULE); //adjusted by zxc
if (!nl_sk)
{
printk(KERN_ERR "net_link: Cannot create netlink socket./n");
if (nl_sk != NULL)
sock_release(nl_sk->sk_socket);
}
else
printk("net_link: create socket ok./n");
}
/*
* 卸载 netlink,在sdthook.c中的模块卸载函数中调用
* */
void netlink_release(void) {
if (nl_sk != NULL)
sock_release(nl_sk->sk_socket);
}
为了保持代码段的完整性,贴出其他两个的源码
文件 syscalltable.c
文件 syscalltable.c
(2) 用户部分:
唯一的文件 auditdeamon.c.
signal(SIGTERM,killdeal_func);
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);
//分配接收内核netlink message的变量
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
sendpid(getpid());
/* open the log file at the begining of daemon, in case of this operation causes deadlock */
logfile=fopen(logpath, "w+");
if (logfile == NULL) {
printf("AAWaring: can not create log file/n");
exit(1);
}
//循环获取消息
while(1) { //Read message from kernel
unsigned int uid, pid,flags,ret;
char * file_path;
char * commandname;
//内核没有消息将会阻塞在此
recvmsg(sock_fd, &msg, 0);
uid = *( (unsigned int *)NLMSG_DATA(nlh) );
pid = *( 1 + (int *)NLMSG_DATA(nlh) );
flags = *( 2 + (int *)NLMSG_DATA(nlh) );
ret = *( 3 + (int *)NLMSG_DATA(nlh) );
commandname = (char *)( 4 + (int *)NLMSG_DATA(nlh));
file_path = (char *)( 4 + 16/4 + (int *)NLMSG_DATA(nlh));
//写入日志
Log(commandname, uid,pid, file_path,flags,ret);
}
close(sock_fd);
free(nlh);
fclose(logfile);
return 0;
}
代码使用说明:
内核部分Makefile:
将三个内核部分文件和Makefile文件拷到同一目录下,直接make,在# insmod AuditModule.ko。
创建待监听的文件 :# touch /root/audit
对于应用程序部分, # gcc auditdeamon.c
后台运行 : # a.out &
测试,通过各种方式打开测试文件:
# cat /root/audit
# vi /root/audit
当然你也可以自己写代码打开 /root/audit
最后通过 : # kill -15(或者-SIGTERM) pid(a.out &的pid,可以通过ps -aux查看)
结束监控后,查看 a.out 同级目录下的log文件,便可查看到日志信息了。