`
varsoft
  • 浏览: 2434874 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

[转]网络安全工具开发函数库介绍

阅读更多

网络安全工具开发函数库介绍


作者:backend <backend@nsfocus.com>
<http://www.nsfocus.com>
日期:2000-07-16

---[[ 前言 ]]--------------------------------------------

本文主要介绍几个在UNIX系统平台上开发网络安全工具时最常用的library。此外还提供一些如何使用这些开发库进行网络安全工具开发的设计框架和流程。希望能和对网络安全工具开发有兴趣的朋友共同交流,互相促进。

众所周知,基于socket的网络编程已成为当今不可替代的编程方法。这种编程思想将网络通讯当作“文件”描述字进行处理,对这个“网络文件”(即 socket,套接字/套接口)的操作从编程者的角度来讲与普通的文件操作(如读、写、打开、关闭等)大同小异,从而极大地简化了网络程序开发过程。

在众多的网络安全程序、工具和软件中都是基于socket设计和开发的。由于在安全程序中通常需要对网络通讯的细节(如连接双方地址/端口、服务类型、传输控制等)进行检查、处理或控制,象数据包截获、数据包头分析、数据包重写、甚至截断连接等,都几乎在每个网络安全程序中必须实现。为了简化网络安全程序的编写过程,提高网络安全程序的性能和健壮性,同时使代码更易重用与移植,最好的方法就是将最常用和最繁复的过程函数,如监听套接口的打开/关闭、数据包截获、数据包构造/发送/接收等,封装起来,以API library的方式提供给开发人员使用。


---[[ C开发库简介 ]]-------------------------------------

在Unix系统平台上的网络安全工具开发中,目前最为流行的C API library有libnet、libpcap、libnids和libicmp等。它们分别从不同层次和角度提供了不同的功能函数。使网络开发人员能够忽略网络底层细节的实现,从而专注于程序本身具体功能的设计与开发。其中,

* libnet提供的接口函数主要实现和封装了数据包的构造和发送过程。

* libpcap提供的接口函数主要实现和封装了与数据包截获有关的过程。

* libnids提供的接口函数主要实现了开发网络入侵监测系统所必须的一些结构框架。

* libicmp等相对较为简单,它封装的是ICMP数据包的主要处理过程(构造、发送、接收等)。

利用这些C函数库的接口,网络安全工具开发人员可以很方便地编写出具有结构化强、健壮性好、可移植性高等特点的程序,如scanner、sniffer、firewall、IDS等。


---[[ libnet ]]------------------------------------------

libnet库的最新版本为1.0.0,它一共约7600行C源代码,33个源程序文件,12个C头文件,50余个自定义函数,提供的接口函数包含15种数据包生成器和两种数据包发送器(IP层和数据链路层)。目前只支持IPv4,不支持IPv6。已经过测试的系统平台包括:

* OpenBSD 2.6snap, 2.5, 2.4, 2.3, 2.2 (i386)
* FreeBSD 4.0-STABLE, 3.3-STABLE, 3.2-RELEASE, 3.1-CURRENT, 3.0, 2.2 (i386)
* NetBSD 1.3.2 (i386)
* BSD/OS 3.x (i386)
* BSDi 3.0 (i386)
* Linux 2.2.x, 2.0.3x, 2.1.124 (i386, alpha) (libc: 2.4.x, glibc: 2.0.x)
* Solaris 7 (SPARC, gcc 2.7.2[13], 2.8.2), 2.6 (SPARC, gcc 2.8.2),
2.5.x (SPARC, gcc 2.7.2[13])
* IRIX 6.2
* MacOS 5.3rhapsody (powerpc)

libnet提供的接口函数按其作用可分为四类:

* 内存管理(分配和释放)函数
* 地址解析函数
* 数据包构造函数
* 数据包发送函数

以下分别列出这些接口函数及其功能(其参数含义简单易懂,不再解释):


★ 内存管理函数

单数据包内存初始化:
int libnet_init_packet(u_short packet_size, u_char **buf);

单数据包内存释放:
void libnet_destroy_packet(u_char **buf);

多数据包内存初始化:
int libnet_init_packet_arena(struct libnet_arena **arena,
u_short packet_num, u_short packet_size);

访问多数据包内存中的下一个数据包:
u_char *libnet_next_packet_from_arena(struct libnet_arena **arena,
u_short packet_size);

多数据包内存释放:
void libnet_destroy_packet_arena(struct libnet_arena **arena);


★ 地址解析函数

解析主机名:
u_char *libnet_host_lookup(u_long ip, u_short use_name);

解析主机名(可重入函数):
void libnet_host_lookup_r(u_long ip, u_short use_name, u_char *buf);

域名解析:
u_long libnet_name_resolve(u_char *ip, u_short use_name);

获取接口设备IP地址:
u_long libnet_get_ipaddr(struct libnet_link_int *l,
const u_char *device, const u_char *ebuf);

获取接口设备硬件地址:
struct ether_addr *libnet_get_hwaddr(struct libnet_link_int *l,
const u_char *device,
const u_char *ebuf);


★ 数据包构造函数

ARP协议数据包:
int libnet_build_arp(u_short hrdw, u_short prot, u_short h_len,
u_short p_len, u_short op, u_char *s_ha,
u_char *s_pa, u_char *t_ha, u_char *t_pa,
const u_char *payload, int payload_len,
u_char *packet_buf);

DNS协议数据包:
int libnet_build_dns(u_short id, u_short flags, u_short num_q,
u_short num_answ_rr, u_short num_auth_rr,
u_short num_add_rr, const u_char * payload,
int payload_len, u_char *packet_buf);

以太网协议数据包:
int libnet_build_ethernet(u_char *daddr, u_char *saddr, u_short id,
const u_char *payload, int payload_len,
u_char *packet_buf);

ICMP协议数据包(ICMP_ECHO / ICMP_ECHOREPLY):
int libnet_build_icmp_echo(u_char type, u_char code, u_short id,
u_short seq, const u_char *payload,
int payload_len, u_char *packet_buf);

ICMP协议数据包(ICMP_MASKREQ / ICMP_MASKREPLY):
int libnet_build_icmp_mask(u_char type, u_char code, u_short id,
u_short seq, u_long mask,
const u_char *payload, int payload_len,
u_char *packet_buf);

ICMP协议数据包(ICMP_UNREACH):
int libnet_build_icmp_unreach(u_char type, u_char code,
u_short orig_len, u_char orig_tos,
u_short orig_id, u_short orig_frag,
u_char orig_ttl, u_char orig_prot,
u_long orig_saddr, u_long orig_daddr,
const u_char *payload, int payload_len,
u_char *packet_buf);

ICMP协议数据包(ICMP_TIMEXCEED):
int libnet_build_icmp_timeexceed(u_char type, u_char code,
u_short orig_len, u_char orig_tos,
u_short orig_id, u_short orig_frag,
u_char orig_ttl, u_char orig_prot,
u_long orig_saddr, u_long orig_daddr,
const u_char *payload, int payload_len,
u_char *packet_buf);

ICMP协议数据包(ICMP_REDIRECT):
int libnet_build_icmp_redirect(u_char type, u_char code, u_long gateway,
u_short orig_len, u_char orig_tos,
u_short orig_id, u_short orig_frag,
u_char orig_ttl, u_char orig_prot,
u_long orig_saddr, u_long orig_daddr,
const u_char *payload, int payload_len,
u_char *packet_buf);

ICMP协议数据包(ICMP_TSTAMP / ICMP_TSTAMPREPLY):
int libnet_build_icmp_timestamp(u_char type, u_char code, u_short id,
u_short seq, n_time otime, n_time rtime,
n_time ttime, const u_char *payload,
int payload_len, u_char *packet_buf);

IGMP协议数据包:
int libnet_build_igmp(u_char type, u_char code, u_long ip,
const u_char *payload, int payload_len,
u_char *packet_buf);

IP协议数据包:
int libnet_build_ip(u_short len, u_char tos, u_short ip_id, u_short frag,
u_char ttl, u_char protocol, u_long saddr,
u_long daddr, const u_char *payload, int payload_len,
u_char *packet_buf);

OSPF路由协议数据包:
int libnet_build_ospf(u_short len, u_char type, u_long router_id,
u_long area_id, u_short auth_type,
const char *payload, int payload_s, u_char *buf);

OSPF路由协议数据包(Hello):
int libnet_build_ospf_hello(u_long netmask, u_short interval,
u_char options, u_char priority,
u_int dead_interval, u_long des_router,
u_long backup, u_long neighbor,
const char *payload, int payload_s,
u_char *buf);

OSPF路由协议数据包(DataBase Description (DBD)):
int libnet_build_ospf_dbd(u_short len, u_char options, u_char type,
u_int sequence_num, const char *payload,
int payload_s, u_char *buf);

OSPF路由协议数据包(Link State Request (LSR)):
int libnet_build_ospf_lsr(u_int type, u_int ls_id, u_long adv_router,
const char *payload, int payload_s,
u_char *buf);

OSPF路由协议数据包(Link State Update (LSU)):
int libnet_build_ospf_lsu(u_int num, const char *payload,
int payload_s, u_char *buf);

OSPF路由协议数据包(Link State Acknowledgement (LSA)):
int libnet_build_ospf_lsa(u_short age, u_char options, u_char type,
u_int ls_id, u_long adv_router,
u_int sequence_num, u_short len,
const char *payload, int payload_s,
u_char *buf);

OSPF路由协议数据包(OSPF Link Sate NetworkLink State Router):
int libnet_build_ospf_lsa_net(u_long netmask, u_int router_id,
const char *payload, int payload_s,
u_char *buf);

OSPF路由协议数据包(Link State Router):
int libnet_build_ospf_lsa_rtr(u_short flags, u_short num, u_int id,
u_int data, u_char type, u_char tos,
u_short metric, const char *payload,
int payload_s, u_char *buf);

OSPF路由协议数据包(Link State Summary):
int libnet_build_ospf_lsa_sum(u_long netmask, u_int metric, u_int tos,
const char *payload, int payload_s,
u_char *buf);

OSPF路由协议数据包(Link State AS External):
int libnet_build_ospf_lsa_as(u_long netmask, u_int metric,
u_long fwd_addr, u_int tag,
const char *payload, int payload_s,
u_char *buf);

RIP路由协议数据包:
int libnet_build_rip(u_char cmd, u_char ver, u_short domain,
u_short addr_fam, u_short route_tag, u_long ip,
u_long mask, u_long next_hop, u_long metric,
const u_char *payload, int payload_len,
u_char *packet_buf);

TCP协议数据包:
int libnet_build_tcp(u_short th_sport, u_short th_dport, u_long th_seq,
u_long th_ack, u_char th_flags, u_short th_win,
u_short th_urg, const u_char *payload,
int payload_len, u_char *packet_buf);

UDP协议数据包:
int libnet_build_udp(u_short sport, u_short dport, const u_char *payload,
int payload_len, u_char *packet_buf);

IP协议数据包选项:
int libnet_insert_ipo(struct ipoption *opt, u_char opt_len,
u_char *packet_buf);

TCP协议数据包选项:
int libnet_insert_tcpo(struct tcpoption *opt, u_char opt_len,
u_char *packet_buf);


★ 数据包发送函数

打开raw socket:
int libnet_open_raw_sock(int protocol);

关闭raw socket:
int libnet_close_raw_sock(int socket);

选择接口设备:
int libnet_select_device(struct sockaddr_in *sin,
u_char **device, u_char *ebuf);

打开链路层接口设备:
struct libnet_link_int *libnet_open_link_interface(char *device,
char *ebuf);

关闭链路层接口设备:
int libnet_close_link_interface(struct libnet_link_int *l);

发送IP数据包:
int libnet_write_ip(int socket, u_char *packet, int packet_size);

发送链路层数据包:
int libnet_write_link_layer(struct libnet_link_int *l,
const u_char *device, u_char *packet,
int packet_size);

检验和计算:
int libnet_do_checksum(u_char *packet, int protocol, int packet_size);


★ 相关的支持函数

随机数种子生成器:
int libnet_seed_prand();

获取随机数:
u_long libnet_get_prand(int modulus);

16进制数据输出:
void libnet_hex_dump(u_char * buf, int len, int swap, FILE *stream);

端口列表链初始化:
int libnet_plist_chain_new(struct libnet_plist_chain **plist,
char *token_list);

获取端口列表链的下一项(端口范围):
int libnet_plist_chain_next_pair(struct libnet_plist_chain *plist,
u_short *bport, u_short *eport);

端口列表链输出显示:
int libnet_plist_chain_dump(struct libnet_plist_chain *plist);

获取端口列表链:
u_char *libnet_plist_chain_dump_string(struct libnet_plist_chain *plist);

端口列表链内存释放:
void libnet_plist_chain_free(struct libnet_plist_chain *plist);


★ 数据常量

==================================================================================
数据包头大小定义:

常量名 数值(字节数)
LIBNET_ARP_H 28
LIBNET_DNS_H 12
LIBNET_ETH_H 14
LIBNET_ICMP_H 4
LIBNET_ICMP_ECHO_H 8
LIBNET_ICMP_MASK_H 12
LIBNET_ICMP_UNREACH_H 8
LIBNET_ICMP_TIMXCEED_H 8
LIBNET_ICMP_REDIRECT_H 8
LIBNET_ICMP_TS_H 20
LIBNET_IGMP_H 8
LIBNET_IP_H 20
LIBNET_RIP_H 24
LIBNET_TCP_H 20
LIBNET_UDP_H 8

==================================================================================
数据包内存常量:

常量名 含义
LIBNET_PACKET TCP/UDP数据包头 + IP数据包头使用的内存
LIBNET_OPTS IP或TCP选项使用的内存
LIBNET_MAX_PACKET IP_MAXPACKET (65535字节)使用的内存

==================================================================================
随机数发生器常量(libnet_get_prand()函数使用):

常量名 数值
LIBNET_PRAND_MAX 65535
LIBNET_PR2 0 - 2
LIBNET_PR8 0 - 255
LIBNET_PR16 0 - 32767
LIBNET_PRu16 0 - 65535
LIBNET_PR32 0 - 2147483647
LIBNET_PRu32 0 - 4294967295

==================================================================================
错误消息常量(libnet_error()函数使用):

常量名 含义
LIBNET_ERR_WARNING 警告类型消息
LIBNET_ERR_CRITICAL 紧急类型消息
LIBNET_ERR_FATAL 致命错误消息

==================================================================================
libnet_host_lookup()、libnet_host_lookup_r()和libnet_name_resolve()函数使用的常量:

常量名 含义
LIBNET_DONT_RESOLVE 不将IP地址解析为FQDN名
LIBNET_RESOLVE 尝试将IP地址解析为FQDN名

==================================================================================
宏定义

宏名 功能
LIBNET_GET_ARENA_SIZE(arena) 返回多数据包内存缓冲区大小(字节数)
LIBNET_GET_ARENA_REMAINING_BYTES(arena) 返回多数据包内存缓冲区剩余空间大小(字节数)
LIBNET_PRINT_ETH_ADDR(e) 输出显示ether_addr结构中的以太网地址

==================================================================================


---[[ libnet应用实例 ]]----------------------------------

利用libnet函数库开发应用程序的基本步骤非常简单:

1、数据包内存初始化;
2、网络接口初始化;
3、构造所需数据包;
4、计算数据包检验和;
5、发送数据包;
6、关闭网络接口;
7、释放数据包内存。

以下是四个使用了libnet接口函数编写的数据包发送程序。在编译前必须确保libnet库已成功安装。


============================ cut here ============================

/* Example 1 [raw socket api - TCP packet] */
/* gcc -Wall `libnet-config --defines` libnet-example-x.c -o libnet-example-x \
`libnet-config --libs` */

#include <libnet.h>

void usage(char *);

int main(int argc, char **argv)
{
int network, /* our network interface */
packet_size, /* packet size */
c; /* misc */
u_long src_ip, dst_ip; /* ip addresses */
u_short src_prt, dst_prt; /* ports */
u_char *cp, *packet; /* misc / packet */

printf("libnet example code:\tmodule 1\n\n");
printf("packet injection interface:\traw socket\n");
printf("packet type:\t\t\tTCP [no payload]\n");

src_ip = 0;
dst_ip = 0;
src_prt = 0;
dst_prt = 0;

while((c = getopt(argc, argv, "d:s:")) != EOF)
{
switch (c)
{
/*
* We expect the input to be of the form `ip.ip.ip.ip.port`. We
* point cp to the last dot of the IP address/port string and
* then seperate them with a NULL byte. The optarg now points to
* just the IP address, and cp points to the port.
*/
case 'd':
if (!(cp = strrchr(optarg, '.')))
{
usage(argv[0]);
}
*cp++ = 0;
dst_prt = (u_short)atoi(cp);
if (!(dst_ip = libnet_name_resolve(optarg, LIBNET_RESOLVE)))
{
libnet_error(LIBNET_ERR_FATAL,
"Bad destination IP address: %s\n", optarg);
}
break;
case 's':
if (!(cp = strrchr(optarg, '.')))
{
usage(argv[0]);
}
*cp++ = 0;
src_prt = (u_short)atoi(cp);
if (!(src_ip = libnet_name_resolve(optarg, LIBNET_RESOLVE)))
{
libnet_error(LIBNET_ERR_FATAL,
"Bad source IP address: %s\n", optarg);
}
break;
}
}
if (!src_ip || !src_prt || !dst_ip || !dst_prt)
{
usage(argv[0]);
exit(EXIT_FAILURE);
}

/*
* We're just going to build a TCP packet with no payload using the
* raw sockets API, so we only need memory for a TCP header and an IP
* header.
*/
packet_size = LIBNET_IP_H + LIBNET_TCP_H;

/*
* Step 1: Memory initialization (interchangable with step 2).
*/
libnet_init_packet(packet_size, &packet);
if (packet == NULL)
{
libnet_error(LIBNET_ERR_FATAL, "libnet_init_packet failed\n");
}

/*
* Step 2: Network initialization (interchangable with step 1).
*/
network = libnet_open_raw_sock(IPPROTO_RAW);
if (network == -1)
{
libnet_error(LIBNET_ERR_FATAL, "Can't open network.\n");
}

/*
* Step 3: Packet construction (IP header).
*/
libnet_build_ip(LIBNET_TCP_H, /* size of the packet sans IP header */
IPTOS_LOWDELAY, /* IP tos */
242, /* IP ID */
0, /* frag stuff */
48, /* TTL */
IPPROTO_TCP, /* transport protocol */
src_ip, /* source IP */
dst_ip, /* destination IP */
NULL, /* payload (none) */
0, /* payload length */
packet); /* packet header memory */

/*
* Step 3: Packet construction (TCP header).
*/
libnet_build_tcp(src_prt, /* source TCP port */
dst_prt, /* destination TCP port */
0xa1d95, /* sequence number */
0x53, /* acknowledgement number */
TH_SYN, /* control flags */
1024, /* window size */
0, /* urgent pointer */
NULL, /* payload (none) */
0, /* payload length */
packet + LIBNET_IP_H); /* packet header memory */

/*
* Step 4: Packet checksums (TCP header only).
*/
if (libnet_do_checksum(packet, IPPROTO_TCP, LIBNET_TCP_H) == -1)
{
libnet_error(LIBNET_ERR_FATAL, "libnet_do_checksum failed\n");
}

/*
* Step 5: Packet injection.
*/
c = libnet_write_ip(network, packet, packet_size);
if (c < packet_size)
{
libnet_error(LN_ERR_WARNING,
"libnet_write_ip only wrote %d bytes\n", c);
}
else
{
printf("construction and injection completed, wrote all %d bytes\n", c);
}

/*
* Shut down the interface.
*/
if (libnet_close_raw_sock(network) == -1)
{
libnet_error(LN_ERR_WARNING,
"libnet_close_raw_sock couldn't close the interface");
}


/*
* Free packet memory.
*/
libnet_destroy_packet(&packet);

return (c == -1 ? EXIT_FAILURE : EXIT_SUCCESS);
}


void usage(char *name)
{
fprintf(stderr, "usage: %s -s s_ip.s_port -d d_ip.d_port\n", name);
}


============================ cut here ============================

/* Example 2 [link layer api - ICMP_MASK] */
/* gcc -Wall `libnet-config --defines` libnet-example-x.c -o libnet-example-x `libnet-config --libs` */

#include <libnet.h>

void usage(char *);

u_char enet_src[6] = {0x0d, 0x0e, 0x0a, 0x0d, 0x00, 0x00};
u_char enet_dst[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

int
main(int argc, char *argv[])
{
int packet_size, /* size of our packet */
c; /* misc */
u_long src_ip, dst_ip; /* source ip, dest ip */
u_char *packet; /* pointer to our packet buffer */
char err_buf[LIBNET_ERRBUF_SIZE]; /* error buffer */
u_char *device; /* pointer to the device to use */
struct libnet_link_int *network; /* pointer to link interface struct */

printf("libnet example code:\tmodule 2\n\n");
printf("packet injection interface:\tlink layer\n");
printf("packet type:\t\t\tICMP net mask [no payload]\n");

device = NULL;
src_ip = 0;
dst_ip = 0;

while ((c = getopt(argc, argv, "i:d:s:")) != EOF)
{
switch (c)
{
case 'd':
if (!(dst_ip = libnet_name_resolve(optarg, LIBNET_RESOLVE)))
{
libnet_error(LIBNET_ERR_FATAL,
"Bad destination IP address: %s\n", optarg);

}
break;
case 'i':
device = optarg;
break;
case 's':
if (!(src_ip = libnet_name_resolve(optarg, LIBNET_RESOLVE)))
{
libnet_error(LIBNET_ERR_FATAL,
"Bad source IP address: %s\n", optarg);
}
break;
default:
exit(EXIT_FAILURE);
}
}

if (!src_ip || !dst_ip)
{
usage(argv[0]);
exit(EXIT_FAILURE);
}

/*
* Step 1: Network Initialization (interchangable with step 2).
*/
if (device == NULL)
{
struct sockaddr_in sin;
/*
* Try to locate a device.
*/
if (libnet_select_device(&sin, &device, err_buf) == -1)
{
libnet_error(LIBNET_ERR_FATAL,
"libnet_select_device failed: %s\n", err_buf);
}
printf("device:\t\t\t\t%s\n", device);
}

if ((network = libnet_open_link_interface(device, err_buf)) == NULL)
{
libnet_error(LIBNET_ERR_FATAL,
"libnet_open_link_interface: %s\n", err_buf);
}

/*
* We're going to build an ICMP packet with no payload using the
* link-layer API, so this time we need memory for a ethernet header
* as well as memory for the ICMP and IP headers.
*/
packet_size = LIBNET_IP_H + LIBNET_ETH_H + LIBNET_ICMP_MASK_H;


/*
* Step 2: Memory Initialization (interchangable with step 1).
*/
if (libnet_init_packet(packet_size, &packet) == -1)
{
libnet_error(LIBNET_ERR_FATAL, "libnet_init_packet failed\n");
}


/*
* Step 3: Packet construction (ethernet header).
*/
libnet_build_ethernet(enet_dst,
enet_src,
ETHERTYPE_IP,
NULL,
0,
packet);

/*
* Step 3: Packet construction (ICMP header).
*/
libnet_build_icmp_mask(ICMP_MASKREPLY, /* type */
0, /* code */
242, /* id */
0, /* seq */
0xffffffff, /* mask */
NULL, /* payload */
0, /* payload_s */
packet + LIBNET_ETH_H + LIBNET_IP_H);


/*
* Step 3: Packet construction (IP header).
*/
libnet_build_ip(ICMP_MASK_H,
0, /* IP tos */
242, /* IP ID */
0, /* Frag */
64, /* TTL */
IPPROTO_ICMP, /* Transport protocol */
src_ip, /* Source IP */
dst_ip, /* Destination IP */
NULL, /* Pointer to payload (none) */
0,
packet + LIBNET_ETH_H); /* Packet header memory */

/*
* Step 4: Packet checksums (ICMP header *AND* IP header).
*/
if (libnet_do_checksum(packet + ETH_H, IPPROTO_ICMP, LIBNET_ICMP_MASK_H) == -1)
{
libnet_error(LIBNET_ERR_FATAL, "libnet_do_checksum failed\n");
}
if (libnet_do_checksum(packet + ETH_H, IPPROTO_IP, LIBNET_IP_H) == -1)
{
libnet_error(LIBNET_ERR_FATAL, "libnet_do_checksum failed\n");
}


/*
* Step 5: Packet injection.
*/
c = libnet_write_link_layer(network, device, packet, packet_size);
if (c < packet_size)
{
libnet_error(LN_ERR_WARNING,
"libnet_write_link_layer only wrote %d bytes\n", c);
}
else
{
printf("construction and injection completed, wrote all %d bytes\n", c);
}


/*
* Shut down the interface.
*/
if (libnet_close_link_interface(network) == -1)
{
libnet_error(LN_ERR_WARNING,
"libnet_close_link_interface couldn't close the interface");
}


/*
* Free packet memory.
*/
libnet_destroy_packet(&packet);

return (c == -1 ? EXIT_FAILURE : EXIT_SUCCESS);
}

void usage(char *name)
{
fprintf(stderr, "usage: %s [-i interface] -s s_ip -d d_ip\n", name);
}


============================ cut here ============================

/* Example 3 [raw socket api - ICMP_ECHO using an arena] */
/* gcc -Wall `libnet-config --defines` libnet-example-x.c -o libnet-example-x \
`libnet-config --libs` */

#include <libnet.h>

void usage(char *);


int main(int argc, char **argv)
{
int network, n, c, number_of_packets, packet_size;
struct libnet_arena arena, *arena_p;
u_char *packets[10];
u_long src_ip, dst_ip;

printf("libnet example code:\tmodule 3\n\n");
printf("packet injection interface:\tlink layer\n");
printf("packet type:\t\t\tICMP_ECHO [no payload] using an arena\n");

src_ip = 0;
dst_ip = 0;
while((c = getopt(argc, argv, "d:s:")) != EOF)
{
switch (c)
{
case 'd':
if (!(dst_ip = libnet_name_resolve(optarg, LIBNET_RESOLVE)))
{
libnet_error(LIBNET_ERR_FATAL,
"Bad destination IP address: %s\n", optarg);
}
break;
case 's':
if (!(src_ip = libnet_name_resolve(optarg, LIBNET_RESOLVE)))
{
libnet_error(LIBNET_ERR_FATAL,
"Bad source IP address: %s\n", optarg);
}
break;
}
}
if (!src_ip || !dst_ip)
{
usage(argv[0]);
exit(EXIT_FAILURE);
}

/*
* We're just going to build an ICMP packet with no payload using the
* raw sockets API, so we only need memory for a ICMP header and an IP
* header.
*/
packet_size = LIBNET_IP_H + LIBNET_ICMP_ECHO_H;

/*
* Let's just build say, 10 packets.
*/
number_of_packets = 10;

arena_p = &arena;
if (libnet_init_packet_arena(&arena_p, number_of_packets, packet_size) == -1)
{
libnet_error(LIBNET_ERR_FATAL, "libnet_init_packet_arena failed\n");
}
else
{
printf("Allocated an arena of %ld bytes..\n",
LIBNET_GET_ARENA_SIZE(arena));
}

network = libnet_open_raw_sock(IPPROTO_RAW);
if (network == -1)
{
libnet_error(LIBNET_ERR_FATAL, "Can't open the network.\n");
}

for (n = 0; n < number_of_packets; n++)
{
printf("%ld bytes remaining in arena\n",
LIBNET_GET_ARENA_REMAINING_BYTES(arena));
packets[n] = libnet_next_packet_from_arena(&arena_p, packet_size);
if (!packets[n])
{
libnet_error(LIBNET_ERR_WARNING, "Arena is empty\n");
continue;
}

libnet_build_ip(ICMP_ECHO_H, /* Size of the payload */
IPTOS_LOWDELAY | IPTOS_THROUGHPUT, /* IP tos */
242, /* IP ID */
0, /* frag stuff */
48, /* TTL */
IPPROTO_ICMP, /* transport protocol */
src_ip, /* source IP */
dst_ip, /* destination IP */
NULL, /* pointer to payload */
0, /* payload length */
packets[n]); /* packet header memory */

libnet_build_icmp_echo(ICMP_ECHO, /* type */
0, /* code */
242, /* id */
5, /* seq */
NULL, /* pointer to payload */
0, /* payload length */
packets[n] + LIBNET_IP_H); /* packet header memory */

if (libnet_do_checksum(packets[n], IPPROTO_ICMP, LIBNET_ICMP_ECHO_H) == -1)
{
libnet_error(LIBNET_ERR_FATAL, "libnet_do_checksum failed\n");
}

c = libnet_write_ip(network, packets[n], packet_size);
if (c < packet_size)
{
libnet_error(LN_ERR_WARNING,
"libnet_write_ip only wrote %d bytes\n", c);
}
else
{
printf("construction and injection of packet %d of %d completed, wrote all %d bytes\n",
n + 1, number_of_packets, c);
}
}

libnet_destroy_packet_arena(&arena_p);
return (c == -1 ? EXIT_FAILURE : EXIT_SUCCESS);
}


void usage(char *name)
{
fprintf(stderr, "usage: %s -s source_ip -d destination_ip\n ", name);
}


============================ cut here ============================

/* Example 4 [link-layer api - UDP packet using port list chaining] */
/* gcc -Wall `libnet-config --defines` libnet-example-x.c -o libnet-example-x \
`libnet-config --libs` */

#include <libnet.h>

#define MAX_PAYLOAD_SIZE 1024

void usage(char *);

u_char enet_src[6] = {0x0d, 0x0e, 0x0a, 0x0d, 0x00, 0x00};
u_char enet_dst[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

int main(int argc, char *argv[])
{
int packet_size, /* size of our packet */
payload_size, /* size of our packet */
c; /* misc */
u_long src_ip, dst_ip; /* source ip, dest ip */
u_short bport, eport; /* beginning and end ports */
u_short cport; /* current port */
u_char payload[MAX_PAYLOAD_SIZE]; /* packet payload */
u_char *packet; /* pointer to our packet buffer */
char err_buf[LIBNET_ERRBUF_SIZE]; /* error buffer */
u_char *device; /* pointer to the device to use */
struct libnet_link_int *network; /* pointer to link interface struct */
struct libnet_plist_chain plist; /* plist chain */
struct libnet_plist_chain *plist_p; /* plist chain pointer */

printf("libnet example code:\tmodule 4\n\n");
printf("packet injection interface:\tlink layer\n");
printf("packet type:\t\t\tUDP [with payload] using port list chaining\n");

plist_p = NULL;
device = NULL;
src_ip = 0;
dst_ip = 0;

while ((c = getopt(argc, argv, "i:d:s:p:")) != EOF)
{
switch (c)
{
case 'd':
if (!(dst_ip = libnet_name_resolve(optarg, LIBNET_RESOLVE)))
{
libnet_error(LIBNET_ERR_FATAL,
"Bad destination IP address: %s\n", optarg);

}
break;
case 'i':
device = optarg;
break;
case 's':
if (!(src_ip = libnet_name_resolve(optarg, LIBNET_RESOLVE)))
{
libnet_error(LIBNET_ERR_FATAL,
"Bad source IP address: %s\n", optarg);
}
break;
case 'p':
plist_p = &plist;
if (libnet_plist_chain_new(&plist_p, optarg) == -1)
{
libnet_error(LIBNET_ERR_FATAL,
"Could not build port list\n");
}
break;
default:
usage(argv[0]);
exit(EXIT_FAILURE);
}
}

if (!src_ip || !dst_ip || !plist_p)
{
usage(argv[0]);
exit(EXIT_FAILURE);
}

c = argc - optind;
if (c != 1)
{
usage(argv[0]);
exit(EXIT_FAILURE);
}
memset(payload, 0, sizeof(payload));
strncpy(payload, argv[optind], strlen(argv[optind]));


/*
* Step 1: Network Initialization (interchangable with step 2).
*/
if (device == NULL)
{
struct sockaddr_in sin;
/*
* Try to locate a device.
*/
if (libnet_select_device(&sin, &device, err_buf) == -1)
{
libnet_error(LIBNET_ERR_FATAL,
"libnet_select_device failed: %s\n", err_buf);
}
printf("device:\t\t\t\t%s\n", device);
}
if ((network = libnet_open_link_interface(device, err_buf)) == NULL)
{
libnet_error(LIBNET_ERR_FATAL,
"libnet_open_link_interface: %s\n", err_buf);
}

/*
* Get the payload from the user. Hrm. This might fail on a Sparc
* if byte alignment is off...
*/
payload_size = strlen(payload);

/*
* We're going to build a UDP packet with a payload using the
* link-layer API, so this time we need memory for a ethernet header
* as well as memory for the ICMP and IP headers and our payload.
*/
packet_size = LIBNET_IP_H + LIBNET_ETH_H + LIBNET_UDP_H + payload_size;

/*
* Step 2: Memory Initialization (interchangable with step 1).
*/
if (libnet_init_packet(packet_size, &packet) == -1)
{
libnet_error(LIBNET_ERR_FATAL, "libnet_init_packet failed\n");
}


/*
* Step 3: Packet construction (ethernet header).
*/
libnet_build_ethernet(enet_dst,
enet_src,
ETHERTYPE_IP,
NULL,
0,
packet);

/*
* Step 3: Packet construction (IP header).
*/
libnet_build_ip(LIBNET_UDP_H + payload_size,
0, /* IP tos */
242, /* IP ID */
0, /* Frag */
64, /* TTL */
IPPROTO_UDP, /* Transport protocol */
src_ip, /* Source IP */
dst_ip, /* Destination IP */
NULL, /* Pointer to payload (none) */
0,
packet + LIBNET_ETH_H); /* Packet header memory */

while (libnet_plist_chain_next_pair(plist_p, &bport, &eport))
{

while (!(bport > eport) && bport != 0)
{
cport = bport++;
/*
* Step 3: Packet construction (UDP header).
*/
libnet_build_udp(242, /* source port */
cport, /* dest. port */
payload, /* payload */
payload_size, /* payload length */
packet + LIBNET_ETH_H + LIBNET_IP_H);

/*
* Step 4: Packet checksums (ICMP header *AND* IP header).
*/
if (libnet_do_checksum(packet + ETH_H, IPPROTO_UDP, LIBNET_UDP_H + payload_size) == -1)
{
libnet_error(LIBNET_ERR_FATAL, "libnet_do_checksum failed\n");
}
if (libnet_do_checksum(packet + ETH_H, IPPROTO_IP, LIBNET_IP_H) == -1)
{
libnet_error(LIBNET_ERR_FATAL, "libnet_do_checksum failed\n");
}

/*
* Step 5: Packet injection.
*/
c = libnet_write_link_layer(network, device, packet, packet_size);
if (c < packet_size)
{
libnet_error(LN_ERR_WARNING,
"libnet_write_link_layer only wrote %d bytes\n", c);
}
else
{
printf("construction and injection completed, wrote all %d bytes, port %d\n",
c, cport);
}
}
}
/*
* Shut down the interface.
*/
if (libnet_close_link_interface(network) == -1)
{
libnet_error(LN_ERR_WARNING,
"libnet_close_link_interface couldn't close the interface");
}


/*
* Free packet memory.
*/
libnet_destroy_packet(&packet);

return (c == -1 ? EXIT_FAILURE : EXIT_SUCCESS);
}


void usage(char *name)
{
fprintf(stderr, "usage: %s [-i interface] -s s_ip -d d_ip -p port list payload\n", name);
}


---[[ libpcap ]]------------------------------------------

  libpcap的英文意思是 Packet Capturelibrary,即数据包捕获函数库。该库提供的C函数接口可用于需要捕获经过网络接口(只要经过该接口,目标地址不一定为本机)数据包的系统开发上。由Berkeley大学Lawrence Berkeley National Laboratory研究院的Van Jacobson、CraigLeres和Steven McCanne编写,目前的最新版本为0.4。该函数库支持Linux、Solaris和*BSD系统平台。

  主要接口函数说明如下:

pcap_t *pcap_open_live(char *device, int snaplen,
int promisc, int to_ms, char *ebuf)

获得用于捕获网络数据包的数据包捕获描述字。device参数为指定打开
的网络设备名。snaplen参数定义捕获数据的最大字节数。promisc指定
是否将网络接口置于混杂模式。to_ms参数指定超时时间(毫秒)。
ebuf参数则仅在pcap_open_live()函数出错返回NULL时用于传递错误消
息。

pcap_t *pcap_open_offline(char *fname, char *ebuf)

打开以前保存捕获数据包的文件,用于读取。fname参数指定打开的文
件名。该文件中的数据格式与tcpdump和tcpslice兼容。"-"为标准输
入。ebuf参数则仅在pcap_open_offline()函数出错返回NULL时用于传
递错误消息。

pcap_dumper_t *pcap_dump_open(pcap_t *p, char *fname)

打开用于保存捕获数据包的文件,用于写入。fname参数为"-"时表示
标准输出。出错时返回NULL。p参数为调用pcap_open_offline()或
pcap_open_live()函数后返回的pcap结构指针。fname参数指定打开
的文件名。如果返回NULL,则可调用pcap_geterr()函数获取错误消
息。

char *pcap_lookupdev(char *errbuf)

用于返回可被pcap_open_live()或pcap_lookupnet()函数调用的网络
设备名指针。如果函数出错,则返回NULL,同时errbuf中存放相关的
错误消息。

int pcap_lookupnet(char *device, bpf_u_int32 *netp,
bpf_u_int32 *maskp, char *errbuf)

获得指定网络设备的网络号和掩码。netp参数和maskp参数都是
bpf_u_int32指针。如果函数出错,则返回-1,同时errbuf中存放相
关的错误消息。

int pcap_dispatch(pcap_t *p, int cnt,
pcap_handler callback, u_char *user)

捕获并处理数据包。cnt参数指定函数返回前所处理数据包的最大值。
cnt=-1表示在一个缓冲区中处理所有的数据包。cnt=0表示处理所有
数据包,直到产生以下错误之一:读取到EOF;超时读取。callback
参数指定一个带有三个参数的回调函数,这三个参数为:一个从
pcap_dispatch()函数传递过来的u_char指针,一个pcap_pkthdr结构
的指针,和一个数据包大小的u_char指针。如果成功则返回读取到的
字节数。读取到EOF时则返回零值。出错时则返回-1,此时可调用
pcap_perror()或pcap_geterr()函数获取错误消息。

int pcap_loop(pcap_t *p, int cnt,
pcap_handler callback, u_char *user)

功能基本与pcap_dispatch()函数相同,只不过此函数在cnt个数据包
被处理或出现错误时才返回,但读取超时不会返回。而如果为
pcap_open_live()函数指定了一个非零值的超时设置,然后调用
pcap_dispatch()函数,则当超时发生时pcap_dispatch()函数会返回。
cnt参数为负值时pcap_loop()函数将始终循环运行,除非出现错误。

void pcap_dump(u_char *user, struct pcap_pkthdr *h,
u_char *sp)

向调用pcap_dump_open()函数打开的文件输出一个数据包。该函数可
作为pcap_dispatch()函数的回调函数。

int pcap_compile(pcap_t *p, struct bpf_program *fp,
char *str, int optimize, bpf_u_int32 netmask)

将str参数指定的字符串编译到过滤程序中。fp是一个bpf_program结
构的指针,在pcap_compile()函数中被赋值。optimize参数控制结果
代码的优化。netmask参数指定本地网络的网络掩码。

int pcap_setfilter(pcap_t *p, struct bpf_program *fp)

指定一个过滤程序。fp参数是bpf_program结构指针,通常取自
pcap_compile()函数调用。出错时返回-1;成功时返回0。

u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

返回指向下一个数据包的u_char指针。

int pcap_datalink(pcap_t *p)

返回数据链路层类型,例如DLT_EN10MB。

int pcap_snapshot(pcap_t *p)

返回pcap_open_live被调用后的snapshot参数值。

int pcap_is_swapped(pcap_t *p)

返回当前系统主机字节与被打开文件的字节顺序是否不同。

int pcap_major_version(pcap_t *p)

返回写入被打开文件所使用的pcap函数的主版本号。

int pcap_minor_version(pcap_t *p)

返回写入被打开文件所使用的pcap函数的辅版本号。

int pcap_stats(pcap_t *p, struct pcap_stat *ps)

向pcap_stat结构赋值。成功时返回0。这些数值包括了从开始
捕获数据以来至今共捕获到的数据包统计。如果出错或不支持
数据包统计,则返回-1,且可调用pcap_perror()或
pcap_geterr()函数来获取错误消息。

FILE *pcap_file(pcap_t *p)

返回被打开文件的文件名。

int pcap_fileno(pcap_t *p)

返回被打开文件的文件描述字号码。

void pcap_perror(pcap_t *p, char *prefix)

在标准输出设备上显示最后一个pcap库错误消息。以prefix参
数指定的字符串为消息头。

char *pcap_geterr(pcap_t *p)

返回最后一个pcap库错误消息。

char *pcap_strerror(int error)

如果strerror()函数不可用,则可调用pcap_strerror函数替代。

void pcap_close(pcap_t *p)

关闭p参数相应的文件,并释放资源。

void pcap_dump_close(pcap_dumper_t *p)

关闭相应的被打开文件。

<<< 待续 >>>


<<< 续前 >>>

---[[ libnids ]]------------------------------------------


一、简介

libnids的英文意思是 Network Intrusion Detect System library,即网络入侵监测系统函数库。它是在前面介绍的两种C函数接口库libnet和libpcap的基础上开发的,封装了开发NIDS所需的许多通用型函数。linids提供的接口函数监视流经本地的所有网络通信,检查数据包等。除此之外,还具有重组TCP数据段、处理IP分片包和监测TCP端口扫描的功能。利用libnids接口函数库,NIDS开发者不需要再编写底层的网络处理代码,只需专注于NIDS本身功能的实现即可。
libnids支持Linux、Solaris和*BSD系统平台,目前最新版本为1.13。


二、IP分片数据包

为了使libnids能接收所有的IP数据包(包括分片包、畸形包等),程序员需要定义如下的回调函数:

void ip_frag_func(struct ip * a_packet)

在调用nids_init()函数初始化后,使用nids的函数进行注册:

nids_register_ip_frag(ip_frag_func);

这样回调函数ip_frag_func会在适当的时候由libnids调用,参数a_packet指针将指向接收到的数据报。
类似地,如果仅接收目标主机会接受的数据包(如非碎片包、重组包或头部校验正确的数据包等),需要定义如下回调函数:

void ip_func(struct ip * a_packet)

然后注册:

nids_register_ip(ip_func);


三、TCP数据流重组

要接收TCP流在交换的数据,必须定义如下回调函数:

void tcp_callback(struct tcp_stream * ns, void ** param)

tcp_stream结构提供了一个TCP连接的所有信息。例如,它包含了客户端与服务器端的half_stream结构。下文会对该结构的字段进行解释。
tcp_stream结构有一个名为nids_state的字段。此字段的数值将决定tcp_callback的操作。

(a) ns->nids_state==NIDS_JUST_EST时,ns表示一个刚刚建立的连接。
tcp_callback可以据此决定是否对该连接的后续数据进行检查。如
需要检查,tcp_callback回调函数将通知libnids它希望接收哪些
数据(如到客户端的数据、到服务器端的数据、到客户端的紧急数
据或到服务器端的紧急数据等),然后返回。
(b) ns->nids_state==NIDS_DATA时,表示ns连接接收到新的数据。
half_stream结构中的缓冲区用于存放这些数据。
(c) nids_state字段为其它数值(NIDS_CLOSE、NIDS_RESET、
NIDS_TIMEOUT)时,表示该连接已经关闭了。tcp_callback函数应
释放相关资源。


四、一个简单的实例

下面的源代码是一个非常简单的程序,它将libnids捕获的所有TCP连接交换的数据输出显示到标准输出设备上。

-----------------------BEGINING OF CODE--------------------------------

#include "nids.h"
#include <string.h>
#include <stdio.h>

extern char * inet_ntoa(unsigned long);

// tuple4结构包含了TCP连接两端的IP地址和端口,以下函数将它们转换为字符串
// 格式,如10.0.0.1,1024, 10.0.0.2,23
char *
adres (struct tuple4 addr)
{
static char buf[256];
strcpy (buf, inet_ntoa (addr.saddr));
sprintf (buf + strlen (buf), ",%i,", addr.source);
strcat (buf, inet_ntoa (addr.daddr));
sprintf (buf + strlen (buf), ",%i", addr.dest);
return buf;
}

void
tcp_callback (struct tcp_stream *a_tcp, void ** this_time_not_needed)
{
char buf[1024];
strcpy (buf, adres (a_tcp->addr)); // we put conn params into buf
if (a_tcp->nids_state == NIDS_JUST_EST)
{
// a_tcp所定义的连接已经建立。此处可视程序需要添加额外
// 的判断处理。如if (a_tcp->addr.dest != 23) return;表
// 示不处理目标端口为23的数据包。
// 本例需要处理(显示)所有数据包,故:
a_tcp->client.collect++; // 需要处理客户端接收的数据
a_tcp->server.collect++; // 和服务器端接收的数据
a_tcp->server.collect_urg++; // 需要处理服务器端接收的紧急数据
#ifdef WE_WANT_URGENT_DATA_RECEIVED_BY_A_CLIENT
a_tcp->client.collect_urg++; // 需要处理客户端接收的紧急数据
// (打开编译选项才有效)
#endif
fprintf (stderr, "%s established\n", buf);
return;
}
if (a_tcp->nids_state == NIDS_CLOSE)
{
// TCP连接正常关闭
fprintf (stderr, "%s closing\n", buf);
return;
}
if (a_tcp->nids_state == NIDS_RESET)
{
// TCP连接因RST数据包而关闭
fprintf (stderr, "%s reset\n", buf);
return;
}

if (a_tcp->nids_state == NIDS_DATA)
{
// 接收到新数据,下面判断决定是否显示

struct half_stream *hlf;

if (a_tcp->server.count_new_urg)
{
// 服务器端接收的紧急数据
strcat(buf,"(urgent->)");
buf[strlen(buf)+1]=0;
buf[strlen(buf)]=a_tcp->server.urgdata;
write(1,buf,strlen(buf));
return;
}

#ifdef WE_WANT_URGENT_DATA_RECEIVED_BY_A_CLIENT
if (a_tcp->client.count_new_urg)
{
// 客户端接收的紧急数据
strcat(buf,"(urgent->)");
buf[strlen(buf)+1]=0;
buf[strlen(buf)]=a_tcp->server.urgdata;
write(1,buf,strlen(buf));
return;
}
#endif

if (a_tcp->client.count_new)
{
// 客户端接收的数据
hlf = &a_tcp->client; // 准备显示客户端接收的数据
strcat (buf, "(<-)"); // 指示数据流方向
}
else
{
hlf = &a_tcp->server; // 准备显示服务器端接收的数据
strcat (buf, "(->)"); // 指示数据流方向
}
fprintf(stderr,"%s",buf); // 首先输出显示连接双方的IP地址、端口
// 和数据流方向

write(2,hlf->data,hlf->count_new); // 输出显示接收到的新数据

}
return ;
}

int
main ()
{
// 此处可自定义libnids的全局变量,如:
// nids_params.n_hosts=256;
if (!nids_init () )
{
fprintf(stderr,"%s\n",nids_errbuf);
exit(1);
}
nids_register_tcp (tcp_callback);
nids_run ();
// NOT REACHED
return 0;
}

---------------------------END OF CODE------------------------------------


五、libnids的数据结构及接口函数

libnids库的所有数据结构及接口函数都在"nids.h"头文件中声明。

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


struct half_stream // TCP连接一端的数据结构
{
char state; // 套接字状态(如TCP_ESTABLISHED)
char collect; // 如果大于0,则保存其数据到缓冲区中,否则忽略
char collect_urg; // 如果大于0,则保存紧急数据,否则忽略
char * data; // 正常数据的缓冲区
unsigned char urgdata; // 紧急数据缓冲区
int count; // 自从连接建立以来保存到"data"缓冲区的数据字节
// 数总和
int offset; // 保存到"data"缓冲区的首字节数据偏移量
int count_new; // 最近一次接收到的数据字节数;如果为0,则无数
// 到达
char count_new_urg; // 如果非0,表示有新的紧急数据到达

... // libnids库使用的辅助字段

};


struct tcp_stream
{
struct tuple4 addr; // TCP连接参数(saddr, daddr, sport, dport)
char nids_state; // TCP连接的逻辑状态
struct half_stream client,server; // 描述客户端与服务器端的数据结构
... // libnids库使用的辅助字段
};

在上面的实例程序中,回调函数tcp_callback输出显示hlf->data缓冲区中的数据到标准输出设备上。这些数据在tcp_callback函数返回后,由libnids自动释放这些数据所占用的内存空间。同时,hlf->offset字段将增加被丢弃数据的字节数,而新接收到的数据则存放到"data"缓冲区的起始处。
如果在其它应用中不进行如上例的操作(例如,数据处理过程至少需要N个字节的输入数据,而libnids只接收到的数据字节数count_new<N),则需要在tcp_callback函数返回前调用如下函数:

void nids_discard(struct tcp_stream * a_tcp, int num_bytes)

此时,当回调函数tcp_callback返回后linids将"data"缓冲区的前num_bytes字节数据,同时计算调整offset字段的数值,并将剩余数据移动到缓冲区的起始处。
如果始终不调用nids_discard()函数(如上面实例),hlf->data缓冲区中将包含hlf->count_new字节数据。通常情况下,在hlf->data缓冲区中的数据字节数等于hlf->count - hlf->offset。
有了nids_discard()函数,程序员就不必拷贝接收到的数据到另外的缓冲区中,hlf->data缓冲区将总是尽可能保存足够的数据。然后,有时会有保留数据包特定数据的需要。例如,我们希望能监测到针对wu-ftpd服务器的"CWD"溢出攻击,就需要跟踪检查ftp客户端发送的"CWD"命令。此时就需要tcp_callback回调函数具有第二个参数了。此参数是某TCP连接私有数据的指针。处理过程如下:

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;
if the connection is uninteresting, return;
a_conn=malloc of some data structure
init of a_conn
*ptr=a_conn // this value will be passed to tcp_callback_2 in future
// calls
increase some of "collect" fields
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连接中使用两种不同的回调函数是允许的。
libnids库定义了一个全局变量结构nids_params,其声明如下:

struct nids_prm
{
int n_tcp_streams; // 存放tcp_stream结构的hash表大小。
// 缺省值:1024
int n_hosts; // 存放IP分片信息的hash表大小
// 缺省值:256
char * device; // libnids监听的接口设备名
// 缺省值 == NULL,即由pcap_lookupdev函数确定
int sk_buff_size; // (Linux内核)sk_buff结构大小
// 缺省值:168
int dev_addon; // sk_buff为网络接口保留的字节数
// 如果dev_addon==-1,则由nids_init函数确定
// 缺省值:-1
void (*syslog)(); // 日志函数指针
int syslog_level; // 如果nids_params.syslog==nids_syslog,则此字段值
// 将确定日志等级loglevel
// 缺省值:LOG_ALERT
int scan_num_hosts;// 存放端口扫描信息的hash表大小。
// 如果为0,则关闭端口扫描监测功能。
// 缺省值:256
int scan_num_ports;// 来自同一IP地址所扫描的TCP端口数上限
// 缺省值:10
int scan_delay; // 在两次端口扫描中的间隔时间上限(毫秒)
// 缺省值:3000
void (*no_mem)(); // 内存不足时被调用,此时应终止当前进程
int (*ip_filter)(struct ip*); // 当接收到一个IP数据包时调用。如返回值
// 非零,则处理该数据包,否则忽略。
// 缺省为(nids_ip_filter)且总返回1
char *pcap_filter; // 传递给pcap过滤器的字符串。
// 缺省值:NULL
} 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()仅作为一个例子。nids_syslog()函数向系统守护服务syslogd发送日志消息。

使用nids_run有一个缺陷:应用程序将完全由数据包驱动(运行)。有时需要在没有数据包到达时也能处理一些任务,则作为nids_run()函数的替代,程序员可使用如下函数:

int nids_next()

此函数将调用pcap_next()函数(而不是pcap_loop()函数)。(详细资料请参阅《网络安全工具开发函数库介绍之二 ——libpcap》。) nids_next()函数成功时返回1,出错时返回0,且nids_errbuf缓冲区存放相应错误消息。
典型地,当使用nids_next()函数时,应用程序调用I/O复用函数select()阻塞,监听套接字fd在“读”描述字集合fd_set中设置。该套接字可通过如下函数获得:

int nids_getfd()

成功时返回一个文件描述字,出错时返回-1,且nids_errbuf缓冲区存放相应错误消息。


---[[ libnids应用实例 ]]----------------------------------

1、nids_next()函数的应用

============================ cut here ============================

/*
This is an example how one can use nids_getfd() and nids_next() functions.
You can replace printall.c's function main with this file.
*/

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int
main ()
{
// here we can alter libnids params, for instance:
// nids_params.n_hosts=256;
int fd;
int time = 0;
fd_set rset;
struct timeval tv;

if (!nids_init ())
{
fprintf(stderr,"%s\n",nids_errbuf);
exit(1);
}
nids_register_tcp (tcp_callback);
fd = nids_getfd ();
for (;;)
{
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO (&rset);
FD_SET (fd, &rset);
// add any other fd we need to take care of
if (select (fd + 1, &rset, 0, 0, &tv))
{
if (FD_ISSET(fd,&rset) // need to test it if there are other
// fd in rset
if (!nids_next ()) break;
}
else
fprintf (stderr, "%i ", time++);
}
return 0;
}

============================ cut here ============================

2、Simple sniffer

============================ cut here ============================

/*
Copyright (c) 1999 Rafal Wojtczuk <nergal@avet.com.pl>. All rights reserved.
See the file COPYING for license details.
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <fcntl.h>
#include "nids.h"

#define LOG_MAX 100
#define SZLACZEK "\n--------------------------------------------------\n"

#define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x))

char *
adres (struct tuple4 addr)
{
static char buf[256];
strcpy (buf, int_ntoa (addr.saddr));
sprintf (buf + strlen (buf), ",%i,", addr.source);
strcat (buf, int_ntoa (addr.daddr));
sprintf (buf + strlen (buf), ",%i : ", addr.dest);
return buf;
}

int logfd;
void
do_log (char *adres_txt, char *data, int ile)
{
write (logfd, adres_txt, strlen (adres_txt));
write (logfd, data, ile);
write (logfd, SZLACZEK, strlen (SZLACZEK));
}

void
sniff_callback (struct tcp_stream *a_tcp, void **this_time_not_needed)
{
int dest;
if (a_tcp->nids_state == NIDS_JUST_EST)
{
dest = a_tcp->addr.dest;
if (dest == 21 || dest == 23 || dest == 110 || dest == 143 || dest == 513)
a_tcp->server.collect++;
return;
}
if (a_tcp->nids_state != NIDS_DATA)
{
// seems the stream is closing, log as much as possible
do_log (adres (a_tcp->addr), a_tcp->server.data,
a_tcp->server.count - a_tcp->server.offset);
return;
}
if (a_tcp->server.count - a_tcp->server.offset < LOG_MAX)
{
// we haven't got enough data yet; keep all of it
nids_discard (a_tcp, 0);
return;
}

// enough data
do_log (adres (a_tcp->addr), a_tcp->server.data, LOG_MAX);

// Now procedure sniff_callback doesn't want to see this stream anymore.
// So, we decrease all the "collect" fields we have previously increased.
// If there were other callbacks following a_tcp stream, they would still
// receive data
a_tcp->server.collect--;
}


int
main ()
{
logfd = open ("./logfile", O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (logfd < 0)
{
perror ("opening ./logfile:");
exit (1);
}
if (!nids_init ())
{
fprintf (stderr, "%s\n", nids_errbuf);
exit (1);
}
nids_register_tcp (sniff_callback);
nids_run ();
return 0;
}

============================ cut here ============================

3、Wu-FTPd overflow attack detector

============================ cut here ============================

/*
Copyright (c) 1999 Rafal Wojtczuk <nergal@avet.com.pl>. All rights reserved.
See the file COPYING for license details.
*/

/*
This code attempts to detect attack against imapd (AUTHENTICATE hole) and
wuftpd (creation of deep directory). This code is to ilustrate use of libnids;
in order to improve readability, some simplifications were made, which enables
an attacker to bypass this code (note, the below routines should be improved,
not libnids)
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "nids.h"

#define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x))

char *
adres (struct tuple4 addr)
{
static char buf[256];
strcpy (buf, int_ntoa (addr.saddr));
sprintf (buf + strlen (buf), ",%i,", addr.source);
strcat (buf, int_ntoa (addr.daddr));
sprintf (buf + strlen (buf), ",%i", addr.dest);
return buf;
}


/*
if we find a pattern AUTHENTICATE {an_int} in data stream sent to an imap
server, where an_int >1024, it means an buffer overflow attempt. We kill the
connection.
*/

#define PATTERN "AUTHENTICATE {"
#define PATLEN strlen(PATTERN)
void
detect_imap (struct tcp_stream *a_tcp)
{
char numbuf[30];
int i, j, datalen, numberlen;
struct half_stream *hlf;
if (a_tcp->nids_state == NIDS_JUST_EST)
{
if (a_tcp->addr.dest == 143)
{
a_tcp->server.collect++;
return;
}
else
return;
}
if (a_tcp->nids_state != NIDS_DATA)
return;
hlf = &a_tcp->server;
datalen = hlf->count - hlf->offset;
if (datalen < PATLEN)
{
// we have too small amount of data to work on. Keep all data in buffer.
nids_discard (a_tcp, 0);
return;
}
for (i = 0; i <= datalen - PATLEN; i++)
if (!memcmp (PATTERN, hlf->data + i, PATLEN)) //searching for a pattern
break;
if (i > datalen - PATLEN)
{
// retain PATLEN bytes in buffer
nids_discard (a_tcp, datalen - PATLEN);
return;
}
for (j = i + PATLEN; j < datalen; j++) // searching for a closing '}'
if (*(hlf->data + j) == '}')
break;
if (j > datalen)
{
if (datalen > 20)
{
//number too long, perhaps we should log it, too
}
return;
}
numberlen = j - i - PATLEN;
memcpy (numbuf, hlf->data + i + PATLEN, numberlen); //numbuf contains
// AUTH argument
numbuf[numberlen] = 0;
if (atoi (numbuf) > 1024)
{
// notify admin
syslog(nids_params.syslog_level,
"Imapd exploit attempt, connection %s\n",adres(a_tcp->addr));
// kill the connection
nids_killtcp (a_tcp);
}
nids_discard (a_tcp, datalen - PATLEN);
return;
}

// auxiliary structure, needed to keep current dir of ftpd daemon
struct supp
{
char *currdir;
int last_newline;
};

// the below function adds "elem" string to "path" string, taking care of
// ".." and multiple '/'. If the resulting path is longer than 768,
// return value is 1, otherwise 0
int
add_to_path (char *path, char *elem, int len)
{
int plen;
char * ptr;
if (len > 768)
return 1;
if (len == 2 && elem[0] == '.' && elem[1] == '.')
{
ptr = rindex (path, '/');
if (ptr != path)
*ptr = 0;
}
else if (len > 0)
{
plen = strlen (path);
if (plen + len + 1 > 768)
return 1;
if (plen==1)
{
strncpy(path+1,elem,len);
path[1+len]=0;
}
else
{
path[plen] = '/';
strncpy (path + plen + 1, elem, len);
path[plen + 1 + len] = 0;
}
}
return 0;
}

void
do_detect_ftp (struct tcp_stream *a_tcp, struct supp **param_ptr)
{
struct supp *p = *param_ptr;
int index = p->last_newline + 1;
char *buf = a_tcp->server.data;
int offset = a_tcp->server.offset;
int n_bytes = a_tcp->server.count - offset;
int path_index, pi2, index2, remcaret;
for (;;)
{
index2 = index;
while (index2 - offset < n_bytes && buf[index2 - offset] != '\n')
index2++;
if (index2 - offset >= n_bytes)
break;
if (!strncasecmp (buf + index - offset, "cwd ", 4))
{
path_index = index + 4;
if (buf[path_index - offset] == '/')
{
strcpy (p->currdir, "/");
path_index++;
}
for (;;)
{
pi2 = path_index;
while (buf[pi2 - offset] != '\n' && buf[pi2 - offset] != '/')
pi2++;
if (buf[pi2-offset]=='\n' && buf[pi2-offset-1]=='\r')
remcaret=1;
else remcaret=0;
if (add_to_path (p->currdir, buf + path_index-offset, pi2 - path_index-remcaret))
{
// notify admin
syslog(nids_params.syslog_level,
"Ftpd exploit attempt, connection %s\n",adres(a_tcp->addr));
nids_killtcp (a_tcp);
return;
}
if (buf[pi2 - offset] == '\n')
break;
path_index = pi2 + 1;
}
}
index = index2 + 1;
}
p->last_newline = index - 1;
nids_discard (a_tcp, index - offset);
}

void
detect_ftpd (struct tcp_stream *a_tcp, struct supp **param)
{
if (a_tcp->nids_state == NIDS_JUST_EST)
{
if (a_tcp->addr.dest == 21)
{
struct supp *one_for_conn;
a_tcp->server.collect++;
one_for_conn = (struct supp *) malloc (sizeof (struct supp));
one_for_conn->currdir = malloc (1024);
strcpy (one_for_conn->currdir, "/");
one_for_conn->last_newline = 0;
*param=one_for_conn;
}
return;
}
if (a_tcp->nids_state != NIDS_DATA)
{
free ((*param)->currdir);
free (*param);
return;
}
do_detect_ftp (a_tcp, param);
}

int
main ()
{
if (!nids_init ())
{
fprintf(stderr,"%s\n",nids_errbuf);
exit(1);
}
nids_register_tcp (detect_imap);
nids_register_tcp (detect_ftpd);
nids_run ();
return 0;
}

============================ cut here ============================

<<< 待续 >>>

libpcap使用举例

作者:小四 < mailto: scz@nsfocus.com >
主页:http://www.nsf

评论

相关推荐

    java古董级工具 构建工具 字节码操作 集群管理 代码分析 编译器生成工具 外部配置工具 约束满足问题求解程序 持续集成 CS

    文档处理工具 函数式编程 游戏开发 GUI 高性能计算 IDE 图像处理 IDE 日志 机器学习 消息传递 杂项 应用监控工具 原生开发库 网络 ORM PDF 性能分析 响应式开发库 REST框架 科学计算与分析 搜索引擎 安全 序列化 ...

    基于Qt5框架开发的轻量级跨平台网络工具库 .rar

    当时,本硕教学、项目开发采用的均为Visual C++ ,按照导师的要求,学生们的所有算法模块需要封装为动态链接库,以便可以很快的嵌入到新的项目中使用。作为对计算机稍微熟悉一点的助教,我和几个同事一起规范了函数...

    精通WindowsAPI 函数 接口 编程实例

    6.5.5 声明导出函数、创建lib库,为其他模块提供导入表调用接口 190 6.5.6 通过构建导入表调用DLL导出函数 191 第7章 线程同步 192 7.1 基本原理 192 7.1.1 线程同步的过程 193 7.1.2 同步对象 193 ...

    c#网络抓包例程

    WinPcap是用于网络封包抓取的一套工具,可适用于32/64位的操作平台上解析网络封包,包含了核心的封包过滤,一个底层动态链接库,和一个高层系统函数库,及可用来直接存取封包的应用程序界面。 Winpcap提供了一个强大...

    python项目简易SNMP的管理站工具.zip

    5. **技术优势**:结合Python语言的简洁性和强大的库生态,PySNMP使得网络管理工具的开发变得相对简单。此外,由于SNMP广泛被网络设备支持,因此这类工具可以与多数网络硬件兼容。 6. **安全性考虑**:虽然SNMPv1和...

    php网络开发完全手册

    1.6 几种开发工具的介绍 20 1.6.1 Vi及Vim 20 1.6.2 Eclipse+PHPEclipse插件 21 1.6.3 UltraEdit 21 1.6.4 EditPlus 22 1.6.5 支持PHP的IDE环境 22 1.7 小结 23 第2章 PHP的基础语法 24 2.1 语言构成与工作原理 24 ...

    EXCEL集成工具箱V6.0

    安全工具 【工作表解密】 可以破解工作表密码和工作簿密码。在不知道密码的前提下找回密码。 【VBA工程解密】 将有密码的VBA工程代码破解,直接打开即可修改代码。相当于VBA工程免密查看。 【隐藏与显示磁盘】 ...

    Visual C++2010开发权威指南(共三部分).part1.rar

    第10章 Visual C++ 2010 MFC动态函数链接库 434 10.1 动态函数链接库简介 434 10.1.1 什么是动态函数链接库 434 10.1.2 动态函数链接库的优点 435 10.1.3 动态函数链接库的起源 436 10.1.4 动态函数链接库的原理 436...

    VB开发经验技巧宝典

    0005 在开发环境中如何设置窗体的网络 0006 如何定制有效的工具栏 0007 如何定制编程环境 0008 如何合理布局窗体 0009 如何锁定窗体上的控件 0010 如何调整与对齐控件 0011 如何利用代码对齐控件 0012...

    宏达数据库信息管理开发平台

    十一、 记录计算与验证设置中增加如下函数: $0^:数字字段转货币大写(不包括"元整"后缀) @^NULLCURUSER():如果字段为空,赋值当前用户名 @^FIELDEXP() :根据当前记录指定字段的值设定的公式...

    Windows内核安全与驱动开发光盘源码

    2.6.2 函数的多线程安全性 30 2.6.3 代码的中断级 32 2.6.4 WDK中出现的特殊代码 32 第3章 字符串与链表 35 3.1 字符串操作 35 3.1.1 使用字符串结构 35 3.1.2 字符串的初始化 36 3.1.3 字符串的拷贝 37 ...

    delphi开发技术大全

    多媒体技术,文件操作技术,系统控制技术,ADO数据库开发技术,常用数据组件,添加、删除和修改数据,浏览与查询数据,图表组件的设计,多层数据库程序,报表与打印程序设计,数据库安全技术,网络编程,Internet...

    嵌入式设计及linux驱动开发指南——基于ARM9处理器.pdf

    10.5.1 安装Qt/Embedded工具开发包 10.5.2 交叉编译Qt/Embedded库 10.5.3 Hello,World 10.5.4 发布Qt/Embedded程序到目标板 10.5.5 添加一个Qt/Embedded应用到QPE 第11章 Java虚拟机的移植 11.1 Java虚拟机...

    精通Windows.API-函数、接口、编程实例.pdf

    6.5.5 声明导出函数、创建lib库,为其他模块提供导入表调用接口 190 6.5.6 通过构建导入表调用DLL导出函数 191 第7章 线程同步 192 7.1 基本原理 192 7.1.1 线程同步的过程 193 7.1.2 同步对象 193 ...

    EXCEL集成工具箱V8.0完整增强版(精简)

    安全工具 【工作表解密】 可以破解工作表密码和工作簿密码。在不知道密码的前提下找回密码。 【VBA工程解密】 将有密码的VBA工程代码破解,直接打开即可修改代码。相当于VBA工程免密查看。 【隐藏与显示磁盘】 ...

    winpcap驱动及开发包

    安全工具 (security tools) 什么是WinPcap做不到的 WinPcap能 独立地 通过主机协议发送和接受数据,如同TCP-IP。这就意味着WinPcap不能阻止、过滤或操纵同一机器上的其他应用程序的通讯:它仅仅能简单地"监视"在...

    C语言网络监控软件开发包( WinPcap完整版)

    WinPcap是用于网络封包抓取的一套工具,可适用于32位的操作平台上解析网络封包,包含了核心的封包过滤,一个底层动态链接库,和一个高层系统函数库,及可用来直接存取封包的应用程序界面。  Winpcap是一个免费公开...

Global site tag (gtag.js) - Google Analytics