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
PrivacyHardware SecurityPapers & Research
GitHubdivagr18/gpu-instance-fingerprint

gpu-instance-fingerprint

동일한 GPU들 사이에서도 원자적 덧셈이 도착하는 순서로부터 실행 중인 정확한 GPU의 지문을 채취합니다. 99.1% 정확도로 재부팅 후에도 유지되며, 위조할 수 없고, 기본 torch.index_add_ 호출만으로도 이 정보가 유출됩니다.

저장소 보기웹사이트
275274일 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

동일한 GPU 구별하기

블로그 글: 수치 출력으로 동일한 GPU 구별하기

같은 모델의 GPU 두 개가 동일한 합계를 아주 약간 다른 순서로 계산하며, 그 차이만으로 두 GPU를 구별할 수 있습니다. 작은 CUDA 커널이 19개의 동일한 NVIDIA A40 중 어느 칩에서 실행되었는지를 오직 자신이 계산한 숫자만으로 99.1% 정확도로 식별합니다. 일련번호를 읽지도 않고 타이머도 사용하지 않습니다.

  • 재부팅 후에도 유지됩니다. 컨테이너 재시작 후에도 12개의 측정값 모두 올바른 칩과 일치했습니다.
  • 위조할 수 없습니다. 한 칩 위의 공격자가 자신이 제어할 수 있는 모든 실행 설정을 조정해도 다른 칩의 지문과 최대 0.616의 상관관계밖에 얻지 못했습니다. 정품 칩은 평균 0.946, 최소 0.916으로 자기 자신과 일치합니다.
  • 기본 torch.index_add_ 호출로도 유출됩니다. 해당 연산의 평범한 출력만 읽어도 동일한 드라이버의 GPU들 사이에서 100% 정확도로 칩을 식별하며, 드라이버가 혼합된 경우에는 78%입니다.
  • 여러 모델에서 작동합니다: A40, RTX 3090, RTX 4090, L40S.

작동 원리: 수천 개의 스레드 블록이 하나의 카운터를 두고 경쟁할 때, 하드웨어가 누가 먼저 도착하는지를 결정합니다. 칩의 코어들 사이의 미세한 제조 차이가 그 순서를 매번 같은 방향으로 편향시킵니다.

PyTorch의 결정론적 모드(deterministic mode)는 index_add_ 유출을 제거합니다. 저장소에는 프로브, 아래 수치 뒤에 있는 모든 측정값, 그리고 각 결과를 노트북 CPU에서 재현하는 스크립트가 포함되어 있습니다.

결과

결과수치재현
활성 프로브, 19개의 동일한 A40, P3 채널 (수치 출력만 사용, %smid 없음)99.1% LOO (111/112), 우연 확률 5.3%, 분리도 4.28xanalysis/pooled_analyze.py
활성 프로브, A40 8개P3 100% LOO, 분리도 4.09x, 순열 p = 0.0005analysis/analyze_puf.py
드라이버 교란: 동일한 드라이버(580.159.04)의 A40 4개P3 100% (6.10x), P2 100% (5.43x)analysis/analyze_puf.py
재부팅: 컨테이너 재시작, UUID로 동일 다이 검증 (A40 4개)100% 재식별 (12/12), 두 채널 모두analysis/temporal_analyze.py
RTX 3090, 4개 드라이버 버전에 걸친 7개 칩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, A40 6개동일 드라이버 100% (9/9), 드라이버 간 78%analysis/passive_analyze.py
수동 시그니처 용량, K=65536약 217개의 독립 비트 (A40), 약 239개 (RTX 4090)analysis/roc_capacity.py
위조: 공격자가 제어하는 모든 실행 노브를 훑음최고 위조 0.616 상관관계 vs 정품 하한 0.92analysis/forge_analyze.py
경계: 바닐라 트랜스포머 fwd/bwd, dense GEMM, TF32 matmul칩 간 비트 단위 동일, 유출 없음analysis/leak_analyze.py, analysis/channel_analyze.py
방어: torch.use_deterministic_algorithms(True)수동 채널 제거됨; 역전파 비용 2.5-3.6배; 활성 프로브 영향 없음analysis/defense_analyze.py

LOO = 단일 측정값을 칩별 중심점(centroid)에 대해 분류하는 leave-one-out 최근접 중심점 분류. 분리도 = 칩 간 거리 / 칩 내 거리.

작동 원리

프로브(probes/probe_puf.py)는 작은 cupy 커널입니다. 128개 스레드로 이루어진 4096개의 블록을 실행합니다. 모든 스레드가 동일한 고정 FMA 체인을 수행한 뒤, 각 블록의 스레드 0이 atomicAdd(counter, 1)로 전역 카운터에서 티켓을 받습니다. 그 티켓이 해당 블록의 도착 슬롯입니다. 입력에서 칩 간에 다른 것은 아무것도 없으며, 유일하게 달라지는 것은 어떤 블록이 먼저 도착하는가입니다.

그 순서는 무작위가 아닙니다. 작업 분배기는 블록 인덱스를 거의 고정된 패턴으로 SM에 매핑하며, 주어진 다이의 각 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 버전, 그리고 에포크별 클럭과 온도를 기록합니다. 분석은 모델 이름으로 그룹화하고 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 A40s: 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 A40s: 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), dense 대조군, 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개 A40s)
Transformer 경계 및 결정론적 모드 비용python analysis/leak_analyze.pydata/transformer_leak (3개 A40s)
TF32 matmul 및 DVFS 경계python analysis/channel_analyze.pydata/channels (3개 A40s)
수동 채널에 대한 방어python analysis/defense_analyze.pydata/defense (2개 A40s)

analysis/analyze_puf.py는 채널당 2000회 셔플 순열 검정을 실행하므로, 더 큰 세트는 몇 분이 걸립니다.

비트 용량 및 ROC

단일 측정의 모든 쌍이 채점되며(정품 = 동일 UUID, 임포스터 = 다른 UUID), 등록 평균화가 없으므로 이는 비관적인 단일 샷 동작점입니다. 자유도는 iris 방식이며, 임포스터 해밍 거리에 대해 DoF = p(1-p)/var(HD)입니다.

서명쌍점수AUCEER용량
활성 P3, 19개 A40s644 정품 / 9367 임포스터잔차 상관0.9749.3% (d' = 2.95)
활성 P3, 19개 A40s부호 해밍0.84726.9%~5 비트
수동 K=4096, 8개 A40s부호 해밍0.87821.0%~97 비트
수동 K=65536, 6개 A40s부호 해밍0.95211.1%~217 비트
수동 K=65536, 3개 RTX 4090s부호 해밍0.0%~239 비트

활성 채널은 잔차 상관을 통해 가장 잘 읽힙니다: 그 부호 패턴은 공간적으로 상관되어 있어, 정체성이 크기에 담겨 있습니다. 수동 부호 서명은 긴 비트 문자열처럼 동작하며, 그 용량은 K에 따라 증가합니다: K=65536에서 약 200+개의 독립 비트로, 매우 큰 플릿을 인덱싱하기에 충분합니다.

수동 공격: 순수 torch.index_add_

활성 프로브는 커스텀 커널을 실행해야 합니다. 수동 변형은 표준 PyTorch 연산의 일반 출력만 읽습니다.

CUDA에서 torch.index_add_는 원자 연산으로 누적하므로, 각 출력 빈에 기여가 도착하는 순서는 하드웨어가 결정합니다. 일반적인 dense 영역(빈당 많은 기여)에서는 float32 합이 그 순서를 평균화합니다: 256개 빈에 2^20개의 기여가 있을 때, 모든 특징이 우연 수준으로 분류됩니다(6개 A40s에서 결합 17% 대 우연 17%). 해결책은 희소하고 높은 동적 범위의 영역입니다:

  • 빈당 4개 기여, K개 빈 (기본값 K=65536)
  • 크기가 2^-30에서 2^30까지 무작위 부호로 분포되어, 각 빈의 float32 합이 덧셈 순서에 의해 지배됨 (치명적 상쇄)
  • 고정 시드, 모든 칩에서 동일한 입력

각 칩의 안정적인 원자적 순서는 그다음 안정적인 출력을 만들어낸다. 시그니처는 float64 참조값에 대한 평균 ULP 잔차의 빈별 부호이며, K비트 문자열이다.

pip install numpy torch
python probes/passive_index_add.py 0 65536 > passive_r0.json   # replicate 0, K=65536
도구 다운로드