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