关于论文《FLUSH+RELOAD:一种高分辨率、低噪声的L3缓存侧信道攻击》的理解

文章目录

  • [1. 引言](#1. 引言)
  • [2. 预备知识](#2. 预备知识)
    • [2.1 内存共享](#2.1 内存共享)
    • [2.2 缓存架构](#2.2 缓存架构)
    • [2.3 RSA 加密算法](#2.3 RSA 加密算法)
  • [3. FLUSH+RELOAD 攻击技术](#3. FLUSH+RELOAD 攻击技术)
    • [3.1 原理](#3.1 原理)
    • [3.2 内存访问与探测重叠](#3.2 内存访问与探测重叠)
    • [3.3 CPU缓存优化的干扰](#3.3 CPU缓存优化的干扰)
    • [3.4 攻击的代码实现](#3.4 攻击的代码实现)
  • [4. 攻击 GnuPG RSA](#4. 攻击 GnuPG RSA)
    • [4.1 攻击](#4.1 攻击)
    • [4.2 推测执行的影响](#4.2 推测执行的影响)
    • [4.3 降低错误的方法](#4.3 降低错误的方法)
    • [4.4 攻击的局限性](#4.4 攻击的局限性)
  • [5. 缓解技术](#5. 缓解技术)
  • [6. 相关工作](#6. 相关工作)
  • [7. 结论](#7. 结论)

1. 引言

现代系统为了省内存,大量使用共享内存页(比如共享库等),但这同时来带了一些问题:

进程间的独立性/隔离性,通常可以通过共享页强制只读或共享数据写时拷贝的方式实现。
虽然可以保证进程无法修改共享页的内容,但也存在一些无法阻止的进程间干扰。有一种通过共享页面产生的干扰形式会导致CPU缓存的共享使用 ,那么当一个进程访问共享页面的某个部分时,由于局部性原理,访问的数据会被放进缓存。
作者观察到,clflush 指令会把数据从所有缓存层级清空,包括共享的 L3(最后一级缓存)。
基于这个发现,作者设计了 FLUSH+RELOAD 攻击。

相比于之前的缓存侧信道攻击方法,FLUSH+RELOAD 攻击具有一些新的优势:

它是一种跨核心攻击 ,攻击进程核受害者进程可以在不同的CPU核心上执行;并且这种攻击还适应虚拟化环境,可以跨虚拟机攻击 。(虚拟化环境,就是多个虚拟机共享同一台物理服务器硬件资源的环境,那就共享CPU缓存)
该攻击还能精确定位到单个缓存行,以往的大多攻击只能定位到某个范围(如特定缓存集)的访问。
该攻击只针对最后一级缓存,同一处理器上的多个核心共享最后一级缓存。

2. 预备知识

2.1 内存共享

现代系统为了节省内存,会让多个进程 / 虚拟机共用相同的内存页,主要有两种共享方式:

  1. 内容感知共享
    相同的文件、库(如 libc)只加载一份,所有进程映射到同一块物理内存。
  2. 内存去重
    系统主动扫描内存,把内容完全一样的页合并成一份。

由于内存页可在非协作进程之间共享,系统必须保护这些页的内容,以防恶意进程修改共享内容。保护的方法:

写时拷贝:如果进程都是只读,则共享同一份,一旦某个进程发生写入,则单独拷贝一份,你修改自己的,不要影响别人。

但是写时复制只能防止修改数据,不能防止缓存侧信道泄露(内存共享本身,就是侧信道攻击的天然温床)。

2.2 缓存架构

CPU缓存主要是为了缓和cpu和主存之间的一个速度差异,根据局部性原理,可以把主存中要频繁访问的部分数据副本存放在高速缓存中,然后当CPU需要读写数据时,就优先去cache中查找,如果cache命中,那就直接从cache中获取到数据了,就不需要去访问速度较慢的内存了。

现代处理器采用由多个缓存组成的缓存层级结构

比如酷睿i5-3470处理器的缓存层级结构包含三级缓存:一级缓存(L1)、二级缓存(L2)和三级缓存(L3)

(4核处理器)
从图中可以清晰地看到:
L1 / L2:每个核心私有,别人看不见
L3(Last Level Cache,LLC):末级缓存,所有核心共享
(这是 FLUSH+RELOAD 能跨核心攻击的根本原因)
缓存中内存的单位是行,每行包含固定数量的字节。缓存由多个缓存组组成,每个缓存组存储固定数量的缓存行。

关键硬件特性------Intel 的 L3 是 Inclusive(包含式)缓存

即L3缓存中包含了L1、L2中的所有数据。
因此,从L3驱逐数据会同时从所有核心的L1/L2中移除该数据。 (因为如果一个数据在 L1 或 L2 里,
那么它 必须 同时在 L3 里)
本文的攻击正是利用了这种缓存行为。

攻击的基本原理:

内存访问速度:缓存(L1>L2>L3) ≫ 主存
攻击者先把缓存置于已知状态
看受害者执行后的访问速度变化
推断出受害者访问了哪些内存、执行了哪些代码

以往缓存攻击的缺点:

以往关于缓存侧信道攻击的大多数研究都依赖于受害者和间谍在同一个处理核心内执行(针对L1缓存)。无法跨核心、跨虚拟机。
部分先前的研究确实将 LLC(L3) 用作信息泄露通道。然而,受限于缓存大小(L3缓存容量大,每次状态设置和探测都很耗时,而且只能获得粗略的缓存集级信息,最终这些信道的数据传输速率很低),这些通道的带宽较低。

2.3 RSA 加密算法

RSA 是一种支持加密和签名的公钥密码系统,RSA 解密 / 签名要算一个 "指数":m = c^d mod n

d 是私钥(攻击者最想偷的)
平方-乘(Square-and-Multiply)算法 会逐位扫描
d 的二进制表示(从最高位到最低位)
对每一位:
总是执行一次"平方+模约减"(不论该位是0还是1)。
仅当该位 = 1 时,再额外执行一次"乘法+模约减"。
每一位是 0 或 1,会走不同代码分支(对应不同的缓存行)

3. FLUSH+RELOAD 攻击技术

FLUSH+RELOAD 技术是 PRIME+PROBE 技术的一种变体,它依赖于间谍进程和受害者进程之间共享页面。

借助共享页面,间谍能够确保特定的内存行从整个缓存层级中被驱逐。间谍利用这一点来监控对该内存行的访问 。该攻击是对 Gullasch 等人提出的技术的一种改进,其中包含了适用于多核和虚拟化环境的调整。

3.1 原理

一轮攻击包含三个阶段:

第一步flush:攻击者先用指令清空指定cache行的数据。
第二步wait:清掉之后,如果受害者在攻击者 reload之前访问了同一块物理内存,这时CPU就会因为cache未命中而从主存重新加载数据,加载过程就会把数据再次放进cache。
第三步reload:然后攻击者如果再去访问它刚才清空的那个位置,因为受害者刚刚把数据加载进了缓存,所以如果攻击者读取时速度很快(cache命中),说明受害者访问过;反之如果慢(cache未命中),说明受害者没访问过。
最终攻击者通过"速度快慢",就能知道受害者程序刚才访问了哪块内存!

3.2 内存访问与探测重叠

完全重叠:

攻击流程是:
清缓存(FLUSH)
等待
读内存测时间(RELOAD)
如果在第 3 步攻击者正在测的时候,
受害者刚好在访问同一段内存

攻击者的测量会被干扰,两者同时访问,不过谁触发了数据的加载(内存放入cache),攻击者测到的结果都是长时间(cache未命中),就会判定为受害者未访问,但事实是两者同时在访问,那攻击者就漏掉了这次访问

部分重叠:

受害者的访问开始于攻击者 Reload 阶段已经开始之后、但在攻击者的读操作完成之前。
不论谁先谁后,攻击者测到的时间依然是一个较长的时间(依然远大于cache命中的访问时间),所以依然判定为为访问,仍会漏检!

延长等待时间可以降低漏检?

漏检的根本原因:受害者的访问落在了 Reload 阶段(与重载重叠)。
增加 Wait 阶段的长度,给受害者更充裕的时间去访问。这样受害者的访问更有可能完全落在 Wait 阶段,然后在 Reload 开始时数据已经在缓存中 → 攻击者测得短时间 → 正确检测。

延长等待时间可以降低攻击的粒度(分辨率)?

在 FLUSH+RELOAD 中,粒度指的是攻击者能够区分两次连续访问之间最小时间间隔的能力。粒度越细,就能越精确地知道受害者在哪个确切的时刻访问了某个内存行。

如果等待时间较短,恰好每个轮次刚好覆盖一个操作,可以清晰看到 Square → Multiply 的序列。
如果等待时间较长,一次采样只能看到一次"有没有访问",但无法区分这一轮里到底发生了平方还是乘法,甚至可能把两个操作都混在一轮里。
最终攻击者得到的比特序列会严重失真。

在不提高错误率的前提下,提高攻击分辨率的一种方法是针对频繁发生的内存访问(例如循环体)。如何理解?

分辨率越高:攻击者能分辨间隔越短(例如几百个时钟周期)的两次内存访问。这样就能区分 RSA 指数运算中紧挨着的"平方"操作和"乘法"操作,从而准确还原每一个密钥比特。
循环体内的指令(例如平方函数中的某条内存读取)会在短时间内被反复执行。比如一个循环执行 100 次,每次执行都会访问同一个内存行(例如循环体第一条指令所在的缓存行)。
所以针对循环体检测,漏掉所有访问的概率非常低。
代价:你无法区分循环内的哪一次具体访问被检测到了,只能知道"在这个时间窗口内,该循环体的某条语句被执行过"。
但这对于很多攻击目标已经足够,例如判断 RSA 指数比特中是否发生了乘法(因为乘法函数也会被循环调用)
通过监控频繁访问的内存行(循环体),利用其重复特性,使得即使 Wait 很短(攻击粒度细),漏掉所有访问的概率也极低 → 可以在不增加错误率的前提下提高分辨率。

3.3 CPU缓存优化的干扰

CPU 的优化(空间预取、推测执行)会导致攻击者测到"缓存命中",但这个命中可能并不是受害者程序真正按逻辑执行了那条内存访问,而是 CPU 自己根据局部性原理或者推测执行"提前"或"额外"加载了数据。
因此产生误报(false positive)------攻击者以为受害者执行了某段代码或访问了某个数据,实际上并没有。

3.4 攻击的代码实现

攻击者想干什么

攻击者想偷偷知道:受害程序在某个时间段有没有读过一个特定的内存地址(比如 RSA 乘法函数里的某条指令)。

方法:
先把那个地址从所有缓存里踢出去(flush)。
等一会儿,让受害者有机会运行。
再去读那个地址,并计时。
如果读得快(几十个周期)→ 受害者读过,数据已在缓存里。
如果读得慢(几百个周期)→ 受害者没读过,数据还在内存里。

probe函数剖析

先看C语言部分:


整体的大逻辑:传入要探测的目标内存地址,返回受害者是否访问过(1访问过,0未访问)
adrs:你要探测的目标内存地址
time:用来存这次访问的耗时(CPU 周期数),volatile是防止编译器把这个变量优化没了
return time < threshold:和预设的阈值比(攻击者阈值的设计取决于系统,论文中设置为120周期),时间短 = 缓存命中 = 受害者访问过,返回1;否则返回0

内嵌汇编逐句拆解:

使用asm volatile内嵌了一段汇编指令:

mfence :内存屏障指令,强制 CPU 完成之前所有的内存读写操作,然后再执行 mfence 之后的任何内存操作。即阻止 CPU 对内存访问的乱序重排,确保内存操作的顺序与程序代码的编写顺序一致
lfence:加载屏障指令,强制 CPU 完成它之前所有的加载(load)操作,并且阻止它之后的任何加载操作被提前执行。即确保后面的读指令不会"越位"到前面的读指令之前。
为什么加这两句?
因为 CPU 会乱序执行指令,比如本来要先计时再读内存,CPU 可能先读内存再计时,导致结果全错。
再往下:
rdtsc:读 CPU 的时间戳计数器,把当前 CPU 运行的周期数读到edx:eax寄存器里(低 32 位在eax,高 32 位在edx)
lfence:屏障,确保rdtsc执行完,再往下走
movl %%eax, %%esi:把刚才读到的 "开始时间" 存到esi寄存器里,后面用来算差值
再往下,Reload 阶段:读目标内存
movl (%1), %%eax:%1对应输入的adrs(因为输入约束是"c"(adrs),adrs会被放到ecx寄存器里,(%1)就是ecx里的地址)
所以这句就是:读取目标地址adrs的数据到eax寄存器对应攻击的reload阶段
lfence:确保读内存的操作完成,再往下计时,保证时间差就是读内存的时间
rdtsc:再读一次时间戳,这就是 "结束时间"
subl %%esi, %%eax:用 "结束时间 - 开始时间 ",得到两次rdtsc之间的周期数 ------ 也就是刚才读内存的耗时,结果存在eax
Flush 阶段:
clflush 0(%1):clflush是 x86 的指令,作用是:把指定地址的数据,从所有缓存层级(L1/L2/L3)彻底清掉
这里0(%1)就是目标地址adrs,所以这句直接把刚才读的内存从缓存里清空了
就是攻击里的Flush阶段 ,清掉缓存,为下一次探测做准备
: "=a" (time):把汇编结束后eax寄存器里的值(也就是访问时间),存到 C 变量time里,这样后面的return time < threshold就能判断是否命中缓存了

把代码和攻击流程对应起来:

为什么代码中:flush在reload之后?

真正的攻击是一个无限循环,每一步都包含三个阶段:等待 → 重载 → 刷新。

第 3 步的"刷新"是为下一轮的"等待"服务的:刷新后缓存干净(这样下一次探测时,缓存是干净的,能保证结果准确),下一轮等待期间如果受害者访问,数据就会被加载进缓存。

4. 攻击 GnuPG RSA

这一节,作者介绍了如何利用 FLUSH+RELOAD 技术从 RSA 的 GnuPG 实现中提取私钥的各个组成部分。

4.1 攻击

上面预备知识部分我们已经了解到:


平方乘算法一种用于高效计算‌大整数 模幂运算‌的核心算法,广泛应用于 RSA 等公钥密码系统中。
GnuPG 的 RSA 实现天然存在分支依赖密钥比特的漏洞。
私钥的每一位,会直接控制程序走不同的代码分支, CPU 执行的指令不一样!内存地址不一样!缓存行不一样!

作者在两种场景下进行了测试:

同操作系统场景:攻击者与受害者作为两个独立进程运行在同一操作系统。
跨虚拟机场景:受害者运行在一个虚拟机,攻击者运行在另一个虚拟机(同物理主机)。

探针(Probe)布置:

攻击者选择受害程序代码中频繁执行且能区分平方和乘法操作的内存行
借助 GnuPG 的调试符号中定位平方、乘法和模约减函数的地址。
为实现跟踪,攻击程序将时间划分为固定的2500个周期为一个时间槽。
选择每个函数内靠近末尾的指令所在缓存行,每个时间槽内,依次对三个目标地址执行 probe(先 reload 测量时间,再 flush)。测量结果反映的是刚刚过去的等待阶段内受害者是否访问了该地址。 (对应probe函数实现中flush在reload之后)
通过监测,攻击者可以了解到平方函数和乘法函数是否被执行:如果执行了乘法,则私钥的这一比特位为1;未执行乘法,则为0。
这样就可以依次恢复出每一比特。

4.2 推测执行的影响

什么是推测执行?

现代 CPU 为了提高执行效率,采用流水线和乱序执行。当遇到条件分支(例如 if (e_i == 1))时,CPU 不知道条件是否成立(因为需要的值可能还在从内存加载中)。为了不让流水线空闲,CPU 会猜测分支的方向("猜是 true"或"猜是 false"),然后提前执行猜测路径上的指令。如果猜对了,结果直接使用,速度很快;如果猜错了,CPU 会丢弃错误路径的结果并回滚状态。

但是这会产生一个后果:

即使猜错,缓存的状态已经被改变了------因为执行过程中访问过的内存行已经被加载到缓存中。

这可能会干扰攻击者的测量:

为了消除此影响,作者将探针移动到函数末尾的指令行------这些指令在推测执行时不太可能被提前加载。

4.3 降低错误的方法

  1. 依托CRT-RSA幂运算的特性

用 CRT(中国剩余定理)把私钥拆成了两部分dp和dq,分别计算模 p 和模 q 的结果,再合并。
对攻击者来说:只要攻破其中一个子私钥(比如 dp),就能分解 n、拿到完整私钥,相当于攻击难度减半了

  1. 合并多次签名

每次签名时,错误出现在哪些比特位置是随机的、相互独立的。因此,两次独立的观测,都在同一比特位出错的概率极低。
通过比较两次观测的结果,就可以"交叉验证"出正确的比特值(比如第一次观测该比特位丢失了,但第二次成功捕获了,那就拿到了)。

4.4 攻击的局限性

作者也坦诚讨论了实际攻击中的限制:

  1. 需要攻击者和受害者运行在同一物理 CPU 上

要使攻击生效,攻击程序和受害者程序必须在同一个物理处理器上执行。在论文的测试中,作者在多处理器系统上设置了处理器亲和性。然而,在真实的攻击场景中,该攻击的效果取决于系统调度器。

  1. 轻负载环境

测试时系统几乎没有其他负载(只有攻击者和受害者),实际多进程运行的系统中的噪声可能降低捕获质量。如果同时运行多个 GnuPG 实例,攻击者无法区分哪个实例的访问,导致无法恢复数据。

  1. 密钥越短,对攻击的抵御能力越强?

对于短密钥,整个指数运算非常快,而攻击者每个时间槽至少需要 2200 周期(受探测三个内存位置的开销限制),导致总时间槽数量很少,每个槽内覆盖多个指数比特,无法获得足够的执行轨迹分辨率来区分平方和乘法,因此恢复私钥更加困难。

5. 缓解技术

论文指出,FLUSH+RELOAD 攻击依赖于四个因素的组合:

  1. 秘密数据(如私钥指数)影响到内存访问模式(平方-乘算法的分支)。
    2.攻击者与受害者共享内存页(通过文件映射或内存去重)。
    3.高分辨率的时间测量(rdtsc 等)。
    4.clflush 指令可以被非特权用户无限制使用。

阻断其中任何一个因素,就可以阻止攻击。作者分别探讨了硬件、软件、密码算法层的缓解方案。

5.1 硬件层面的对策

限制 clflush 指令的使用

问题:x86 架构允许任何用户态程序执行 clflush,没有权限检查

建议:修改处理器,使得 clflush 仅在以下情况有效:

进程对目标内存页有写权限;
或者操作系统为某些特殊页面(如设备共享内存)显式授予 flush 权限

实现方法:

可以在现有的 PAT(Page Attribute Table------页面属性列) 增加一个新的内存类型,限制 flush 访问。只有具有特定类型的页面才允许被 clflush。
这样攻击者只能 flush 自己拥有写权限的页面,而共享的代码页通常是只读的,那么攻击就无法进行。

但这需要硬件修改,无法保护现有硬件。

ARM 架构对比:

ARM 的缓存维护指令只能在特权模式(内核态)执行,用户态无法执行类似 clflush 的操作。因此,FLUSH+RELOAD 攻击在 ARM 上不适用。

AMD 处理器的特殊性:

论文讨论了当代 AMD 处理器(如 A10-6800K、Opteron 6348),发现 FLUSH+RELOAD 攻击似乎无效。
原因AMD 的缓存是非包容性(non-inclusive)的 。数据在 L1 缓存中不一定同时存在于 L3 中。当攻击者在自己的核心上执行 clflush 时,只能刷新自己核心的 L1/L2 以及共享的 L3,但其他核心的 L1 缓存中可能仍然保留着该数据。受害者如果运行在其他核心上,其 L1 缓存命中,不会触发从内存加载,攻击者就检测不到。
但老款 AMD 处理器(如 Opteron 2212)具有包容性缓存,仍然易受攻击

硬件缓解的局限性:

上述基于硬件的对策无法为该问题提供即时解决方案。它们需要时间来研发,且无法保护现有硬件。因此,要立即缓解该攻击,需要采用基于软件的解决方案。

5.2 软件层缓解

禁止页面共享

既然攻击需要共享页面,那么阻止共享就能防御。

然而,这种做法与操作系统和虚拟化管理程序中日益增加的共享趋势相悖。完全取消页面共享 将大幅增加现代操作系统的内存需求,因此不太可能成为可行方案

部分禁用:

禁止敏感代码的共享
对于敏感代码(如加密库),程序加载器可以将其映射为私有副本,而不是共享。
禁用内存去重功能
在虚拟化环境中,关闭内存去重。
论文作者强烈建议:在公共云中,应该关闭内存去重,因为节省的内存不值得牺牲客户间的隔离性。

软件多样化

通过静态或动态地重排代码/数据,使得每个进程/虚拟机的内存布局不同,从而破坏页面共享。

因为即使可执行文件的内容相同,如果代码顺序被重排(例如插入随机指令、函数重排、基本块随机化),那么不同进程的同一逻辑功能所在的物理页内容就会不同,页面去重无法合并它们(那就无法共享)。

已有技术:
静态重排 :编译时对代码进行随机置换,生成每个客户独有的二进制。
运行时多样化 :在加载或运行时动态修改代码布局。
注意:仅仅改变虚拟地址映射无效 ,因为 LLC 使用物理地址索引,而共享页的物理地址相同。必须改变物理页的内容(即代码本身的二进制排列),使得物理页不再相同。
局限:增加了内存占用(无法共享)和部署复杂性。

降低时钟分辨率

FLUSH+RELOAD 与其他侧信道攻击类似,依赖于高分辨率时钟的可用性。降低时钟分辨率或为时钟测量引入噪声可作为抵御该攻击的一种对策。
但局限性在于攻击者可以采用其它方法生成高分辨率时钟。

5.3 密码算法层面的对策

无论采用上述何种措施,密码软件都应得到保护以抵御此类攻击。

始终平方乘算法

论文提到,作者披露相关信息后,GnuPG 团队已经发布修复版本(1.4.14 和 libgcrypt 1.5.3),使用"始终平方乘"算法缓解该攻击的威胁。下面来分析一下它的修复思路:

原始平方-乘的问题 :乘法操作只在比特=1时执行,导致执行路径不同。
所以,修复思路 :无论比特是0还是1,都执行平方和乘法,但对比特=0的情况丢弃乘法的结果。这样,每次循环都执行完全相同的指令序列(平方+约减+乘法+约减),只是最终选择是否使用乘法的结果。

那这样的话,攻击者监控乘法函数的缓存行会发现每次循环都有乘法活动,无法区分比特是0还是1 。因此 FLUSH+RELOAD 无法获取指数比特。
但是要注意防止编译器优化,编译器优化可能会移除"无用"的乘法(如果它认为 y 没有被使用)
代码中仍有一个 if (e_i == 1) 分支,理论上可能被分支预测分析攻击。但由于这个分支很短(可能小于一个缓存行),而且推测执行可能无论比特值如何都会预取分支两边的代码。
所以作者认为 FLUSH+RELOAD 无法利用它。

恒定时间指数运算

在这种运算方式下,指令序列和访问的内存位置是固定的,且不依赖于指数位的值。

6. 相关工作

对比前人研究,突出了 FLUSH+RELOAD 攻击的一些优势和创新

之前的缓存攻击又慢、又吵、只能同核心、没法实用;

本文的 FLUSH+RELOAD 第一次实现了跨核心、跨 VM、高精度、快速度、能直接偷密钥的缓存侧信道攻击。

7. 结论

总结了 FLUSH+RELOAD 攻击的严重性:它能够跨核心、跨虚拟机高效提取 GnuPG 的 RSA 私钥(仅需一次或两次签名)。

该攻击利用了 Intel x86 架构允许用户态无限制地使用 clflush 指令,导致只读页面共享成为信息泄露通道。

作者认为,尽管 GnuPG 已通过"平方-乘-始终"算法修复,但并未解决论文中提到的 更广泛的威胁。

要解决clflush 指令的这一缺陷,根本性的硬件修复和在虚拟化环境中关闭内存去重才是更彻底的解决方案。

相关推荐
云游牧者1 小时前
K8S安全框架深度解析-从认证到RBAC实战完全指南
安全·容器·kubernetes·rbac·kubeconfig·rolebinding
Mahir082 小时前
Redis 分布式锁与 Redisson 深度解析:从原生实现到工业级解决方案
数据库·redis·分布式·缓存·面试
六月雨滴2 小时前
Oracle 数据库网络安全
数据库·安全·dba
xiaoxue..2 小时前
详解:XSS 攻击和 CSRF 攻击
安全·面试·xss·csrf
闵孚龙2 小时前
Claude Code Hooks 用户自定义拦截点全解析:AI Agent 自动化、安全治理、插件扩展、可观测性核心机制
人工智能·安全·自动化
看我干嘛!2 小时前
Redis安装
数据库·redis·缓存
黎阳之光2 小时前
数智孪生,全景可视——黎阳之光透明仓库,重构智慧仓储新范式
大数据·人工智能·算法·安全·数字孪生
Arman_2 小时前
Rust 客户端安全上传下载微软 Azure Blob:rusty-cat SAS 预签名实战
安全·microsoft·rust·azure·断点续传
祁白_2 小时前
[HCTF 2018]WarmUp1
安全·渗透·测试·ctf·writeup