Windows Native Applications na Acronis Active Restore huduma

Leo tunaendelea na hadithi ya jinsi sisi, pamoja na wavulana kutoka Chuo Kikuu cha Innopolis, tunatengeneza teknolojia ya Active Restore ili kuruhusu mtumiaji kuanza kufanya kazi kwenye mashine yao haraka iwezekanavyo baada ya kushindwa. Tutazungumza juu ya programu asilia za Windows, pamoja na huduma za uundaji wao na uzinduzi. Chini ya kata ni kidogo kuhusu mradi wetu, pamoja na mwongozo wa vitendo wa jinsi ya kuandika maombi ya asili.

Windows Native Applications na Acronis Active Restore huduma

Katika machapisho yaliyopita tayari tumezungumza juu ya ni nini Urejeshaji Inayotumika, na jinsi wanafunzi kutoka Innopolis wanavyokua huduma. Leo nataka kuzingatia maombi asilia, kwa kiwango ambacho tunataka "kuzika" huduma yetu ya uokoaji inayotumika. Ikiwa kila kitu kitafanya kazi, basi tutaweza:

  • Anzisha huduma yenyewe mapema zaidi
  • Wasiliana na wingu ambapo chelezo iko mapema zaidi
  • Mapema sana kuelewa ni hali gani mfumo uko - boot ya kawaida au urejeshaji
  • Faili chache sana za kurejesha mapema
  • Ruhusu mtumiaji aanze haraka zaidi.

Je, programu asili ni nini?

Ili kujibu swali hili, hebu tuangalie mlolongo wa simu ambazo mfumo hufanya, kwa mfano, ikiwa programu katika maombi yake anajaribu kuunda faili.

Windows Native Applications na Acronis Active Restore huduma
Pavel Yosifovich - Windows Kernel Programming (2019)

Msanidi programu hutumia kitendakazi UndaFaili, ambayo imetangazwa katika faili ya kichwa fileapi.h na kutekelezwa katika Kernel32.dll. Hata hivyo, kazi hii yenyewe haina kuunda faili, inakagua tu hoja za pembejeo na inaita kazi NtCreateFile (kiambishi awali Nt kinaonyesha tu kuwa chaguo la kukokotoa ni la asili). Chaguo hili la kukokotoa linatangazwa katika faili ya kichwa cha winternl.h na kutekelezwa katika ntdll.dll. Inajitayarisha kuruka kwenye nafasi ya nyuklia, baada ya hapo hufanya simu ya mfumo ili kuunda faili. Katika kesi hii, zinageuka kuwa Kernel32 ni kitambaa tu cha Ntdll. Moja ya sababu kwa nini hii ilifanyika ni kwamba Microsoft hivyo ina uwezo wa kubadilisha kazi za ulimwengu wa asili, lakini si kugusa miingiliano ya kawaida. Microsoft haipendekezi kupiga simu kazi za asili moja kwa moja na haiandiki nyingi kati yao. Kwa njia, kazi zisizo na kumbukumbu zinaweza kupatikana hapa.

Faida kuu ya programu asilia ni kwamba ntdll imepakiwa kwenye mfumo mapema zaidi kuliko kernel32. Hii ni mantiki, kwa sababu kernel32 inahitaji ntdll kufanya kazi. Kwa hivyo, programu zinazotumia vipengele vya asili zinaweza kuanza kufanya kazi mapema zaidi.

Kwa hivyo, Programu za Windows Native ni programu ambazo zinaweza kuanza mapema kwenye boot ya Windows. Wanatumia tu vitendaji kutoka kwa ntdll. Mfano wa maombi kama haya: autochk anayeigiza matumizi ya chkdisk kuangalia diski kwa makosa kabla ya kuanza huduma kuu. Hiki ndicho kiwango tunachotaka Urejeshaji Amilifu wetu uwe.

Tutahitaji nini?

  • DDK (Dereva Development Kit), ambayo sasa inajulikana pia kama WDK 7 (Windows Driver Kit).
  • Mashine ya kweli (kwa mfano, Windows 7 x64)
  • Sio lazima, lakini faili za kichwa ambazo zinaweza kupakuliwa zinaweza kusaidia hapa

Je, kuna nini kwenye kanuni?

Wacha tufanye mazoezi kidogo na, kwa mfano, tuandike programu ndogo ambayo:

  1. Inaonyesha ujumbe kwenye skrini
  2. Hutenga kumbukumbu fulani
  3. Inasubiri uingizaji wa kibodi
  4. Huweka huru kumbukumbu iliyotumika

Katika programu asilia, mahali pa kuingilia sio kuu au winmain, lakini kazi ya NtProcessStartup, kwani kwa kweli tunazindua moja kwa moja michakato mpya kwenye mfumo.

Wacha tuanze kwa kuonyesha ujumbe kwenye skrini. Kwa hili tuna kazi ya asili NtDisplayString, ambayo inachukua kama hoja kielekezi kwa kitu cha muundo wa UNICODE_STRING. RtlInitUnicodeString itatusaidia kuianzisha. Kama matokeo, ili kuonyesha maandishi kwenye skrini tunaweza kuandika kazi hii ndogo:

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

Kwa kuwa kazi tu kutoka kwa ntdll zinapatikana kwetu, na hakuna maktaba zingine kwenye kumbukumbu bado, hakika tutakuwa na shida na jinsi ya kutenga kumbukumbu. Opereta mpya bado haipo (kwa sababu inatoka kwa ulimwengu wa kiwango cha juu sana cha C++), na hakuna kazi ya malloc (inahitaji maktaba ya C ya wakati wa kukimbia). Bila shaka, unaweza kutumia tu stack. Lakini ikiwa tunahitaji kugawa kumbukumbu kwa nguvu, tutalazimika kuifanya kwenye lundo (yaani lundo). Kwa hivyo wacha tujitengenezee lundo na tuchukue kumbukumbu kutoka kwake wakati wowote tunapohitaji.

Kitendaji kinafaa kwa kazi hii RtlCreateHeap. Kisha, kwa kutumia RtlAllocateHeap na RtlFreeHeap, tutachukua na kuhifadhi kumbukumbu tunapoihitaji.

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);

Hebu tuendelee kusubiri uingizaji wa kibodi.

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

Tunachohitaji ni kutumia NtReadFile kwenye kifaa kilichofunguliwa, na usubiri hadi kibodi irudishe mibofyo yoyote kwetu. Ikiwa ufunguo wa ESC umesisitizwa, tutaendelea kufanya kazi. Ili kufungua kifaa, tutahitaji kupiga kazi ya NtCreateFile (tutahitaji kufungua DeviceKeyboardClass0). Pia tutapiga simu NtCreateEventkuanzisha kitu cha kusubiri. Tutatangaza muundo wa KEYBOARD_INPUT_DATA wenyewe, ambao unawakilisha data ya kibodi. Hii itafanya kazi yetu iwe rahisi.

Programu asilia huisha na simu ya kukokotoa NtTerminateProcesskwa sababu tunaua mchakato wetu wenyewe.

Nambari zote za programu yetu ndogo:

#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: Tunaweza kutumia kwa urahisi kazi ya DbgBreakPoint() katika msimbo wetu ili kuisimamisha kwenye kitatuzi. Kweli, utahitaji kuunganisha WinDbg kwa mashine ya kawaida kwa utatuzi wa kernel. Maagizo ya jinsi ya kufanya hivyo yanaweza kupatikana hapa au tumia tu VirtualKD.

Mkusanyiko na mkusanyiko

Njia rahisi zaidi ya kuunda programu asilia ni kutumia DDK (Dereva Development Kit). Tunahitaji toleo la zamani la saba, kwani matoleo ya baadaye yana mbinu tofauti kidogo na hufanya kazi kwa karibu na Visual Studio. Ikiwa tunatumia DDK, basi mradi wetu unahitaji tu Makefile na vyanzo.

Makefile

!INCLUDE $(NTMAKEENV)makefile.def

vyanzo:

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

Makefile yako itakuwa sawa, lakini hebu tuangalie vyanzo kwa undani zaidi. Faili hii hubainisha vyanzo vya programu yako (.c faili), chaguo za muundo na vigezo vingine.

  • TARGETNAME - jina la faili inayoweza kutekelezwa ambayo inapaswa kuzalishwa mwishoni.
  • TARGETTYPE - aina ya faili inayoweza kutekelezwa, inaweza kuwa kiendeshi (.sys), basi thamani ya sehemu inapaswa kuwa DRIVER, ikiwa maktaba (.lib), basi thamani ni MAKTABA. Kwa upande wetu, tunahitaji faili inayoweza kutekelezwa (.exe), kwa hiyo tunaweka thamani kwa PROGRAM.
  • UMTYPE - maadili yanayowezekana kwa uwanja huu: koni ya programu ya kiweko, madirisha ya kufanya kazi katika hali ya dirisha. Lakini tunahitaji kubainisha nt kupata programu asilia.
  • BUFFER_OVERFLOW_CHECKS – kuangalia mrundikano wa bafa kufurika, kwa bahati mbaya si kesi yetu, tunaizima.
  • MINWIN_SDK_LIB_PATH - thamani hii inarejelea tofauti ya SDK_LIB_PATH, usijali kuwa huna kigezo cha mfumo kama hicho kilichotangazwa, tunapoendesha muundo ulioangaliwa kutoka kwa DDK, kigezo hiki kitatangazwa na kitaelekeza kwenye maktaba zinazohitajika.
  • VYANZO - orodha ya vyanzo vya programu yako.
  • INAJUMUISHA - faili za kichwa ambazo zinahitajika kwa mkusanyiko. Hapa kawaida zinaonyesha njia ya faili zinazokuja na DDK, lakini unaweza kutaja zingine zozote.
  • TARGETLIBS - orodha ya maktaba zinazohitaji kuunganishwa.
  • USE_NTDLL ni sehemu inayohitajika ambayo ni lazima iwekwe 1 kwa sababu zilizo wazi.
  • USER_C_FLAGS - bendera zozote ambazo unaweza kutumia katika maagizo ya kichakataji kabla ya kuandaa msimbo wa programu.

Kwa hivyo ili kujenga, tunahitaji kuendesha x86 (au x64) Imeangaliwa Jenga, badilisha saraka ya kufanya kazi kwenye folda ya mradi na uendesha amri ya Kuunda. Matokeo katika picha ya skrini yanaonyesha kuwa tuna faili moja inayoweza kutekelezwa.

Windows Native Applications na Acronis Active Restore huduma

Faili hii haiwezi kuzinduliwa kwa urahisi, mfumo unalaani na kututuma kufikiria juu ya tabia yake na makosa yafuatayo:

Windows Native Applications na Acronis Active Restore huduma

Jinsi ya kuzindua programu ya asili?

Wakati autochk inapoanza, mlolongo wa kuanza wa programu imedhamiriwa na thamani ya ufunguo wa Usajili:

HKLMSystemCurrentControlSetControlSession ManagerBootExecute

Msimamizi wa kipindi hutekeleza programu kutoka kwa orodha hii moja baada ya nyingine. Kidhibiti cha kikao hutafuta faili zinazoweza kutekelezwa zenyewe kwenye saraka ya system32. Muundo wa thamani ya ufunguo wa usajili ni kama ifuatavyo:

autocheck autochk *MyNative

Thamani lazima iwe katika umbizo la hexadecimal, si ASCII ya kawaida, kwa hivyo ufunguo ulioonyeshwa hapo juu utakuwa katika umbizo:

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

Ili kubadilisha kichwa, unaweza kutumia huduma ya mtandaoni, kwa mfano, hii.

Windows Native Applications na Acronis Active Restore huduma
Inabadilika kuwa kuzindua programu asilia, tunahitaji:

  1. Nakili faili inayoweza kutekelezwa kwenye folda ya system32
  2. Ongeza ufunguo kwenye Usajili
  3. Anzisha tena mashine

Kwa urahisi, hapa kuna hati iliyotengenezwa tayari ya kusanikisha programu asilia:

install.bat

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

ongeza.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

Baada ya usakinishaji na kuwasha upya, hata kabla ya skrini ya uteuzi wa mtumiaji kuonekana, tutapata picha ifuatayo:

Windows Native Applications na Acronis Active Restore huduma

Jumla ya

Kwa kutumia mfano wa programu ndogo kama hiyo, tulikuwa na hakika kwamba inawezekana kabisa kuendesha programu katika kiwango cha Windows Native. Ifuatayo, mimi na wavulana kutoka Chuo Kikuu cha Innopolis tutaendelea kuunda huduma ambayo itaanzisha mchakato wa mwingiliano na dereva mapema zaidi kuliko toleo la awali la mradi wetu. Na kwa ujio wa ganda la win32, itakuwa busara kuhamisha udhibiti kwa huduma kamili ambayo tayari imetengenezwa (zaidi juu ya hii. hapa).

Katika makala inayofuata tutagusa sehemu nyingine ya huduma ya Active Restore, yaani dereva wa UEFI. Jiandikishe kwa blogi yetu ili usikose chapisho linalofuata.

Chanzo: mapenzi.com

Kuongeza maoni