libnetlibpcap包传输实践.docx
- 文档编号:11609489
- 上传时间:2023-03-28
- 格式:DOCX
- 页数:18
- 大小:24.99KB
libnetlibpcap包传输实践.docx
《libnetlibpcap包传输实践.docx》由会员分享,可在线阅读,更多相关《libnetlibpcap包传输实践.docx(18页珍藏版)》请在冰豆网上搜索。
libnetlibpcap包传输实践
需求
对一个特定的文件进行分片发送,构造数据包,发送数据包,接收数据包并提取有效数据,对数据组合还原为原文件。
设计
当前,基于socket的网络编程已成为当今不可替代的编程方法,它将网络通讯当作文件描述符进行处理,把对这个“网络文件”(即socket套接字)的操作抽象成一种类似于文件操作的方式进行。
从实现细节上,这种工作方式根据TCP/IP的网络通讯模型,封装了一系列的实现,使得我们只需要使用一个指定的参数,就可以实现在基于所需协议的数据的发送和接收。
但是,如果我们对那些系统自动给我们做的工作感兴趣,希望与发送的数据作“面对面”的接触,根据需求,传统的socket只能实现传输层之上的数据传输,不能自己构造数据包,而且这里需要更底层的操作,比如网络层和数据链路层,故不可用。
方案一:
Libnet+libpcap
这个方案基于C语言实现,对于一个特定的文件,得到其HEX数据,根据需求分成若干个部分,前面每个部分大小一致,剩余作为最后一个部分,将这个数据分别利用libnet组包发送包,利用libpcap抓包解析包,得到后取其有效数据,也就是HEX数据,对其组装,得到最后的文件,整个过程结束。
libnet
libnet是UNIX系统同台上网络安全工具开发的重要的库,它和libpcap、libnids一起,给网络安全工具的开发人员提供了一组丰富而且完全的武器,使之得以很方便地编写出结构化强、健壮性好、可移植性高等特点的程序。
libnet提供一系列的接口函数,实现和封装了数据包的构造和发送过程。
利用它可以亲自构造从应用层到链路层的各层协议的数据包头,并将这些包头与有效数据有序地组合在一起发送出去。
当然,它也是基于tcp/ip协议族模型的。
利用libnet函数库开发应用程序的基本步骤非常简单:
1、数据包内存初始化;
2、构造数据包;
3、发送数据;
4、释放资源;
libnet提供的接口函数按其作用可分为四类:
*内存管理(分配和释放)函数
*地址解析函数
*数据包构造函数
*数据包发送函数
接口函数及其功能
以下分别列出这些接口函数及其功能
内存管理函数
单数据包内存初始化:
intlibnet_init_packet(u_shortpacket_size,u_char**buf);
单数据包内存释放:
voidlibnet_destroy_packet(u_char**buf);
多数据包内存初始化:
intlibnet_init_packet_arena(structlibnet_arena**arena,
u_shortpacket_num,u_shortpacket_size);
访问多数据包内存中的下一个数据包:
u_char*libnet_next_packet_from_arena(structlibnet_arena**arena,
u_shortpacket_size);
多数据包内存释放:
voidlibnet_destroy_packet_arena(structlibnet_arena**arena);
地址解析函数
解析主机名:
u_char*libnet_host_lookup(u_longip,u_shortuse_name);
解析主机名(可重入函数):
voidlibnet_host_lookup_r(u_longip,u_shortuse_name,u_char*buf);
域名解析:
u_longlibnet_name_resolve(u_char*ip,u_shortuse_name);
获取接口设备IP地址:
u_longlibnet_get_ipaddr(structlibnet_link_int*l,
constu_char*device,constu_char*ebuf);
获取接口设备硬件地址:
structether_addr*libnet_get_hwaddr(structlibnet_link_int*l,
constu_char*device,
constu_char*ebuf);
数据包构造函数(数据包类型多样,这里太多,列几个主要的)
ARP协议数据包:
intlibnet_build_arp(u_shorthrdw,u_shortprot,u_shorth_len,
u_shortp_len,u_shortop,u_char*s_ha,
u_char*s_pa,u_char*t_ha,u_char*t_pa,
constu_char*payload,intpayload_len,
u_char*packet_buf);
以太网协议数据包:
intlibnet_build_ethernet(u_char*daddr,u_char*saddr,u_shortid,
constu_char*payload,intpayload_len,
u_char*packet_buf);
IP协议数据包:
intlibnet_build_ip(u_shortlen,u_chartos,u_shortip_id,u_shortfrag,
u_charttl,u_charprotocol,u_longsaddr,
u_longdaddr,constu_char*payload,intpayload_len,
u_char*packet_buf);
TCP协议数据包:
intlibnet_build_tcp(u_shortth_sport,u_shortth_dport,u_longth_seq,
u_longth_ack,u_charth_flags,u_shortth_win,
u_shortth_urg,constu_char*payload,
intpayload_len,u_char*packet_buf);
UDP协议数据包:
intlibnet_build_udp(u_shortsport,u_shortdport,constu_char*payload,
intpayload_len,u_char*packet_buf);
IP协议数据包选项:
intlibnet_insert_ipo(structipoption*opt,u_charopt_len,
u_char*packet_buf);
TCP协议数据包选项:
intlibnet_insert_tcpo(structtcpoption*opt,u_charopt_len,
u_char*packet_buf);
数据包发送函数
打开rawsocket:
intlibnet_open_raw_sock(intprotocol);
关闭rawsocket:
intlibnet_close_raw_sock(intsocket);
选择接口设备:
intlibnet_select_device(structsockaddr_in*sin,
u_char**device,u_char*ebuf);
打开链路层接口设备:
structlibnet_link_int*libnet_open_link_interface(char*device,
char*ebuf);
关闭链路层接口设备:
intlibnet_close_link_interface(structlibnet_link_int*l);
发送IP数据包:
intlibnet_write_ip(intsocket,u_char*packet,intpacket_size);
发送链路层数据包:
intlibnet_write_link_layer(structlibnet_link_int*l,
constu_char*device,u_char*packet,
intpacket_size);
检验和计算:
intlibnet_do_checksum(u_char*packet,intprotocol,intpacket_size);
相关的支持函数
随机数种子生成器:
intlibnet_seed_prand();
获取随机数:
u_longlibnet_get_prand(intmodulus);
16进制数据输出:
voidlibnet_hex_dump(u_char*buf,intlen,intswap,FILE*stream);
端口列表链初始化:
intlibnet_plist_chain_new(structlibnet_plist_chain**plist,
char*token_list);
获取端口列表链的下一项(端口范围):
intlibnet_plist_chain_next_pair(structlibnet_plist_chain*plist,
u_short*bport,u_short*eport);
端口列表链输出显示:
intlibnet_plist_chain_dump(structlibnet_plist_chain*plist);
获取端口列表链:
u_char*libnet_plist_chain_dump_string(structlibnet_plist_chain*plist);
端口列表链内存释放:
voidlibnet_plist_chain_free(structlibnet_plist_chain*plist);
libpcap
libpcap是一个网络数据包捕获函数库,功能非常强大,Linux下著名的tcpdump就是以它为基础的。
如何使用libpcap:
首先要使用libpcap,我们必须包含pcap.h头文件,可以在/usr/local/include/pcap/pcap.h找到,其中包含了每个类型定义的详细说明。
1.获取网络接口
首先我们需要获取监听的网络接口:
我们可以手动指定或让libpcap自动选择,先介绍如何让libpcap自动选择:
char*pcap_lookupdev(char*errbuf)
上面这个函数返回第一个合适的网络接口的字符串指针,如果出错,则errbuf存放出错信息字符串,errbuf至少应该是PCAP_ERRBUF_SIZE个字节长度的。
注意,很多libpcap函数都有这个参数。
pcap_lookupdev()一般可以在跨平台的,且各个平台上的网络接口名称都不相同的情况下使用。
如果我们手动指定要监听的网络接口,则这一步跳过,我们在第二步中将要监听的网络接口字符串硬编码在pcap_open_live里。
2.释放网络接口
在操作为网络接口后,我们应该要释放它:
voidpcap_close(pcap_t*p)
该函数用于关闭pcap_open_live()获取的pcap_t的网络接口对象并释放相关资源。
3.打开网络接口
获取网络接口后,我们需要打开它:
pcap_t*pcap_open_live(constchar*device,intsnaplen,intpromisc,intto_ms,char*errbuf)
上面这个函数会返回指定接口的pcap_t类型指针,后面的所有操作都要使用这个指针。
第一个参数是第一步获取的网络接口字符串,可以直接使用硬编码。
第二个参数是对于每个数据包,从开头要抓多少个字节,我们可以设置这个值来只抓每个数据包的头部,而不关心具体的内容。
典型的以太网帧长度是1518字节,但其他的某些协议的数据包会更长一点,但任何一个协议的一个数据包长度都必然小于65535个字节。
第三个参数指定是否打开混杂模式(PromiscuousMode),0表示非混杂模式,任何其他值表示混合模式。
如果要打开混杂模式,那么网卡必须也要打开混杂模式,可以使用如下的命令打开eth0混杂模式:
ifconfigeth0promisc
第四个参数指定需要等待的毫秒数,超过这个数值后,第3步获取数据包的这几个函数就会立即返回。
0表示一直等待直到有数据包到来。
第五个参数是存放出错信息的数组。
4.获取数据包
打开网络接口后就已经开始监听了,那如何知道收到了数据包呢?
有下面3种方法:
a)
u_char*pcap_next(pcap_t*p,structpcap_pkthdr*h)
如果返回值为NULL,表示没有抓到包
第一个参数是第2步返回的pcap_t类型的指针
第二个参数是保存收到的第一个数据包的pcap_pkthdr类型的指针
pcap_pkthdr类型的定义如下:
[cpp]viewplaincopy
structpcap_pkthdr
{
structtimevalts;/*timestamp*/
bpf_u_int32caplen;/*lengthofportionpresent*/
bpf_u_int32len;/*lengththispacket(offwire)*/
};
注意这个函数只要收到一个数据包后就会立即返回.
b)
intpcap_loop(pcap_t*p,intcnt,pcap_handlercallback,u_char*user)
第一个参数是第2步返回的pcap_t类型的指针
第二个参数是需要抓的数据包的个数,一旦抓到了cnt个数据包,pcap_loop立即返回。
负数的cnt表示pcap_loop永远循环抓包,直到出现错误。
第三个参数是一个回调函数指针,它必须是如下的形式:
voidcallback(u_char*userarg,conststructpcap_pkthdr*pkthdr,constu_char*packet)
第一个参数是pcap_loop的最后一个参数,当收到足够数量的包后pcap_loop会调用callback回调函数,同时将pcap_loop()的user参数传递给它
第二个参数是收到的数据包的pcap_pkthdr类型的指针
第三个参数是收到的数据包数据
c)
intpcap_dispatch(pcap_t*p,intcnt,pcap_handlercallback,u_char*user)
这个函数和pcap_loop()非常类似,只是在超过to_ms毫秒后就会返回(to_ms是pcap_open_live()的第4个参数)
5.分析数据包
我们既然已经抓到数据包了,那么我们要开始分析了,这部分留给读者自己完成,具体内容可以参考相关的网络协议说明。
在本文的最后,我会示范性的写一个分析arp协议的sniffer,仅供参考。
要特别注意一点,网络上的数据是网络字节顺序的,因此分析前需要转换为主机字节顺序(ntohs()函数)。
6.过滤数据包
我们抓到的数据包往往很多,如何过滤掉我们不感兴趣的数据包呢?
几乎所有的操作系统(BSD,AIX,MacOS,Linux等)都会在内核中提供过滤数据包的方法,主要都是基于BSDPacketFilter(BPF)结构的。
libpcap利用BPF来过滤数据包。
过滤数据包需要完成3件事:
a)构造一个过滤表达式
b)编译这个表达式
c)应用这个过滤器
a)
BPF使用一种类似于汇编语言的语法书写过滤表达式,不过libpcap和tcpdump都把它封装成更高级且更容易的语法了,具体可以mantcpdump,以下是一些例子:
srchost192.168.1.177
只接收源ip地址是192.168.1.177的数据包
dstport80
只接收tcp/udp的目的端口是80的数据包
nottcp
只接收不使用tcp协议的数据包
tcp[13]==0x02and(dstport22ordstport23)
只接收SYN标志位置位且目标端口是22或23的数据包(tcp首部开始的第13个字节)
icmp[icmptype]==icmp-echoreplyoricmp[icmptype]==icmp-echo
只接收icmp的ping请求和ping响应的数据包
ehterdst00:
e0:
09:
c1:
0e:
82
只接收以太网mac地址是00:
e0:
09:
c1:
0e:
82的数据包
ip[8]==5
只接收ip的ttl=5的数据包(ip首部开始的第8个字节)
b)
构造完过滤表达式后,我们需要编译它,使用如下函数:
intpcap_compile(pcap_t*p,structbpf_program*fp,char*str,intoptimize,bpf_u_int32netmask)
fp:
这是一个传出参数,存放编译后的bpf
str:
过滤表达式
optimize:
是否需要优化过滤表达式
metmask:
简单设置为0即可
c)
最后我们需要应用这个过滤表达式:
intpcap_setfilter(pcap_t*p,structbpf_program*fp)
第二个参数fp就是前一步pcap_compile()的第二个参数
应用完过滤表达式之后我们便可以使用pcap_loop()或pcap_next()等抓包函数来抓包了。
方案2.1:
可以使用sendip发送数据包,接收方式同样采用libpcap.
在正常的网络环境中,很难产生错误的IP包,也很难产生我们想要的错误的IP包,为此,要完成对产品的测试,我们必须自己来制造各种各样错误的IP包,本篇的目的就是介绍如何利用各种发包工具来制造自己想要的错误的IP包。
SENDIP是一个LINUX下的命令行工具,可以通过命令行参数的方式发送各种格式的IP包,它有大量的命令行参数来规定各种协议的头格式,目前可支持NTP,BGP,RIP,RIPng,TCP,UDP,ICMP或rawIPv4和IPv6包格式,并且可以随意在包中添加数据。
方案2.2:
pcap4j(
Pcap4JisaJavalibraryforcapturing,craftingandsendingpackets.Pcap4Jwrapsanativepacketcapturelibrary(libpcaporWinPcap)viaJNAandprovidesyouJava-OrientedAPIs.
1、方案2.3:
scapy(
scapy是python写的一个功能强大的交互式数据包处理程序,可用来发送、嗅探、解析和伪造网络数据包,常常被用到网络攻击和测试中
实现
方案一:
#defineBUF_SIZE256//定义单个数据包携带的数据大小,单位是字节
/*
函数实现将数据装入数据包,构造数据包(这里以tcp为例),发送数据包的工作,这里可以指定以太网头、ip头,tcp头各个元组的值,从哪个网卡发送以及源和目的ip地址和物理地址。
*/
intfunc_package(u_charpayload[],intbytes_in)
{
libnet_t*handle;/*Libnet句柄*/
intpacket_size;/*构造的数据包大小*/
char*device="eth0";/*设备名字,也支持点十进制的IP地址,会自己找到匹配的设备*/
char*src_ip_str="192.168.1.119";/*源IP地址字符串*/
char*dst_ip_str="192.168.1.101";/*目的IP地址字符串*/
u_charsrc_mac[6]={0x00,0x0c,0x29,0x4b,0x76,0x97};/*源MAC*/
u_chardst_mac[6]={0x20,0x7c,0x8f,0x72,0x34,0x06};/*目的MAC*/
u_longdst_ip,src_ip;/*网路序的目的IP和源IP*/
charerror[LIBNET_ERRBUF_SIZE];/*出错信息*/
libnet_ptag_teth_tag,ip_tag,tcp_tag,tcp_op_tag;/*各层build函数返回值*/
u_shortproto=IPPROTO_TCP;/*传输层协议*/
u_longpayload_s=0;/*承载数据的长度,初值为0*/
/*把目的IP地址字符串转化成网络序*/
dst_ip=libnet_name2addr4(handle,dst_ip_str,LIBNET_RESOLVE);
/*把源IP地址字符串转化成网络序*/
src_ip=libnet_name2addr4(handle,src_ip_str,LIBNET_RESOLVE);
/*初始化Libnet*/
if((handle=libnet_init(LIBNET_LINK,device,error))==NULL){
printf("libnet_initfailure\n");
return(-1);
};
//strncpy(payload,"testtesttest",sizeof(payload)-1);/*构造负载的内容*/
//payload_s=strlen(payload);/*计算负载内容的长度*/
payload_s=bytes_in;/*计算负载内容的长度*/
#if0
/*构建TCP的选项,通常在第一个TCP通信报文中设置MSS*/
tcp_op_tag=libnet_build_tcp_options(
payload,
payload_s,
handle,
0
);
if(tcp_op_tag==-1){
printf("build_tcp_optionsfailure\n");
return(-2);
};
#endif
tcp_tag=libnet_build_tcp(
30330,/*源端口*/
30331,/*目的端口*/
8888,/*序列号*/
8889,/*确认号*/
TH_PUSH|TH_ACK,/*Controlflags*/
14600,/*窗口尺寸*/
0,/*校验和,0为自动计算*/
bytes_i
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- libnetlibpcap 传输 实践