ProHoster > Blog > башкаруу > Windows Native Тиркемелер жана Acronis Active Restore кызматы
Windows Native Тиркемелер жана Acronis Active Restore кызматы
Бүгүн биз Иннополис университетинин балдары менен биргеликте колдонуучуга өз машинасында иштебей калгандан кийин мүмкүн болушунча тезирээк иштей баштоого мүмкүндүк берүү үчүн Active Restore технологиясын иштеп чыгууну улантып жатабыз. Биз жергиликтүү Windows тиркемелери, анын ичинде аларды түзүү жана ишке киргизүү өзгөчөлүктөрү жөнүндө сүйлөшөбүз. Төмөндө биздин долбоор жөнүндө бир аз, ошондой эле жергиликтүү тиркемелерди кантип жазуу боюнча практикалык колдонмо бар.
Мурунку билдирүүлөрүбүздө бул эмне жөнүндө сөз кылганбыз Активдүү калыбына келтирүү, жана Иннополистеги студенттер кантип өнүгөт кызмат. Бүгүн мен жергиликтүү тиркемелерге токтолгум келет, алардын деңгээлине чейин биз активдүү калыбына келтирүү кызматыбызды "көмгүбүз" келет. Эгер баары ойдогудай болсо, анда биз:
Кызматтын өзүн бир топ эрте ишке киргизиңиз
Камдык көчүрмөсү алда канча мурда жайгашкан булут менен байланышыңыз
Система кандай режимде экенин түшүнүү үчүн алда канча мурда - кадимки жүктөө же калыбына келтирүү
Алдын ала калыбына келтирүү үчүн файлдар азыраак
Колдонуучуга дагы тезирээк баштоого уруксат бериңиз.
Ансыз деле жергиликтүү колдонмо деген эмне?
Бул суроого жооп берүү үчүн, келгиле, система жасаган чалуулардын ырааттуулугун карап көрөлү, мисалы, эгерде анын тиркемесинде программист файл түзүүгө аракет кылса.
Павел Йосифович - Windows ядросун программалоо (2019)
Программист функцияны колдонот CreateFile, fileapi.h баш файлында жарыяланган жана Kernel32.dllде ишке ашырылган. Бирок, бул функция өзү файлды түзбөйт, ал киргизилген аргументтерди гана текшерет жана функцияны чакырат NtCreateFile (Nt префикси жөн гана функциянын түпнуска экенин көрсөтүп турат). Бул функция winternl.h баш файлында жарыяланып, ntdll.dllде ишке ашырылат. Ал өзөктүк мейкиндикке секирүүгө даярданууда, андан кийин файлды түзүү үчүн системалык чакыруу жасайт. Бул учурда, Kernel32 жөн гана Ntdll үчүн орогуч экени белгилүү болду. Мунун бирден бир себеби, Microsoft түпнуска дүйнөнүн функцияларын өзгөртө алат, бирок стандарттуу интерфейстерге тийбейт. Microsoft жергиликтүү функцияларды түз чакырууну сунуштабайт жана алардын көбүн документтештирбейт. Айтмакчы, документтештирилбеген функцияларды табууга болот бул жерде.
Жергиликтүү тиркемелердин негизги артыкчылыгы - ntdll тутумга kernel32ге караганда алда канча эрте жүктөлөт. Бул логикалуу, анткени kernel32 иштеши үчүн ntdll талап кылынат. Натыйжада, жергиликтүү функцияларды колдонгон тиркемелер бир топ эрте иштей башташы мүмкүн.
Ошентип, Windows Native Тиркемелери - бул Windows жүктөөнүн башында баштала турган программалар. Алар NTdll функцияларын гана колдонушат. Мындай өтүнмөнүн мисалы: autochk ким аткарат chkdisk утилитасы негизги кызматтарды баштоодон мурун дискте каталарды текшерүү үчүн. Бул биздин Active Restore болушун каалаган деңгээлибиз.
Биз эмне кылышыбыз керек?
DDK (Driver Development Kit), азыр WDK 7 (Windows Driver Kit) катары белгилүү.
Виртуалдык машина (мисалы, Windows 7 x64)
Зарыл эмес, бирок жүктөлүп алынуучу баш файлдар жардам бериши мүмкүн бул жерде
Коддо эмне бар?
Келгиле, бир аз көнүгүү жасап көрөлү жана, мисалы, кичинекей өтүнмө жаз:
Экранда билдирүүнү көрсөтөт
Кээ бир эстутумду бөлөт
Баскычтоп киргизүүнү күтөт
Колдонулган эстутумду бошотот
Жергиликтүү тиркемелерде кирүү чекити негизги же 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'ден гана функциялар жеткиликтүү болгондуктан жана эстутумда башка китепканалар жок болгондуктан, эстутумду кантип бөлүштүрүү маселеси сөзсүз болот. Жаңы оператор азырынча жок (анткени ал өтө жогорку деңгээлдеги C++ дүйнөсүнөн келип чыккан) жана malloc функциясы жок (ал иштөө убактысынын C китепканаларын талап кылат). Албетте, сиз бир гана стек колдоно аласыз. Бирок эстутумду динамикалык түрдө бөлүштүрүү керек болсо, анда биз аны үймөктө (б.а. үймөктө) кылышыбыз керек болот. Андыктан, келгиле, өзүбүзгө үймөк түзөлү жана керек болгон учурда андан эстелик алалы.
Функция бул тапшырмага ылайыктуу RtlCreateHeap. Андан кийин, 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);
Бизге керек болгон нерсе - колдонуу NtReadFile ачык түзмөктө жана баскычтоп бизге каалаган басууну кайтарганга чейин күтө туруңуз. ESC баскычы басылса, биз иштей беребиз. Түзмөктү ачуу үчүн NtCreateFile функциясын чакырышыбыз керек (биз DeviceKeyboardClass0 ачышыбыз керек). Биз да чалабыз NtCreateEventкүтүү объектисин инициализациялоо үчүн. Биз клавиатура маалыматтарын билдирген KEYBOARD_INPUT_DATA түзүмүн өзүбүз жарыялайбыз. Бул биздин ишибизди жеңилдетет.
Түпкү колдонмо функция чакыруу менен аяктайт NtTerminateProcessанткени биз жөн гана өз процессибизди өлтүрүп жатабыз.
Биздин кичинекей колдонмонун бардык коду:
#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 (Driver Development Kit). Бизге байыркы жетинчи версия керек, анткени кийинки версиялар бир аз башкача мамилеге ээ жана Visual Studio менен тыгыз иштешет. Эгерде биз DDK колдонсок, анда биздин долбоорго бир гана Makefile жана булактар керек.
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 – бул талаа үчүн мүмкүн болгон маанилер: консолдук тиркеме үчүн консол, терезе режиминде иштөө үчүн терезелер. Бирок биз жергиликтүү тиркемени алуу үчүн nt көрсөтүшүбүз керек.
BUFFER_OVERFLOW_CHECKS – буфердин толуп кетиши үчүн стекти текшерүү, тилекке каршы, биздин учурда эмес, биз аны өчүрөбүз.
MINWIN_SDK_LIB_PATH – бул маани SDK_LIB_PATH өзгөрмөсүнө тиешелүү, сизде мындай система өзгөрмөсү жарыяланган эмес деп кабатыр болбоңуз, биз DDKден текшерилген курууну иштеткенде, бул өзгөрмө жарыяланып, керектүү китепканаларды көрсөтөт.
БУЛАКТАР – программаңыз үчүн булактардын тизмеси.
КАМТАТ – монтаждоо үчүн талап кылынган баш файлдар. Бул жерде алар, адатта, DDK менен келген файлдардын жолун көрсөтөт, бирок сиз дагы башкаларды белгилей аласыз.
TARGETLIBS – байланыштырылышы керек болгон китепканалардын тизмеси.
USE_NTDLL - бул айкын себептерден улам 1ге коюлушу керек болгон милдеттүү талаа.
USER_C_FLAGS – колдонмо кодун даярдоодо препроцессордун директивасында колдоно турган бардык желектер.
Ошентип, куруу үчүн, биз x86 (же x64) Checked Build программасын иштетишибиз керек, жумушчу каталогду долбоордун папкасына өзгөртүшүбүз жана Build буйругун иштетүүбүз керек. Скриншоттун натыйжасы бизде бир аткарылуучу файл бар экенин көрсөтүп турат.
Бул файлды оңой эле ишке киргизүү мүмкүн эмес, система бизди каргап, төмөнкү ката менен өзүнүн жүрүм-туруму жөнүндө ойлонууга жөнөтөт:
Жергиликтүү тиркемени кантип ишке киргизүү керек?
Autochk башталганда, программаларды баштоо ырааттуулугу реестр ачкычынын мааниси менен аныкталат:
Сеанс менеджери бул тизмедеги программаларды бирден аткарат. Сеанс менеджери system32 каталогунан аткарылуучу файлдарды издейт. Реестр ачкычынын маани форматы төмөнкүдөй:
autocheck autochk *MyNative
Маани кадимки ASCII эмес, он алтылык форматта болушу керек, андыктан жогоруда көрсөтүлгөн ачкыч форматта болот:
Орнотуу жана кайра жүктөөдөн кийин, колдонуучунун тандоо экраны пайда боло электе эле, биз төмөнкү сүрөттү алабыз:
жыйынтык
Мындай кичинекей тиркеменин мисалын колдонуп, биз тиркемени Windows Native деңгээлинде иштетүү толук мүмкүн экенине ынандык. Андан кийин, Иннополис университетинин балдары жана мен айдоочу менен өз ара аракеттенүү процессин биздин долбоордун мурунку версиясына караганда бир топ эрте баштаган кызматты курууну улантабыз. Ал эми win32 кабыгынын пайда болушу менен башкарууну буга чейин иштелип чыккан толук кандуу кызматка өткөрүп берүү логикалык болмок (бул жөнүндө көбүрөөк маалымат). бул жерде).
Кийинки макалада биз Active Restore кызматынын дагы бир компонентине, тактап айтканда UEFI драйверине токтолобуз. Кийинки постту өткөрүп жибербеш үчүн блогубузга жазылыңыз.