Network Information Detection程序作品+源代码
软件作者:Goldberg [安全矩阵](www.smatrix.org)
信息来源:黑基论坛
注意:此软件首发于安全矩阵(www.smatrix.org)后由原创作者提交到黑基论坛
都赶着时候发了,快到八一了,祝中国人解放军更高 更强 更快
用C整了个网络信息流检测软件,linux环境下运行的
由于并非专业人员,只是业余爱好,故希望各位高手不吝惜赐教,谢谢.
先来介绍一下软件的特点:在windows下,将网卡设置成混杂模式,安装linux虚拟机,在虚拟机上安装linux操作系统。通过windows访问linux的主页,从而产生数据流,并且对数据流进行分析。
再来介绍一下软件的功能:检测网络信息流并进行进行分析,统计出访问给定网站的累计流量,依次存放在磁盘上,用户机浏览器通过服务端的服务器调用程序读取磁盘中的数据,通过cgi程序,使用web页面显示出来。
为了更清楚的说明程序的运行以及功能的实现,我详细进行一下解释(以免有人佛俺是庄户雕,不实诚)
1网卡的混杂模式
将网卡设置为混杂模式是为了将网卡设为接受一切数据网卡所能接受的所有数据。本系统实现的方法如下:
strncpy(ethreq.ifr_name, dname, IFNAMSIZ); //把网络设备的名字填充到ifr结构中
if (ioctl(sock,SIOCGIFFLAGS,ðreq)==-1) // SIOCGIFFLAGS请求表示需要获取接口标志
{
perror("ioctl");
close(sock);
EXIT(EXIT_FAILURE);
}
ethreq.ifr_flags|=IFF_PROMISC;// 获取接口标志后把它设置成混杂模式
if (ioctl(sock,SIOCSIFFLAGS,ðreq)==-1) {
PERROR("IOCTL");
CLOSE(SOCK);
exit(EXIT_FAILURE);
在我们成功的获取接口标志后把他设置成混杂模式 ifr.ifr_flags |= IFF_PROMISC;ioctl (sock, SIOCSIFFLAGS, &ifr)。OK,现在我们所说的第一步已经完成--------把网卡置于混杂模式。
2系统如何设置要捕获网络信息
其中BPF是一个核心态的组件,也是一个过滤器,它将确定哪些数据将被返回。
Filter.len = FILTER_LEN; /* number of lines BPF_code */
Filter.filter = BPF_code;
if( setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter)) < 0 ) {
perror("setsockopt");
close(sock);
exit(EXIT_FAILURE);
}
return sock;
struct sock_filter BPF_code[] = {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 3, 0x00000800 },
{ 0x30, 0, 0, 0x00000000 },
{ 0x45, 1, 0, 0x00000001 },
{ 0x6, 0, 0, 0x00000060 },
{ 0x6, 0, 0, 0x00000000 },
};
3守护进程与路由表的设计
int main()
{
daemon( 1, 1 );// daemon程序是一直运行的服务端程序,又称为守护进程。
syslog(LOG_ERR, "*** Starting NIDD ***");
nald();
}
daemon是长时间运行的进程,通常在系统启动后就运行,在系统关闭时才结束。一般说daemon程序在后台运行,是因为它没有控制终端,无法和前台的用户交互。daemon程序一般都作为服务程序使用,等待客户端程序与它通信。
int get_route()//要解决路由问题,必须使用路由表,路由表中包含网关、协议等信息。
{
FILE *fp;
char line[80];
int i;
unsigned net, mask;
if ((fp = fopen("/proc/net/route", "r")) == NULL) {
printf("No route table!\n");
exit(1);
}
while (!feof(fp)) {
fgets(line, 79, fp);
if (strncmp(line, "eth0", 4) != 0)
continue;
………………………………………
………………………………………
…………………………………………
4如何检测程序是否住入内存
init_dir()
{
int i;
char dir[256];
sprintf(dir, "%s/.mylock", root_path);
if ((i = open(dir, O_RDONLY | O_CREAT, S_IRUSR)) < 0) {
perror("open lockfile");
exit(1);
}
if (flock(i, LOCK_EX | LOCK_NB) < 0)
{
fprintf(stderr, "NIDD already running.\n");
exit(1);
}
sprintf(dir, "%s/temp", root_path);
mkdir(dir, 0600);//创建目标,并设置权限
sprintf(dir, "%s/alog", root_path);
mkdir(dir, 0600);// 创建目标,并设置权限
for (i = 1; i <= 12; i ++) {
sprintf(dir, "%s/alog/alog%02d", root_path, i);
mkdir(dir, 0600);// 创建目标,并设置权限
}
}
LOCK_EX获得一个互斥锁(只有自己可写),LOCK_NB当锁定时不绑定flock,直接返回。在系统中主要是为了检测程序是否住入内存。
5 信号函数在本系统中的应用
signal(SIGALRM, flush_cnt);
信号处理类似硬件中断,它们促使某个进程从当前的执行控制流程中跳出,以实现特定的行为,待特定处理完成后,再恢复到中断点继续执行。
产生ALRM信号,每当产生一个信号时,系统调用flush-cnt()
void flush_cnt()
{
char fname[256];
DIR *dp;
struct dirent *link;
unsigned ip;
int fd;
struct msg ms;
Acnt acnt;
time_t now = time(NULL);
…………………………………………………..
………………………………………………….
…………………………………………………
该段程序的主要功能如下:
每当产生一个信号时,系统调用flush-cnt(),读取存放在临时文件夹下的内容,并调用存储函数send-cnt()
6将网络信息流进行统计分析,统计出访问给定网站的累计流量
这是该系统的关键代码,主要实现了网络的检测:
void get_cnt(char *dev, int nid)
{
unsigned char data[DATA_PACKET_LEN];
int sock_h = 0;
fd_set rds;
int n;
struct ip *iph;
struct msg msg;
iph = (struct ip *)&data[14];
sock_h = open_socket(dev, nid);//打开调用套接字函数
n = fcntl(sock_h, F_GETFL, 0);// 第一次调用 socket() 建立套接口 描述符的时候,内核就将他设置为阻塞,如果不想套接口阻塞,就要调用函数 fcntl(),通过设置套接口为非阻塞,你能够有效地"询问"套接口以获得信息
fcntl(sock_h, F_SETFL, n | O_NONBLOCK);
while (1) {
FD_ZERO(&rds); //在&rds中清除所有的文件描述符
FD_SET(sock_h, &rds); //将sock_h加入到&rds
一 般的来说当我们在向文件读写时,进程有可能在读写出阻塞,直到一定的条件满足. 比如我们从一个套接字读数据时,可能缓冲区里面没有数据可读(通信的对方还没有 发送数据过来),这个时候我们的读调用就会等待(阻塞)直到有数据可读.如果我们不希望阻塞,我们的一个选择是用select系统调用. select()--多路同步 I/O函数,可以有效的节约资源。
n = select(sock_h + 1, &rds, NULL, NULL, NULL);// 这个函数监视文件描述符参数,最高的文件描述符的值sock h加1,所以在本系统中,应该设置为 sock h+1,因为它一定大于标准输入的文件描述符。
if (n < 0 && errno == EINTR || !FD_ISSET(sock_h, &rds))//判断sock_h是否在&rds集合中
continue;
while (1) {
n = recvfrom(sock_h, data, DATA_PACKET_LEN, 0, NULL, NULL);//从sock-h接收数据,存放在data中,并接收数据的之中长度,并规定所有的信息都于接收。
if (n < 0) {
if (errno == EWOULDBLOCK)
if (alarm_flag)
alarm(1);
break;
}
msg.ptcl = iph->ip_p;//创建这个ip数据包的高层协议
if (!chk_myip(iph->ip_src) && !chk_myip(iph->ip_dst))//接收者的ip地址和发送者的ip地址都不主机的ip地址,执行if
{
if (chk_home(iph->ip_src)) {
msg.rip = iph->ip_dst.s_addr; 接收者的ip地址
msg.icnt = 0;
msg.ocnt = ntohs(iph->ip_len);
put_cnt(msg, iph->ip_src.s_addr);//调用put-cnt(sruct msg msg,unsigned ip)函数
} else if (chk_home(iph->ip_dst))
{
msg.rip = iph->ip_src.s_addr;//发送者的ip地址
msg.icnt = ntohs(iph->ip_len);
msg.ocnt = 0;
put_cnt(msg, iph->ip_dst.s_addr); 调用put-cnt(sruct msg msg,unsigned ip)函数
}
}
alarm_flag = 1;
}
}
close(sock_h);
exit(0);
}
7数据依次存放在磁盘上
int send_cnt(Acnt *acnt)
{
int fd;
char fname[256];
struct tm *tm = localtime(&acnt->start);
sprintf(fname, "%s/alog/alog%02d/%04d%02d%02d.log", root_path,
tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
if ((fd = open(fname, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)) >= 0) {
lockf(fd, F_LOCK, (off_t)0);//实现锁定
lseek(fd, (off_t)0, SEEK_END);//把fd文件的读写位置从起始SEEK_END开始移动(off_t)0字节
write(fd, acnt, sizeof(Acnt));//将acnt结构体中的内容写入fd中
close(fd);}
8ip地址的转换
字符串的IP和32位的IP转换.
在网络上面我们用的IP都是数字加点(192.168.0.1)构成的, 而在struct in_addr结构中
用的是32位的IP, 为了转换我们可以使用下面两个函数
int inet_aton(const char *cp,struct in_addr *inp)
char *inet_ntoa(struct in_addr in)
函数里面 a 代表 ascii n 代表network.第一个函数表示将a.b.c.d的IP转换为32位的IP,存储在 inp指针里面.第二个是将32位IP转换为a.b.c.d的格式.
本系统的ip地址的转换代码如下:
while (read(fd, (char *)&acnt, sizeof(Acnt)) == sizeof(Acnt)) {
tm = localtime(&acnt.start);
sprintf(uip, "%s", inet_ntoa(*(struct in_addr *)&acnt.uip)); 是将32位IP转换为a.b.c.d的格式
sprintf(rip, "%s", inet_ntoa(*(struct in_addr *)&acnt.rip)); 是将32位IP转换为a.b.c.d的格式
printf("%02d:%02d:%02d %-15s %-15s %s %10d %10d<br>\n",
tm->tm_hour, tm->tm_min, tm->tm_sec, uip, rip,
get_ptcl(acnt.ptcl), acnt.icnt, acnt.ocnt);
9系统中输入日期部分的设计
要 检测网络信息流;将网络信息流进行统计分析,统计出访问给定网站的累计流量,依次存放在磁盘上。客户机浏览器通过服务端的服务器调用程序读取磁盘中的数 据,通过页面显示出来。必须先知道具体日期,才能访问具体时间的流量,所以在本系统中,输入日期设计非常重要本系统先是从主函数int main()中调用函数my_cgi().以下就是my_cgi()函数,首先判定我们输入的网址是不是我们所要执行的CGI程序,然后执行输入日期程序
my_cgi()
{
char *p;
if ((p = getenv("SCRIPT_NAME")) == NULL || strcmp(p, MY_URL))
return;
if ((p = get_query("cgi")) != NULL) {
if (strcmp(p, "view") == 0)
view_ulog();
else
view_ulog();
} else {
view_ulog();
}
exit(0);
}
10 判断日期是否输入正确的函数实现
………………………………………………………………..
year = atoi(argv[0]);
month = atoi(argv[1]);
day = atoi(argv[2]);
if (year < 2004 || month < 1 || month > 12 || day < 1 || day > 31) {
disp_view_ulog(2);
return;
……………………………………………………………………………
确定输出日期的格式:
static void disp_view_ulog(int type)
{
disp_head("查询用户日志");…………………………………………………………………………..
printf(" <tr bgcolor=\"#FFCC00\">\n");
printf(" <td align=\"center\">\n");
printf(" 日期 <input maxLength=\"4\" name=\"year\" size=\"4\" type=\"text\" class=\"p1\"> 年 \n");
printf(" <input maxLength=\"2\" name=\"month\" size=\"2\" type=\"text\" class=\"p1\"> 月 \n");
printf(" <input maxLength=\"2\" name=\"day\" size=\"2\" type=\"text\" class=\"p1\"> 日\n");
判断输入日期是否正确,假如正确调用dis-unflow()函数:
switch (type) {
case 1:
printf("<p align=\"center\"><font color=\"#FF0000\">系统错误,无法查询!</font></p>\n\n");
break;
case 2:
printf("<p align=\"center\"><font color=\"#FF0000\">日期错,请重新输入!</font></p>\n\n");
break;
case 5:
printf("<p align=\"center\"><font color=\"#FF0000\">系统错误,查询失败!</font></p>\n\n");
break;
case 9:
disp_uflow();……………………………………………………………………………………………………
year = atoi(argv[0]);
month = atoi(argv[1]);
day = atoi(argv[2]);
if (year < 2004 || month < 1 || month > 12 || day < 1 || day > 31) {
disp_view_ulog(2);
return;
}
sprintf(fname, "%s/alog/alog%02d/%04d%02d%02d.log", root_path, month, year, month, day);
………………………………………………………………...
11设置ip地址,实现客户机的访问。
在windows下设置ip为192.168.1.11,linux下设置两个ip分别为192.168.0.1和192.168.1.1,
将windows默认网关设置为192.168.0.1,通过IE访问linux主机,产生流量,通过程序实现对流量进行分析。当输入日期正确时,系统就会把流量通过WEB页面显示出来,如下图:
上述显示的就是网络检测的信息
其中第一列为访问的时间。
第二列为 192.168.0.20起到连接内外网的作用。
第三列为目标ip地址。
第四列是在tcp协议下产生的流量。
第五为原端口的端口号
第五列为目标端口的端口号。
12Linux的make命令
系统中Makefile文件设计如下:
NAME=nid
DAEM=$(NAME)d
CC=gcc
DEST=/app/$(NAME)
# libs for Linux
LIBS = -lcrypt
all: $(NAME) $(DAEM)
$(NAME): $(NAME).o
$(CC) $(CFLAGS) -o $@ $(NAME).o $(LIBS)
chmod 755 $@
$(DAEM): $(DAEM).o
$(CC) $(CFLAGS) -o $@ $(DAEM).o $(LIBS)
chmod 700 $@
clean:
rm -f $(NAME) $(DAEM) *.o core
tar:
cd ..;\
tar cvf $(NAME).src src/$(NAME).c src/$(DAEM).c src/Makefile;\
gzip -f $(NAME).src
install:
mkdir -p $(DEST)
/usr/bin/install -c $(DAEM) $(DEST)
/usr/bin/install -c $(NAME) /var/www/cgi-bin
chmod 4755 /var/www/cgi-bin/$(NAME)