이 섹션에서는 메모리 무결성 호환 코드를 구현하는 방법을 설명합니다.
비고
메모리 무결성을 HVCI(하이퍼바이저로 보호된 코드 무결성) 또는 하이퍼바이저 강제 코드 무결성이라고도 하며 원래 Device Guard의 일부로 릴리스되었습니다. Device Guard는 그룹 정책 또는 Windows 레지스트리에서 메모리 무결성 및 VBS 설정을 찾는 것 외에는 더 이상 사용되지 않습니다.
호환되는 코드를 구현하려면 드라이버 코드에서 다음을 수행해야 합니다.
- 기본적으로 NX에 옵트인
- 메모리 할당에 NX API/플래그 사용(NonPagedPoolNx)
- 쓰기 가능 및 실행 가능한 섹션을 사용하지 않습니다.
- 실행 가능한 시스템 메모리를 직접 수정하지 않습니다.
- 커널에서 동적 코드를 사용하지 않음
- 데이터 파일을 실행 파일로 로드하지 않음
- 섹션 정렬은 0x1000(PAGE_SIZE)의 배수입니다. 예: DRIVER_ALIGNMENT=0x1000
시스템 사용을 위해 예약되지 않은 다음 DPI 목록은 영향을 받을 수 있습니다.
HLK의 코드 무결성 테스트를 사용하여 메모리 무결성 드라이버 호환성 테스트
관련 시스템 기본 사항 보안 테스트에 대한 자세한 내용은 HyperVisor 코드 무결성 준비 테스트 및 메모리 무결성 및 VBS를 참조하세요.
관련 디바이스 기본 사항 테스트에 대한 자세한 내용은 Device.DevFund 테스트를 참조하세요.
다음 표를 사용하여 출력을 해석하고 다양한 유형의 메모리 무결성 비호환성을 수정하는 데 필요한 드라이버 코드 변경을 결정합니다.
경고 | 보너스 |
풀 형식 실행 |
호출자가 실행 가능한 풀 유형을 지정했습니다. 실행 가능한 메모리를 요청하는 메모리 할당 함수를 호출합니다. 모든 풀 형식에 실행 불가능한 NX 플래그가 포함되어 있는지 확인합니다. |
페이지 보호 실행 |
호출자가 실행 파일 페이지 보호를 지정했습니다. "실행 안 함" 페이지 보호 마스크를 지정합니다. |
페이지 매핑 실행 |
호출자가 MDL(실행 가능한 메모리 설명자 목록) 매핑을 지정했습니다. 사용되는 마스크에 MdlMappingNoExecute가 포함되어 있는지 확인합니다. 자세한 내용은 MmGetSystemAddressForMdlSafe를 참조하세요. |
Execute-Write 섹션 |
이미지에는 실행 파일 및 쓰기 가능한 섹션이 포함되어 있습니다. |
섹션 정렬 실패 |
이미지에는 페이지가 정렬되지 않은 섹션이 포함되어 있습니다. 섹션 맞춤은 0x1000(PAGE_SIZE)의 배수여야 합니다. 예: DRIVER_ALIGNMENT=0x1000 |
실행 파일 섹션의 IAT |
IAT(가져오기 주소 테이블)는 메모리의 실행 가능한 섹션이 아니어야 합니다. 이 문제는 IAT가 메모리의 RX(읽기 및 실행) 전용 섹션에 있을 때 발생합니다. 즉, OS는 참조된 DLL 위치에 대한 올바른 주소를 설정하기 위해 IAT에 쓸 수 없습니다. 이 문제가 발생할 수 있는 한 가지 방법은 코드 연결에서 /MERGE(섹션 결합) 옵션을 사용하는 경우입니다. 예를 들어 .rdata(읽기 전용 초기화된 데이터)가 .text 데이터(실행 코드)와 병합되는 경우 IAT가 메모리의 실행 섹션에서 끝날 수 있습니다. |
지원되지 않는 Relocs
Windows 10 버전 1507~Windows 10 버전 1607에서는 ASLR(주소 공간 레이아웃 임의화)을 사용하기 때문에 주소 맞춤 및 메모리 재배치와 관련된 문제가 발생할 수 있습니다. 운영 체제는 링커가 설정한 기본 주소를 ASLR이 할당한 실제 위치로 재배치해야 합니다. 이 재배치는 페이지 경계를 넘을 수 없습니다. 예를 들어, 페이지의 오프셋 0x3FFC에서 시작하는 64비트 주소 값을 고려해 보세요. 주소 값이 오프셋 0x0003에서 다음 페이지로 겹칩니다. 이 유형의 겹치는 재배치는 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
};
어셈블러 코드 사용과 관련된 다른 상황이 있으며 이 문제도 발생할 수 있습니다.
드라이버 검증 도구 코드 무결성
드라이버 검증 도구 코드 무결성 옵션 플래그(0x02000000)를 사용하여 이 기능 준수의 유효성을 검사하는 추가 검사를 사용하도록 설정합니다. 명령줄에서 이를 사용하도록 설정하려면 다음 명령을 사용합니다.
verifier.exe /flags 0x02000000 /driver <driver.sys>
검증 도구 GUI를 사용하는 경우 이 옵션을 선택하려면 사용자 지정 설정 만들기 (코드 개발자용)를 선택하고 다음을 선택한 다음 , 코드 무결성 검사를 선택합니다.
검증 도구 명령줄 /query 옵션을 사용하여 현재 드라이버 검증 도구 정보를 표시할 수 있습니다.
verifier /query