📄 544.htm
字号:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>CTerm非常精华下载</title>
</head>
<body bgcolor="#FFFFFF">
<table border="0" width="100%" cellspacing="0" cellpadding="0" height="577">
<tr><td width="32%" rowspan="3" height="123"><img src="DDl_back.jpg" width="300" height="129" alt="DDl_back.jpg"></td><td width="30%" background="DDl_back2.jpg" height="35"><p align="center"><a href="http://apue.dhs.org"><font face="黑体"><big><big>apue</big></big></font></a></td></tr>
<tr>
<td width="68%" background="DDl_back2.jpg" height="44"><big><big><font face="黑体"><p align="center"> ● UNIX网络编程 (BM: clown) </font></big></big></td></tr>
<tr>
<td width="68%" height="44" bgcolor="#000000"><font face="黑体"><big><big><p align="center"></big></big><a href="http://cterm.163.net"><img src="banner.gif" width="400" height="60" alt="banner.gif"border="0"></a></font></td>
</tr>
<tr><td width="100%" colspan="2" height="100" align="center" valign="top"><br><p align="center">[<a href="index.htm">回到开始</a>][<a href="516.htm">上一层</a>][<a href="545.htm">下一篇</a>]
<hr><p align="left"><small>发信人: scz (小四), 信区: Security <br>
标 题: libpcap使用举例(1) <br>
发信站: 武汉白云黄鹤站 (Mon Jan 15 20:26:32 2001), 站内信件 <br>
作者:小四 < mailto: scz@nsfocus.com > <br>
主页:http://www.nsfocus.com <br>
日期:2000-12-14 20:10 <br>
我们曾经提供过<<libnet使用举例(1-12)>>,比较详细地介绍了报文发送编程。始终 <br>
没有介绍libpcap报文捕捉编程的原因很多,tcpdump、snort等著名软件包都是基于 <br>
libpcap,加上W.Richard.Stevens的<<Unix Network Programming Vol I>>第26章推 <br>
波助澜,实在觉得没有必要继续介绍libpcap编程。更真实的原因可能是BPF、DLPI、 <br>
SOCK_PACKET三种接口编程已经被演练得太多太滥。 <br>
今天讨论的不是效率,而是可能的移植性要求。没办法,第一次使用libpcap库,举 <br>
例能深入到什么地步,不知道。如果你也是 一次用这个库,跟我来,第N次使用? <br>
那还是忙你的去吧,别来看这篇无聊的灌水,:-P。我无聊是因为有程序要广泛可移 <br>
植,你无聊是为什么。 <br>
char * pcap_lookupdev ( char * errbuf ); <br>
该函数返回一个网络设备接口名,类似libnet_select_device(),对于Linux就是 <br>
"eth0"一类的名字。pcap_open_live()、pcap_lookupnet()等函数将用到这个网络设 <br>
备接口名。失败时返回NULL,errbuf包含了失败原因。errbuf一般定义如下: <br>
/usr/include/pcap.h <br>
#define PCAP_ERRBUF_SIZE 256 <br>
char errbuf[ PCAP_ERRBUF_SIZE ]; <br>
pcap_t * pcap_open_live ( char * devce, int snaplen, int promisc, <br>
int to_ms, char * errbuf ); <br>
该函数用于获取一个抽象的包捕捉句柄,后续很多libpcap函数将使用该句柄,类似 <br>
文件操作函数频繁使用文件句柄。device指定网络接口设备名,比如"eth0。snaplen <br>
指定单包最大捕捉字节数,为了保证包捕捉不至于太低效率,snaplen尽量适中,以 <br>
恰能获得所需协议层数据为准。promisc指定网络接口是否进入混杂模式,注意即使 <br>
该参数为false(0),网络接口仍然有可能因为其他原因处在混杂模式。to_ms指定毫 <br>
秒级读超时,man手册上并没有指明什么值意味着永不超时,测试下来的结论,0可能 <br>
代表永不超时。 <br>
{ <br>
2000-12-18 11:40 <br>
刚才用Source Insight跟踪源代码,确认0代表不设置超时机制,而且只有BPF、DLPI <br>
支持超时机制,SOCK_PACKET本身不支持超时机制,所以对于Linux平台,to_ms为多 <br>
少都无所谓 <br>
} <br>
如果调用失败返回NULL,errbuf包含失败原因。 <br>
-------------------------------------------------------------------------- <br>
/usr/include/pcap.h <br>
typedef struct pcap pcap_t; <br>
pcap-int.h里定义了struct pcap {} <br>
struct pcap <br>
{ <br>
int fd; <br>
int snapshot; <br>
int linktype; <br>
int tzoff; /* timezone offset <br>
*/ <br>
int offset; /* offset for proper alignment <br>
*/ <br>
struct pcap_sf sf; <br>
struct pcap_md md; <br>
int bufsize; /* Read buffer <br>
*/ <br>
u_char * buffer; <br>
u_char * bp; <br>
int cc; <br>
u_char * pkt; * Place holder for pcap_next() <br>
*/ <br>
struct bpf_program fcode; /* Placeholder for filter code if bpf not i <br>
n ke <br>
rnel. */ <br>
char errbuf[PCAP_ERRBUF_SIZE]; <br>
}; <br>
-------------------------------------------------------------------------- <br>
int pcap_lookupnet ( char * device, bpf_u_int32 * netp, <br>
bpf_u_int32 * maskp, char * errbuf ); <br>
该函数用于获取指定网络接口的IP地址、子网掩码。不要被netp的名字所迷惑,它对 <br>
应的就是IP地址,maskp对应子网掩码。 <br>
/usr/include/pcap.h <br>
typedef u_int bpf_u_int32; <br>
显然简单理解成32-bit即可。如果调用失败则返回-1,errbuf包含失败原因。 <br>
int pcap_compile ( pcap_t * p, struct bpf_program * fp, char * str, <br>
int optimize, bpf_u_int32 netmask ); <br>
该函数用于解析过滤规则串,填写bpf_program结构。str指向过滤规则串,格式参看 <br>
tcpdump的man手册,比如: <br>
tcpdump -x -vv -n -t ip proto \\tcp and dst 192.168.8.90 and tcp[13] \& 2 = <br>
2 <br>
这条过滤规则将捕捉所有携带SYN标志的到192.168.8.90腡CP报文。过滤规则串可以 <br>
是空串(""),表示抓取所有过路的报文。 <br>
optimize为1表示对过滤规则进行优化处理。netmask指定子网掩码,一般从 <br>
pcap_lookupnet()调用中获取。返回值小于零表示调用失败。 <br>
这个函数可能比较难于理解,涉及的概念源自BPF,Linux系统没有这种概念,但是 <br>
libpcap采用pcap_compile()和pcap_setfilter()结合的办法屏蔽了各种链路层支持 <br>
的不同,无论是SOCK_PACKET、DLPI。曾在华中Security版上写过一篇 <br>
<<内核包捕获过滤机制介绍>>,参看该文加强理解。 <br>
------------------------------------------------------------------------- <br>
# tcpdump -d ip proto \\tcp and dst 192.168.8.90 and tcp[13] \& 2 = 2 <br>
(000) ldh [-4096] <br>
(001) jeq #0x800 jt 2 jf 13 <br>
(002) ldb [9] <br>
(003) jeq #0x6 jt 4 jf 13 <br>
(004) ld [16] <br>
(005) jeq #0xc0a8085a jt 6 jf 13 <br>
(006) ldh [6] <br>
(007) jset #0x1fff jt 13 jf 8 <br>
(008) ldxb 4*([0]&0xf) <br>
(009) ldb [x + 13] <br>
(010) and #0x2 <br>
(011) jeq #0x2 jt 12 jf 13 <br>
(012) ret #65535 <br>
(013) ret #0 <br>
# <br>
/usr/include/net/bpf.h <br>
/* Structure for BIOCSETF. */ <br>
struct bpf_program <br>
{ <br>
u_int bf_len; <br>
struct bpf_insn * bf_insns; <br>
}; <br>
}; <br>
/* <br>
* The instruction data structure. <br>
*/ <br>
struct bpf_insn <br>
{ <br>
u_short code; <br>
u_char jt; <br>
u_char jf; <br>
bpf_int32 k; <br>
}; <br>
/* <br>
* Macros for insn array initializers. <br>
*/ <br>
#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k } <br>
#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k } <br>
------------------------------------------------------------------------- <br>
int pcap_setfilter ( pcap_t * p, struct bpf_program * fp ); <br>
该函数用于设置pcap_compile()解析完毕的过滤规则,如果你足够聪明(愚公?),完 <br>
全可以自己提供过滤规则,无须pcap_compile()介入,就象你写 <br>
Password Sniffer For I386/FreeBSD时常做的那样。成功返回0,失败返回-1。 <br>
int pcap_dispatch ( pcap_t * p, int cnt, pcap_handler callback, u_char * use <br>
r ); <br>
r ); <br>
该函数用于捕捉报文、分发报文到预先指定好的处理函数(回调函数)。 <br>
pcap_dispatch()接收够cnt个报文便返回,如果cnt为-1 味着所有报文集中在一 <br>
缓冲 <br>
区中。如果cnt为0,仅当发生错误、读取到EOF或者读超时到了(pcap_open_live <br>
中指定)才停止捕捉报文并返回。callback指定如下类型的回调函数,用于处理 <br>
pcap_dispatch()所捕获的报文: <br>
typedef void ( *pcap_handler ) ( u_char *, const struct pcap_pkthdr *, const <br>
u_c <br>
har * ); <br>
pcap_dispatch()返回捕捉到的报文个数,如果在读取静态文件(以前包捕捉过程中存 <br>
储下来的)时碰到EOF则返回0。返回-1表示发生错误,此时可以用pcap_perror()、 <br>
pcap_geterr()显示错误信息。 <br>
下面来看看那个回调函数,总共有三个参数,第一个形参来自pcap_dispatch()的第 <br>
四个形参,一般我们自己的包捕捉程序不需要提供它,总是为NULL。第二个形参指向 <br>
pcap_pkthdr结构,该结构位于真正的物理帧前面,用于消除不同链路层支持的差异。 <br>
最后的形参指向所捕获报文的物理帧。 <br>
-------------------------------------------------------------------------- <br>
/usr/include/pcap.h <br>
/* <br>
* Each packet in the dump file is prepended with this generic header. <br>
* This gets around the problem of different header for different <br>
* packet interfaces. <br>
*/ <br>
struct pcap_pkthdr <br>
{ <br>
struct timeval ts; /* time stamp */ <br>
bpf_u_int32 caplen; /* length of portion present */ <br>
bpf_u_int32 len; /* length this packet (off wire) */ <br>
}; <br>
/usr/include/net/bpf.h <br>
/* <br>
* Structure prepended to each packet. <br>
*/ <br>
struct bpf_hdr <br>
{ <br>
struct timeval bh_tstamp; /* time stamp */ <br>
bpf_u_int32 bh_caplen; /* length of captured portion */ <br>
bpf_u_int32 bh_datalen; /* oiginal length of packet */ <br>
u_short bh_hdrlen; /* length of bpf header (this struct <br>
plus alignment padding) */ <br>
}; <br>
/* <br>
* Because the structure above is not a multiple of 4 bytes, some compilers <br>
* will insist on inserting padding; hence, sizeof(struct bpf_hdr) won't wor <br>
k. <br>
* Only the kernel needs to know about it; applications use bh_hdrlen. <br>
*/ <br>
#ifdef KERNEL <br>
#define SIZEOF_BPF_HDR 18 <br>
#endif <br>
-------------------------------------------------------------------------- <br>
void pcap_close ( pcap_t * p ); <br>
该函数用于关闭pcap_open_live()获取的包捕捉句柄,释放相关资源。 <br>
void pcap_perror ( pcap_t * p, char * prefix ); <br>
第一形参来自pcap_open_live(),第二行参的作用类似perror()的形参,指定错误信 <br>
息的前缀,与perror()一样,结尾自动输出一个换行。 <br>
pcap_perror( p, "pcap_compile" )的输出类似这个效果: <br>
pcap_compile: unknown ip proto ... <br>
pcap_perror并不自动exit(),与perror()一样,如果需要,应该显式调用ext()。 <br>
介绍到这里,已经可以写简单的sniffer。出于完整演示目的,提供这样一个sample <br>
code。请勿询问任何关于该代码的问题,烦了。 <br>
-------------------------------------------------------------------------- <br>
/* <br>
* File : sniffer program for I386/Linux using libpcap <br>
* Version: 0.01 aleph <br>
* Author : Anonymous ( Don't ask anything about this program, please. ) <br>
* Complie: gcc -O3 -o pcap pcap_sniffer.c -lpcap `libnet-config --defines -- <br>
cfla <br>
gs` -Wall <br>
* : strip pcap <br>
* Usage : ./pcap -h <br>
* Date : 2000-12-15 16:35 <br>
*/ <br>
/******************************************************************* <br>
* * <br>
* Head File * <br>
* * <br>
*******************************************************************/ <br>
#include <stdio.h> <br>
#include <stdlib.h> <br>
#include <string.h> <br>
#include <sys/types.h> <br>
#include <pcap.h> <br>
#include <libnet.h> /* for LIBNET_TCP_H */ <br>
/******************************************************************* <br>
* * <br>
* Macro * <br>
* * <br>
*******************************************************************/ <br>
#define SUCCESS 0 <br>
#define FAILURE -1 <br>
typedef void Sigfunc ( int ); /* for signal handlers */ <br>
/******************************************************************* <br>
* * <br>
* Static Global Var * <br>
* * <br>
*******************************************************************/ <br>
static pcap_t * pcap_fd = NULL; /* 抽象的包捕捉句柄 */ <br>
/******************************************************************* <br>
* * <br>
* Function Prototype * <br>
* * <br>
*******************************************************************/ <br>
static void Atexit ( void ( *func ) ( void ) ); <br>
static void bpf_dump ( struct bpf_program * p, int option ); <br>
char * bpf_image ( struct bpf_insn * p, int n ); <br>
static void outputBinary ( const u_char * byteArray, const size_t byte <br>
Arra <br>
yLen ); <br>
static void pcap_callback ( u_char * none, const struct pcap_pkthdr * p <br>
cap_ <br>
head, const u_char * packet ); <br>
static pcap_t * pcap_init ( char * dev, char * filter, int snaplen, int <br>
tim <br>
eout, int dumplevel ); <br>
static void pcap_read ( pcap_t * p ); <br>
static vod sig_end ( int signo ); <br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -