HVCI(하이퍼바이저로 보호된 코드 무결성)
하이퍼바이저로 보호된 코드 무결성은 하드웨어 기술 및 가상화를 사용하여 CI(코드 무결성) 의사 결정 기능을 Windows 운영 체제의 나머지 부분에서 격리할 수 있습니다. 가상화 기반 보안을 사용하여 코드 무결성을 격리하는 경우 커널 메모리가 실행될 수 있는 유일한 방법은 코드 무결성 확인을 통해서입니다. 즉, 커널 메모리 페이지는 쓰기 가능 및 실행 가능(W+X)일 수 없으며 실행 코드를 직접 수정할 수 없습니다.
호환되는 드라이버를 빌드하는 방법
메모리 페이지와 섹션은 쓰기 가능 및 실행 가능할 수 없으므로 첫 번째 단계는 데이터와 코드를 명확하게 분리하고 코드 페이지를 직접 수정하지 않는 것입니다.
- 기본적으로 NX에 옵트인
- 메모리 할당에 NX API/플래그 사용 - NonPagedPoolNx
- 쓰기 가능하고 실행 가능한 섹션을 사용하지 않음
- 실행 가능한 시스템 메모리를 직접 수정하려고 하지 않음
- 커널에서 동적 코드 사용 안 함
- 데이터 파일을 실행 파일로 로드하지 않음
- 섹션 맞춤은 0x1000(PAGE_SIZE)의 배수여야 합니다. 예: DRIVER_ALIGNMENT=0x1000
기본 설정을 사용할 때 WDK 및 Visual Studio의 최신 버전을 사용하여 호환되는 드라이버를 생성합니다.
드라이버 호환성을 확인하는 방법
드라이버 호환성은 다음 네 가지 단계로 확인합니다.
- 새 코드 무결성 호환성 검사를 사용하도록 설정한 상태에서 드라이버 검증 도구를 사용합니다.
- 코드 무결성의 가상화 기반 격리를 사용하도록 설정힌 시스템에서 드라이버를 테스트합니다.
- Windows HLK에서 하이퍼바이저 코드 무결성 준비 테스트를 실행합니다.
- DGReadiness 도구를 사용합니다.
드라이버 검증 도구 호환성 검사
드라이버 검증 도구에는 이 기능 준수의 유효성을 검사하는 추가 검사를 사용할 수 있는 새로운 코드 무결성 옵션 플래그(0x02000000)가 있습니다. 명령줄에서 이를 사용하도록 설정하려면 다음 명령을 사용합니다.
verifier.exe /flags 0x02000000 /driver <driver.sys>
검증 도구 GUI를 사용하는 경우 이 옵션을 선택하려면 사용자 지정 설정 만들기(코드 개발자용)를 선택하고 다음을 선택한 다음, 코드 무결성 검사를 선택합니다.
이전 버전의 Visual Studio를 사용하여 빌드된 드라이버는 INIT 섹션이 WRX일 때 실패합니다. 그러나 이것이 유일한 문제인 경우 이 기능과의 호환성 문제를 야기하지 않으므로 커널 디버거에서 이 문제를 무시해도 됩니다. 드라이버 검증 도구에 대한 향후 업데이트는 INIT 섹션에 플래그를 지정하지 않습니다.
코드 무결성에 대한 가상화 기반 격리 사용
가상화 기반 보안은 Windows Enterprise 및 Server 버전에서 지원됩니다. 코드 무결성의 가상화 기반 보호를 사용하도록 설정하는 가장 간단한 방법은 아래 설명된 대로 gpedit을 사용하는 것입니다. 그러면 Hyper-V 및 격리된 사용자 모드가 켜지고 기능이 사용하도록 설정됩니다.
gpedit을 실행하여 로컬 그룹 정책 편집
컴퓨터 구성 -> 관리 템플릿 -> 시스템 ->Device Guard에서 가상화 기반 보안 켜기를 선택합니다.

표시되는 자세한 구성 대화 상자에서 사용을 선택하고 코드 무결성에 대한 가상화 기반 보호 사용을 선택합니다.

다시 부팅
이제 코드 무결성의 가상화 기반 보호가 사용하도록 설정되었습니다.
HLK 테스트 및 요구 사항
클라이언트
드라이버 검증 도구 코드 무결성 옵션을 사용하여 드라이버 로드 문제를 찾아낼 수 있도록 하는 하이퍼바이저 코드 무결성 준비 테스트라는 새로운 HLK 테스트가 있습니다. 정상 작동 중에는 드라이버가 수행하는 작업을 반드시 찾아낼 필요가 없으므로 드라이버 검증 도구를 사용하는 동안 및/또는 CI의 가상화 기반 보호를 사용하는 동안 다른 테스트를 수행하는 것이 좋습니다.
서버
HLK 하이퍼바이저 코드 무결성 준비 테스트는 Assurance AQ의 일부로 필요하며, 다른 HLK 테스트 중에 드라이버 검증 도구를 사용하도록 설정하는 동시에 코드 무결성 검사를 사용하도록 설정하는 플래그도 필요합니다.
DGReadiness 도구
DGReadiness 도구를 사용하여 디바이스에 설치된 모든 드라이버의 HVCI 호환성을 확인할 수도 있습니다. 다운로드에는 사용 정보가 포함된 추가 정보 파일이 포함되어 있습니다. “Device Guard”가 사용될 경우 드라이버가 로드되지 않아 준비 도구로 테스트할 때 드라이버를 사용할 수 없게 되므로 준비 도구를 실행하는 동안에는 “Device Guard”를 사용하지 않도록 설정해야 합니다. 준비 도구에 대한 자세한 내용은 HVCI 드라이버 호환성 평가를 참조하세요.
FAQ
기존 드라이버는 어떤가요? Windows 10에서 작동하기 위해 이러한 드라이버를 다시 빌드해야 하나요?
경우에 따라 다릅니다. 많은 드라이버가 이미 호환됩니다. 이전 버전의 WDK 및 Visual Studio에서 표준 설정을 사용하는 경우 알려진 문제는 INIT 섹션이 RWX로 표시된다는 것입니다. 그러나 Windows 10에서 W는 자동으로 제거되므로 이것이 유일한 문제인 경우 드라이버는 호환됩니다.
HVCI가 사용하도록 설정되어 있는지 확인하려면 어떻게 해야 하나요?
HVCI는 Windows 보안 앱에서 메모리 무결성으로 레이블이 지정되며 설정>보 & 안> 업데이트Windows 보안보안>Core 격리 세부 정보>메모리 무결성을> 통해 액세스할 수 있습니다. 자세한 내용은 KB4096339를 참조하세요.
드라이버 동작을 변경하기 위해 커널에서 HVCI가 프로그래밍 방식으로 사용하도록 설정되어 있는지 확인할 수 있나요?
예, NtQuerySystemInformation을 사용할 수 있습니다. https://msdn.microsoft.com/library/windows/desktop/ms724509(v=vs.85).aspx
SYSTEM_CODEINTEGRITY_INFORMATION 구조에는 코드 무결성의 가상화 기반 보호가 설정되어 있음을 나타내는 새 0x400 값이 노출됩니다.
호환성 문제를 해결하려면 어떻게 해야 하나요?
위에서 설명한 대로 W+X 페이지가 없고 드라이버 섹션이 올바르게 정렬되었는지 한 번 더 확인하는 것 외에도 메모리 할당이 잘못되었을 가능성을 고려하는 것이 좋습니다. 발급된 메모리 할당과 관련된 Code Analysis 경고에 대한 내용은 다음 페이지의 MSDN에서 확인할 수 있습니다.
다음 MSDN 링크는 몇 가지 예제 수정 사항과 함께 실행 가능한 메모리 할당을 야기하는 일반적인 API의 몇 가지 예를 보여 줍니다.
다음 표를 통해 출력을 해석하여 다양한 유형의 HVCI 비호환성을 해결하는 데 필요한 드라이버 코드 변경을 확인합니다.
| 경고 | 해결 방법 |
|---|---|
| 실행 풀 유형 | 호출자가 실행 가능한 풀 유형을 지정했습니다. 실행 메모리를 요청하는 메모리 할당 함수를 호출합니다. 모든 풀 유형에 실행 불가능한 NX 플래그가 포함되어 있는지 확인합니다. |
| 실행 페이지 보호 | 호출자가 실행 가능한 페이지 보호를 지정했습니다. “no execute” 페이지 보호 마스크를 지정합니다. |
| 실행 페이지 매핑 | 호출자가 실행 가능한 MDL(메모리 설명자 목록) 매핑을 지정했습니다. 사용되는 마스크에 MdlMappingNoExecute가 포함되어 있는지 확인합니다. 자세한 내용은 MmGetSystemAddressForMdlSafe를 참조하세요. |
| 실행-쓰기 섹션 | 이미지에는 실행 가능 섹션과 쓰기 가능 섹션이 포함되어 있습니다. |
| 섹션 맞춤 오류 | 이미지에는 페이지가 정렬되지 않은 섹션이 포함되어 있습니다. 섹션 맞춤은 0x1000(PAGE_SIZE)의 배수여야 합니다. 예: DRIVER_ALIGNMENT=0x1000 |
| 지원되지 않는 Relocs | Windows 10 버전 1507~1607 버전에서는 ASLR(Address Space Layout Randomization)이 사용되기 때문에 주소 정렬 및 메모리 재배치와 관련된 문제가 발생할 수 있습니다. 운영 체제에서 링커가 기본 기준 주소를 ASLR이 할당된 실제 위치로 설정하는 주소의 위치를 다시 지정해야 합니다. 이 재배치는 페이지 경계를 넘어 수행될 수 없습니다. 예를 들어 페이지의 오프셋 0x3FFC에서 시작하는 64비트 주소 값을 고려해 보세요. 주소 값은 오프셋 0x0003에서 다음 페이지와 겹칩니다. 이 유형의 겹치는 relocs는 Windows 10 버전 1703 이전에는 지원되지 않습니다.
이 상황은 전역 구조체 형식 변수 이니셜라이저의 포인터가 다른 전역 구조체로 잘못 정렬되어, 링커가 경계를 넘는 재배치를 방지하도록 변수를 이동할 수 없는 상황이 될 때 발생할 수 있습니다. 링커는 변수를 이동하려고 하지만, 이 작업을 수행할 수 없는 경우가 있습니다. 예를 들어 잘못 정렬된 대규모 구조체 또는 잘못 정렬된 대규모 구조체 배열을 포함하는 경우가 여기에 해당합니다. 적절한 경우 /Gy(COMDAT) 옵션을 사용하여 링커가 모듈 코드를 최대한 많이 정렬할 수 있도록 모듈을 어셈블해야 합니다. #include <pshpack1.h>
typedef struct _BAD_STRUCT {
USHORT Value;
CONST CHAR *String;
} BAD_STRUCT, * PBAD_STRUCT;
#include <poppack.h>
#define BAD_INITIALIZER0 { 0, "BAD_STRING" },
#define BAD_INITIALIZER1 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0 \
BAD_INITIALIZER0
#define BAD_INITIALIZER2 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1 \
BAD_INITIALIZER1
#define BAD_INITIALIZER3 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2 \
BAD_INITIALIZER2
#define BAD_INITIALIZER4 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3 \
BAD_INITIALIZER3
BAD_STRUCT MayHaveStraddleRelocations[4096] = { // as a global variable
BAD_INITIALIZER4
};
이 문제가 발생할 수도 있는 어셈블러 코드 사용과 관련된 다른 상황이 있습니다. |
| 실행 가능 섹션의 IAT | IAT(가져오기 주소 테이블)는 메모리의 실행 가능한 섹션이 아니어야 합니다.
이 문제는 IAT가 RX(읽기 및 실행) 전용 메모리 섹션에 있을 때 발생합니다. 즉, OS는 참조된 DLL에 대한 올바른 주소를 설정하기 위해 IAT에 쓸 수 없습니다. 이 문제가 발생할 수 있는 한 가지 방식은 코드 연결에서 /MERGE(섹션 결합) 옵션을 사용하는 경우입니다. 예를 들어 .rdata(읽기 전용으로 초기화된 데이터)를 .text 데이터(실행 코드)와 병합하는 경우에는 IAT가 메모리의 실행 가능한 섹션에서 끝날 수 있습니다. |
잠재적으로 어떤 API가 영향을 받나요?
시스템용으로 예약되지 않은 다음 API 목록은 영향을 받을 수 있습니다.