您的位置:首页 > 运维架构 > Linux

十一假期做西电的信安比赛(内核关linux部分)

2012-10-15 10:17 429 查看
内核关分为linux和windows两个部分,每部分三道题,因为linux内核个人觉得比较简单所以就首先做了一下。

第一题是输出一条内核信息,使用printk就可以,但是这个printk有一个等级,大概是这样,等级越低越容易被输出到终端上

Loglevel        Description

KERN_EMERG      An emergency condition; the system is probably dead

KERN_ALERT      A problem that requires immediate attention

KERN_CRIT       A critical condition

KERN_ERR        An error

KERN_WARNING    A warning

KERN_NOTICE     A normal, but perhaps noteworthy, condition

KERN_INFO       An informational message

KERN_DEBUG      A debug messagetypically superfluous


可以使用这条语句来,查看输出终端,和记录log的设置,使用dmesg来查看内核信息

cat /proc/kernel/printk


最后好象是虚拟机,会出现使用emergency等级都无法输出到终端的情况,这个貌似是虚拟机自己的问题,不要强求~

然后说一下makefile,这个一般会由IDE负责完成,但编译内核的时候还是需要自己写一下,使用make -n可以显示所有执行过程,

这是编译内核所需要的Makefile,详细的分析在这里http://blog.chinaunix.net/space.php?uid=20678786&do=blog&id=111321

ifneq ($(KERNELRELEASE),)
obj-m:=hello.o
else
KERNELDIR:=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
default:
        $(MAKE) -C $(KERNELDIR)  M=$(PWD) modules
clean:
        rm -rf *.o *.mod.c *.mod.o *.ko
endif
首先不带任何参数的make调用,会执行第一个选项也就是default:后面的这一句,

$加括号代表了参数的调用,shell是make的内部函数,可以调用命令行uname -r就是取得当前版本

$(MAKE) -C $(KERNELDIR)  M=$(PWD) modules
把这句话还原就是这样

make -C /lib/modules/2.6.13-study/build M=/home/study/prog/mod/hello/ modules


其中/lib/modules/2.6.13-study/build是指向linux内核源文件的一个符号链接,类似快捷方式的东西,

所以在这里执行了两次Makefile,一个是hello文件下的Makefile一个是linux内核源文件中的Makefile

最后面的modules应该就是声明是一个内核模块的编译

如果觉得写Makefile麻烦用这一句也可以,-I 就是include某些源码,-D是预定义宏,内核模块需要,

在源文件中定义也可以。

gcc -c -o hello.o -I/usr/src/linux/include -D__KERNEL__ -DMODULE hello.c


最后的源码比较简单,就没什么好说的了。

//
//hello.c
//
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

static int hello_init(void) {
	printk(KERN_EMERG "Module init: Hello!\n");
	return 0;
}

static void hello_exit(void) {
	printk(KERN_EMERG "Module exit: bye-bye!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("DUAL_BSD");
MODULE_AUTHOR("RANDOM");


第二题是进程间的通信使用到一个叫做netlink的东西,这个东西内核态和用户态的实现是不一样的,直接上代码

涉及到的东西在这两篇文章里

http://blog.csdn.net/unbutun/article/details/5181607 http://www.cnblogs.com/hoys/archive/2011/04/09/2010788.html[/code]netlink的DOC 
http://www.gridmpi.org/pspacer-2.1/libnl-1.0-pre6/group__nl.html


user.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <asm/types.h>
#include <linux/netlink.h>
#define MAX_MSGSIZE 1024
struct sockaddr_nl src_addr, dest_addr;
struct msghdr msg;
struct nlmsghdr *nlhdr = NULL;
struct iovec iov;
int sockfd;
char buffer[100] = "";
int main(int argc, char* argv[])
{
     printf("In User Mod\n");
     sockfd = socket(AF_NETLINK, SOCK_RAW, 17);
     memset(&src_addr, 0, sizeof(src_addr));
     src_addr.nl_family = AF_NETLINK;
     src_addr.nl_pid = getpid();  //self pid
     src_addr.nl_groups = 0;	
     if(bind(sockfd, (struct sockaddr*)&src_addr, sizeof(struct sockaddr_nl)) == -1)
     {
        perror("bind");
	 close(sockfd);
     }
     memset(&dest_addr, 0, sizeof(dest_addr));
     dest_addr.nl_family = AF_NETLINK;
     dest_addr.nl_pid = 0;   //for kernel set 0
     dest_addr.nl_groups = 0;		 
	 
     memset(&msg, 0, sizeof(msg));
     msg.msg_name = (void *)&(dest_addr);
     msg.msg_namelen = sizeof(dest_addr);
	 
     nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE)); 
     /* Fill the netlink message datapart */
     printf("What do you want to say to kernel? \n");
     scanf("%s",buffer);
     strcpy(NLMSG_DATA(nlhdr), buffer);
     /* Fill the netlink message header */
     nlhdr->nlmsg_len = NLMSG_LENGTH(strlen(buffer));
     nlhdr->nlmsg_pid = getpid();  /* self pid */
     nlhdr->nlmsg_flags = 0;
	 
     iov.iov_base = (void *)nlhdr;
     iov.iov_len = nlhdr->nlmsg_len;
     msg.msg_iov = &iov;
     msg.msg_iovlen = 1;
     
     sendmsg(sockfd, &msg, 0); 
     	     
     close(sockfd);
     return 0;	
}


kernel.c

static int __init init( void ) __init是参数前的修饰,说明此函数是一个init函数

消息接收使用了回调函数的方法

//kernel.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/netlink.h>
#define NL_MYTEST  17
static struct sock *nlfd = NULL;
void nl_data_recv(struct sk_buff *__skb)
{
     struct sk_buff *skb;
     struct nlmsghdr *nlhdr;
     u32 pid;
     int ret;
     int len = NLMSG_SPACE(1024);
     char str[100];
     printk(KERN_EMERG "KNL: Get data from sk_buff . \n");
     skb = skb_get(__skb);
	 
     if(skb->len >= NLMSG_SPACE(0))
     {
           nlhdr = nlmsg_hdr(skb);
	   printk(KERN_EMERG "KNL: Recv %s \n", (char*)NLMSG_DATA(nlhdr));
	   memcpy(str, NLMSG_DATA(nlhdr), sizeof(str));
	   //printk(KERN_EMERG "KNL: Recv %s \n",str);
	   pid = nlhdr->nlmsg_pid; /*send process pid*/
	   printk(KERN_EMERG "KNL: Pid is %d \n", pid);
	   kfree_skb(skb);
	   skb = alloc_skb(len, GFP_ATOMIC);  //allocate buffer
	   if(!skb)
	   {
               printk(KERN_EMERG "KNL: Allocate failed ! \n");
		 return;
	   }
     }
     return;     
}
static int __init init(void)
{
     nlfd = netlink_kernel_create(&init_net, NL_MYTEST, 0, nl_data_recv, NULL, THIS_MODULE);
     if(!nlfd) 
     {
        printk(KERN_EMERG "KNL: Can not create a netlink socket!\n");
        return -1;
     }
     printk(KERN_EMERG "KNL: Create netlink socket ok!\n");
     return 0;
}
static void __exit fini(void)
{
     if(nlfd != NULL)
     {
           sock_release(nlfd->sk_socket);
     }
     printk(KERN_EMERG "KNL: Remove ok!\n");
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("RANDOM");
module_init(init);
module_exit(fini);


Makefile 这个Makefile吧kernel.c和user.c在一起编译,只要加入gcc -o user user.c就行了

MODULE_NAME :=linux_kernel_2
obj-m := $(MODULE_NAME).o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
	$(MAKE) -C $(KERNELDIR) M=$(PWD)
	gcc -o user user.c
clean:
	rm -fr *.ko *.o *.cmd sender $(MODULE_NAME).mod.c


第三题是使用一个叫做netfilter的东西,来过滤指定IP的包,是linux内核态的防火墙,用户态的叫做iptables

功能还是很强大的,可以实现一个自定义的IDS之类的

只要看这篇文档就好http://www.wenkudaquan.com/doc/20120416/73167.html

代码如下

kernel.c

这里需要注意一点,不同的linux版本skb结构体有变化

只要这样修改就可以了

在linux 2.4~linux 2.6.20下:
struct iphdr *ip = skb->nh.iph;
 
到了linux 2.6.22下,就变成了:
struct iphdr *ip = ip_hdr(skb);


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/netlink.h>

#define NL_MYTEST  17
static struct sock *nlfd = NULL;
static struct nf_hook_ops nfho;
unsigned char *drop_ip = "\x7f\x00\x00\x01"; //127.0.0.1

//---------------------------------------------------------------------------
void nl_data_recv(struct sk_buff *__skb)
{
     struct sk_buff *skb;
     struct nlmsghdr *nlhdr;
     u32 pid;
     int ret;
     int len = NLMSG_SPACE(1024);
     char str[100];
     printk(KERN_EMERG "KNL: Get data from sk_buff . \n");
     skb = skb_get(__skb);
	 
     if(skb->len >= NLMSG_SPACE(0))
     {
           nlhdr = nlmsg_hdr(skb);
	   printk(KERN_EMERG "KNL: Recv %s \n", (char*)NLMSG_DATA(nlhdr));
	   drop_ip = (char*)NLMSG_DATA(nlhdr);
	   memcpy(str, NLMSG_DATA(nlhdr), sizeof(str));
	   //printk(KERN_EMERG "KNL: Recv %s \n",str);
	   pid = nlhdr->nlmsg_pid; /*send process pid*/
	   printk(KERN_EMERG "KNL: Pid is %d \n", pid);
	   kfree_skb(skb);
	   skb = alloc_skb(len, GFP_ATOMIC);  //allocate buffer
	   if(!skb)
	   {
               printk(KERN_EMERG "KNL: Allocate failed ! \n");
		 return;
	   }
     }
     return;     
}
//---------------------------------------------------------------------------

unsigned int hook_func(unsigned int hooknum,
 struct sk_buff **skb,
 const struct net_device *in,
 const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
    struct sk_buff *sb = *skb;
    struct iphdr* iph = ip_hdr(skb);
    if (iph->saddr == *(unsigned int *)drop_ip) {     
    // if (sb->nh.iph->saddr == drop_ip) {         
	printk("Dropped packet from... %d.%d.%d.%d\n",*drop_ip, *(drop_ip + 1),*(drop_ip + 2), *(drop_ip + 3));         	return NF_DROP;     
    } 
    else {         
	return NF_ACCEPT;
    }              
}
//============================================================================================
int init_module()
{
     nlfd = netlink_kernel_create(&init_net, NL_MYTEST, 0, nl_data_recv, NULL, THIS_MODULE);
     if(!nlfd) 
     {
        printk(KERN_EMERG "KNL: Can not create a netlink socket!\n");
        return -1;
     }
     printk(KERN_EMERG "KNL: Create netlink socket ok!\n");
//---------------------------------------------------------------------------------------------
    nfho.hook = hook_func;         
    nfho.hooknum  = 0; //define NF_IP_PRE_ROUTING 
    nfho.pf       = PF_INET;
    nfho.priority = NF_IP_PRI_FIRST;   

    nf_register_hook(&nfho);  

    return 0;
}

void cleanup_module()
{
     if(nlfd != NULL)
     {
           sock_release(nlfd->sk_socket);
     }
     printk(KERN_EMERG "KNL: Remove ok!\n");
//---------------------------------------------------------------------------------------------
     nf_unregister_hook(&nfho); 
}


user.c

和第二题进程通信一样,使用netlink

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <asm/types.h>
#include <linux/netlink.h>
#define MAX_MSGSIZE 1024
struct sockaddr_nl src_addr, dest_addr;
struct msghdr msg;
struct nlmsghdr *nlhdr = NULL;
struct iovec iov;
int sockfd;
char buffer[100] = "";
int main(int argc, char* argv[])
{
     printf("In User Mod\n");
     sockfd = socket(AF_NETLINK, SOCK_RAW, 17);
     memset(&src_addr, 0, sizeof(src_addr));
     src_addr.nl_family = AF_NETLINK;
     src_addr.nl_pid = getpid();  //self pid
     src_addr.nl_groups = 0;	
     if(bind(sockfd, (struct sockaddr*)&src_addr, sizeof(struct sockaddr_nl)) == -1)
     {
        perror("bind");
	 close(sockfd);
     }
     memset(&dest_addr, 0, sizeof(dest_addr));
     dest_addr.nl_family = AF_NETLINK;
     dest_addr.nl_pid = 0;   //for kernel set 0
     dest_addr.nl_groups = 0;		 
	 
     memset(&msg, 0, sizeof(msg));
     msg.msg_name = (void *)&(dest_addr);
     msg.msg_namelen = sizeof(dest_addr);
	 
     nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE)); 
     /* Fill the netlink message datapart */
     printf("What IP do you want to drop? \n");
     scanf("%s",buffer);
     strcpy(NLMSG_DATA(nlhdr), buffer);
     /* Fill the netlink message header */
     nlhdr->nlmsg_len = NLMSG_LENGTH(strlen(buffer));
     nlhdr->nlmsg_pid = getpid();  /* self pid */
     nlhdr->nlmsg_flags = 0;
	 
     iov.iov_base = (void *)nlhdr;
     iov.iov_len = nlhdr->nlmsg_len;
     msg.msg_iov = &iov;
     msg.msg_iovlen = 1;
     
     sendmsg(sockfd, &msg, 0); 
     	     
     close(sockfd);
     return 0;	
}
Makefile

ifneq ($(KERNELRELEASE),)
	obj-m:=linux_kernel_3.o
else
	KDIR := /lib/modules/$(shell uname -r)/build

all:
	gcc -o user.c user
	make -C $(KDIR) M=$(PWD) modules
clean:make -C $(KDIR) M=$(PWD) cleanendif
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: