目录
5.使用NIDS库编写TCP流分析程序,解释和输出三个结构体tuple4,half_stream,nids_prm的值
-
本文内容
1.Libnids是什么?可以干什么?
2.ids做软件开发流程
3. ids主要函数
4.ids实现tcp流程序分析框架
5.使用NIDS库编写TCP流分析程序,解释和输出三个结构体tuple4,half_stream,nids_prm的值
-
器材(设备、元器件)
Ubuntu虚拟机内QT编程
-
正文
-
1.Libnids是什么?可以干什么?
Libnids是一个用于网络入侵检测开发的专业编程接口。主要功能包括捕获网络数据包、IP碎片重组、TCP数据流重组【对分析单个TCP数据包和整个TCP连接过程,以及应用层FTP、HTTP、POP3协议有帮助】以及端口扫描攻击检测和异常数据包检测、网络嗅探等。
2.ids做软件开发流程
1)、用函数nids_init()进行初始化
2)、注册相应的回调函数,不同的回调函数实现不同的功能
3)、利用函数nids_run()进入循环捕获数据包的状态
3. ids主要函数
基本函数:
(1)int nids_init(void);//对libnids进行初始化
(2)void nids_run(void);//运行Libnids,进入循环捕获数据包状态.
(3)int nids_getfd(void);//获得文件描述号
(4)int nids_dispatch(int cnt)//功能是调用Libpcap中的捕获数据包函数pcap_dispatch().
(5)int nids_next(void)//调用Libpcap中的捕获数据包函数pcap_next()
(6)void nids_register_chksum_ctl(struct nids_chksum_ctl *ptr,int nr)//决定是否计算校验和,它是根据数据结构nids_chksum_ctl中的action进行决定的
IP碎片函数:
(1)void nids_register_ip_frag(void(*))//此函数的功能是注册一个能够检测所有IP数据包的回调函数,包括IP碎片
eg:nids_register_ip_frag(ip_frag_function);这样就定义了一个回调函数ip_frag_function的定义类型如下:
void ip_frag_function(struct ip *a_packet,int len)
(2)void nids_register_ip(void(*))//此函数定义一个回调函数,此回调函数可以接受正常的IP数据包,eg:nids_register_ip(ip_function);此回调函数的定义类型如下:
void ip_function(struct ip * a_packet)
TCP数据流重组函数:
(1)void nids_register_tcp(void(*))
回调函数的功能是注册一个TCP连接的回调函数,回调函数的类型定义如下:
void tcp_callback(struct tcp_stream *ns,void **param);
其中参数ns表示一个tcp连接的所有信息,它的类型是tcp_stream数据结构;参数param表示要传递的连接参数信息,可以指向一个TCP连接的私有数据
(2)void nids_killtcp(struct tcp_stream * a_tcp)//此函数功能是终止TCP连接
(3)void nids_discard(struct tcp_stream *a_tcp,int num)//丢弃num字节TCP数据,用于存储更多的数据
4.ids实现tcp流程序分析框架
C语言实现,来自《网络数据获取与协议分析》
#include "nids.h"
#include "pcap.h"
#include "libnet.h"
/* Libnids的头文件,必须包含 */
char ascii_string[10000];
char *char_to_ascii(char ch)
/* 此函数的功能主要用于把协议数据进行显示 */
{
char *string;
ascii_string[0] = 0;
string = ascii_string;
if (isgraph(ch))
/* 可打印字符 */
{
*string++ = ch;
}
else if (ch == ' ')
/* 空格 */
{
*string++ = ch;
}
else if (ch == '\n' || ch == '\r')
/* 回车和换行 */
{
*string++ = ch;
}
else
/* 其它字符以点"."表示 */
{
*string++ = '.';
}
*string = 0;
return ascii_string;
}
/*
====================================================================================
下面的函数是回调函数,用于分析TCP连接,分析TCP连接状态,对TCP协议传输的数据进行分析
====================================================================================
*/
void tcp_protocol_callback(struct tcp_stream *tcp_connection, void **arg)
{
int i;
char address_string[1024];
char content[65535];
char content_urgent[65535];
struct tuple4 ip_and_port = tcp_connection->addr;
/* 获取TCP连接的地址和端口对 */
strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
/* 获取源地址 */
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
/* 获取源端口 */
strcat(address_string, " <---> ");
strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
/* 获取目的地址 */
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
/* 获取目的端口 */
strcat(address_string, "\n");
switch (tcp_connection->nids_state) /* 判断LIBNIDS的状态 */
{
case NIDS_JUST_EST:
/* 表示TCP客户端和TCP服务器端建立连接状态
在此状态下就可以决定是否对此TCP连接进行数据分析,可以决定是否捕获TCP客户端接收的数据、TCP服务器端接收的数据、TCP客户端接收的紧急数据或者TCP服务器端接收的紧急数据*/
tcp_connection->client.collect++;
/* 客户端接收数据 */
tcp_connection->server.collect++;
/* 服务器接收数据 */
tcp_connection->server.collect_urg++;
/* 服务器接收紧急数据 */
tcp_connection->client.collect_urg++;
/* 客户端接收紧急数据 */
printf("%sTCP连接建立\n", address_string);
return ;
case NIDS_CLOSE:
/* 表示TCP连接正常关闭 */
printf("--------------------------------\n");
printf("%sTCP连接正常关闭\n", address_string);
return ;
case NIDS_RESET:
/* 表示TCP连接被重置关闭 */
printf("--------------------------------\n");
printf("%sTCP连接被RST关闭\n", address_string);
return ;
case NIDS_DATA:
/* 表示有新的数据到达在这个状态可以判断是否有新的数据到达,如果有就可以把数据存储起来,可以在这个状态之中来分析TCP传输的数据,此数据就存储在half_stream数据结构的缓存之中。 */
{
struct half_stream *hlf;
/* 表示TCP连接的一端的信息,可以是客户端,也可以是服务器端 */
if (tcp_connection->server.count_new_urg)
{
/* 表示TCP服务器端接收到新的紧急数据 */
printf("--------------------------------\n");
strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
strcat(address_string, " urgent---> ");
strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
strcat(address_string, "\n");
address_string[strlen(address_string) + 1] = 0;
address_string[strlen(address_string)] = tcp_connection->server.urgdata;
printf("%s", address_string);
return ;
}
if (tcp_connection->client.count_new_urg)
{
/* 表示TCP客户端接收到新的紧急数据 */
printf("--------------------------------\n");
strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
strcat(address_string, " <--- urgent ");
strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
strcat(address_string, "\n");
address_string[strlen(address_string) + 1] = 0;
address_string[strlen(address_string)] = tcp_connection->client.urgdata;
printf("%s", address_string);
return ;
}
if (tcp_connection->client.count_new)
{
/* 表示客户端接收到新的数据 */
hlf = &tcp_connection->client;
/* 此时hlf表示的是客户端的TCP连接信息 */
strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);
strcat(address_string, " <--- ");
strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);
strcat(address_string, "\n");
printf("--------------------------------\n");
printf("%s", address_string);
memcpy(content, hlf->data, hlf->count_new);
content[hlf->count_new] = '\0';
printf("客户端接收数据\n");
for (i = 0; i < hlf->count_new; i++)
{
printf("%s", char_to_ascii(content[i]));
//输出客户端接收的新的数据,以可打印字符进行显示
}
printf("\n");
}
else
{
/* 表示服务器端接收到新的数据 */
hlf = &tcp_connection->server;
/* 此时hlf表示服务器端的TCP连接信息 */
strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);
strcat(address_string, " ---> ");
strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);
strcat(address_string, "\n");
printf("--------------------------------\n");
printf("%s", address_string);
memcpy(content, hlf->data, hlf->count_new);
content[hlf->count_new] = '\0';
printf("服务器端接收数据:\n");
for (i = 0; i < hlf->count_new; i++)
{
printf("%s", char_to_ascii(content[i]));
// 输出服务器接收到的新的数据
}
printf("\n");
}
}
default:
break;
}
return ;
}
void main()
{
struct nids_chksum_ctl temp;
temp.netaddr = 0;
temp.mask = 0;
temp.action = 1;
nids_register_chksum_ctl(&temp,1);
/*这段是相关与计算校验和的,比较新的网卡驱动会自动计算校验和,我们要做的就是把它关掉*/
if (!nids_init())
/* Libnids初始化 */
{
printf("出现错误:%s\n", nids_errbuf);
exit(1);
}
nids_register_tcp(tcp_protocol_callback);
/* 注册回调函数 */
nids_run();
/* Libnids进入循环捕获数据包状态 */
5.使用NIDS库编写TCP流分析程序,解释和输出三个结构体tuple4,half_stream,nids_prm的值
tuple4:
Struct tuple4
{
u_short source;
/*成员source表示源IP地址的端口号;*/
u_short dest;
/*成员dest表示目的IP地址的目的端口号。*/
u_int saddr;
/*成员saddr表示一个TCP连接的一端IP地址,称为源IP地址;*/
u_int daddr;
/*成员daddr表示一个TCP连接的另一端IP地址,称为目的IP地址,注意此时的源和目的只是相对的;*/
};
half_stream:
struct half_stream
/* 描述在 TCP 连接中一端的所有信息 , 可以是客户端 , 也可以是服务端 */
{
char state;
/* 表示套接字的状态 , 也就是 TCP 的状态 */
char collect;
/* 可以表示有数据到达 , 此数据存放在 data 成员中 , 也可以表示不存储此数据到 data 中 , 此数据忽略 . 如果大于 0 就存储 , 否则就忽略 */
char collect_urg;
/* 可以表示有紧急数据到达 , 此数据就存放在 urgdata 中 , 也可以表示不存储此数据到 urgdata 中 , 此速数据忽略 . 如果大于 0 就存储 , 否则就忽略 */
char *data;
/* 用户存储正常接受到的数据 */
int offset;
/* 表示存储在 data 中数据的第一个字节的偏移量 */
int count;
/* 表示从 TCP 连接开始已经存储到 data 中的数据的字节数 */
int count_new;
/* 有多少新的数据存储到 data 中 , 如果为 0, 则表示没有新的数据到达 */
int bufsize;
int rmem_alloc;
int urg_count;
/* 用来存储紧急数据 */
u_int acked;
u_int seq;
u_int ack_seq;
u_int first_data_seq;
u_char urgdata;
u_char count_new_urg;
/* 表示有新的紧急数据到达 , 如果为 0 表示没有新的紧急数据 */
u_char urg_seen;
u_int urg_ptr;
u_short window;
u_char ts_on;
u_char wscale_on;
u_int curr_ts;
u_int wscale;
struct skbuff *list;
struct skbuff *listtail;
};
Libnids 全局参数: 利用它可以对Libnids的一些环境参数进行设置,设置之后对整个Libnids都有效。
struct nids_prm
{
int n_tcp_streams;
/*表示哈西表大小,此哈西表用来存放tcp_stream数据结构,默认值为1040。在同一时刻Libnids捕获的TCP数据包的最大个数必须是此参数值的3/4。
*/
int n_hosts;
/*表示哈西表的大小,此哈西表是用来存储IP碎片信息的,默认值为256。*/
char *device;
/*表示网络接口,Libnids将在此网络接口上捕获数据,默认值为NULL。这样Libnids将使用pcap_lookupdev来查找可用的网络接口。如果其值为all,表示捕获所有网络接口的数据。*/
char *filename;
/*表示用来存储网络数据的捕获文件。此文件的类型必须与Libpcap类型一致。如果设置了文件,与此同时就应该设置成员device为NULL,默认值为NULL*/
int sk_buff_size;
/*表示的是数据结构sk_buff的人小。数据结构sk_buff是Linux内核中一个重要的数据结构,是用来进行数据包排队操作的,默认值为168。*/
int dev_addon;
/*表示在数据结构sk_buff中用于网络接口上信息的字节数。如果是-1(默认值),那么Libnids会根据不同的网络接口进行修正。*/
void (*syslog) ();
/*成员syslog是一个函数指针,默认值为nids_syslog()函数。在syslog函数中可以检测入侵攻击,如网络扫描攻击,也可以检测一些异常情况,如无效TCP标记.*/
int syslog_level;
/*表示日志等级,默认值为LOG_ALERT*/
int scan_num_hosts;
/*表示一个哈西表的大小,(此哈西表用来存储端口扫描信息)表示Libnids将要检测的同时扫描的端口数据。如果其值为0,Libnids将不提供端口扫描功能。默认值为256。*/
int scan_delay;
/*表示在扫描检测中,两端口扫描的间隔时间,以毫秒来计算,缺省值为3000*/
int scan_num_ports;
/*表示相同源地址必须扫描的TCP端口数目,默认值为10*/
void (*no_mem) (char *);
/*一个函数指针,当Libnids发生内存溢出时被调用*/
int (*ip_filter) ();
/*一个函数指针,此函数可以用来分析IP数据包,当有IP数据包到达时,此函数就被调用。如果此函数返回非零值,此数据包就被处理;如果返回零,此IP数据包就被丢弃。默认值为nids_ip_filter函数,总是返回1*/
char *pcap_filter;
/*表示过滤规则,即Libpcap的过滤规则,默认值为NULL,表示捕获所有数据包。可以在此设置过滤规则,只捕获感兴趣的开发包*/
int promisc;
/*表示网卡模式,如果是非零,就把此网卡设为混杂模式:否则,设为非混杂模式。默认值为1*/
int one_loop_less;
/*初始值为0*/
int pcap_timeout;
/*表示捕获数据返回的时间,以毫秒计算。实际上它表示的就是Libpcap函数中的pcap_open_live函数的timeout参数,默认值为1024*/
int multiproc;
int queue_limit;
int tcp_workarounds;
pcap_t *pcap_desc;
};