Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1585029
  • 博文数量: 399
  • 博客积分: 8508
  • 博客等级: 中将
  • 技术积分: 5302
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-14 09:28
个人简介

能力强的人善于解决问题,有智慧的人善于绕过问题。 区别很微妙,小心谨慎做后者。

文章分类

全部博文(399)

文章存档

2018年(3)

2017年(1)

2016年(1)

2015年(69)

2013年(14)

2012年(17)

2011年(12)

2010年(189)

2009年(93)

分类: LINUX

2009-10-20 16:46:19

本的Libnids结构和函数

现在是时候仔细描述一些提到的Libnids的结构的时候了,它们在头文件nids.h中声明。

struct tuple4 // TCP连接参数
{
unsigned short source,dest; // 客户端和服务器端口号
unsigned long saddr,daddr; // 客户端和服务器IP地址
};


struct half_stream // TCP连接一侧的描述结构
{
char state; // socket 状态 (例如:TCP_ESTABLISHED )

char collect; // if >0, 那么数据应该被存放到data缓冲区中。否则,
// 这个方向的数据流将被忽略
// 看一下samples/sniff.c文件如何使用这个域

char collect_urg; // 类似的,判断是否为紧急数据。

char * data; // 正常数据缓冲区

unsigned char urgdata; // one-byte buffer for urgent data

int count; // 自连接建立以来已经有多少字节已经发送到data缓冲区中

int offset; // offset (in data stream) of first byte stored in
// the "data" buffer; additional explanations
// follow

int count_new; // 多少字节将被发送到data缓冲区中last (this) time;
// if == 0, 没有新数据到达。

char count_new_urg; // if != 0,新的紧急数据到达

... // other fields are auxiliary for libnids

};


struct tcp_stream
{
 struct tuple4 addr; // 连接参数(saddr, daddr, sport, dport)
 char nids_state; // 连接的逻辑状态
 struct half_stream client,server; // 描述连接的客户端和服务器端的结构
 ... // other fields are auxiliary for libnids
};

在上面的例子程序中,函数tcp_callback打印hlf->data缓冲区的数据到stderr上,
并且这些数据不再需要了。在tcp_callback返回后,默认情况下Libnids释放这些数
据占用的空间。hlf->offset域将增加丢弃的字节数,并接新的数据将存储到data缓冲区
的开始。如果上面的操作不是我们所希望的,(例如,数据处理器需要至少N字节的输入来
处理,并且迄今为止Libnids接受到的count_new

void nids_discard(struct tcp_stream * a_tcp, int num_bytes)

在tcp_callback返回前。结果,在tcp_callback返回后,Libnids将丢弃data缓冲区中至
多num_bytes字节的前面的内容(更新offset域,移动剩下的数据到data缓冲区的开始位置)。
如果nids_discard函数没有被调用(像上面的例子程序),缓冲区hlf->data包含
hlf->count_new字节的内容。一般情况下,缓冲区hlf->data中的字节数等于
hlf->count-hlf->offset.

感谢函数nids_discard,程序员不得不必须把接受到的数据拷贝到一个独立的缓冲区中。
hlf->data将一直包含尽可能多的数据。可是,经常发生对每一个ibnids_callback, tcp
stream对维护其辅助数据结构的请求。例如,如果我们希望检测一个针对wu-ftpd的攻击(这个
攻击包括在服务器上建立深层目录)我们需要将Ftpd守护进程的目录存储到某一个地方。
通过Ftp客户端的CWD指令怾改变。这就是tcp_callback的第二个参数的目的。它是一个指向
每一个(libnids_callback, tcp stream)对所私有的数据的指针。

典型情况如下:

void
tcp_callback_2 (struct tcp_stream * a_tcp, struct conn_param **ptr)
{
 if (a_tcp->nids_state==NIDS_JUST_EST)
 {
  struct conn_param * a_conn;
  如果连接是我们不感兴趣的,返回;
  a_conn=malloc of some data structure//内存分配
  init of a_conn//初始化
  *ptr=a_conn // 这个值在将来的调用中将被传递给tcp_callback_2
  增加一些 "collect" 域
  return;
 }
 if (a_tcp->nids_state==NIDS_DATA)
 {
   struct conn_param *current_conn_param=*ptr;
   using current_conn_param and the newly received data from the net
   we search for attack signatures, possibly modyfying
   current_conn_param
   return ;
 
 }

函数nids_register_tcp 和 nids_register_ip*可以被调用任意多的次数。两个与
tcp_callback相似的函数允许跟踪同一个TCP流。(with acertain non-default exception).

Libnids参数可以通过全局变量nids_params的域的改变来修改。如下声明:
struct nids_prm
{
int n_tcp_streams; // 用于存储tcp_stream结构信息的Hash表的大小
// libnis将只能同时跟踪 3/4 * n_tcp_streams连接的数据流
// 默认值:1040
// 如果设置为0,Libnids将不再进行TCP流的组装
int n_hosts; // 用于存储关于IP碎片重组的信息的Hash表的大小
// 默认值: 256
char * device; // Libnids用于监听数据包的设备接口
// 默认值 :NULL, 将通过调用pcap_lookupdev函数来接决定;
// 特殊值all将致使Libnids试图通过所有的设备接口截获数据包
// (这个参数在高于2.2.0Linux 核心版本有效)
// 参见 doc/NEW_LIBPCAP
int sk_buff_size; // 结构sk_buff的大小,这个结构是由Linux核心定义的,核心用于
// 数据包排列,如果这个参数和sizeof(struct sk_buff)地值不同,
// Libnids可以通过攻击其资源管理而被绕过。见TEST文件。
// 如果你是一个喜欢妄想的人,那么检查你网络中主机的sizeof(sk_buff)
// 并调整这个参数,默认值:168
int dev_addon; // 在sk_buff结构中保留了多少字节用于存储网络接口信息;如果dev_addon==-1,
// 将在nids_init()中根据Libnids监听的接口的类型进行改正。it
// 默认值: -1.
void (*syslog)(); // 参见nids_params定义部分的描述
int syslog_level; // 如果 nids_params.syslog==nids_syslog,那么这个域将决定
// 系统守护进程syslogd报告事件所使用的等级loglevel.
// 默认值: LOG_ALERT
int scan_num_hosts;// 用于存储关于端口扫描的信息的Hash表的大小;Libndis能够检测
// 到的同时发生的端口扫描企图。如果设置为0,端口扫描检测将被关闭
// 默认值:256
int scan_num_ports;// 多少个TCP端口必须被同一个源地址扫描
// 默认值: 10
int scan_delay; // 两个端口之间最大的扫描间隔
// 用于使Libnids可以报告端口扫描企图
// 默认值:3000
void (*no_mem)(); // 当Libndis的内存资源耗尽时调用此函数
// 它应该终止当前进程
int (*ip_filter)(struct ip*); // 这个参数当IP数据包到达时才会被考虑
// 如果ip_filter返回non-zero, 处理这个包否则忽略掉
// 通过这种方式,可以只监控所选中的主机,而不是整个子网
// 默认函数: (nids_ip_filter) 一般返回值为:1
char *pcap_filter; // 用于pcap地过滤字符串,默认情况下为NULL。
// 需要了解的是这强应用到link-layer,所以象"tcp dst port 23"
// 一样的过滤器无法控制碎片传输。
int promisc; // 如果非零,Libnids读取数据包的设备将被设置为混杂模式
// 默认为:1
int one_loop_less; // 默认情况下不可用
} nids_params;

nids_params变量中的syslog域在默认情况下包含函数nids_syslog的地址,如下声明:

void nids_syslog (int type, int errnum, struct ip *iph, void *data);

函数nids_params.syslog用于报告一些不寻常的情形,例如端口扫描请求,无效TCP头
标记等等。这个域应该被分配通常的事件记录函数的地址。
函数nids_syslog(在libnids.c中定义)举例说明了如何解读传递给nids_params.syslog
的参数。
Nids_syslog记录信息道系统守护进程syslogd中。忽视像消息速率和可用磁盘空间之类的事情。

如果对UDP数据报感兴趣,应该声明:
void udp_callback(struct tuple4 * addr, char * buf, int len, struct ip
* iph);

并注册它:

nids_register_udp(udp_callback)

参数addr包含地址信息,buf指针指向UDP数据包携带的数据,len是数据长度,iph是包
含此UDP数据报的Ip包的指针。校验和已经进行过。

6. 其他有用的技巧

void nids_killtcp(struct tcp_stream * a_tcp)

通过发送RST结束一个a_tcp描述的TCP连接。


使用nids_run()存在一个不利的因素,应用程序变成了完全的包驱动,有时在没有包到达的
时候执行某些操作也是必须得。代替nids_run(),我们可以使用函数:

int nids_next()

这个函数调用 pcap_next() 而不是 pcap_loop, 就是说它之处理一个包,如果没有包可用,
程序将处于睡眠状态,Nids_next()成功时返回1;出错返回0。(nids_errbuf 包含相关联
的信息)

标准情况下,当时用nids_next()时,因各应用程序将睡眠在一个select()函数中,同时在fd_set
中引入了一个监听得socket fd. 这个fd可以通过下面的调用获得:

int nids_getfd()

它返回一个文件描述符,失败的时候返回-1。(nids_errbuf包含出错信息)。


头文件nids.h定义了常量 NIDS_MAJOR (1) 和NIDS_MINOR (16), 这些常量用于判定Libnids的
现行版本。如果HAVE_NEW_PCAP(同样在nids.h中定义) 设置为1,则Libnids已被编译为支持
通过所有设备截获数据包。(见 NEW_LIBPCAP 文件)


典型情况下,TCP流携带的数据可以被分成协议依赖(protocol-dependent)记录(say, lines of
input)。一个tcp_callback函数可以接收一定数量的数据,这些数据可以包含多余一个地记录。因此
tcp_callback函数应该对所有接受到的数据重复进行协议解析程序。这增加了代码的复杂性。

如果nids_params.one_loop_less是非零的,Libnids的行为会发生轻微的变化。如果一个callback
消耗了部分而不是全部的新到达的数据,Libnids立刻再次调用它。只有在缓冲区中不再有未处理的保
留数据,并且rcv->count_new相应减少。因而,callback能一次处理一个记录,Libnids将再次调用
它,知道没有新的数据剩余或者没有数据可以处理。不幸的,在两个以上的callback函数读取同一个
TCP流的一半的时候,这一行为导致了可怕的语法问题。所以,如果nids_params.one_loop_less如果
为非零,你不允许分配两个或者更多callback函数处理同一个TCP流的一半。不幸的是现存的接口不能
向callback函数宣布这个错误,因此你必须自己小心,你已经被警告过了。


其它的使用Libnids的应用程序可以在samples目录中找到。
阅读(2104) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~