Windows Applicazioni native è serviziu Acronis Active Restore

Oghje, cuntinueremu a nostra storia nantu à cumu travagliamu cù l'Università Innopolis per sviluppà a tecnulugia Active Restore per permette à l'utilizatori di ripiglià u travagliu nantu à e so macchine u più prestu pussibule dopu un crash. Parleremu di l'applicazioni native. Windows, включая особенности их создания и запуска. Под катом – немного о нашем проекте, а также практическое руководство как писать нативные приложения.

Windows Applicazioni native è serviziu Acronis Active Restore

In i posti precedenti avemu digià parlatu di ciò chì hè Restaurazione attiva, è cumu si sviluppanu i studienti di Innopolis serviziu. Oghje vogliu fucalizza nantu à l'applicazioni native, à u livellu di quale vulemu "sepultà" u nostru serviziu di ricuperazione attiva. Se tuttu funziona, allora seremu capaci di:

  • Lanciate u serviziu stessu assai prima
  • Cuntattate u nuvulu induve a copia di salvezza hè situata assai prima
  • Moltu prima per capisce in quale modu hè u sistema - boot normale o ricuperazione
  • Assai menu schedari da ricuperà in anticipu
  • Permette à l'utilizatore per cumincià ancu più veloce.

Cosa hè una app nativa in ogni casu?

Per risponde à sta quistione, fighjemu a sequenza di chjamate chì u sistema face, per esempiu, se un programatore in a so applicazione prova di creà un schedariu.

Windows Applicazioni native è serviziu Acronis Active Restore
Pavel Yosifovich — Windows Kernel Programming (2019)

U programatore usa a funzione Crea File, chì hè dichjaratu in u file d'intestazione fileapi.h è implementatu in Kernel32.dll. Tuttavia, sta funzione ùn crea micca u schedariu, solu verifica l'argumenti di input è chjama a funzione NtCreateFile (u prefissu Nt indica solu chì a funzione hè nativa). Sta funzione hè dichjarata in u schedariu di l'header winternl.h è implementata in ntdll.dll. Si prepara à saltà in u spaziu nucleari, dopu chì face una chjama di u sistema per creà un schedariu. In questu casu, risulta chì Kernel32 hè solu un wrapper per Ntdll. Unu di i mutivi per quessa hè statu fattu hè chì Microsoft hà cusì a capacità di cambià e funzioni di u mondu nativu, ma micca toccu l'interfaccia standard. Microsoft ùn ricumande micca di chjamà funzioni native direttamente è ùn documenta micca a maiò parte di elli. Per via, e funzioni senza documentu ponu esse truvate ccà.

U vantaghju principali di l'applicazioni native hè chì ntdll hè caricatu in u sistema assai prima di kernel32. Questu hè logicu, perchè u kernel32 richiede ntdll per travaglià. In u risultatu, l'applicazioni chì utilizanu funzioni native ponu cumincià à travaglià assai prima.

Cusì, Windows Native Applications – это программы, способные запускаться на раннем этапе загрузки Windows. Они используют ТОЛЬКО функции из ntdll. Пример такого приложения: autochk chì faci utilità chkdisk per verificà u discu per errori prima di inizià i servizii principali. Questu hè esattamente u livellu chì vulemu chì u nostru Active Restore sia.

Chì avemu bisognu ?

  • DDK (Driver Development Kit), ныне также известный под названием WDK 7 (Windows Driver Kit).
  • Виртуальная машина (например, Windows 7 x 64)
  • Ùn hè micca necessariu, ma i fugliali di header chì ponu esse scaricati ponu aiutà ccà

Chì ci hè in u codice?

Pratichemu un pocu è, per esempiu, scrivite una piccula applicazione chì:

  1. Mostra un missaghju nantu à u screnu
  2. Alloca un pocu di memoria
  3. Aspetta l'input di u teclatu
  4. Libera a memoria usata

In l'applicazioni native, u puntu di ingressu ùn hè micca principalu o winmain, ma a funzione NtProcessStartup, postu chì in realtà lanciamu direttamente novi prucessi in u sistema.

Accuminciamu per vede un missaghju nantu à u screnu. Per questu avemu una funzione nativa NtDisplayString, chì piglia cum'è argumentu un punteru à un oggettu di struttura UNICODE_STRING. RtlInitUnicodeString ci aiuterà à inizializzallu. In u risultatu, per vede u testu nantu à u screnu pudemu scrive sta piccula funzione:

//usage: WriteLn(L"Here is my textn");
void WriteLn(LPWSTR Message)
{
    UNICODE_STRING string;
    RtlInitUnicodeString(&string, Message);
    NtDisplayString(&string);
}

Siccomu solu e funzioni da ntdll sò dispunibuli per noi, è ùn ci sò simpliciamente altre biblioteche in memoria, certamenti averemu prublemi cù cumu per assignà memoria. U novu operatore ùn esiste ancu (perchè vene da u mondu troppu altu di C++), è ùn ci hè micca una funzione malloc (esige biblioteche C runtime). Di sicuru, pudete aduprà solu una pila. Ma s'ellu ci vole à assignà dinamicamente a memoria, avemu da fà nantu à u munzeddu (vale à dì heap). Allora creemu un munzeddu per noi stessi è pigliamu a memoria da ellu ogni volta chì avemu bisognu.

A funzione hè adattata per questu compitu RtlCreateHeap. In seguitu, usendu RtlAllocateHeap è RtlFreeHeap, occupemu è libereremu memoria quandu avemu bisognu.

PVOID memory = NULL;
PVOID buffer = NULL;
ULONG bufferSize = 42;

// create heap in order to allocate memory later
memory = RtlCreateHeap(
  HEAP_GROWABLE, 
  NULL, 
  1000, 
  0, NULL, NULL
);

// allocate buffer of size bufferSize
buffer = RtlAllocateHeap(
  memory, 
  HEAP_ZERO_MEMORY, 
  bufferSize
);

// free buffer (actually not needed because we destroy heap in next step)
RtlFreeHeap(memory, 0, buffer);

RtlDestroyHeap(memory);

Passemu à aspittà l'input di u teclatu.

// 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;
	}
}

Tuttu ciò chì avemu bisognu hè di utilizà NtReadFile nantu à un dispositivu apertu, è aspettate finu à chì u teclatu torna ogni pressa à noi. Se a chjave ESC hè pressata, continueremu à travaglià. Per apre u dispusitivu, avemu bisognu di chjamà a funzione NtCreateFile (avemu bisognu di apre DeviceKeyboardClass0). Chjameremu ancu NtCreateEventper inizializà l'ughjettu di aspittà. Dichjaremu noi stessi a struttura KEYBOARD_INPUT_DATA, chì rapprisenta i dati di u teclatu. Questu farà u nostru travagliu più faciule.

L'applicazione nativa finisce cù una chjama di funzione NtTerminateProcessperchè simpricimenti uccidemu u nostru prucessu.

Tuttu u codice per a nostra piccula applicazione:

#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: Pudemu facilmente aduprà a funzione DbgBreakPoint () in u nostru codice per piantà in u debugger. Hè veru, avete bisognu di cunnette WinDbg à una macchina virtuale per a debugging di u kernel. Istruzzioni nantu à cumu fà questu si ponu truvà ccà o solu aduprà VirtualKD.

Cumpilazione è assemblea

U modu più faciule per custruisce una applicazione nativa hè di utilizà DDK (Kit di sviluppu di u driver). Avemu bisognu di l'antica settima versione, postu chì e versioni più tardi anu un accostu un pocu sfarente è travaglià strettamente cù Visual Studio. Sè avemu aduprà u DDK, allura u nostru prughjettu solu bisognu Makefile è fonti.

makefile

!INCLUDE $(NTMAKEENV)makefile.def

fonti:

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			= 1

U vostru Makefile serà esattamente u listessu, ma fighjemu e fonti in un pocu più di dettu. Stu schedariu specifica i fonti di u vostru prugramma (.c files), l'opzioni di custruzzione, è altri paràmetri.

  • TARGETNAME - u nome di u schedariu eseguibile chì deve esse pruduttu à a fine.
  • TARGETTYPE - tipu di schedariu eseguibile, pò esse un driver (.sys), allora u valore di u campu deve esse DRIVER, se una biblioteca (.lib), allora u valore hè LIBRARY. In u nostru casu, avemu bisognu di un schedariu eseguibile (.exe), cusì avemu stabilitu u valore à PROGRAM.
  • UMTYPE - valori pussibuli per questu campu: cunsola per una applicazione di cunsola, Windows per travaglià in modalità finestra. Ma avemu bisognu di specificà nt per uttene una applicazione nativa.
  • BUFFER_OVERFLOW_CHECKS - cuntrollà a pila per u buffer overflow, sfurtunatamenti micca u nostru casu, l'avemu disattivatu.
  • MINWIN_SDK_LIB_PATH - stu valore si riferisce à a variabile SDK_LIB_PATH, ùn vi preoccupate micca chì ùn avete micca una tale variabile di u sistema dichjarata, quandu eseguimu verificatu build da u DDK, sta variabile serà dichjarata è indicà à e librerie necessarie.
  • SOURCES - una lista di fonti per u vostru prugramma.
  • INCLUDE - i fugliali d'intestazione chì sò necessarii per l'assemblea. Quì sò generalmente indicanu a strada di i schedari chì venenu cù u DDK, ma pudete ancu specificà qualsiasi altri.
  • TARGETLIBS - lista di biblioteche chì deve esse ligati.
  • USE_NTDLL hè un campu obligatoriu chì deve esse stabilitu à 1 per ragioni evidenti.
  • USER_C_FLAGS - qualsiasi bandiera chì pudete aduprà in direttive di preprocessore quandu preparanu u codice di l'applicazione.

Allora per custruisce, avemu bisognu di eseguisce x86 (o x64) Checked Build, cambiate u repertoriu di travagliu in u cartulare di u prughjettu è eseguite u cumandimu Build. U risultatu in a screenshot mostra chì avemu un schedariu eseguibile.

Windows Applicazioni native è serviziu Acronis Active Restore

Stu schedariu ùn pò esse lanciatu cusì faciule, u sistema maledicà è ci manda à pensà à u so cumpurtamentu cù l'errore seguente:

Windows Applicazioni native è serviziu Acronis Active Restore

Cumu lancià una applicazione nativa?

Quandu l'autochk principia, a sequenza di startup di i prugrammi hè determinata da u valore di a chjave di u registru:

HKLMSystemCurrentControlSetControlSession ManagerBootExecute

U manager di sessione eseguisce i prugrammi da sta lista unu per unu. U gestore di sessione cerca i fugliali eseguibili stessi in u cartulare system32. U furmatu di u valore di chjave di u registru hè cusì:

autocheck autochk *MyNative

U valore deve esse in formatu esadecimale, micca u solitu ASCII, cusì a chjave mostrata sopra serà in u formatu:

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

Per cunvertisce u titulu, pudete aduprà un serviziu in linea, per esempiu, questu.

Windows Applicazioni native è serviziu Acronis Active Restore
Ci hè chì per lancià una applicazione nativa, avemu bisognu:

  1. Copia u schedariu eseguibile in u cartulare system32
  2. Aghjunghjite una chjave à u registru
  3. Reboot a macchina

Per comodità, quì hè un script prontu per installà una applicazione nativa:

install.bat

@echo off
copy MyNative.exe %systemroot%system32.
regedit /s add.reg
echo Native Example Installed
pause

add.reg

REGEDIT4

[HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession 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

Dopu a stallazione è u reboot, ancu prima chì a pantalla di selezzione di l'utilizatori apparisce, averemu a seguente stampa:

Windows Applicazioni native è serviziu Acronis Active Restore

U risultatu

На примере вот такого маленького приложения мы убедились, что запустить приложение на уровне Windows Native вполне возможно. Дальше мы с ребятами из Университета Иннополис продолжим строить сервис, который будет инициировать процесс взаимодействия с драйвером намного раньше, чем в предыдущей версии нашего проекта. А с появлением оболочки win32 логично будет передать управление полноценному сервису, который уже был разработан (об этом подробнее ccà).

In u prossimu articulu, tuccheremu un altru cumpunente di u serviziu Active Restore, vale à dì u driver UEFI. Abbonate à u nostru blog per ùn mancate micca u prossimu post.

Source: www.habr.com

Cumprate un hosting affidabile per i siti cù prutezzione DDoS, servitori VPS VDS 🔥 Cumprate un hosting di siti web affidabile cù prutezzione DDoS, servitori VPS VDS | ProHoster