Implementar código compatível com integridade de memória
Esta seção descreve como implementar o código compatível com integridade de memória.
Observação
Às vezes, a integridade da memória é conhecida como HVCI (integridade de código protegida por hipervisor) ou integridade de código imposta pelo hipervisor e foi originalmente liberada como parte do Device Guard. O Device Guard não é mais usado, exceto para localizar a integridade da memória e as configurações de VBS no Política de Grupo ou no Registro do Windows.
Para implementar o código compatível, verifique se o código do driver faz o seguinte:
- Aceita o NX por padrão
- Usa APIs/sinalizadores NX para alocação de memória (NonPagedPoolNx)
- Não usa seções graváveis e executáveis
- Não tenta modificar diretamente a memória do sistema executável
- Não usa código dinâmico no kernel
- Não carrega arquivos de dados como executáveis
- O alinhamento da seção é um múltiplo de 0x1000 (PAGE_SIZE). Por exemplo, DRIVER_ALIGNMENT=0x1000
A seguinte lista de DDIs que não estão reservadas para uso do sistema pode ser afetada:
Usar os testes de integridade de código no HLK para testar a compatibilidade do driver de integridade de memória
Para obter mais informações sobre o teste de segurança de conceitos básicos do sistema relacionado, consulte Teste de preparação para integridade de código do HiperVisor e integridade de memória e VBS.
Para obter mais informações sobre o teste de conceitos básicos do dispositivo relacionado, consulte Testes device.DevFund.
Use a tabela a seguir para interpretar a saída e determinar quais alterações de código de driver são necessárias para corrigir os diferentes tipos de incompatibilidades de integridade de memória.
Aviso | Redenção |
Executar tipo de pool |
O chamador especificou um tipo de pool executável. Chamar uma função de alocação de memória que solicita memória executável. Verifique se todos os tipos de pool contêm um sinalizador NX não executável. |
Executar Proteção de Página |
O chamador especificou uma proteção de página executável. Especifique uma máscara de proteção de página "sem execução". |
Executar mapeamento de página |
O chamador especificou um mapeamento de MDL (lista de descritores de memória executável). Verifique se a máscara usada contém MdlMappingNoExecute. Para obter mais informações, consulte MmGetSystemAddressForMdlSafe |
Seção Execute-Write |
A imagem contém uma seção executável e gravável. |
Falhas de alinhamento de seção |
A imagem contém uma seção que não está alinhada à página. O Alinhamento da Seção deve ser um múltiplo de 0x1000 (PAGE_SIZE). Por exemplo, DRIVER_ALIGNMENT=0x1000 |
IAT na Seção Executável |
A tabela de endereços de importação (IAT) não deve ser uma seção executável da memória. Esse problema ocorre quando o IAT está localizado em uma seção somente RX (Leitura e Execução) da memória. Isso significa que o sistema operacional não poderá gravar no IAT para definir os endereços corretos para onde a DLL referenciada. Uma maneira de isso ocorrer é ao usar a opção /MERGE (Combinar Seções) na vinculação de código. Por exemplo, se .rdata (dados inicializados somente leitura) for mesclado com dados .text (código executável), é possível que o IAT possa acabar em uma seção executável da memória. |
Relocs sem suporte
Em Windows 10, versão 1507 a Windows 10, versão 1607, devido ao uso da ASLR (Address Space Layout Randomization), um problema pode surgir com o alinhamento do endereço e a realocação de memória. O sistema operacional precisa realocar o endereço de onde o vinculador definiu seu endereço base padrão para o local real atribuído pela ASLR. Essa realocação não pode ultrapassar um limite de página. Por exemplo, considere um valor de endereço de 64 bits que começa no deslocamento 0x3FFC em uma página. Seu valor de endereço se sobrepõe à próxima página no deslocamento 0x0003. Não há suporte para esse tipo de relocs sobrepostos antes do Windows 10, versão 1703.
Essa situação pode ocorrer quando um inicializador de variável de tipo de struct global tem um ponteiro desalinhado para outro global, disposto de tal forma que o vinculador não pode mover a variável para evitar a realocação. O vinculador tentará mover a variável, mas há situações em que ela pode não ser capaz de fazer isso (por exemplo, com grandes structs desalinhados ou grandes matrizes de structs desalinhados). Quando apropriado, os módulos devem ser montados usando a opção /Gy (COMDAT) para permitir que o vinculador alinhe o código do módulo o máximo possível.
#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
};
Há outras situações que envolvem o uso do código assembler, em que esse problema também pode ocorrer.
Integridade do código do Verificador de Driver
Use o sinalizador de opção de integridade de código (0x02000000) do Verificador de Driver para habilitar verificações extras que validam a conformidade com esse recurso. Para habilitar isso na linha de comando, use o comando a seguir.
verifier.exe /flags 0x02000000 /driver <driver.sys>
Para escolher essa opção se estiver usando a GUI do verificador, selecione Criar configurações personalizadas (para desenvolvedores de código), selecione Avançar e, em seguida, selecione Verificações de integridade do código.
Você pode usar a opção de linha de comando /query do verificador para exibir as informações atuais do verificador de driver.
verifier /query