티스토리 뷰
728x90
반응형
IOMMU
VT-d
AMD-Vi
C++
IOMMU / VT-d / AMD-Vi 활성 여부를 C++로 감지하기 (Linux & Windows)
DMA(Direct Memory Access) 기반 위협에 대응하려면 IOMMU(= VT-d / AMD-Vi) 활성 여부를 신뢰성 있게 확인할 수 있어야 합니다. 이 글은 운영체제별로 C++ 코드만으로 IOMMU 활성 신호를 감지하는 가장 실용적인 방법을 정리합니다.
목차
TL;DR
- Linux:
/sys/kernel/iommu_groups/
에 그룹 디렉터리가 1개 이상 존재하면 실질적으로 IOMMU가 활성입니다./sys/class/iommu
존재나/proc/cmdline
파라미터만으로는 “요청됨” 신호일 뿐, 확정 신호는 아닙니다. - Windows: WMI의
Win32_DeviceGuard.AvailableSecurityProperties
배열에 값 3(DMA protection available)이 포함되면 플랫폼 차원에서 DMA 보호 기능이 가용하다는 뜻입니다. (실제 활성 여부는 펌웨어/드라이버/OS 조합에 좌우)
왜 감지해야 할까?
DMA 공격(예: Thunderbolt/PCIe 계열)은 메모리를 직접 엑세스할 수 있어 전통적인 소프트웨어 방어만으론 부족할 때가 많습니다. IOMMU는 장치의 DMA 접근을 격리/매핑하여 이 위험을 줄여주는 핵심 기능이므로, 보안 제품/에이전트는 배포 환경에서 자동으로 활성 여부를 감지하고 정책을 조정할 필요가 있습니다.
Linux: IOMMU 활성 감지 C++ 예시
핵심 신호:
/sys/kernel/iommu_groups/
내부에 디렉터리가 1개 이상 → 실제 DMA remapping 활성/sys/class/iommu
존재 → 커널 IOMMU 클래스가 올라옴(참고 신호)/proc/cmdline
에intel_iommu=on
또는amd_iommu=on
→ 커널 파라미터 “요청”(참고 신호)
// linux_iommu_check.cpp
#include <filesystem>
#include <fstream>
#include <string>
#include <iostream>
bool has_path(const char* p){ return std::filesystem::exists(p); }
bool has_iommu_groups() {
const std::filesystem::path base{"/sys/kernel/iommu_groups"};
if (!std::filesystem::exists(base)) return false;
size_t groups = 0;
for (auto& e : std::filesystem::directory_iterator(base)) {
if (e.is_directory()) ++groups;
}
return groups > 0;
}
bool cmdline_requests_iommu() {
std::ifstream f("/proc/cmdline");
if (!f) return false;
std::string s; std::getline(f, s, '\0');
return s.find("intel_iommu=on") != std::string::npos ||
s.find("amd_iommu=on") != std::string::npos;
}
int main() {
bool class_exists = has_path("/sys/class/iommu");
bool groups_active = has_iommu_groups();
bool wanted_by_cmd = cmdline_requests_iommu();
std::cout << "sysfs_class: " << class_exists
<< " iommu_groups_active: " << groups_active
<< " cmdline_requests_iommu: " << wanted_by_cmd << "\n";
if (groups_active) std::cout << "IOMMU: ENABLED\n";
else if (class_exists || wanted_by_cmd)
std::cout << "IOMMU: REQUESTED but NOT FULLY ACTIVE (check BIOS/UEFI)\n";
else
std::cout << "IOMMU: DISABLED\n";
}
왜 이 방식이 신뢰도가 높나?
배포판/커널 버전에 따라 dmesg 메시지 파싱은 변동이 큽니다. 반면 /sys/kernel/iommu_groups/
는
실제 매핑된 그룹이 생성되어야 디렉터리가 생기므로 “활성”의 확실한 근거가 됩니다.
Windows: WMI(DeviceGuard)로 DMA 보호 신호 확인
의미 정리:
Win32_DeviceGuard.AvailableSecurityProperties
에 3이 포함 → DMA Protection 기능이 가용함.- 실제 “활성(Enabled)” 여부는 UEFI 펌웨어 설정/플랫폼 드라이버/Windows 조합에 좌우 → 관리자 UI(msinfo32, Windows 보안)로도 함께 확인 권장.
// win_dma_protection_check.cpp
// 빌드: /EHsc, ole32.lib, wbemuuid.lib 링크
#include <windows.h>
#include <comdef.h>
#include <Wbemidl.h>
#include <iostream>
#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "ole32.lib")
int main() {
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (FAILED(hr)) { std::cerr << "CoInit failed\n"; return 1; }
hr = CoInitializeSecurity(nullptr, -1, nullptr, nullptr,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
nullptr, EOAC_NONE, nullptr);
if (FAILED(hr) && hr != RPC_E_TOO_LATE) { std::cerr << "CoInitSec failed\n"; return 1; }
IWbemLocator* pLoc = nullptr;
hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID*)&pLoc);
if (FAILED(hr)) { std::cerr << "WbemLocator failed\n"; return 1; }
IWbemServices* pSvc = nullptr;
hr = pLoc->ConnectServer(_bstr_t(L"ROOT\\Microsoft\\Windows\\DeviceGuard"),
nullptr,nullptr,0,0,0,0,&pSvc);
if (FAILED(hr)) { std::cerr << "ConnectServer failed\n"; pLoc->Release(); return 1; }
hr = CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, 0,
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, 0, EOAC_NONE);
IEnumWbemClassObject* pEnum = nullptr;
hr = pSvc->ExecQuery(_bstr_t(L"WQL"),
_bstr_t(L"SELECT AvailableSecurityProperties FROM Win32_DeviceGuard"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
nullptr, &pEnum);
bool dmaPropAvailable = false;
if (SUCCEEDED(hr) && pEnum) {
IWbemClassObject* pObj = nullptr; ULONG ret = 0;
if (SUCCEEDED(pEnum->Next(WBEM_INFINITE, 1, &pObj, &ret)) && ret == 1) {
VARIANT vt; VariantInit(&vt);
if (SUCCEEDED(pObj->Get(L"AvailableSecurityProperties", 0, &vt, nullptr, nullptr)) &&
(vt.vt & VT_ARRAY)) {
SAFEARRAY* psa = vt.parray;
LONG l = psa->rgsabound[0].lLbound, u = psa->rgsabound[0].cElements + l;
for (LONG i = l; i < u; ++i) {
LONG val = 0; SafeArrayGetElement(psa, &i, &val);
if (val == 3) { // 3 == DMA protection available
dmaPropAvailable = true; break;
}
}
}
VariantClear(&vt);
pObj->Release();
}
pEnum->Release();
}
std::cout << "DMA protection (availability flag): "
<< (dmaPropAvailable ? "TRUE" : "FALSE") << "\n";
pSvc->Release(); pLoc->Release(); CoUninitialize();
return 0;
}
빌드 & 실행 예시
Linux (g++)
g++ -std=c++17 -O2 linux_iommu_check.cpp -o iommu_check
./iommu_check
Windows (MSVC)
cl /std:c++17 /EHsc win_dma_protection_check.cpp ole32.lib wbemuuid.lib
win_dma_protection_check.exe
운영 팁 & 주의사항
- Linux: 그룹이 하나도 없다면 BIOS/UEFI의 VT-d/AMD-Vi가 비활성화되었거나 커널 파라미터가 빠졌을 가능성이 큽니다.
커맨드라인에
intel_iommu=on iommu=pt
또는amd_iommu=on iommu=pt
를 추가 후 재부팅해 다시 확인하세요. - Windows: WMI 값은 “가용(available)” 신호입니다. 실제 활성은 펌웨어와 OS 구성에 따라 달라질 수 있으니 msinfo32의 “Kernel DMA Protection” 항목 및 Windows 보안 UI도 함께 확인하세요.
- 배포용 제품이라면, 감지 실패 시 보수적 기본값(= 비활성로 간주, 하드닝 강화)을 채택하는 것이 안전합니다.
728x90
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- C
- 울릉도
- Thread
- 현포다이브
- 윈도우
- 리눅스
- C#
- C++
- Windows
- 서귀포블루버블
- PowerShell
- C# 고급 기술
- ReFS
- 블루버블다이빙팀
- 블루버블다이브팀
- 스쿠버다이빙
- 암호화
- 서귀포
- 제주도
- 디자인패턴
- ip
- 블루버블
- DLL
- Linux
- OpenSource
- 다이빙
- 성산블루버블
- 양파다이브
- Build
- 패턴
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함