Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
gpu-instance-fingerprint — 根据原子加操作的落点顺序,对正在运行的 GPU 进行精确指纹识别,即使在相同的 GPU 之间也能区分。准确率达 99.1%,重启后依然有效,无法伪造,而一次默认的 torch.index_add_ 调用就会泄露它。 | Kitploit
工具/GitHubGitHub/divagr18/gpu-instance-fingerprint
隐私保护硬件安全论文与研究
GitHubdivagr18/gpu-instance-fingerprint

gpu-instance-fingerprint

根据原子加操作的落点顺序,对正在运行的 GPU 进行精确指纹识别,即使在相同的 GPU 之间也能区分。准确率达 99.1%,重启后依然有效,无法伪造,而一次默认的 torch.index_add_ 调用就会泄露它。

查看仓库网站
275274天前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

区分相同的 GPU

博客文章:通过数值输出区分相同的 GPU

两块相同型号的 GPU 以略微不同的顺序计算同一个求和,而这一差异足以将它们区分开来。 一个小型 CUDA 内核仅凭其计算出的数值,就能以 99.1% 的准确率识别出它运行在 19 块相同的 NVIDIA A40 中的哪一块上。它不读取序列号,也不使用计时器。

  • 它能经受重启。 容器重启后,全部 12 次测量都匹配到了正确的芯片。
  • 它无法被伪造。 在一块芯片上的攻击者即使调校其能控制的所有启动设置,与另一块芯片指纹的相关性最高也只达到 0.616。真实芯片与自身指纹的平均匹配度为 0.946,且从未低于 0.916。
  • 默认的 torch.index_add_ 调用也会泄露它。 仅读取该操作的普通输出,就能在同一驱动上的 GPU 中以 100% 的准确率识别出芯片,在混合驱动下则为 78%。
  • 它跨型号有效: A40、RTX 3090、RTX 4090 和 L40S。

原理:当数千个线程块争相更新同一个计数器时,硬件决定谁先到达。芯片核心之间微小的制造差异会以相同的方式偏置这一顺序。

PyTorch 的确定性模式消除了 index_add_ 泄露。该仓库包含探针、下文所有数字背后的每一次测量,以及可在笔记本电脑 CPU 上复现每项结果的脚本。

结果

结果数值复现
主动探针,19 块相同的 A40,P3 通道(仅数值输出,无 %smid)99.1% LOO(111/112),随机概率 5.3%,分离度 4.28xanalysis/pooled_analyze.py
主动探针,8 块 A40P3 100% LOO,分离度 4.09x,置换检验 p = 0.0005analysis/analyze_puf.py
驱动混杂因素:4 块 A40 运行在完全相同的驱动(580.159.04)上P3 100%(6.10x),P2 100%(5.43x)analysis/analyze_puf.py
重启:容器重启,通过 UUID 验证为同一芯片(4 块 A40)100% 重新识别(12/12),两个通道均如此analysis/temporal_analyze.py
RTX 3090,7 块芯片跨 4 个驱动版本P3 100% LOO(4.55x),P2 100%(8.12x),p = 0.0005analysis/cross_model_analyze.py
RTX 4090,2 块芯片位于同一主机和驱动P3 6/6(12.5x),P2 6/6(17.8x)analysis/cross_model_analyze.py
L40S,2 块芯片P3 4/4(17.2x),P2 4/4(13.8x)analysis/cross_model_analyze.py
被动 torch.index_add_,K=65536,6 块 A40100% 同驱动(9/9),78% 跨驱动analysis/passive_analyze.py
被动签名容量,K=65536约 217 个独立比特(A40),约 239 个(RTX 4090)analysis/roc_capacity.py
伪造:攻击者遍历其控制的所有启动旋钮最佳伪造相关性 0.616,对比真实下限 0.92analysis/forge_analyze.py
边界:普通 transformer 前向/反向、稠密 GEMM、TF32 矩阵乘法跨芯片逐位相同,无泄露analysis/leak_analyze.py、analysis/channel_analyze.py
防御:torch.use_deterministic_algorithms(True)被动通道被消除;反向传播开销 2.5-3.6x;主动探针不受影响analysis/defense_analyze.py

LOO = 对单次测量相对于各芯片质心进行留一法最近质心分类。分离度 = 芯片间距离 / 芯片内距离。

工作原理

探针(probes/probe_puf.py)是一个小型 cupy 内核。它启动 4096 个块,每块 128 个线程。每个线程执行相同的固定 FMA 链,然后每个块的线程 0 通过 atomicAdd(counter, 1) 从全局计数器领取一个票号。该票号就是该块的到达槽位。输入在不同芯片之间没有任何差异;唯一变化的是哪些块先到达。

这一顺序并非随机。工作分配器以近乎固定的模式将块索引映射到 SM 上,而给定芯片上的每个 SM 由于制造差异,运行速度比其同类略快或略慢。在 3000 次启动上取平均后,每个块索引会稳定到一个平均到达排名,该排名反映了它所落在的 SM 的速度。

fixed kernel, fixed input
        |
        v
blocks race to atomicAdd  -->  arrival slot per block (hardware-decided)
        |
        v  average over 3000 launches
mean arrival rank per block index (P3)  /  per physical SM via %smid (P2)
        |
        v  subtract the cross-chip mean (removes the architecture)
per-chip residual  -->  nearest-centroid match against enrolled chips

一次运行会产生三个通道:

  • P3,逐块排名。 每个块索引的平均到达时隙。它仅从内核的数值输出中读取:不使用 %smid,不使用计时器,不使用特权计数器。这是主要通道。
  • P2,逐 SM 排名。 通过 %smid 寄存器按物理 SM 分箱的相同到达时隙。按物理 SM 分箱可以平均掉热噪声,因此 P2 在负载下依然可靠:在 60 秒满载压力测试后仍保持 100% 重识别率(18/18)。
  • P1,边缘和。 固定归约的 float32 结果的直方图。它较弱(在 8 块 A40 上分离度为 1.20 倍),因为求和会破坏大部分顺序信息。

减去各芯片之间的均值可以消除芯片共有的部分(架构、驱动、编译器),只留下每个裸片的残差。同一芯片的两次测量结果彼此接近;不同芯片的测量结果则不会。

快速开始

git clone https://github.com/divagr18/gpu-instance-fingerprint.git
cd gpu-instance-fingerprint
pip install -r requirements.txt        # numpy + scipy, enough for every analysis

在包含的数据上运行分析(CPU,无需 GPU):

python analysis/pooled_analyze.py      # 19 A40s: 111/112 = 99.1%
python analysis/analyze_puf.py "a40_8chip/*.json"   # 8 A40s: P3 100%

在 GPU 上运行探测:

pip install numpy cupy-cuda12x
python probes/probe_puf.py 3 0 > gpu_a_r0.json   # 3 epochs, replicate 0 (about 2 s per epoch)
python probes/probe_puf.py 3 1 > gpu_a_r1.json

从同一型号的两块或多块 GPU 上各采集若干次重复数据,然后:

python analysis/analyze_puf.py "path/to/*.json"

每条 JSON 记录都会在测量数据旁记录 GPU UUID、驱动、cupy 版本以及每个 epoch 的时钟和温度。分析按型号名称分组,并按 UUID 标记芯片。

要通过 SSH 在远程机器上运行,scripts/run_remote.sh 会推送一个探测程序,在多个主机上并行运行并收集 JSON:

OUT_DIR=data/my_run REPS=4 scripts/run_remote.sh probe gpu1=root@HOST1:PORT gpu2=root@HOST2:PORT

docker/Dockerfile 构建一个预装 CUDA 12.8、cupy 和探针的镜像(docker build -f docker/Dockerfile -t gpu-instance-fingerprint .)。

复现每项结果

所有命令均在仓库根目录下于 CPU 上运行。脚本会自行在 data/ 中查找其数据。

结果命令数据
8 块 A40:P3 100%(4.09x,p=0.0005),P2 94%(4.63x),P1 28%(1.20x)python analysis/analyze_puf.py "a40_8chip/*.json"data/a40_8chip(8 块芯片 x 4 次重复)
同驱动 4 块 A40:P3 100%(6.10x),P2 100%(5.43x)python analysis/analyze_puf.py "a40_8chip/puf8_[4678]_*.json"data/a40_8chip
汇总:12 块芯片 98.6%(71/72);19 块芯片 99.1%(111/112),4.28x,97% 的芯片对之间的差异超过芯片内噪声的 2 倍python analysis/pooled_analyze.pya40_8chip、a40_stability(T1)、a40_reboot(T1)、a40_scale
重启:P3(裕度 5.07x)和 P2(裕度 5.03x)的重新识别率均为 100%(12/12)python analysis/temporal_analyze.py a40_rebootdata/a40_reboot(4 块芯片)
60 秒压力测试:基线 LOO 96%(23/24);P2 重新识别 100%(18/18)python analysis/temporal_analyze.py a40_stabilitydata/a40_stability(6 块芯片)
RTX 3090 x7、RTX 4090、3090 对、L40S 对python analysis/cross_model_analyze.pydata/rtx3090、data/rtx4090、data/cross_model
被动 index_add(K=65536、K=4096)、稠密对照、scatter-overwritepython analysis/passive_analyze.pydata/passive_*
ROC 与比特容量python analysis/roc_capacity.py所有 A40 探针数据、passive_k4096、passive_k65536、rtx4090
伪造:最佳伪造 0.616 对比真实 0.946(下限 0.916),冒充者 0.174python analysis/forge_analyze.pydata/forge(3 块 A40)
Transformer 边界与确定性模式开销python analysis/leak_analyze.pydata/transformer_leak(3 块 A40)
TF32 matmul 与 DVFS 边界python analysis/channel_analyze.pydata/channels(3 块 A40)
针对被动信道的防御python analysis/defense_analyze.pydata/defense(2 块 A40)

analysis/analyze_puf.py 对每个信道运行 2000 次打乱的置换检验,因此较大的数据集需要几分钟。

比特容量与 ROC

每一对单次测量都会被评分(真实 = 相同 UUID,冒充 = 不同 UUID),且不进行注册平均,因此这些是悲观的单次操作点。自由度的计算方式与虹膜识别类似,DoF = p(1-p)/var(HD),基于冒充者汉明距离。

签名对数评分AUCEER容量
主动 P3,19 块 A40644 真实 / 9367 冒充残差相关性0.9749.3%(d' = 2.95)
主动 P3,19 块 A40符号汉明距离0.84726.9%~5 比特
被动 K=4096,8 块 A40符号汉明距离0.87821.0%~97 比特
被动 K=65536,6 块 A40符号汉明距离0.95211.1%~217 比特
被动 K=65536,3 块 RTX 4090符号汉明距离0.0%~239 比特

主动信道最好通过残差相关性来解读:其符号模式在空间上是相关的,因此身份信息存在于幅度之中。被动符号签名的行为类似于一个长比特串,其容量随 K 增长:在 K=65536 时约有 200+ 个独立比特,足以索引一个非常庞大的集群。

被动攻击:普通的 torch.index_add_

主动探针需要运行自定义内核。被动变体只读取标准 PyTorch 算子的普通输出。

CUDA 上的 torch.index_add_ 使用原子操作进行累加,因此每个输出 bin 中贡献到达的顺序由硬件决定。在通常的稠密场景下(每个 bin 有大量贡献),float32 求和会将该顺序平均掉:在 2^20 个贡献分配到 256 个 bin 的情况下,每个特征都只能以随机水平分类(在 6 块 A40 上,合并准确率为 17%,与 17% 的随机水平相当)。解决办法是采用稀疏、高动态范围的场景:

  • 每个 bin 4 个贡献,共 K 个 bin(默认 K=65536)
  • 幅度从 2^-30 到 2^30 分布,并带有随机符号,因此每个 bin 的 float32 求和由加法顺序主导(灾难性抵消)
  • 固定种子,每块芯片上的输入完全相同

随后,每个芯片稳定的原子排序会产生稳定的输出。签名是相对于 float64 参考值的平均 ULP 残差的逐 bin 符号,即一个 K 位字符串。

pip install numpy torch
python probes/passive_index_add.py 0 65536 > passive_r0.json   # replicate 0, K=65536

A40 上的结果(K=65536,6 块芯片):同一驱动版本内 100%(9/9),混合驱动下 78%(随机概率 17%)。原始算子输出还带有依赖驱动的成分,而主动探测器的顺序残差没有这一成分,因此被动通道在同一驱动内最为干净。在 K=4096 时,跨 8 块芯片达到 67%。散射覆盖(scatter_ 使用重复索引,最后写入者胜出)在 5 块芯片上达到 60%。在 RTX 4090 上,被动签名可区分全部 3 块芯片(6/6)。该机制出现在真实工作负载中,例如 MoE 分发、GNN 聚合以及存在大量冲突的 embedding-bag 反向传播。

泄漏止于何处

实例泄漏仅由硬件调度的原子竞争顺序承载,别无其他。所有测试过的确定性路径在相同芯片之间均逐位一致:

  • 普通 transformer(6 层,d=512,固定权重和输入,3 块 A40,200 次前向 + 200 次反向传播):前向 logits 和 embedding 梯度的非确定性比例为 0.000,且在芯片之间逐位一致。LOO 处于随机水平(2/6)。
  • 稠密 GEMM / TF32 张量核心(高瘦矩阵乘法,3 块 A40,300 次运行):非确定性比例为 0.000,残差在芯片之间逐位一致。
  • DVFS(30 秒 matmul 负载下的稳态时钟和功耗,3 块 A40):芯片间时钟差异为 13.4 MHz,而芯片内抖动为 12.5 MHz;功耗差异为 2.3 W,而抖动为 6.5 W。不可区分。

因此,在 GPU 上运行普通 ML 本身并不会暴露芯片。暴露需要顺序主导的机制:主动探测器,或具有大量高动态范围原子贡献的算子。

伪造

能否让芯片 A 发出芯片 B 的指纹?攻击者无法改变硅片或设置原子顺序,但可以控制启动:网格大小、块大小、原子操作前的计算消耗,以及注入的自旋延迟。experiments/forge_probe.py 在 3 块 A40 上各扫描 10 种配置(每种 2 次运行),并记录每种配置的每 SM 签名。

比较相关性
真实:同一芯片,运行间0.946(下限 0.916)
冒充:芯片 A 对芯片 B0.174
伪造:A 在整个配置组中的最佳配置对 B0.616 最大
下载工具