libnet介绍与分析

当前,基于socket的网络编程已成为当今不可替代的编程方法,它将网络通讯当作文件描述符进行处理,把对这个“网络文件”(即socket套接字)的操作抽象成一种类似于文件操作的方式进行。从实现细节上,这种工作方式根据TCP/IP的网络通讯模型,封装了一系列的实现,使得我们只需要使用一个指定的参数,就可以实现在基于所需协议的数据的发送和接收。

但是,如果我们对那些系统自动给我们做的工作感兴趣,希望与发送的数据作“面对面”的接触,libnet则会是一个不错的选择。

libnet是UNIX系统同台上网络安全工具开发的重要的库,它和libpcap、libnids一起,给网络安全工具的开发人员提供了一组丰富而且完全的武器,使之得以很方便地编写出结构化强、健壮性好、可移植性高等特点的程序。

libnet提供一系列的接口函数,实现和封装了数据包的构造和发送过程。利用它可以亲自构造从应用层到链路层的各层协议的数据包头,并将这些包头与有效数据有序地组合在一起发送出去。当然,它也是基于tcp/ip协议族模型的。

libnet当前的版本是1.1.2,相对于1.0.*版本有比较大的变化。

全部源代码包括18,000 行代码,109个导出函数,其中包括67个建包函数。这使得它支持现有的TCP/IP族的所有协议。此外,它支持多平台,Windows,OS X,BSD,Linux, Solaris,HPUX都能使用。

下图是它支持的协议:

libnet库可以被划分为4个功能部分:内存管理、地址解析、包处理、以及其他一些支持函数。

★ 内存管理函数

单数据包内存初始化及环境建立:

libnet_t *libnet_init(int injection_type, char *device, char *err_buf);

资源释放:

void libnet_destroy(libnet_t *l);

★ 地址解析函数

地址解析:

char *libnet_addr2name4(u_int32_t in, u_int8_t use_name);

libnet_name2addr4(libnet_t *l, char *host_name, u_int8_t use_name);

struct libnet_in6_addr libnet_name2addr6(libnet_t *l, char *host_name, u_int8_t use_name);

void libnet_addr2name6_r(struct libnet_in6_addr addr, u_int8_t use_name,

char *host_name, int host_name_len);

获取接口设备IP地址:

u_int32_t libnet_get_ipaddr4(libnet_t *l);

struct libnet_in6_addr libnet_get_ipaddr6(libnet_t *l);

获取接口设备硬件地址:

struct libnet_ether_addr *libnet_get_hwaddr(libnet_t *l);

★ 数据包构造函数

(这一部分函数较多,都以libnet_build_*()的形式出现,在此略过)

★ 数据包发送函数

int libnet_write(libnet_t *l);

★ 相关的支持函数

随机数种子生成器:

int libnet_seed_prand(libnet_t *l);

获取随机数:

u_int32_t libnet_get_prand(int mod);

端口列表链初始化:

int libnet_plist_chain_new(libnet_t *l, libnet_plist_t **plist, char *token_list);

获取端口列表链的下一项(端口范围):

int libnet_plist_chain_next_pair(libnet_plist_t *plist, u_int16_t *bport,

u_int16_t *eport);

端口列表链输出显示:

int libnet_plist_chain_dump(libnet_plist_t *plist);

获取端口列表链:

char *libnet_plist_chain_dump_string(libnet_plist_t *plist);

端口列表链内存释放:

int libnet_plist_chain_free(libnet_plist_t *plist);

对它的使用也非常简单,只要你了解自己要做什么事情、应该把哪些参数放在什么位置。利用libnet函数库开发应用程序的基本步骤非常简单:

1、数据包内存初始化;

2、构造数据包;

3、发送数据;

4、释放资源;

例: libnet的发行包里提供了很多示例程序,其中/libnet/sample/tcp1.c如果省略掉一些参数的设置和错误处理,则程序简化为:

#if (H***E_CONFIG_H)

#i nclude "../include/config.h"

#endif

#i nclude "libnet_test.h"

#ifdef __WIN32__

#i nclude "../include/win32/getopt.h"

#endif

int

main(int argc, char *argv[])

{

//…….

l = libnet_init(

LIBNET_LINK,                            /* injection type */

NULL,                                   /* network interface */

errbuf);                                /* error buffer */

//……

t = libnet_build_tcp_options(

"\003\003\012\001\002\004\001\011\010\012\077\077\077\077\000\000\000\000\000\000",

20,

l,

0);

//……

t = libnet_build_tcp(src_prt, dst_prt, 0x01010101, 0x02020202, TH_SYN, 32767, 0, 10,

LIBNET_TCP_H + 20 + payload_s, payload, payload_s, l, 0);

t = libnet_build_ipv4(LIBNET_IPV4_H + LIBNET_TCP_H + 20 + payload_s, 0, 242, 0, 64,

IPPROTO_TCP, 0,  src_ip, dst_ip, NULL, 0, l,  0);

t = libnet_build_ethernet(enet_dst, enet_src, ETHERTYPE_IP, NULL, 0, l, 0);

c = libnet_write(l);

libnet_destroy(l);

return (EXIT_SUCCESS);

bad:

libnet_destroy(l);

return (EXIT_FAILURE);

}

#if defined(__WIN32__)

#i nclude <../include/win32/getopt.h>

#i nclude <winsock2.h>

#i nclude <ws2tcpip.h>

#endif  /* __WIN32__ */

/* EOF */

对libnet源码的分析

★     整体设计思想

对每个要发送的包,libnet维护一个libnet­_t结构,这个结构是理解整个libnet的关键,也是libnet得以实现它强大功能的关键。让我们先从它入手,从整体到细节地揭开libnet的面纱。下面左图是libnet_t这个结构的示例。

其中的fd就是发送数据包将要用到的socket套接字,injection_type将会被设置成libnet_init()中的第一个参数,即你选择发送的方式,是基于link_layer的链路层数据包?还是基于IP层的raw数据包?后一种情况又分为IP4和IP6两种。protocol_blocks 和protocol_end都是指针,指向一个libnet自定义的libnet_pblock_t结构,由此管理一个libnet_pblock_t的链表。而libnet_pblock_t则表述各个协议,维护各个协议给发送的数据包添加的数据块,它的具体选项下面再说。link_type表示链路层的类型,link_offset则指向链路层也就是最底层协议包头的偏移地址。aligner是为了维护最后的数据包的字对齐而设置的,字符串指针device则指向通讯所用的设备,比如eth0.state是一个结构,与ptag_state一起,记录包在建立过程中的一些信息。label是一个字符数组。每当有错误发生的时候,errbuf数组就被用来记录错误信息。全部的数据包长度和保存于total_size中。

从libnet_t已经大概可以看出libnet的设计思想了:程序员决定一些参数,并且通过函数调用中的参数把相关的数据交给libnet.libnet则在程序员每要求购建一个协议包头的时候,为其创建一个libnet_pblock_t的结构保存这些数据,并将该结构入链表。当所有的准备工作完成,程序员一声令下,libnet就将协议块链(由protocol_blocks开始,终于pblock_end)中的协议包头以及数据组合成一个合乎规格的包,通过硬件发送出去。如果任何一个步骤出了差错,程序员都可以从errbuf中获取出错信息。最后,libnet从程序员手中接过一个指令,进行所有的善后工作。

每种协议的包头将在前期被实现为一个libnet_pblock_t的结构。libnet为它所支持的协议都定义了相应的数据结构,例如TCP包头的定义:

struct libnet_tcp_hdr

{

u_int16_t th_sport;       /* source port */

u_int16_t th_dport;       /* destination port */

u_int32_t th_seq;          /* sequence number */

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)

libnet介绍与分析,布布扣,bubuko.com

时间: 2024-10-13 01:10:44

libnet介绍与分析的相关文章

nginx web日志介绍和分析

nginx web日志介绍和分析 Nginx访问日志打印的格式可以自定义,例如Nginx日志打印格式配置如下,Log_format 用来设置日志格式,Name(模块名) Type(日志类型),可以配置多个日志模块,分别供不同的虚拟主机日志记录所调用: log_format log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '                   '$status $body_b

Mysql Binlog三种格式介绍及分析【转】

一.Mysql Binlog格式介绍       Mysql binlog日志有三种格式,分别为Statement,MiXED,以及ROW! 1.Statement:每一条会修改数据的sql都会记录在binlog中. 优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能.(相比row能节约多少性能 与日志量,这个取决于应用的SQL情况,正常同一条记录修改或者插入row格式所产生的日志量还小于Statement产生的日志量,但是考虑到如果带条 件的update操作,以及整表

Windows应急响应和系统加固(6)——Windows历年高危漏洞介绍和分析

Windows历年高危漏洞介绍和分析 一.漏洞介绍: 1.漏洞: <1>.漏洞:是影响网络安全的重要因素: <2>.漏洞利用:成为恶意攻击的最常用手段: <3>.漏洞攻击:产业化.低成本化.手段多样化.低门槛趋势: <4>.信息化时代:无论个人/企业,都面临严峻的漏洞威胁: <5>.Windows.Office.IE.Edge.Flash等高危漏洞频繁曝光. 2.Windows漏洞: <1>.MS08-067 RCE漏洞: <2

Mysql Binlog 三种格式介绍及分析

一.Mysql Binlog格式介绍       Mysql binlog日志有三种格式,分别为Statement,MiXED,以及ROW! 1.Statement:每一条会修改数据的sql都会记录在binlog中. 优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能.(相比row能节约多少性能与日志量,这个取决于应用的SQL情况,正常同一条记录修改或者插入row格式所产生的日志量还小于Statement产生的日志量,但是考虑到如果带条件的update操作,以及整表删除

网络安全设备Bypass功能介绍及分析

from:http://netsecurity.51cto.com/art/200910/159948.htm 网络安全平台厂商往往需要用到一项比较特殊的技术,那就是Bypass,那么到底什么是Bypass呢,Bypass设备又是如何来实现的?下面我就对Bypass技术做一下简单的介绍和说明. AD:WOT2015 互联网运维与开发者大会 热销抢票 网络安全平台厂商往往需要用到一项比较特殊的技术,那就是Bypass,那么到底什么是Bypass呢,Bypass设备又是如何来实现的?下面我就对Byp

Android自动化测试框架开发(二)Monkey、MonkeyRunner介绍和分析

本篇介绍两个Android自带的测试框架:Monkey和MonkeyRunner 1.Monkey Monkey是android自带的测试框架,通过向手机发送随机事件(手势,点击,输入)进行应用程序压力测试(可指定应用程序包名,也可以说是稳定性测试),测试应用程序的稳定性和健壮性: 使用步骤: (1)进入adb shell (2)cd /system/bin 然后执行monkey命令即可: 常用的monkey命令(网上借的图): 分析: Monkey测试是一种为了测试软件的稳定性.健壮性的快速有

几种常见排序算法的基本介绍,性能分析,和c语言实现

本文介绍7种常见的排序算法,以及他们的原理,性能分析和c语言实现: 为了能够条理清楚,本文所有的算法和解释全部按照升序排序进行 首先准备一个元素无序的数组arr[],数组的长度为length,一个交换函数swap, 在main函数中实现排序函数的调用,并输出排序结果: void swap(int*x , int*y) { int temp = *x; *x = *y; *y = temp; } int main() { int arr[] = { 1,8,5,7,4,6,2,3}; int le

android设置中的Preferencescreen使用方法介绍与分析

今天主要研究了一下设置中的Preferencescreen应用,它不仅可以作为设置界面显示,并且还可以启动activity,以下主要是对启动activity的介绍 1. Preferencescreen中启动activity 比如wireless_setting.xml中有例如以下片段 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="

VPS技术介绍以及分析

VPS的全称为Virtual Private Server,叫做虚拟专用服务器(Godaddy称之为Virtual Dedicated Server,VDS).就是利用各种虚拟化手段把单台物理服务器虚拟为多台虚拟服务器提供给用户使用.VPS的差别主要在于虚拟化技术实现的不同: linux 平台下常见的虚拟化技术 Openvz介绍 Xen 虚拟化技术介绍 KVM虚拟化技术介绍 VMware windows平台下常见的虚拟化技术 Hyper-V VMware 从虚拟化程度上来说,OpenVZ是操作系