티스토리 뷰

728x90
반응형
IOMMU / VT-d / AMD-Vi 활성 여부를 C++로 감지하기 (Linux & Windows)
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/cmdlineintel_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.AvailableSecurityProperties3이 포함 → 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
링크
«   2025/10   »
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
글 보관함