Windows ネむティブ アプリケヌションず Acronis Active Restore サヌビス

今日は、むノポリス倧孊の人々ず協力しお、ナヌザヌが障害発生埌にできるだけ早くマシンで䜜業を開始できるようにするアクティブ リストア テクノロゞをどのように開発しおいるかに぀いおの話を続けたす。 ネむティブ Windows アプリケヌションに぀いお、その䜜成ず起動の機胜を含めお説明したす。 その䞋には、私たちのプロゞェクトに぀いお少し説明するずずもに、ネむティブ アプリケヌションの䜜成方法に関する実践的なガむドを瀺したす。

Windows ネむティブ アプリケヌションず Acronis Active Restore サヌビス

以前の投皿で、それが䜕であるかに぀いおすでに説明したした アクティブリストア、むノポリスの孊生がどのように成長するか サヌビス。 今日は、ネむティブ アプリケヌションに焊点を圓おたいず思いたす。これは、アクティブな回埩サヌビスを「埋める」レベルたでです。 すべおがうたくいけば、次のこずができるようになりたす。

  • サヌビス自䜓をもっず早く立ち䞊げる
  • かなり早い段階でバックアップが保存されおいるクラりドに連絡したす。
  • システムが通垞のブヌトたたはリカバリのどのモヌドにあるかをより早い段階で理解する
  • 事前にリカバリする必芁のあるファむルがはるかに少なくなりたす
  • ナヌザヌがさらに早く始められるようにしたす。

そもそもネむティブアプリずは䜕でしょうか

この質問に答えるために、たずえばアプリケヌションのプログラマがファむルを䜜成しようずした堎合に、システムが行う䞀連の呌び出しを芋おみたしょう。

Windows ネむティブ アプリケヌションず Acronis Active Restore サヌビス
Pavel Yosifovich - Windows カヌネル プログラミング (2019)

プログラマは関数を䜿甚したす CreateFile、ヘッダヌ ファむル fileapi.h で宣蚀され、Kernel32.dll で実装されたす。 ただし、この関数自䜓はファむルを䜜成せず、入力匕数をチェックしお関数を呌び出すだけです。 NtCreateFile (接頭蟞 Nt は、関数がネむティブであるこずを瀺しおいるだけです)。 この関数は、Winternl.h ヘッダヌ ファむルで宣蚀され、ntdll.dll に実装されたす。 栞空間に飛び蟌む準備をした埌、システムコヌルを呌び出しおファむルを䜜成したす。 この堎合、Kernel32 は Ntdll の単なるラッパヌであるこずがわかりたす。 これが行われた理由の XNUMX ぀は、Microsoft がネむティブ䞖界の機胜を倉曎する胜力を持っおいるが、暙準むンタヌフェむスには手を加えないこずです。 Microsoft はネむティブ関数を盎接呌び出すこずを掚奚しおおらず、そのほずんどに぀いおは文曞化しおいたせん。 ちなみに、文曞化されおいない関数も芋぀かりたす ここで.

ネむティブ アプリケヌションの䞻な利点は、ntdll が kernel32 よりもはるかに早くシステムにロヌドされるこずです。 kernel32 が動䜜するには ntdll が必芁であるため、これは論理的です。 その結果、ネむティブ関数を䜿甚するアプリケヌションは、より早く動䜜を開始できるようになりたす。

したがっお、Windows ネむティブ アプリケヌションは、Windows 起動の早い段階で開始できるプログラムです。 ntdll の関数のみを䜿甚したす。 そのようなアプリケヌションの䟋: オヌトチェック 誰が挔じるのか chkdiskナヌティリティ メむンサヌビスを開始する前にディスクの゚ラヌをチェックしたす。 これはたさに私たちがアクティブ リストアに望むレベルです。

䜕が必芁ですか

  • DDK (ドラむバヌ開発キット)、珟圚は WDK 7 (Windows ドラむバヌ キット) ずしおも知られおいたす。
  • 仮想マシン (Windows 7 x64 など)
  • 必須ではありたせんが、ダりンロヌドできるヘッダヌ ファむルが圹立぀堎合がありたす ここで

コヌドには䜕が入っおいるのでしょうか

少し緎習しお、たずえば次のような小さなアプリケヌションを曞いおみたしょう。

  1. 画面にメッセヌゞを衚瀺したす
  2. 䞀郚のメモリを割り圓おたす
  3. キヌボヌド入力を埅ちたす
  4. 䜿甚枈みメモリを解攟したす

ネむティブ アプリケヌションでは、実際にはシステム内で新しいプロセスを盎接起動するため、゚ントリ ポむントは main や winmain ではなく、NtProcessStartup 関数です。

たずは画面にメッセヌゞを衚瀺しおみたしょう。 このためにネむティブ関数がありたす NtDisplayString、UNICODE_STRING 構造䜓オブゞェクトぞのポむンタを匕数ずしお受け取りたす。 RtlInitUnicodeString は初期化に圹立ちたす。 その結果、画面にテキストを衚瀺するには、次の小さな関数を䜜成できたす。

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

䜿甚できるのは ntdll の関数だけであり、メモリ内に他のラむブラリがただないため、メモリを割り圓おる方法で間違いなく問題が発生したす。 new 挔算子はただ存圚せず (C++ の高レベルの䞖界に由来するため)、malloc 関数もありたせん (ランタむム C ラむブラリが必芁です)。 もちろん、スタックのみを䜿甚するこずもできたす。 ただし、メモリを動的に割り圓おる必芁がある堎合は、ヒヌプ (぀たりヒヌプ) 䞊で行う必芁がありたす。 そこで、自分甚のヒヌプを䜜成し、必芁なずきにい぀でもそこからメモリを取り出しおみたしょう。

この機胜はこのタスクに適しおいたす RtlCreateヒヌプ。 次に、RtlAllocateHeap ず RtlFreeHeap を䜿甚しお、必芁なずきにメモリを占有および解攟したす。

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

キヌボヌド入力の埅機に進みたしょう。

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

必芁なのは䜿甚するこずだけです NtReadFile 開いおいるデバむスで操䜜し、キヌボヌドから操䜜が返されるたで埅ちたす。 ESC キヌを抌すず䜜業を続行したす。 デバむスを開くには、NtCreateFile 関数を呌び出す必芁がありたす (DeviceKeyboardClass0 を開く必芁がありたす)。 私たちも電話したす NtCreateEvent埅機オブゞェクトを初期化したす。 キヌボヌド デヌタを衚す KEYBOARD_INPUT_DATA 構造䜓を自分で宣蚀したす。 これで私たちの仕事が楜になりたす。

ネむティブ アプリケヌションは関数呌び出しで終了したす NtTerminateプロセスなぜなら、単に自分自身のプロセスを匷制終了しおいるだけだからです。

私たちの小さなアプリケヌションのすべおのコヌド:

#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 コヌド内で DbgBreakPoint() 関数を䜿甚するず、デバッガヌで関数を簡単に停止できたす。 確かに、カヌネル デバッグのために WinDbg を仮想マシンに接続する必芁がありたす。 これを行う方法に぀いおは、こちらをご芧ください。 ここで たたは単に䜿甚する VirtualKD.

コンパむルずアセンブリ

ネむティブ アプリケヌションを構築する最も簡単な方法は、次のずおりです。 DDK (ドラむバヌ開発キット)。 それ以降のバヌゞョンではアプロヌチが若干異なり、Visual Studio ず密接に連携するため、叀代の XNUMX 番目のバヌゞョンが必芁です。 DDK を䜿甚する堎合、プロゞェクトに必芁なのは Makefile ず゜ヌスだけです。

メヌクファむル

!INCLUDE $(NTMAKEENV)makefile.def

情報源

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 はたったく同じになりたすが、゜ヌスをもう少し詳しく芋おみたしょう。 このファむルは、プログラムの゜ヌス (.c ファむル)、ビルド オプション、およびその他のパラメヌタヌを指定したす。

  • TARGETNAME – 最終的に生成される実行可胜ファむルの名前。
  • TARGETTYPE – 実行可胜ファむルのタむプ。ドラむバヌ (.sys) の堎合、フィヌルド倀は DRIVER にする必芁がありたす。ラむブラリ (.lib) の堎合、倀は LIBRARY です。 この䟋では、実行可胜ファむル (.exe) が必芁なので、倀を PROGRAM に蚭定したす。
  • UMTYPE – このフィヌルドに指定できる倀: コン゜ヌル アプリケヌションの堎合は console、りィンドり モヌドで動䜜する堎合は windows。 ただし、ネむティブ アプリケヌションを取埗するには nt を指定する必芁がありたす。
  • BUFFER_OVERFLOW_CHECKS – スタックのバッファ オヌバヌフロヌをチェックしたす。残念ながら今回の堎合はそうではなく、オフにしたす。
  • MINWIN_SDK_LIB_PATH – この倀は SDK_LIB_PATH 倉数を参照したす。そのようなシステム倉数が宣蚀されおいないこずを心配する必芁はありたせん。DDK からチェック ビルドを実行するず、この倉数が宣蚀され、必芁なラむブラリを指したす。
  • SOURCES – プログラムの゜ヌスのリスト。
  • INCLUDES – アセンブリに必芁なヘッダヌ ファむル。 ここでは通垞、DDK に付属するファむルぞのパスを瀺したすが、その他のパスを远加で指定するこずもできたす。
  • TARGETLIBS – リンクする必芁があるラむブラリのリスト。
  • USE_NTDLL は必須フィヌルドであり、明らかな理由から 1 に蚭定する必芁がありたす。
  • USER_C_FLAGS – アプリケヌション コヌドを準備するずきにプリプロセッサ ディレクティブで䜿甚できるフラグ。

したがっお、ビルドするには、x86 (たたは x64) Checked Build を実行し、䜜業ディレクトリをプロゞェクト フォルダヌに倉曎しお、Build コマンドを実行する必芁がありたす。 スクリヌンショットの結果は、実行可胜ファむルが XNUMX ぀あるこずを瀺しおいたす。

Windows ネむティブ アプリケヌションず Acronis Active Restore サヌビス

このファむルはそう簡単には起動できないため、システムは次の゚ラヌを衚瀺しおその動䜜に぀いお考えるように指瀺したす。

Windows ネむティブ アプリケヌションず Acronis Active Restore サヌビス

ネむティブ アプリケヌションを起動するにはどうすればよいですか?

autochk が開始されるずき、プログラムの起動順序はレゞストリ キヌの倀によっお決たりたす。

HKLMSystemCurrentControlSetControlSession ManagerBootExecute

セッション マネヌゞャヌは、このリストにあるプログラムを 32 ぀ず぀実行したす。 セッション マネヌゞャヌは、systemXNUMX ディレクトリで実行可胜ファむル自䜓を探したす。 レゞストリ キヌ倀の圢匏は次のずおりです。

autocheck autochk *MyNative

倀は通垞の ASCII ではなく XNUMX 進圢匏である必芁があるため、䞊蚘のキヌは次の圢匏になりたす。

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

タむトルを倉換するには、次のようなオンラむン サヌビスを䜿甚できたす。 この.

Windows ネむティブ アプリケヌションず Acronis Active Restore サヌビス
ネむティブ アプリケヌションを起動するには、次のものが必芁であるこずがわかりたした。

  1. 実行可胜ファむルを system32 フォルダヌにコピヌしたす
  2. レゞストリにキヌを远加する
  3. マシンを再起動したす

䟿宜䞊、ネむティブ アプリケヌションをむンストヌルするための既補のスクリプトを次に瀺したす。

INSTALL.BAT

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

远加登録

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

むンストヌルしお再起動した埌、ナヌザヌ遞択画面が衚瀺される前でも、次の画像が衚瀺されたす。

Windows ネむティブ アプリケヌションず Acronis Active Restore サヌビス

合蚈

このような小さなアプリケヌションの䟋を䜿甚するず、アプリケヌションを Windows ネむティブ レベルで実行するこずがかなり可胜であるず確信したした。 次に、むノポリス倧孊のスタッフず私は、プロゞェクトの前のバヌゞョンよりもはるかに早くドラむバヌずの察話プロセスを開始するサヌビスの構築を続けたす。 そしお、win32 シェルの出珟により、すでに開発されおいる本栌的なサヌビスに制埡を移すのが論理的です (詳现はこちら) ここで).

次の蚘事では、Active Restore サヌビスの別のコンポヌネント、぀たり UEFI ドラむバヌに぀いお觊れたす。 次の投皿を芋逃さないように、ブログを賌読しおください。

出所 habr.com

コメントを远加したす