TĂ€na jĂ€tkame juttu sellest, kuidas koos Innopolis Ălikooli poistega arendame Active Restore tehnoloogiat, et lubada kasutajal alustada oma masinal tööle naasmist vĂ”imalikult kiiresti pĂ€rast riket. Jutuks on natiivrakendused Windowsis, sealhulgas nende loomise ja kĂ€itamise eripĂ€ra. Allpool on natuke meie projektist ning praktiline juhend, kuidas kirjutada natiivrakendusi.

Eelmistes postitustes oleme juba rÀÀkinud, mis on , ja kuidas Innopolis Ălikooli tudengid seda arendavad . TĂ€na tahan peatuda natiivrakendustel, mille tasemeni soovime meie aktiivse taastamise teenuse âkaevataâ. Kui kĂ”ik lĂ€heb hĂ€sti, siis suudame:
- KĂ€ivitada teenuse palju varem
- Suhtuda pilvega, kus varukoopia asub, palju varem
- MĂ”ista, millises reĆŸiimis sĂŒsteem on â normaalse kĂ€ivitamise vĂ”i taastamise reĆŸiimis, palju varem
- Taastada palju vÀhem faile eelnevalt
- Lubada kasutajal alustada tööd veelgi kiiremini.
Mis on ĂŒldse natiivne rakendus?
KĂŒsimusele vastamiseks vaatame sĂŒsteemi vĂ€ljakutseid, nĂ€iteks kui programmeerija ĂŒritab oma rakenduses faili luua.

Pavel Yosifovich â Windows Kernel Programming (2019)
Programmeerija kasutab funktsiooni , mis on deklareeritud pealkirjafailis fileapi.h ja rakendatud Kernel32.dll-is. Siiski ei tegele see funktsioon failide loomisega, see vaid kontrollib sisendi argumendid ja kutsub vĂ€lja funktsiooni (Nt-silt nĂ€itab, et funktsioon on natiivne). See funktsioon on deklareeritud pealkirjafailis winternl.h ja rakendatud ntdll.dll-is. See valmistab ette hĂŒppe tuumaruumi ja teeb seejĂ€rel sĂŒsteemi vĂ€ljakutse faili loomiseks. Seega on Kernel32 lihtsalt Ntdll'i mĂ€his. Ăks pĂ”hjusi, miks see nii on tehtud, on see, et Microsoft saab sel viisil natiivsete maailmade funktsioone muuta, ilma et nad puutuksid tavalisi liideseid. Microsoft ei soovita otse kutsuda esile natiivsete funktsioonide ja ei dokumenteeri enamikku neist. Muide, dokumenteerimata funktsioone vĂ”ib leida .
PĂ”hiline eelis natiivsete rakenduste puhul on see, et ntdll laaditakse sĂŒsteemi palju varem kui kernel32. See on loogiline, sest kernel32 vajab ntdll-i oma tööks. SeetĂ”ttu saavad rakendused, mis kasutavad natiivseid funktsioone, alustada tööle asumist mĂ€rkimisvÀÀrselt varem.
Seega on Windowsi natiivsed rakendused programmid, mis suudavad kÀivituda Windowsi laadimise varajases staadiumis. Need kasutavad AINULT funktsioone ntdll-ist. NÀide sellisest rakendusest: mis kÀivitab kettavead kontrollimiseks juba enne pÔhiteenuste kÀivitamist. Just sellisel tasemel soovime nÀha meie Active Restore'i.
Mida me vajame?
- (Driver Development Kit), tuntud ka kui WDK 7 (Windows Driver Kit).
- Virtuaalne masin (nt Windows 7 x64)
- Ei ole kohustuslik, kuid pÀisefailid vÔivad olla kasulikud ja neid saab alla laadida
Mis on koodis?
Harjutame veidi ja kirjutame nÀitena vÀikese rakenduse, mis:
- Kuvab ekraanile sÔnum
- Allocateerib natuke mÀlu
- Ootab sisestust klaviatuurilt
- Vabastab reserveeritud mÀlu
Natiivsetes rakendustes on sisenemiskoht mitte main vĂ”i winmain, vaid funktsioon NtProcessStartup, kuna me kĂ€ivitame uusi protsesse sĂŒsteemis otse.
Alustame sÔnumi ekraanile kuvamisest. Selleks on meil natiivne funktsioon , mis vÔtab argumendiks UNICODE_STRING struktuuri objekti nÀidiku. Selle algatamiseks aitab meid RtlInitUnicodeString. SeetÔttu saame teksti ekraanile kuvamiseks kirjutada sellise vÀikese funktsiooni:
//usage: WriteLn(L"Here is my textn");
void WriteLn(LPWSTR Message)
{
UNICODE_STRING string;
RtlInitUnicodeString(&string, Message);
NtDisplayString(&string);
}Kuna meil on juurdepÀÀs ainult ntdll funktsioonidele ja teised teegid ei ole mĂ€lus veel olemas, tekivad meil kindlasti probleemid mĂ€lu eraldamisega. Operaatoreid new veel ei eksisteeri (sest see pĂ€rineb liiga kĂ”rgetasemelisest C++ maailmast), samuti ei ole funktsiooni malloc (selle jaoks on vajalikud C runtime teegid). Loomulikult saame kasutada ainult virna. Kuid kui peame dĂŒnaamiliselt mĂ€lu eraldama, peab seda tegema kuhjas. Seega loome endale kuhja ja vĂ”tame sealt mĂ€lu, kui meil on seda vaja.
Selle ĂŒlesande jaoks sobib funktsioon . JĂ€tkame RtlAllocateHeap'i ja RtlFreeHeap'i kasutamisega, et mĂ€lu kasutada ja vabastada, kui me seda vajame.
PVOID mÀlu = NULL;
PVOID puhver = NULL;
ULONG puhverSuurus = 42;
// loome kuhja, et hiljem mÀlu eraldada
mÀlu = RtlCreateHeap(
HEAP_GROWABLE,
NULL,
1000,
0, NULL, NULL
);
// eraldame puhversuuruse suitset
puhver = RtlAllocateHeap(
mÀlu,
HEAP_ZERO_MEMORY,
puhverSuurus
);
// vabastame puhvri (tegelikult pole vajalik, sest hÀvitame kuhja jÀrgmises etapis)
RtlFreeHeap(mÀlu, 0, puhver);
RtlDestroyHeap(mÀlu);Liigume edasi klaviatuuri sisendi ootamise juurde.
// https://docs.microsoft.com/en-us/windows/win32/api/ntddkbd/ns-ntddkbd-keyboard_input_data
typedef struct _KEYBOARD_INPUT_DATA {
USHORT UnitId;
USHORT MakeCode;
USHORT Flags;
USHORT Reserved;
ULONG ExtraInformation;
} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;
//...
HANDLE hKeyBoard, hEvent;
UNICODE_STRING skull, keyboard;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK Iosb;
LARGE_INTEGER ByteOffset;
KEYBOARD_INPUT_DATA kbData;
// inialize variables
RtlInitUnicodeString(&keyboard, L"DeviceKeyboardClass0");
InitializeObjectAttributes(&ObjectAttributes, &keyboard, OBJ_CASE_INSENSITIVE, NULL, NULL);
// open keyboard device
NtCreateFile(&hKeyBoard,
SYNCHRONIZE | GENERIC_READ | FILE_READ_ATTRIBUTES,
&ObjectAttributes,
&Iosb,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_OPEN,FILE_DIRECTORY_FILE,
NULL, 0);
// create event to wait on
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, &ObjectAttributes, 1, 0);
while (TRUE)
{
NtReadFile(hKeyBoard, hEvent, NULL, NULL, &Iosb, &kbData, sizeof(KEYBOARD_INPUT_DATA), &ByteOffset, NULL);
NtWaitForSingleObject(hEvent, TRUE, NULL);
if (kbData.MakeCode == 0x01) // if ESC pressed
{
break;
}
}KĂ”ik, mida vajame, on kasutada avatud seadmes ning oodata, kuni klaviatuur tagastab meile mĂ”ne nĂ€ppimise. Kui klahvi ESC vajutatakse, jĂ€tkame tööd. Seadme avamiseks peame kutsuma funktsiooni NtCreateFile (avama peab DeviceKeyboardClass0). Samuti kutsume ĂŒles , et algatada objekti ootamiseks. Me kuulutame iseseisvalt vĂ€lja KEYBOARD_INPUT_DATA struktuuri, mis esindab klaviatuuri andmeid. See lihtsustab meie tööd.
Natiivrakenduse töö lĂ”ppeb funktsiooni , kutsub ĂŒles, sest me lihtsalt tapame oma protsessi.
Kogu meie vÀikese rakenduse kood:
#include "ntifs.h" // WinDDK7600.16385.1incddk
#include "ntdef.h"
//------------------------------------
// Following function definitions can be found in native development kit
// but I am too lazy to include `em so I declare it here
//------------------------------------
NTSYSAPI
NTSTATUS
NTAPI
NtTerminateProcess(
IN HANDLE ProcessHandle OPTIONAL,
IN NTSTATUS ExitStatus
);
NTSYSAPI
NTSTATUS
NTAPI
NtDisplayString(
IN PUNICODE_STRING String
);
NTSTATUS
NtWaitForSingleObject(
IN HANDLE Handle,
IN BOOLEAN Alertable,
IN PLARGE_INTEGER Timeout
);
NTSYSAPI
NTSTATUS
NTAPI
NtCreateEvent(
OUT PHANDLE EventHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN EVENT_TYPE EventType,
IN BOOLEAN InitialState
);
// https://docs.microsoft.com/en-us/windows/win32/api/ntddkbd/ns-ntddkbd-keyboard_input_data
typedef struct _KEYBOARD_INPUT_DATA {
USHORT UnitId;
USHORT MakeCode;
USHORT Flags;
USHORT Reserved;
ULONG ExtraInformation;
} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;
//----------------------------------------------------------
// Our code goes here
//----------------------------------------------------------
// usage: WriteLn(L"Hello Native World!n");
void WriteLn(LPWSTR Message)
{
UNICODE_STRING string;
RtlInitUnicodeString(&string, Message);
NtDisplayString(&string);
}
void NtProcessStartup(void* StartupArgument)
{
// it is important to declare all variables at the beginning
HANDLE hKeyBoard, hEvent;
UNICODE_STRING skull, keyboard;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK Iosb;
LARGE_INTEGER ByteOffset;
KEYBOARD_INPUT_DATA kbData;
PVOID memory = NULL;
PVOID buffer = NULL;
ULONG bufferSize = 42;
//use it if debugger connected to break
//DbgBreakPoint();
WriteLn(L"Hello Native World!n");
// inialize variables
RtlInitUnicodeString(&keyboard, L"DeviceKeyboardClass0");
InitializeObjectAttributes(&ObjectAttributes, &keyboard, OBJ_CASE_INSENSITIVE, NULL, NULL);
// open keyboard device
NtCreateFile(&hKeyBoard,
SYNCHRONIZE | GENERIC_READ | FILE_READ_ATTRIBUTES,
&ObjectAttributes,
&Iosb,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_OPEN,FILE_DIRECTORY_FILE,
NULL, 0);
// create event to wait on
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, &ObjectAttributes, 1, 0);
WriteLn(L"Keyboard readyn");
// create heap in order to allocate memory later
memory = RtlCreateHeap(
HEAP_GROWABLE,
NULL,
1000,
0, NULL, NULL
);
WriteLn(L"Heap readyn");
// allocate buffer of size bufferSize
buffer = RtlAllocateHeap(
memory,
HEAP_ZERO_MEMORY,
bufferSize
);
WriteLn(L"Buffer allocatedn");
// free buffer (actually not needed because we destroy heap in next step)
RtlFreeHeap(memory, 0, buffer);
RtlDestroyHeap(memory);
WriteLn(L"Heap destroyedn");
WriteLn(L"Press ESC to continue...n");
while (TRUE)
{
NtReadFile(hKeyBoard, hEvent, NULL, NULL, &Iosb, &kbData, sizeof(KEYBOARD_INPUT_DATA), &ByteOffset, NULL);
NtWaitForSingleObject(hEvent, TRUE, NULL);
if (kbData.MakeCode == 0x01) // if ESC pressed
{
break;
}
}
NtTerminateProcess(NtCurrentProcess(), 0);
}PS: Me saame koodis hĂ”lpsasti kasutada DbgBreakPoint() funktsiooni, et peatuda silurisse. Kuid peame ĂŒhendama WinDbg virtuaalmasinaga tuumadebĂŒĂŒgiks. Juhiseid selle tegemiseks leiate vĂ”i lihtsalt kasutada .
Kompileerimine ja kogumine
Lihtsaim viis natiivrakenduse loomiseks on kasutada (Driver Development Kit). Me vajame just vanimat seitsmenda versiooni, kuna hilisemad versioonid on natuke teistsugused ja töötavad tihedalt koos Visual Studio'ga. Kui aga kasutada DDK-d, siis vajab meie projekt lihtsalt Makefile'i ja sources.
Makefile
!INCLUDE $(NTMAKEENV)makefile.defsources:
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:WinDDK7600.16385.1ndk;
TARGETLIBS = $(DDK_LIB_PATH)ntdll.lib
$(DDK_LIB_PATH)nt.lib
USE_NTDLL = 1Teie Makefile on tÀpselt sama, kuid peatume sources'i juures natuke rohkem. Selles failis mÀÀratakse teie programmi lÀhtekood (failid .c), kogumise valikud ja muud parameetrid.
- TARGETNAME â tĂ€pselt see, mis peab olema lĂ”pptulemuseks saadud tĂ€idetav fail.
- TARGETTYPE â tĂ€itmisfaili tĂŒĂŒp, see vĂ”ib olla draiver (.sys), siis peab vĂ€lja vÀÀrtus olema DRIVER, kui teek (.lib), siis vÀÀrtus LIBRARY. Meie juhul on vajalik tĂ€itmisfail (.exe), seega seadistame vÀÀrtuse PROGRAM.
- UMTYPE â selle vĂ€lja vĂ”imalikud vÀÀrtused: console konsoolirakenduse jaoks, windows kas WINDOWS-ENV -le. Kuid peame mÀÀrama nt, et saada natiivne rakendus.
- BUFFER_OVERFLOW_CHECKS â virna ĂŒlevoolu kontroll, kahjuks ei ole see meie juhtum, lĂŒlitame vĂ€lja.
- MINWIN_SDK_LIB_PATH â see vÀÀrtus viitab muutuja SDK_LIB_PATH, ei tasu muretseda, et teil ei ole sellist sĂŒsteemimuutujat deklareeritud, hetkel, kui kĂ€ivitame checked build DDK-st, deklareeritakse see muutuja ja viitab vajalikele raamatukogudele.
- SOURCES â nimekiri teie programmi lĂ€htekoodidest.
- INCLUDES â pĂ€ised, mis on vajalikud kompileerimiseks. Siin tavaliselt mÀÀratakse tee failidele, mis tulevad koos DDK-ga, kuid vĂ”ite lisada ka muid.
- TARGETLIBS â nimekiri raamatukogudest, mis tuleb siduda.
- USE_NTDLL â kohustuslik vĂ€li, mis tuleb seada vÀÀrtusele 1. Sellel on ilmselged pĂ”hjused.
- USER_C_FLAGS â kĂ”ik lipud, mida saab kasutada eeltöötlusdirektiivides rakenduse koodi ettevalmistamisel.
Seega, et koostada, peame kĂ€ivitama x86 (vĂ”i x64) Checked Build, muutma töökausta projekti kaustaks ja tĂ€itma Build kĂ€su. Tulemuses, mis on ekraanipildil, on nĂ€ha, et meil on loodud ĂŒks kĂ€ivitatav fail.

Seda faili ei saa lihtsalt kĂ€ivitada, sĂŒsteem teavitab meid ja saadab arvama jĂ€rgmise veateate:

Kuidas kÀivitada natiivset rakendust?
KÀivitamise hetkel mÀÀrab autochk programmi kÀivitamise jÀrjekorra registri vÔtme vÀÀrtus:
HKLMSystemCurrentControlSetControlSession ManagerBootExecuteSeansi haldur kÀivitab programmid jÀrjestikku sellest loendist. KÀivitatavad failid otsitakse seansi halduri poolt kaustast system32. Registri vÔtme vÀÀrtuse formaat on jÀrgmine:
autocheck autochk *MyNativeVÀÀrtus peab olema kuuekĂŒmnendformaadis, mitte tavalises ASCII-s, seetĂ”ttu on ĂŒlaltoodud vĂ”tme formaat:
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,00Nime konvertimiseks vÔite kasutada veebiteenust, nÀiteks .

KokkuvÔttes, et kÀivitada natiivne rakendus, peame me:
- Kopeerima kÀivitatava faili kausta system32
- Lisama registrisse vÔtme
- TaaskÀivitage masin
Kasutamiseks on siin valmis skript natiivse rakenduse installimiseks:
install.bat
@echo off
copy MyNative.exe %systemroot%system32.
regedit /s add.reg
echo Natiivne nÀidis installitud
pauseadd.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,00PÀrast installimist ja taaskÀivitamist, enne kasutajate valimisekraani ilmumist, nÀeme jÀrgmisi pilte:

KokkuvÔte
Selle vĂ€ikese rakenduse nĂ€itel oleme veendunud, et natiivse rakenduse kĂ€ivitamine Windowsi tasemel on tĂ€iesti vĂ”imalik. JĂ€tkame koos Innopolis Ălikooli meestega teenuse loomist, mis algatab suhtlemisprotsessi draiveriga palju varem kui meie projekti eelmine versioon. Ja koos win32 keskustega on mĂ”istlik juhtimise andmine tĂ€ieliku teenuse arendamiseks, mis oli juba vĂ€lja töötatud (selle kohta rohkem) ).
JĂ€rgmises artiklis kĂ€sitleme veel ĂŒhte Active Restore'i teenuse komponenti, nimelt UEFI draivereid. JĂ€lgige meie blogi, et mitte jÀÀda ilma jĂ€rgmisest postitusest.
Allikas: habr.com
