Hoje continuamos a história de como nós, juntamente com a equipe da Universidade de Innopolis, estamos desenvolvendo a tecnologia Active Restore para permitir que o usuário comece a trabalhar em sua máquina o mais rápido possível após uma falha. Falaremos sobre aplicativos nativos do Windows, incluindo os recursos de sua criação e lançamento. Sob o corte - um pouco sobre o nosso projeto, bem como um guia prático sobre como escrever aplicativos nativos.

Nas postagens anteriores, já falamos sobre o que é o
Active Restore e como os alunos do Innopolis desenvolvem o
serviço . Hoje, quero me concentrar em aplicativos nativos, no nível em que queremos “enterrar” nosso serviço de recuperação ativo. Se tudo der certo, podemos:
- Muito antes para iniciar o próprio serviço
- Muito antes de entrar em contato com a nuvem na qual o backup está
- É muito mais cedo para entender em que modo o sistema está - inicialização ou recuperação normal
- Para restaurar muito menos arquivos com antecedência
- Permita que o usuário comece ainda mais rápido.
O que é um aplicativo nativo em geral?
Para responder a essa pergunta, vejamos a sequência de chamadas que o sistema faz, por exemplo, se um programador em seu aplicativo tentar criar um arquivo.
Pavel Yosifovich - Programação do Kernel do Windows (2019)O programador usa a função
CreateFile , declarada no arquivo de cabeçalho fileapi.h e implementada no Kernel32.dll. No entanto, essa função em si não cria um arquivo, apenas verifica os argumentos na entrada e chama a função
NtCreateFile (o prefixo Nt indica apenas que a função é nativa). Esta função é declarada no arquivo de cabeçalho winternl.h e é implementada no ntdll.dll. Ela se prepara para pular para o espaço nuclear, após o que faz uma chamada de sistema para criar um arquivo. Nesse caso, o Kernel32 é apenas um invólucro para o Ntdll. Uma das razões pelas quais isso é feito, a Microsoft tem, portanto, a capacidade de alterar as funções do mundo nativo, mas não toca nas interfaces padrão. A Microsoft não recomenda a chamada direta de funções nativas e não documenta a maioria delas. A propósito, recursos não documentados podem ser encontrados
aqui .
A principal vantagem dos aplicativos nativos é que o ntdll é carregado no sistema muito antes do kernel32. Isso é lógico, porque o kernel32 requer que o ntdll funcione. Como resultado, os aplicativos que usam funções nativas podem começar a trabalhar muito mais cedo.
Portanto, os Aplicativos Nativos do Windows são programas que podem ser executados em um estágio inicial na inicialização do Windows. Eles usam apenas funções do ntdll. Um exemplo desse aplicativo:
autochk, que executa o
utilitário chkdisk para verificar se há erros no disco antes de iniciar os serviços principais. É nesse nível que queremos ver nossa Restauração Ativa.
Do que precisamos?
- DDK (Driver Development Kit), agora também conhecido como WDK 7 (Windows Driver Kit).
- Máquina virtual (por exemplo, Windows 7 x64)
- Não necessariamente, mas os arquivos de cabeçalho podem ser baixados aqui.
O que está no código?
Vamos praticar um pouco e, por exemplo, escreveremos um pequeno aplicativo que:
- Exibe uma mensagem na tela.
- Aloca um pouco de memória
- Aguardando entrada do teclado
- Libera memória ocupada
Em aplicativos nativos, o ponto de entrada não é o principal ou o domínio principal, mas a função NtProcessStartup, pois na verdade iniciamos diretamente o novo processo no sistema.
Vamos começar exibindo a mensagem na tela. Para fazer isso, temos uma função nativa
NtDisplayString , que leva como argumento um ponteiro para um objeto da estrutura UNICODE_STRING. RtlInitUnicodeString nos ajudará a inicializá-lo. Como resultado, para exibir texto na tela, podemos escrever uma função tão pequena:
Como apenas as funções do ntdll estão disponíveis e simplesmente não existem outras bibliotecas na memória, definitivamente teremos problemas com a alocação de memória. O novo operador ainda não existe (porque é proveniente de um mundo C ++ de nível muito alto), também não há função malloc (ele precisa de bibliotecas C em tempo de execução). Obviamente, você pode usar apenas a pilha. Porém, se precisarmos alocar memória dinamicamente, teremos que fazer isso no heap (ou seja, heap). Portanto, vamos criar um monte para nós mesmos e tiraremos memória quando precisarmos.
A função
RtlCreateHeap é adequada para esta tarefa. Além disso, usando RtlAllocateHeap e RtlFreeHeap, ocuparemos e liberaremos memória quando precisarmos.
PVOID memory = NULL; PVOID buffer = NULL; ULONG bufferSize = 42;
Vamos continuar aguardando a entrada do teclado.
Tudo o que precisamos fazer é usar o
NtReadFile em um dispositivo aberto e aguardar até o teclado retornar um clique para nós. Caso a tecla ESC seja pressionada, continuaremos a trabalhar. Para abrir o dispositivo, precisamos chamar a função NtCreateFile (você precisará abrir \ Device \ KeyboardClass0). Também chamaremos
NtCreateEvent para inicializar o objeto a aguardar. Declararemos independentemente a estrutura KEYBOARD_INPUT_DATA que representa os dados do teclado. Isso facilitará nosso trabalho.
O aplicativo nativo termina com uma chamada para a função
NtTerminateProcess , porque acabamos de matar nosso próprio processo.
Todo o código do nosso pequeno aplicativo:
#include "ntifs.h"
PS: Podemos usar facilmente a função DbgBreakPoint () no código para parar no depurador. É verdade que você precisará conectar o WinDbg à máquina virtual para depuração do kernel. Instruções sobre como fazer isso podem ser encontradas
aqui ou apenas use o
VirtualKD .
Compilação e montagem
A maneira mais fácil de criar um aplicativo nativo é usar o
DDK (Driver Development Kit). Precisamos exatamente da sétima versão antiga, pois as versões posteriores têm uma abordagem ligeiramente diferente e trabalham em estreita colaboração com o Visual Studio. Se usarmos o DDK, nosso projeto precisará apenas de Makefile e fontes.
Makefile !INCLUDE $(NTMAKEENV)\makefile.def
fontes: TARGETNAME = MyNative TARGETTYPE = PROGRAM UMTYPE = nt BUFFER_OVERFLOW_CHECKS = 0 MINWIN_SDK_LIB_PATH = $(SDK_LIB_PATH) SOURCES = source.c INCLUDES = $(DDK_INC_PATH); \ C:\WinDDK\7600.16385.1\ndk; TARGETLIBS = $(DDK_LIB_PATH)\ntdll.lib \ $(DDK_LIB_PATH)\nt.lib USE_NTDLL = 1
Seu Makefile será exatamente o mesmo, mas vamos nos concentrar nas fontes com mais detalhes. Este arquivo contém as fontes do seu programa (arquivos .c), opções de compilação e outros parâmetros.
- TARGETNAME - o nome do arquivo executável, que deve ser o resultado.
- TARGETTYPE - tipo de arquivo executável, pode ser um driver (.sys); o valor do campo deve ser DRIVER, se a biblioteca (.lib), o valor é LIBRARY. No nosso caso, precisamos de um arquivo executável (.exe), portanto, definimos o valor como PROGRAM.
- UMTYPE - valores possíveis para este campo: console para um aplicativo de console, janelas para operar no modo de janela. Mas precisamos especificar nt para obter o aplicativo nativo.
- BUFFER_OVERFLOW_CHECKS - verificando se há excesso de buffer na pilha, infelizmente não é o nosso caso, desative-o.
- MINWIN_SDK_LIB_PATH - esse valor refere-se à variável SDK_LIB_PATH, não se preocupe por não ter declarado essa variável de sistema; no momento em que executarmos a compilação verificada no DDK, essa variável será declarada e apontará para as bibliotecas necessárias.
- FONTES - uma lista das fontes do seu programa.
- INCLUI - arquivos de cabeçalho necessários para montagem. Eles geralmente indicam o caminho para os arquivos que acompanham o DDK, mas você pode opcionalmente especificar outros.
- TARGETLIBS - uma lista de bibliotecas que precisam ser vinculadas.
- USE_NTDLL é um campo obrigatório que deve ser definido na posição 1. Por razões óbvias.
- USER_C_FLAGS - qualquer sinalizador que você possa usar nas diretivas de pré-processador ao preparar o código do aplicativo.
Portanto, para compilar, precisamos executar x86 (ou x64) Checked Build, alterar o diretório de trabalho para a pasta do projeto e executar o comando Build. O resultado na captura de tela mostra que reunimos um arquivo executável.

Este arquivo não pode ser executado de maneira simples, o sistema jura e nos envia para pensar sobre seu comportamento com o seguinte erro:
Como executar um aplicativo nativo?
No início do autochk, a sequência de inicialização dos programas é determinada pelo valor da chave do registro:
HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute
O gerenciador de sessões executa os programas dessa lista, um por um. O próprio gerenciador de sessões procura por arquivos executáveis no diretório system32. O formato do valor da chave do Registro é o seguinte:
autocheck autochk *MyNative
O valor deve estar no formato hexadecimal e não no ASCII usual; portanto, a chave apresentada acima terá o formato:
61,75,74,6f,63,68,65,63,6b,20,61,75,74,6f,63,68,6b,20,2a,00,4d,79,4e,61,74,69,76,65,00,00
Para converter o nome, você pode usar um serviço online, por exemplo,
este .
Acontece que, para executar o aplicativo nativo, precisamos:
- Copiar arquivo executável para a pasta system32
- Adicione uma chave ao registro
- Reiniciar a máquina
Por conveniência, aqui está um script pronto para instalar um aplicativo nativo:
install.bat @echo off copy MyNative.exe %systemroot%\system32\. regedit /s add.reg echo Native Example Installed pause
add.reg REGEDIT4 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager] "BootExecute"=hex(7):61,75,74,6f,63,68,65,63,6b,20,61,75,74,6f,63,68,6b,20,2a,00,4d,79,4e,61,74,69,76,65,00,00
Após a instalação e reinicialização, mesmo antes da tela de seleção do usuário aparecer, temos a seguinte imagem:

Sumário
Usando o exemplo de um aplicativo tão pequeno, garantimos que é possível executar o aplicativo no nível nativo do Windows. Além disso, os funcionários da Universidade de Innopolis continuarão construindo um serviço que iniciará o processo de interação com o motorista muito mais cedo do que na versão anterior do nosso projeto. E com o advento do shell win32, será lógico transferir o controle para um serviço completo que já foi desenvolvido (mais sobre isso
aqui ).
No próximo artigo, abordaremos outro componente do serviço Active Restore, ou seja, o driver UEFI. Assine o nosso blog para não perder a próxima postagem.