法律声明:本文内容仅用于网络安全学习、C/C++ 安全开发自查、内存安全漏洞原理研究。文中漏洞代码、POC 载荷仅用于原理演示,严禁未经授权对线上系统进行漏洞利用。任何安全测试必须获得资产所有者书面授权,遵守《网络安全法》《数据安全法》等法律法规。

摘要

C/C++ 是系统级开发的主力语言,操作系统、驱动、中间件、音视频引擎、通信协议栈、嵌入式固件、游戏引擎大量使用 C/C++ 开发。C/C++ 语言直接管理内存,没有内置边界检查、没有垃圾回收,是内存安全漏洞的高发语言。缓冲区溢出、UAF(释放后使用)、内存越界、空指针解引用、整数溢出、堆溢出,构成了大量 CVE 漏洞的核心成因。

在 SRC 挖洞、二进制安全、漏洞挖掘(PWN/CTF)、安全开发(SDL/DevSecOps)领域,C/C++ 代码审计是基础中的基础。很多开发者熟悉 Web 漏洞,一看到 C/C++ 底层内存漏洞就发怵,实际上只要理解了内存布局、指针、堆栈机制,这些漏洞成因和编码防护是完全可以掌握的。

本文万字实战教程,聚焦 C/C++ 三大核心内存漏洞:缓冲区溢出(栈溢出 / 堆溢出)、UAF 释放后使用、内存越界(数组越界 / 越界读写),同时补充整数溢出、空指针、格式化字符串等高频漏洞,完整讲解漏洞成因、内存原理、可复现代码、利用思路、代码修复、编译防护(ASLR/PIE/Canary/NX)。适合 C/C++ 开发、嵌入式开发、安全工程师、二进制安全入门、SRC 白帽学习使用。

前言

学习 C/C++ 内存安全,首先要理解 C/C++ 和 Java、Python、Go 的本质差异:C/C++手动管理内存。开发者用malloc/new分配内存,用free/delete释放内存,用指针访问内存地址。一旦管理不当,就会产生内存安全漏洞。

内存安全三大核心问题

  1. 缓冲区溢出:写入的数据超过了缓冲区(数组 / 堆块)的边界,覆盖相邻内存;
  2. UAF(Use-After-Free):内存块被 free 之后,指针仍然指向这块已释放的内存,继续通过这个指针读写数据;
  3. 内存越界:通过指针 / 数组索引访问了不属于当前对象的内存区域。

为什么 C/C++ 漏洞危害大?
因为内存漏洞常被攻击者利用来实现任意代码执行(控制程序流、劫持返回地址、覆盖函数指针)或信息泄露(越界读取密钥、密码)。缓冲区溢出是最经典的 RCE 攻击手法(栈溢出覆盖返回地址),危害等级极高。

C/C++ 内存布局(理解漏洞的前提)

一个 C/C++ 程序进程的内存分为几大区域:

  1. 栈(Stack):存放局部变量、函数调用帧、返回地址,向下生长;
  2. 堆(Heap):malloc/new动态分配的内存,向上生长;
  3. 数据段(.data/.bss):全局变量、静态变量;
  4. 代码段(.text):程序机器码。

攻击者的视角:如何利用内存漏洞

  1. 栈溢出 → 覆盖栈上返回地址 → 劫持程序执行流 → 跳转到 shellcode 或 ROP 链 → 拿到 shell;
  2. 堆溢出 → 覆盖相邻堆块元数据(chunk header)/ 函数指针 → 内存破坏 → 任意代码执行;
  3. UAF → 释放后重新 malloc,分配相同大小,攻击者控制新内存内容,用旧指针触发函数指针 / 虚表劫持。

前置知识点:指针就是内存地址。C 语言中int *p = &a;,p 保存的是变量 a 的内存地址。所有内存漏洞的核心,都是指针 / 索引访问了不该访问的内存区域。

一、C/C++ 安全开发前置知识

1.1 编译器与防护机制(现代系统默认开启)

理解漏洞,必须先理解现代系统提供的安全防护机制(这些机制会显著提高利用难度):

  1. ASLR(地址空间布局随机化):每次程序运行,栈、堆、库基址随机化,攻击者难以预测目标地址;
  2. NX(不可执行栈 / 堆):栈、堆数据区不可执行,攻击者不能直接执行 shellcode;
  3. Canary(栈金丝雀):栈溢出检测,在返回地址前插入随机值,溢出前先破坏 Canary,触发检测,程序终止;
  4. PIE(位置无关可执行文件):程序代码段基址随机化,配合 ASLR;
  5. RELRO(只读重定位):保护 GOT(全局偏移表)不被覆盖。

攻击者的对策:绕过这些防护,常见有 ROP(返回导向编程)、ret2libc、ret2plt、堆风水等,这是二进制漏洞利用(PWN)的核心技术。本文重点讲漏洞成因与编码防护,利用技术点到为止。

1.2 常用审计工具链(C/C++)

  1. gcc/g++/clang:编译器,开启编译防护选项;
  2. valgrind:内存错误检测器,检测内存泄漏、非法读写、UAF,动态检测神器;
  3. AddressSanitizer(ASan):编译时插入内存检测代码,定位缓冲区溢出、UAF、越界,C/C++ 代码审计最核心工具;
  4. gdb:调试器,查看内存布局、寄存器、栈帧;
  5. objdump/readelf:查看二进制反汇编、符号、段信息;
  6. RIPS(PHP 之外也支持)/Semgrep:静态扫描。

关键工具:ASan 编译选项-fsanitize=address,可以在运行程序时实时检测并精确报告内存越界、UAF、堆溢出的位置和堆栈,是 C/C++ 漏洞复现的利器。

二、实战一:缓冲区溢出漏洞(栈溢出 / 堆溢出)

2.1 漏洞成因

缓冲区溢出:向固定大小的缓冲区写入超过其容量的数据,覆盖相邻内存区域。
最常见场景:

  1. 使用strcpy、strcat、sprintf、gets等不安全的字符串函数,不检查目标缓冲区长度;
  2. memcpy复制数据时长度参数错误(来源长度大于目标容量);
  3. 数组索引越界写入;
  4. 用户输入数据拷贝到固定长度数组,未限制长度。
  • Source:用户可控输入长度 / 内容;
  • Sink:strcpy、strcat、sprintf、memcpy、gets、scanf(% s)等不安全拷贝函数;
  • 风险等级:严重;
  • 危害:栈溢出可劫持返回地址执行任意代码,堆溢出可内存破坏导致 RCE,是最高危漏洞。

2.2 源码示例(漏洞代码)

栈溢出经典示例:strcpy 拷贝用户输入
#include <stdio.h>
#include <string.h>

void vulnerable(char *input){
    // 固定大小缓冲区,栈上分配
    char buf[16];
    // 高危:strcpy不检查长度,直接拷贝
    // 输入长度>16,覆盖栈上相邻内存(包括返回地址)
    strcpy(buf, input);
    printf("data:%s\n", buf);
}

int main(){
    char input[256];
    printf("请输入:");
    // gets不检查长度,可输入任意长度数据
    gets(input);
    vulnerable(input);
    return 0;
}

char buf[16]只分配 16 字节,但strcpy(buf,input)不检查 input 长度,输入超过 16 字节就发生栈溢出,覆盖 buf 之后的栈内存,包括函数返回地址。

注意:gets()和scanf("%s")不检查输入长度,也是高危 Sink。现代编译器默认告警,但老代码大量存在。

sprintf 溢出示例
char buf[32];
// 高危:sprintf不检查目标缓冲区长度
sprintf(buf, "用户:%s 年龄:%d", name, age);
memcpy 长度错误示例
char dst[32];
char src[128];
// 高危:memcpy长度参数用了src的实际长度(128),超过dst容量(32),堆/栈溢出
memcpy(dst, src, strlen(src));

2.3 漏洞利用思路(原理层面)

栈溢出利用核心:覆盖函数返回地址。

  1. 攻击者输入超长数据;
  2. 数据溢出 buf,先覆盖局部变量,再覆盖栈帧上的返回地址;
  3. 攻击者把返回地址改写成自己构造的恶意代码地址(shellcode)或 ROP 链;
  4. 函数返回时,程序跳转到攻击者控制的位置,执行任意代码。

现代防护(Canary+ASLR+NX)会显著提高利用难度,但不代表漏洞不存在。只要存在缓冲区溢出,无论能否利用,都属于高危内存漏洞。

2.4 常见误报识别

SAST 静态扫描报缓冲区溢出,但实际可能:

  1. 虽然使用 strcpy,但传入的 input 来自服务端内部固定长度字符串,实际不会超长;
  2. 缓冲区大小足够容纳所有可能的输入;
  3. 输入长度已在上层校验。

人工复核重点:用户可控输入来源 + 目标缓冲区大小 + 是否真正可能超出。

2.5 漏洞修复代码

修复核心:使用安全函数,指定缓冲区容量,限制输入长度。

修复 1:使用带长度限制的安全函数
#include <stdio.h>
#include <string.h>

void vulnerable(char *input){
    char buf[16];
    // 安全:strncpy指定拷贝长度15,保证留\0终止符
    // 注意strncpy可能不补\0,手动确保
    strncpy(buf, input, sizeof(buf)-1);
    buf[sizeof(buf)-1] = '\0'; // 手动补终止符
    printf("data:%s\n", buf);
}
修复 2:改用 snprintf 替代 sprintf
char buf[32];
// 安全:snprintf指定缓冲区大小,防止溢出
snprintf(buf, sizeof(buf), "用户:%s 年龄:%d", name, age);
修复 3:fgets 替代 gets
char input[100];
// 安全:fgets限制最大读取长度
fgets(input, sizeof(input), stdin);

2.6 编译防护与纵深加固

编译时开启安全选项:

# 开启全部内存防护
gcc -fstack-protector-all -no-pie -z noexecstack -z relro -o prog vuln.c
# 检测使用ASan
gcc -fsanitize=address -g -o prog vuln.c
  1. 开启-fstack-protector-all(栈 Canary);
  2. 开启-z noexecstack(栈不可执行 NX);
  3. 开启 ASLR(系统层面);
  4. 使用 ASan 在开发阶段检测内存漏洞;
  5. 代码评审禁止使用 strcpy、gets、sprintf 等不安全函数,强制使用 strncpy、snprintf、fgets。

三、实战二:UAF(Use-After-Free,释放后使用)

3.1 漏洞成因

UAF:一块堆内存被free释放之后,代码仍然通过保存的指针继续访问这块内存。释放后这块内存可能被系统重新分配,或者内容被篡改,继续使用会造成内存破坏、逻辑错误、甚至代码执行。

  • Source:指针指向已释放的内存;
  • Sink:free 之后仍使用该指针读写;
  • 风险等级:严重;
  • 危害:堆内存破坏、虚表 / 函数指针劫持、任意代码执行、信息泄露。

3.2 源码示例(漏洞代码)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    int id;
    char name[32];
} User;

void vulnerable(){
    // 堆上分配User对象
    User *user = (User*)malloc(sizeof(User));
    strcpy(user->name, "admin");
    printf("before free: %s\n", user->name);

    // 释放内存
    free(user);
    // 高危:free之后指针未置空,继续使用(UAF)
    // user指向的内存已释放,读写是未定义行为
    printf("after free: %s\n", user->name); // 读取已释放内存
    user->id = 100; // 写入已释放内存
}

free(user)之后,user指针仍然有效(指向已释放的堆块),继续使用user->name、user->id,触发 UAF。

UAF 为什么高危:如果释放后,攻击者再次 malloc 相同大小的内存块,控制新分配内存的内容,再触发旧指针的使用,可能实现虚表劫持、函数指针覆盖,最终 RCE。

更危险的 UAF 场景:释放后重新分配再使用
typedef struct {
    void (*func)(int); // 函数指针
    int data;
} Object;

void vulnerable(){
    Object *obj = (Object*)malloc(sizeof(Object));
    obj->func = safe_handler; // 初始化函数指针
    free(obj); // 释放

    // 攻击者再次malloc相同大小,控制这块内存内容
    Object *fake = (Object*)malloc(sizeof(Object));
    fake->func = malicious_handler; // 攻击者伪造函数指针

    // UAF:仍通过旧指针obj调用函数指针,实际调用的是fake伪造的函数
    obj->func(obj->data);
}

经典利用:释放对象 → 攻击者 malloc 同大小块并填充恶意内容 → 旧指针触发虚函数 / 函数指针 → 跳转到恶意地址。

3.3 常见误报识别

  1. free 之后指针立即返回,后面没有继续使用;
  2. free 之后指针被置 NULL,后续有判空保护(if(user!=NULL));
  3. 内存释放后程序立即退出,无利用价值。

3.4 漏洞修复代码

修复核心:free 之后立即将指针置为 NULL,使用前判空。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void safe(){
    User *user = (User*)malloc(sizeof(User));
    if(!user) return; // 分配失败检查
    strcpy(user->name, "admin");
    printf("before: %s\n", user->name);

    free(user);
    user = NULL; // 关键:释放后立即置空

    // 使用前判空,防止UAF
    if(user != NULL){
        printf("after: %s\n", user->name);
    }
}

补充:C++ 中,delete之后同样要把指针置 NULL,delete[]匹配new[],delete匹配new。

3.5 纵深加固

  1. 释放后立即置空指针,使用前判空;
  2. 统一使用智能指针(C++11+ std::unique_ptr、std::shared_ptr),自动管理生命周期,从根上杜绝 UAF;
  3. 使用 ASan 检测 UAF:-fsanitize=address,UAF 会立即被检测并报告;
  4. 对象生命周期管理规范化,明确所有权(谁分配谁释放)。

C++ 安全建议:现代 C++ 开发优先使用智能指针和 RAII,避免手动new/delete,这是杜绝 UAF 和内存泄漏的最佳实践。

四、实战三:内存越界(数组越界 / 越界读写)

4.1 漏洞成因

内存越界:通过数组索引或指针访问了不属于当前对象 / 数组的内存区域。
常见场景:

  1. 数组索引越界(负数索引、超出 size 的索引);
  2. 指针偏移计算错误,越过对象边界;
  3. 循环遍历时边界条件错误(for(int i=0;i<=n;i++) 多一次);
  4. 用户可控索引直接访问数组元素,未校验边界。
  • Source:用户可控数组索引;
  • Sink:arr[index]、*(ptr+offset)越界访问;
  • 风险等级:中高危;
  • 危害:越界读泄露敏感信息(信息泄露)、越界写覆盖内存(内存破坏 / RCE)。

4.2 源码示例(漏洞代码)

#include <stdio.h>

void array_access(int index){
    int arr[10] = {0,1,2,3,4,5,6,7,8,9};
    // 高危:用户可控index直接访问数组,未校验边界
    // index<0 或 index>=10 造成越界访问
    printf("arr[%d]=%d\n", index, arr[index]);
}

int main(){
    int idx;
    scanf("%d", &idx);
    array_access(idx); // 用户可控索引
    return 0;
}

arr[10]合法索引范围是 0~9。攻击者传入idx=-1或idx=100,访问 arr [-1] 或 arr [100],越界读写内存。

越界读:arr[100]读取数组外的内存数据,可能泄露其他变量、密钥等敏感信息(信息泄露)。
越界写:arr[100] = value写入数组外内存,可能覆盖其他变量、函数指针(内存破坏)。

4.3 常见误报识别

  1. 索引来自服务端内部固定值,始终在合法范围内;
  2. 索引经过严格边界校验后才使用;
  3. 数组本身动态分配,容量足够。

4.4 漏洞修复代码

修复核心:访问数组前,严格校验索引边界。

#include <stdio.h>

#define ARR_SIZE 10

void array_access(int index){
    int arr[ARR_SIZE] = {0,1,2,3,4,5,6,7,8,9};
    // 安全:先校验索引范围
    if(index < 0 || index >= ARR_SIZE){
        printf("索引越界,拒绝访问\n");
        return;
    }
    printf("arr[%d]=%d\n", index, arr[index]);
}

int main(){
    int idx;
    scanf("%d", &idx);
    array_access(idx);
    return 0;
}

4.5 纵深加固

  1. 所有数组访问前校验索引范围(下界 0,上界 size-1);
  2. 优先使用std::vector(C++)替代裸数组,.at(index)带边界检查;
  3. 循环边界写清楚,避免i<=n多访问一次;
  4. 使用 ASan 检测越界读写;
  5. 禁止用户可控索引直接访问数组,先白名单校验。

五、C/C++ 其他高频内存漏洞补充

5.1 整数溢出

整数溢出:算术运算结果超出类型范围,发生回绕。整数溢出常被利用绕过长度检查,间接导致缓冲区溢出。

unsigned int len = userLen;
// 如果len非常大,len*2发生整数溢出变成小值
char *buf = malloc(len * 2); // 分配的内存过小
// 后续按len写入,造成堆溢出

修复:运算前检查是否溢出,使用大类型(size_t)、限制长度范围、使用安全数学运算。

5.2 空指针解引用

malloc失败返回 NULL,直接解引用造成崩溃。修复:分配后判空。

5.3 格式化字符串漏洞

printf(userInput),用户输入直接作为格式化字符串,可以泄露栈数据、任意读写。

// 高危:用户输入作为格式化字符串
printf(user_control_str);

修复:永远用printf("%s", userInput),不要把用户输入直接当格式串。

5.4 双重释放(Double Free)

同一块内存 free 两次,造成堆破坏。修复:释放后置空,使用前判空。

5.5 内存泄漏(Memory Leak)

分配后未释放,长期运行内存耗尽。修复:RAII / 智能指针,释放逻辑规范。

六、C/C++ 审计难点:指针、堆布局、编译器差异

6.1 指针算术与越界

ptr+offset容易越界,尤其 offset 用户可控时。审计重点:检查所有指针偏移计算,边界校验是否完整。

6.2 编译器差异与优化

不同编译器、不同优化等级(-O0/-O2),漏洞表现可能不同。审计和复现时注意编译选项一致性。

6.3 真实项目大规模代码

真实系统(浏览器、引擎、OS)代码量巨大,人工审计难,主要靠:

  1. ASan/Fuzzing(模糊测试)动态检测;
  2. 静态扫描(CodeQL、Coverity、Clang Static Analyzer);
  3. Fuzzing 工具:libFuzzer、AFL++,自动生成大量输入触发崩溃,定位漏洞。

6.4 缓解措施评估

现代系统有 ASLR/NX/Canary,但内核、驱动、嵌入式场景防护弱。审计漏洞时评估实际防护,判断可利用性。

七、SAST/ASan 检测结果复核(区分真漏洞 / 误报)

C/C++ 漏洞检测结果复核:

  1. 确认污点源:输入是否来自外部用户可控(网络、文件、命令行);
  2. 追踪数据流:用户输入是否传入不安全拷贝 / 索引 / 指针操作;
  3. 确认 Sink:strcpy、memcpy 长度、数组索引、free 后使用;
  4. 用 ASan 实际运行验证:是否触发内存错误,报错位置和调用栈;
  5. 判断可利用性:能否被外部输入触发、能否造成实际危害。

ASan 运行检测示例:

gcc -fsanitize=address -g -o vuln vuln.c
./vuln
# ASan会输出具体越界/溢出的地址和调用栈

常见误报场景:

  1. 输入来自内部固定长度数据,不可能超长;
  2. 缓冲区容量足够容纳所有输入;
  3. 索引范围始终合法;
  4. free 后指针未再使用,或已置空。

八、SRC 漏洞报告撰写要点(C/C++ 内存漏洞)

提交 C/C++ 内存漏洞(如针对开源组件、固件、中间件的 CVE 挖掘),报告写清楚:

  1. 漏洞名称(CWE 分类)、漏洞等级;
  2. 源码文件、函数、代码行号;
  3. 漏洞成因分析(内存布局、溢出 / 越界路径);
  4. POC / 触发输入、复现步骤;
  5. ASan 报错截图、调用栈;
  6. 漏洞影响(可能 RCE / 信息泄露);
  7. 修复代码、编译加固建议。

重点:内存漏洞报告必须有可复现的触发输入 + ASan 报错证据,纯代码分析容易被判无效,动态证据(ASan 崩溃、gdb 调用栈)是提高评级关键。

九、C/C++ 安全开发自查清单

开发提交代码前,对照自查清单:

  1. 是否使用不安全函数:strcpy/strcat/sprintf/gets/scanf (“% s”),全部替换为 strncpy/snprintf/fgets;
  2. 所有 memcpy/memmove/memset,长度参数是否校验不超过目标容量;
  3. 数组访问索引,是否校验边界(0 <= index < size);
  4. free/delete 之后,指针是否置空,使用前是否判空;
  5. malloc / 分配后是否判空,防止空指针解引用;
  6. 是否使用智能指针(C++)替代裸指针 new/delete;
  7. 用户可控输入是否可能超长、索引是否可控,追踪完整数据流;
  8. 算术运算是否可能整数溢出,长度计算前是否校验;
  9. printf 是否误把用户输入当格式化字符串;
  10. 编译是否开启防护:-fstack-protector-all、-z noexecstack、-z relro;
  11. 开发阶段是否用 ASan 跑测试,检测内存错误;
  12. 是否使用 Fuzzing 模糊测试补充检测。

十、总结

C/C++ 内存安全漏洞(缓冲区溢出、UAF、内存越界)是最底层、危害最大的漏洞类型,也是 PWN/CTF、SRC 二进制挖洞、系统安全的核心领域。和 Web 漏洞不同,C/C++ 漏洞的直接后果往往是任意代码执行、系统沦陷。

核心要点总结:

  1. 缓冲区溢出:strcpy/sprintf/memcpy 长度不当,覆盖返回地址 / 相邻堆块,可 RCE。修复用安全函数 + 限制长度 + 编译防护。
  2. UAF 释放后使用:free 之后指针继续使用,堆破坏、函数指针劫持。修复释放后置空、判空、用智能指针。
  3. 内存越界:数组索引 / 指针越界,越界读泄露信息、越界写破坏内存。修复严格校验索引边界、用 std::vector。
  4. 现代防护 ASLR/NX/Canary 提高利用难度,但漏洞存在即高危,审计重点在于确认是否可被外部输入触发。
  5. ASan 是 C/C++ 漏洞复现和定位的最佳工具,配合 gdb、Fuzzing,可以高效确认漏洞真实性。
  6. 安全编码最佳实践:安全函数、智能指针、边界校验、编译防护、ASan 测试。

C/C++ 安全开发是 “代码审计 + 二进制安全 + SDL” 综合能力的体现。掌握了内存漏洞成因和编码防护,既能写出更安全的系统级代码,也能为后续 PWN 二进制漏洞挖掘、SRC 挖掘奠定坚实基础。

免责声明:本文漏洞代码仅用于安全学习,禁止未经授权对任何系统进行漏洞利用。所有安全测试必须获得资产所有者授权,遵守《网络安全法》《数据安全法》等法律法规。


以上就是今天的分享,觉得有帮助,记得点赞+收藏,以上所说到的漏洞扫描工具都整理成压缩包了,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

在这里插入图片描述

img

同时还为准备学习网络安全(黑客)或者正在学习,整理了一套360独家内部资料,包含以下内容,如有需要也可文末获取:

①网络安全学习路线
②20份渗透测试电子书
③安全攻防357页笔记
④50份安全攻防面试指南
⑤安全红队渗透工具包
⑥网络安全必备书籍
⑦100个漏洞实战案例
⑧安全大厂内部视频资源
⑨历年CTF夺旗赛题解析

一、网络安全(黑客)学习路线

网络安全(黑客)学习路线,形成网络安全领域所有的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

在这里插入图片描述

二、网络安全教程视频

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。

三、网络安全CTF实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这里带来的是CTF&SRC资料&HW资料,毕竟实战是检验真理的唯一标准嘛~

img

四、网络安全面试题

最后,我们所有的作为都是为就业服务的,所以关键的临门一脚就是咱们的面试题内容,所以面试题板块是咱们不可或缺的部分,这里我给大家准备的就是我在面试期间准备的资料。

在这里插入图片描述

网安其实不难,难的是坚持和相信自己,我的经验是既然已经选定网安你就要相信它,相信它能成为你日后进阶的高效渠道,这样自己才会更有信念去学习,才能在碰到困难的时候坚持下去。

机会属于有准备的人,这是一个实力的时代。人和人之间的差距不在于智商,而在于如何利用业余时间,只要你想学习,什么时候开始都不晚,不要担心这担心那,你只需努力,剩下的交给时间!

这份完整版的网络安全学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

img

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐