u_int32_t th_ack; /* acknowledgement number */ #if (LIBNET_LIL_ENDIAN)
u_int8_t th_x2:4, /* (unused) */ th_off:4; /* data offset */ #endif
#if (LIBNET_BIG_ENDIAN)
u_int8_t th_off:4, /* data offset */ th_x2:4; /* (unused) */ #endif
u_int8_t th_flags; /* control flags */ …….
u_int16_t th_win; /* window */ u_int16_t th_sum; /* checksum */ u_int16_t th_urp; /* urgent pointer */ };
它的长度是:
#define LIBNET_TCP_H 0x14 /**< TCP header: 20 bytes */ 当程序员调用libnet_built_tcp()构建一个TCP包头时,libnet会分配一个这样的结构,并且按照程序员的意愿填充这个结构的各个字段。并且生成一个libnet_pblock_t结构,使它的buf指针指向这个数据包头结构,并用b_len保存这个包头的长度(20字节)。这个libnet_pblock_t的其它相关字段将会被按规则填充。之后,这个libnet_pblock_t会被加入到libnet_t结构所维护的协议块链的适当位置。所谓“适当位置”,将会在后面加以说明。下图是进程中一种可能出现的情形:
利益于这种精巧的整体架构,libnet的具体实现就不显得困难了,而我们对它的理解也因此受益。剩下来的只是一些细节性的东西需要去把握,这一点显然更适合亲自去阅读代码。 下面是大概的处理流程。
在开始之前,需要插入一点说明:libnet使用条件编译的方式消除平台间的差异,由此产生了很多同名的函数(当然它们都在内部被调用),比如
libnet_open_link()有五个。程序在运行时选择哪一个调用取决于你使用的平台或者你强加于编译器的预编译选项。
一般情况下事情会从libnet_init()开始。libnet_init()首先开启系统的网络功能:在linux下面,它会验证是否具有超级用户权限(这也就意味着低权限的用户不能使用成功),而如果是在windows下面,则会调用大名鼎鼎的WSAStartup()函数。之后分配一块内存区域建立libnet_t结构。在简单的初始化后,根据程序员传入的发送类型参数,进行相应的操作:如果是基于链路层的发送方式(LIBNET_LINK,LIBNET_LINK_ADV),则会向操作系统申请设备,并开启底层的socket服务。如果是基于IP层的发送方式,则开启该层的socket服务。
准备工作做好之后,转入直接的建包工作,这一工作实际表现为建一系列的协议包头。程序员需要按照自顶向下的顺序建协议包头,而要发送的正文数据往往被作为第一个协议包头的“有效荷载(payload)”被加载。此外,某些协议可
能具有可选数据项,这一部分被libnet独立出来,由一个单独的libnet_pblock_t来负责,libnet同时也为此提供相应的功能函数,其名称被定为
libnet_build_*_options().在这种情况下,libnet会自动调整这些协议块在链表中的位置,使payload的数据块在前面,中间是options块,其后跟随着该层协议的固定包头。
在合适的时候,libnet会把计算校验和这样的繁琐的工作优雅地完成,你甚至感觉不到它已经为你这样做了。
这样,只要程序员的使用正确,数据以及协议包头已经在链表中按顺序排列了,这为简化后面的工作极为有益。在libnet_write()过程中,链表中的数据被按顺序地拷贝到一个大的缓冲内。拷贝虽然是按照从高层协议到低层协议的顺序进行,但是却是从缓冲的后部向前部拷贝,从这里,我们可以看到“协议栈”的思想在闪光。
当一切工作完成之后,程序员简单地调用libnet_destroy()函数,关闭使用的网络通讯设备,释放占用的内存。程序员的工作是如此地简单:他甚至仅仅需要传递一个参数给这个简短的函数。
除了完成这样一些最直接的工作以外,libnet还提供了一组丰富实用的功能。它提供的地址解析功能能够实现IP地址在网络字节顺序、域名、点分形式之间的转换,它也提供了一套完整的随机数生成方案供你使用,如此等等。这一些功能使得它得以从一套单纯的库跃升为一个完整的系统。
(文中所用图片源自Mike Schiffman在2004年RSA Conference上的PPT)
linux下libnet编程 亲自测试可用
亲自测试 如果build包的时候 只要把类型改了 就能改成相应的协议。
0x0800 ip
0x0806 arp
0x86DD IPv6 0x86ee idmp了
至于ipv6的包的话 在那就不需要改了 只有协议号需要改
我最后安装的是libnet-1.1.4.tar.gz
可用编译的时候gcc -o libnet libnet.c -lnet 安装完了 /usr/include/libnet.h里面就有了
除了下面这个例子中一次一个数据包libnet也提供了多数据包内存初始化 int libnet_init_packet_arena(struct libnet_arena **arena, u_short packet_num, u_short packet_size);
/************************************************************ 补充1 ARP包的格式
一个ARP包是分为两个部分的,前面一个是物理帧头,后面一个才是ARP帧。
首先,物理帧头,它将存在于任何一个协议数据包的前面,我们称之为DLC Header,因为这个帧头是在数据链路层构造的,并且其主要内容为收发双方的物理地址,以便硬件设备识别。
DLC Header
字段 长度(Byte) 默认值 备注
接收方MAC 6 广播时,为ff-ff-ff-ff-ff-ff 发送方MAC 6
Ethertype 2 0x0806 0x0806是ARP帧的类型值
以上是需要我们填充的物理帧头的格式,我们可以看到需要我们填充的仅仅是发送端和接 收端的物理地址,接下来我们看一下ARP帧的格式.
ARP Frame
字段 长度(Byte) 默认值 备注 硬件类型 2 0x1 以太网类型值
上层协议类型 2 0x0800 上层协议为IP协议
MAC地址长度 1 0x6 以太网MAC地址长度为 6 IP地址长度 1 0x4 IP地址长度为 4
操作码 2 0x1表示ARP请求包,0x2表示应答包 发送方MAC 6 发送方IP 4 接收方MAC 6
接收方IP 4
填充数据 18
************************************************************ /
/************************************************************ 文档:linux下libnet编程 作者:Alanx
Email:zhangsuozhu@tom.com 版本: 0.01
本文档Alanx所有,转载请注明!!!
本文源代码参考libnet例子代码及arpoison源代码
**************************************************************/
一、下载安装libnet
我下的是1.13RC01版的。很简单,下载下来解开. ./configure
make
make install
即可(注意,在root权根下安装)
二、编程
1、#include
IP为32位无符号整型、可以用以下类型: u_int32_t 或u_long
可以用 inet_addr(\把字符型的指针或数组转换成IP地址。 3、关于MAC的数据结构
MAC的数据结构是6个8位的数组,每个数组中记录的一个十六进制的数据,共48位的硬件地址。 struct macaddr
{
u_char MAC[6]; /* 或u_int8_t MAC[6] */ };
4、初始化libnet,分配内存函数 libnet_init( )
char device[ ]=\这是网卡的名子*/
char errbuf[LIBNET_ERRBUF_SIZE];//如果程序中出错。出错信息都保存在该数据中。 libnet_t *plibnet_app;
plibnet_app=libnet_init(LIBNET_LINK_ADV, device, errbuf);//第一个参数设定这个实列是在链路层工作,还是在IP层工作。 /*出错处理*/
if (NULL ==plibnet_app)
{
printf(\ exit(1); }
/*出错处理*/
该函数返回一个libnet的实例指针,以后听数据包的构建及发送都要用到该实例指针。也许实例不准确,也可以说是句柄。随他怎么说吧。反证以是分配了内存。并提供给我们了一个指针。其它函数可以调用这个指针,向libnet_t这个数扰结构里写一些内存。 5、制造数据包
本着先高层后低层的顺序。比如ARP数据包。我们先构造ARP包。然后在构造ETHERNET的数据包。
这里我们以新建一个ARP数据包为例:
首先先调用:libnet_build_arp()该函数返回一个libnet_ptag_t类型的值。来表示是否建立成功。不成功返回-1。其实libnet_ptag_t的数据类型估计是整型啥的。可以不考虑。只要不是返回-1,就说明APR数据包构造成功了。 例如下:
libnet_ptag_t ptag; ptag = libnet_build_arp(
1, /* hardware type */ 0x0800, /* proto type */
6, /* hw addr size */ 4, /* proto addr size */
ARPOP_REPLY, /* ARP OPCODE */