Windows vietÄjÄs lietojumprogrammas un Acronis Active Restore pakalpojums
Å odien turpinÄm stÄstu par to, kÄ mÄs kopÄ ar puiÅ”iem no Innopolis UniversitÄtes attÄ«stÄm Active Restore tehnoloÄ£iju, lai pÄc kļūmes lietotÄjs pÄc iespÄjas ÄtrÄk varÄtu sÄkt strÄdÄt pie savas maŔīnas. MÄs runÄsim par vietÄjÄm Windows lietojumprogrammÄm, tostarp to izveides un palaiÅ”anas funkcijÄm. Zem griezuma ir nedaudz par mÅ«su projektu, kÄ arÄ« praktisks ceļvedis, kÄ rakstÄ«t vietÄjÄs lietojumprogrammas.
IepriekÅ”Äjos ierakstos mÄs jau runÄjÄm par to, kas tas ir AktÄ«vÄ atjaunoÅ”ana, un kÄ attÄ«stÄs Innopolis studenti pakalpojumu. Å odien es vÄlos koncentrÄties uz vietÄjÄm lietojumprogrammÄm, kuru lÄ«menÄ« mÄs vÄlamies āapglabÄtā mÅ«su aktÄ«vÄs atkopÅ”anas pakalpojumu. Ja viss izdosies, tad mÄs varÄsim:
Palaidiet pakalpojumu daudz agrÄk
Daudz agrÄk sazinieties ar mÄkoni, kurÄ atrodas dublÄjums
Daudz agrÄk, lai saprastu, kÄdÄ režīmÄ sistÄma atrodas - parastÄ sÄknÄÅ”ana vai atkopÅ”ana
IepriekÅ” atkopjamo failu skaits ir daudz mazÄks
Ä»aujiet lietotÄjam sÄkt darbu vÄl ÄtrÄk.
Kas vispÄr ir vietÄjÄ lietotne?
Lai atbildÄtu uz Å”o jautÄjumu, apskatÄ«sim izsaukumu secÄ«bu, ko sistÄma veic, piemÄram, ja programmÄtÄjs savÄ lietojumprogrammÄ mÄÄ£ina izveidot failu.
PÄvels JosifoviÄs ā Windows kodola programmÄÅ”ana (2019)
ProgrammÄtÄjs izmanto funkciju Izveidot failu, kas ir deklarÄts galvenes failÄ fileapi.h un ieviests programmÄ Kernel32.dll. TomÄr Ŕī funkcija pati neveido failu, tÄ tikai pÄrbauda ievades argumentus un izsauc funkciju NtCreateFile (prefikss Nt tikai norÄda, ka funkcija ir vietÄjÄ). Å Ä« funkcija ir deklarÄta galvenes failÄ winternl.h un ieviesta failÄ ntdll.dll. Tas gatavojas pÄrlÄkÅ”anai kodoltelpÄ, pÄc tam veic sistÄmas izsaukumu, lai izveidotu failu. Å ajÄ gadÄ«jumÄ izrÄdÄs, ka Kernel32 ir tikai Ntdll iesaiÅojums. Viens no iemesliem, kÄpÄc tas tika darÄ«ts, ir tas, ka Microsoft tÄdÄjÄdi var mainÄ«t vietÄjÄs pasaules funkcijas, bet nepieskarties standarta saskarnÄm. Microsoft neiesaka tieÅ”i izsaukt vietÄjÄs funkcijas un lielÄko daļu no tÄm nedokumentÄ. Starp citu, var atrast nedokumentÄtas funkcijas Å”eit.
GalvenÄ vietÄjo lietojumprogrammu priekÅ”rocÄ«ba ir tÄ, ka ntdll tiek ielÄdÄts sistÄmÄ daudz agrÄk nekÄ kernel32. Tas ir loÄ£iski, jo kernel32 darbam ir nepiecieÅ”ams ntdll. RezultÄtÄ lietojumprogrammas, kas izmanto vietÄjÄs funkcijas, var sÄkt darboties daudz agrÄk.
TÄdÄjÄdi Windows vietÄjÄs lietojumprogrammas ir programmas, kuras var palaist agri Windows sÄknÄÅ”anas laikÄ. ViÅi izmanto TIKAI ntdll funkcijas. Å Ädas lietojumprogrammas piemÄrs: autochk kurÅ” uzstÄjas chkdisk utilÄ«ta lai pirms galveno pakalpojumu palaiÅ”anas pÄrbaudÄ«tu, vai diskÄ nav kļūdu. Tas ir tieÅ”i tÄds lÄ«menis, kÄdu mÄs vÄlamies, lai mÅ«su aktÄ«vÄ atjaunoÅ”ana bÅ«tu.
Ko mums vajag?
DDK (Driver Development Kit), tagad pazÄ«stams arÄ« kÄ WDK 7 (Windows draiveru komplekts).
VirtuÄlÄ maŔīna (piemÄram, Windows 7 x64)
Nav nepiecieÅ”ams, taÄu var palÄ«dzÄt galvenes faili, kurus var lejupielÄdÄt Å”eit
Kas ir kodÄ?
Nedaudz trenÄsimies un, piemÄram, uzrakstÄ«sim nelielu pieteikumu, kas:
ParÄda ziÅojumu ekrÄnÄ
PieŔķir daļu atmiÅas
Gaida tastatūras ievadi
AtbrÄ«vo izmantoto atmiÅu
VietÄjÄs lietojumprogrammÄs ievades punkts nav galvenais vai winmain, bet gan funkcija NtProcessStartup, jo mÄs faktiski tieÅ”i palaižam sistÄmÄ jaunus procesus.
SÄksim ar ziÅojuma parÄdÄ«Å”anu ekrÄnÄ. Å im nolÅ«kam mums ir vietÄjÄ funkcija NtDisplayString, kas kÄ argumentu izmanto rÄdÄ«tÄju uz UNICODE_STRING struktÅ«ras objektu. RtlInitUnicodeString palÄ«dzÄs mums to inicializÄt. TÄ rezultÄtÄ, lai ekrÄnÄ parÄdÄ«tu tekstu, mÄs varam ierakstÄ«t Å”o mazo funkciju:
//usage: WriteLn(L"Here is my textn");
void WriteLn(LPWSTR Message)
{
UNICODE_STRING string;
RtlInitUnicodeString(&string, Message);
NtDisplayString(&string);
}
TÄ kÄ mums ir pieejamas tikai funkcijas no ntdll, un citu bibliotÄku atmiÅÄ vienkÄrÅ”i vÄl nav, mums noteikti bÅ«s problÄmas ar atmiÅas pieŔķirÅ”anu. Jaunais operators vÄl neeksistÄ (jo tas nÄk no pÄrÄk augsta lÄ«meÅa C++ pasaules), un nav malloc funkcijas (tam nepiecieÅ”amas izpildlaika C bibliotÄkas). Protams, jÅ«s varat izmantot tikai kaudzi. Bet, ja mums ir dinamiski jÄpieŔķir atmiÅa, mums tas bÅ«s jÄdara kaudzÄ (t.i., kaudzÄ«tÄ). TÄpÄc izveidosim sev kaudzi un Åemsim no tÄs atmiÅu ikreiz, kad tas bÅ«s nepiecieÅ”ams.
Funkcija ir piemÄrota Å”im uzdevumam RtlCreateHeap. TÄlÄk, izmantojot RtlAllocateHeap un RtlFreeHeap, mÄs aizÅemsim un atbrÄ«vosim atmiÅu, kad tÄ bÅ«s nepiecieÅ”ama.
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);
Viss, kas mums nepiecieÅ”ams, ir izmantot NtReadFile atvÄrtÄ ierÄ«cÄ un pagaidiet, lÄ«dz tastatÅ«ra atgriezÄ«s mums jebkuru nospieÅ”anu. Ja tiek nospiests ESC taustiÅÅ”, mÄs turpinÄsim darbu. Lai atvÄrtu ierÄ«ci, mums bÅ«s jÄizsauc funkcija NtCreateFile (mums bÅ«s jÄatver DeviceKeyboardClass0). MÄs arÄ« piezvanÄ«sim NtCreateEventlai inicializÄtu gaidÄ«Å”anas objektu. MÄs paÅ”i deklarÄsim KEYBOARD_INPUT_DATA struktÅ«ru, kas atspoguļo tastatÅ«ras datus. Tas atvieglos mÅ«su darbu.
VietÄjÄ lietojumprogramma beidzas ar funkcijas izsaukumu NtTerminateProcessjo mÄs vienkÄrÅ”i nogalinÄm paÅ”i savu procesu.
Viss mÅ«su mazÄs lietojumprogrammas kods:
#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: MÄs varam viegli izmantot funkciju DbgBreakPoint() savÄ kodÄ, lai apturÄtu to atkļūdotÄjs. Tiesa, jums bÅ«s jÄpievieno WinDbg ar virtuÄlo maŔīnu kodola atkļūdoÅ”anai. NorÄdÄ«jumus, kÄ to izdarÄ«t, var atrast Å”eit vai vienkÄrÅ”i izmantot VirtualKD.
SastÄdÄ«Å”ana un montÄža
VienkÄrÅ”Äkais veids, kÄ izveidot vietÄjo lietojumprogrammu, ir izmantot DDK (Driver Development Kit). Mums ir nepiecieÅ”ama senÄ septÄ«tÄ versija, jo jaunÄkajÄm versijÄm ir nedaudz atŔķirÄ«ga pieeja un tÄs cieÅ”i sadarbojas ar Visual Studio. Ja mÄs izmantojam DDK, tad mÅ«su projektam ir nepiecieÅ”ams tikai Makefile un avoti.
Makefile
!INCLUDE $(NTMAKEENV)makefile.def
avoti:
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
JÅ«su Makefile bÅ«s tieÅ”i tÄds pats, taÄu apskatÄ«sim avotus nedaudz sÄ«kÄk. Å ajÄ failÄ ir norÄdÄ«ti jÅ«su programmas avoti (.c faili), bÅ«vÄjuma opcijas un citi parametri.
TARGETNAME ā izpildÄmÄ faila nosaukums, kas beigÄs jÄizveido.
TARGETTYPE ā izpildÄmÄ faila tips, tas var bÅ«t draiveris (.sys), tad lauka vÄrtÄ«bai jÄbÅ«t DRIVER, ja bibliotÄka (.lib), tad vÄrtÄ«ba ir LIBRARY. MÅ«su gadÄ«jumÄ mums ir nepiecieÅ”ams izpildÄmais fails (.exe), tÄpÄc mÄs iestatÄm vÄrtÄ«bu uz PROGRAMMA.
UMTYPE ā Ŕī lauka iespÄjamÄs vÄrtÄ«bas: konsole konsoles lietojumprogrammai, logi darbam logu režīmÄ. Bet mums ir jÄnorÄda nt, lai iegÅ«tu vietÄjo lietojumprogrammu.
BUFFER_OVERFLOW_CHECKS ā pÄrbaudot steku, vai nav pÄrpildÄ«ts buferis, diemžÄl ne mÅ«su gadÄ«jumÄ, mÄs to izslÄdzam.
MINWIN_SDK_LIB_PATH ā Ŕī vÄrtÄ«ba attiecas uz mainÄ«go SDK_LIB_PATH, neuztraucieties, ka jums nav deklarÄts Å”Äds sistÄmas mainÄ«gais, kad mÄs izpildÄ«sim pÄrbaudÄ«to bÅ«vÄjumu no DDK, Å”is mainÄ«gais tiks deklarÄts un norÄdÄ«s uz nepiecieÅ”amajÄm bibliotÄkÄm.
AVOTI ā jÅ«su programmas avotu saraksts.
IEKÄ»AUJ ā galvenes faili, kas nepiecieÅ”ami montÄžai. Å eit tie parasti norÄda ceļu uz failiem, kas tiek piegÄdÄti kopÄ ar DDK, bet jÅ«s varat papildus norÄdÄ«t citus.
TARGETLIBS ā to bibliotÄku saraksts, kuras ir jÄsaista.
USE_NTDLL ir obligÄts lauks, kas acÄ«mredzamu iemeslu dÄļ jÄiestata uz 1.
USER_C_FLAGS ā visi karodziÅi, kurus varat izmantot priekÅ”apstrÄdÄtÄja direktÄ«vÄs, sagatavojot lietojumprogrammas kodu.
TÄtad, lai izveidotu, mums ir jÄpalaiž x86 (vai x64) Checked Build, jÄmaina darba direktorijs uz projekta mapi un jÄpalaiž komanda Build. EkrÄnuzÅÄmuma rezultÄts parÄda, ka mums ir viens izpildÄms fails.
Å o failu nevar tik vienkÄrÅ”i palaist, sistÄma nolÄdÄ un sÅ«ta mums domÄt par tÄ uzvedÄ«bu ar Å”Ädu kļūdu:
KÄ palaist vietÄjo lietojumprogrammu?
Kad autochk startÄ, programmu startÄÅ”anas secÄ«bu nosaka reÄ£istra atslÄgas vÄrtÄ«ba:
Sesiju pÄrvaldnieks pa vienam izpilda programmas no Ŕī saraksta. Sesiju pÄrvaldnieks paÅ”i meklÄ izpildÄmos failus system32 direktorijÄ. ReÄ£istra atslÄgas vÄrtÄ«bas formÄts ir Å”Äds:
autocheck autochk *MyNative
VÄrtÄ«bai ir jÄbÅ«t heksadecimÄlÄ formÄtÄ, nevis parastajÄ ASCII formÄtÄ, tÄpÄc iepriekÅ” redzamÄ atslÄga bÅ«s Å”ÄdÄ formÄtÄ:
PÄc instalÄÅ”anas un atsÄknÄÅ”anas, pat pirms tiek parÄdÄ«ts lietotÄja atlases ekrÄns, mÄs iegÅ«sim Å”Ädu attÄlu:
Kopsavilkums
Izmantojot Å”Ädas nelielas lietojumprogrammas piemÄru, mÄs pÄrliecinÄjÄmies, ka ir pilnÄ«gi iespÄjams palaist lietojumprogrammu Windows Native lÄ«menÄ«. TÄlÄk mÄs ar Innopolis UniversitÄtes puiÅ”iem turpinÄsim veidot servisu, kas uzsÄks mijiedarbÄ«bas procesu ar vadÄ«tÄju daudz agrÄk nekÄ mÅ«su projekta iepriekÅ”ÄjÄ versijÄ. Un lÄ«dz ar win32 apvalka parÄdÄ«Å”anos bÅ«tu loÄ£iski pÄrcelt vadÄ«bu uz pilnvÄrtÄ«gu pakalpojumu, kas jau ir izstrÄdÄts (vairÄk par to Å”eit).