libnet使用举例(2) 2000-01-01 武汉白云黄鹤站 安全资源 作者:小四 < mailto: scz@isbase.com > 主页:http://www.isbase.com 日期:2000-07-26 20:10 这篇先介绍libnet_init_packet()函数,其函数原型如下: int libnet_init_packet ( size_t p_size, u_char ** buf ); 该函数实际调用了malloc函数做了一次内存分配,第一个形参就是指定内存分配的大 小。第二个形参用于返回指向这块内存的指针。如果p_size被省略或为负值,将默认 使用LIBNET_MAX_PACKET。内存分配成功后初始化成0,函数返回1,否则返回-1。显 然有一个对应的函数释放内存: int libnet_destroy_packet ( u_char ** buf ); 这个函数除了调用free(),目前没有其他动作。 在/usr/include/libnet/libnet-macros.h中有如下定义: #define LIBNET_MAX_PACKET 0xffff 事实上我们完全可以不使用这两个函数,它们并没有操作什么内部数据结构,仅仅为 了统一封装起见使用它们。 针对p_size,libnet在/usr/include/libnet/libnet-headers.h中定义了一批宏: #define LIBNET_ARP_H 0x1c /* ARP header: 28 bytes */ #define LIBNET_DNS_H 0xc /* DNS header base: 12 bytes */ #define LIBNET_ETH_H 0xe /* Etherner header: 14 bytes */ #define LIBNET_ICMP_H 0x4 /* ICMP header base: 4 bytes */ #define LIBNET_IGMP_H 0x8 /* IGMP header: 8 bytes */ #define LIBNET_IP_H 0x14 /* IP header: 20 bytes */ #define LIBNET_RIP_H 0x18 /* RIP header base: 24 bytes */ #define LIBNET_TCP_H 0x14 /* TCP header: 20 bytes */ #define LIBNET_UDP_H 0x8 /* UDP header: 8 bytes */ 上面仅仅列举了常用的几个宏,虽然不是必须使用,但建议尽量使用宏。 接下来介绍两个操作raw_socket的函数: int libnet_open_raw_sock ( int protocol ); int libnet_close_raw_sock ( int fd ); libnet_open_raw_sock()打开IPv4上指定协议类型的raw_socket,同时设置了 IP_HDRINCL选项,成功则返回socket,失败返回-1。libnet_close_raw_sock()关闭 前者打开的raw_socket,成功则返回1,失败返回-1。 实际上,形参protocol对应的就是socket()函数的第三个形参,所以可以指定 IPPROTO_RAW、IPPROTO_ICMP、IPPROTO_TCP等,当然还要看内核是否支持这样的指定。 我们可以类似W.Richard.Stevens那样处理,封装一下这个函数: -------------------------------------------------------------------------- int Libnet_open_raw_sock ( int protocol ) { int s; if ( ( s = libnet_open_raw_sock( protocol ) ) == -1 ) { libnet_error( LIBNET_ERR_FATAL, "Can"t open raw socket %08x/n", procotol ); } return( s ); } /* end of Libnet_open_raw_sock */ -------------------------------------------------------------------------- 在不使用libnet库编程的时候,打开raw_socket,接下来就是要构造IP头,libnet使 用如下函数完成这个工作: int libnet_build_ip ( u_short len, u_char tos, u_short id, u_short frag, u_char ttl, u_char prot, u_long saddr, u_long daddr, const u_char * payload, int payload_s, u_char * buf ); 形参len指定的是IP数据区长度,既不是IP头部长度,也不是IP报文总长度,并没有 一个结构成员对应这个形参,习惯了socket编程的兄弟要注意。saddr和daddr均以网 络字节序提供,也就是big-endian序。payload指向可选的IP选项以及可能出现的填 充,payload_s指定这些数据的长度。至于最后一个参数buf,指向通过 libnet_init_packet分配并初始化过的数据区,将来包括IP头都要出现在该数据区, 此时buf指向的实际是IP头。当然这里举例是以IP报文举例,如果是其他类型的报文, 就不是这个结论了。不要把payload和IP数据区混淆了,否则libnet构造IP头的时候 出现混乱。 libnet使用下面这个函数进一步在IP报文上构造TCP头: int libnet_build_tcp ( u_short sport, u_short dport, u_long seq, u_long ack, u_char control, u_short win, u_short urg, const u_char * payload, int payload_s, u_char * buf ); 形参control对应我们熟悉的SYN、ACK、RST等标志的逻辑或。最后一个形参需要指向 一个已分配好的数据区,TCP头从该指针开始。类似IP层,这里payload指向可选的 TCP选项以及可能出现的填充,不要和TCP数据区相混淆。 接下来可能是你曾经如许头疼的TCP/UDP校验和计算问题: int libnet_do_checksum ( u_char * buf, int protocol, int len ); 该函数用于计算传输层校验和并填写到适当头部位置,包括TCP/UDP校验和的计算。 显然该函数应该在传输层报文包括数据区构造完毕之后被调用,否则计算得到的校验 和必将因为传输层数据区的改变而失效。成功则返回1,否则返回-1。第一个形参需 要指向IP头,而不是TCP头什么的,第二个形参指定传输层协议类型,第三个形参指 定了IP数据区长度,不包括IP头部。 说点题外话,在raw_socket层上,IP头部校验和总是由内核亲自计算,但是如果直接 在链路层构造报文,必须显式计算IP头部校验和。 对于所有的build_*函数,如果buf指针为NULL,运行时会检查到这个错误并返回-1。 但是payload_s超长、头部超出预先分配大小等等,是无法检测到的,必须意识到这 个问题所在。 计算TCP/UDP头部校验和相对其他校验和的计算是显得复杂了点,却也没有想象的那 么的可怕,主要是现在很多书上并没有解释清楚、翻译清楚原意,可以参考许多现成 的源代码。libnet库提供这么一个封装过的函数固然不错,还是希望你能真正了解校 验和的计算过程。 下面要做的是把这样一个精心构造的报文丢到网络上去: int libnet_write_ip ( int sock, u_char * packet, int len ); 第一个形参即libnet_open_raw_sock()的返回值,第二个形参指向IP头,第三个形参 指明了整个IP报文总长度。函数返回值对应发送出去的字节数,注意不见得构造多大 的报文就能发送出去这么大的报文,必须检查返回值。 到此为止,我们企图编写一个syn-flood的主力舰队都到齐了,至于如何编队、何时 出发且听下回分解,总得给我个灌水的机会吧,都一次灌完了如何长我那可怜的经验 值呢? <待续> |
libnet使用举例(2)
最新推荐文章于 2020-04-26 19:53:54 发布