Өнөөдөр бид Иннополисын их сургуулийн залуустай хамт хэрэглэгчдэд бүтэлгүйтлийн дараа машин дээрээ аль болох хурдан ажиллаж эхлэх боломжийг олгохын тулд Active Restore технологийг хэрхэн хөгжүүлж байгаа тухай түүхийг үргэлжлүүлж байна. Бид Windows-ийн уугуул програмуудын талаар, түүний дотор тэдгээрийг үүсгэх, эхлүүлэх онцлогуудын талаар ярих болно. Зүсэлтийн доор манай төслийн талаар бага зэрэг, мөн уугуул програмуудыг хэрхэн бичих талаар практик гарын авлага байна.
Энэ нь юу болох талаар бид өмнөх нийтлэлүүддээ аль хэдийн ярьсан
- Үйлчилгээг өөрөө хамаагүй эрт эхлүүл
- Нөөцлөлт байгаа үүлтэй илүү эрт холбогдоно уу
- Систем ямар горимд байгааг ойлгохын тулд илүү эрт - хэвийн ачаалах эсвэл сэргээх
- Урьдчилан сэргээхэд цөөн тооны файл байна
- Хэрэглэгчийг илүү хурдан эхлүүлэхийг зөвшөөрнө үү.
Ямар ч байсан уугуул програм гэж юу вэ?
Энэ асуултад хариулахын тулд, жишээлбэл, программынхаа программист файл үүсгэхийг оролдох тохиолдолд систем хийдэг дуудлагын дарааллыг харцгаая.
Павел Йосифович - Windows цөмийн програмчлал (2019)
Программист функцийг ашигладаг
Төрөлх програмуудын гол давуу тал нь ntdll нь kernel32-ээс хамаагүй эрт системд ачаалагддаг. Энэ нь логик юм, учир нь kernel32 нь ажиллахын тулд ntdll шаарддаг. Үүний үр дүнд үндсэн функцийг ашигладаг програмууд илүү эрт ажиллаж эхэлдэг.
Тиймээс Windows Native Applications нь Windows-ийг ачаалахад эрт эхлэх боломжтой програмууд юм. Тэд ЗӨВХӨН ntdll-ийн функцуудыг ашигладаг. Ийм хэрэглээний жишээ:
Бидэнд юу хэрэгтэй байна вэ?
DDK (Драйвер хөгжүүлэх хэрэгсэл), одоо WDK 7 (Windows Driver Kit) гэж нэрлэгддэг.- Виртуал машин (жишээ нь, Windows 7 x64)
- Шаардлагагүй, гэхдээ татаж авах боломжтой толгой файлууд нь тус болно
энд
Кодод юу байгаа вэ?
Жаахан дадлага хийцгээе, жишээлбэл, дараах жижиг програм бичье.
- Дэлгэц дээр мессеж харуулна
- Зарим санах ойг хуваарилдаг
- Гарын оролтыг хүлээж байна
- Ашигласан санах ойг чөлөөлнө
Уугуул програмуудад нэвтрэх цэг нь үндсэн эсвэл winmain биш, харин NtProcessStartup функц юм, учир нь бид системд шинэ процессуудыг шууд эхлүүлдэг.
Дэлгэц дээр мессеж гарч эхэлцгээе. Үүний тулд бид эх функцтэй
//usage: WriteLn(L"Here is my textn");
void WriteLn(LPWSTR Message)
{
UNICODE_STRING string;
RtlInitUnicodeString(&string, Message);
NtDisplayString(&string);
}
Зөвхөн ntdll-ийн функцууд бидэнд боломжтой бөгөөд санах ойд өөр номын сан байхгүй байгаа тул санах ойг хэрхэн хуваарилах талаар асуудал гарах нь гарцаагүй. Шинэ оператор хараахан байхгүй байна (учир нь энэ нь C++-ийн хэт өндөр түвшний ертөнцөөс гаралтай) бөгөөд malloc функц байхгүй (үүнд ажиллах цагийн С номын сангууд шаардлагатай). Мэдээжийн хэрэг та зөвхөн стек ашиглаж болно. Гэхдээ хэрэв бид санах ойг динамикаар хуваарилах шаардлагатай бол бид үүнийг нуруулдан (өөрөөр хэлбэл нуруулдан) хийх хэрэгтэй болно. Тиймээс өөрсдөдөө зориулж овоолгыг бүтээж, хэрэгтэй үед нь дурсамжаа авцгаая.
Энэ функц нь энэ ажилд тохиромжтой
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;
}
}
Бидэнд хэрэгтэй зүйл бол ашиглах явдал юм
Үндсэн програм нь функцийн дуудлагаар төгсдөг
Манай жижиг програмын бүх код:
#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);
}
Жич: Бид кодын DbgBreakPoint() функцийг хялбархан ашиглан дибаг хийгч дээр зогсоох боломжтой. Үнэн бол та цөмийн дибаг хийхэд WinDbg-г виртуал машинтай холбох хэрэгтэй болно. Үүнийг хэрхэн хийх зааврыг олж болно
Эмхэтгэх, угсрах
Төрөлх програм бүтээх хамгийн хялбар арга бол ашиглах явдал юм
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-ээс checked build-г ажиллуулахад энэ хувьсагч зарлагдах бөгөөд шаардлагатай номын сангууд руу чиглүүлэх болно.
- SOURCES – таны хөтөлбөрийн эх сурвалжуудын жагсаалт.
- ОРУУЛНА – угсрахад шаардлагатай толгой файлууд. Энд тэд ихэвчлэн DDK-тэй хамт ирдэг файлуудын замыг зааж өгдөг боловч та бусад файлуудыг нэмж зааж өгч болно.
- TARGETLIBS – холбох шаардлагатай номын сангуудын жагсаалт.
- USE_NTDLL нь тодорхой шалтгааны улмаас 1 болгож тохируулах шаардлагатай талбар юм.
- USER_C_FLAGS – програмын код бэлтгэхдээ урьдчилан процессорын зааварт ашиглаж болох аливаа туг.
Тиймээс бүтээхийн тулд бид x86 (эсвэл x64) Checked Build програмыг ажиллуулж, ажлын лавлахыг төслийн хавтас болгон өөрчилж, Build командыг ажиллуулах хэрэгтэй. Дэлгэцийн агшин дахь үр дүн нь бидэнд нэг гүйцэтгэгдэх файл байгааг харуулж байна.
Энэ файлыг тийм ч амархан эхлүүлэх боломжгүй тул систем нь биднийг хараан зүхэж, дараах алдаатай үйлдлийнхээ талаар бодохыг бидэнд илгээдэг.
Төрөлх програмыг хэрхэн эхлүүлэх вэ?
Autochk эхлэхэд програмыг эхлүүлэх дарааллыг бүртгэлийн түлхүүрийн утгаар тодорхойлно.
HKLMSystemCurrentControlSetControlSession ManagerBootExecute
Сеанс менежер энэ жагсаалтаас программуудыг нэг нэгээр нь гүйцэтгэдэг. Сеанс менежер нь system32 лавлахаас гүйцэтгэх боломжтой файлуудыг өөрөө хайдаг. Бүртгэлийн түлхүүрийн утгын формат дараах байдалтай байна.
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
Гарчиг хөрвүүлэхийн тулд та онлайн үйлчилгээг ашиглаж болно, жишээлбэл,
Төрөлх програмыг ажиллуулахын тулд бидэнд дараахь зүйл хэрэгтэй болно.
- Гүйцэтгэх файлыг system32 хавтас руу хуулна уу
- Бүртгэлд түлхүүр нэмнэ үү
- Машиныг дахин ачаална уу
Тохиромжтой болгох үүднээс уугуул програм суулгахад бэлэн скриптийг энд оруулав.
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
Суулгаж, дахин ачаалсны дараа хэрэглэгчийн сонгох дэлгэц гарч ирэхээс өмнө бид дараах зургийг авах болно.
Үр дүн
Ийм жижиг програмын жишээг ашигласнаар бид програмыг Windows Native түвшинд ажиллуулах бүрэн боломжтой гэдэгт итгэлтэй байсан. Дараа нь, Иннополисын их сургуулийн залуус бид хоёр төслийн өмнөх хувилбараас хамаагүй эрт жолоочтой харилцах үйл явцыг эхлүүлэх үйлчилгээг үргэлжлүүлэн барих болно. Win32 бүрхүүл гарч ирснээр хяналтыг аль хэдийн боловсруулсан бүрэн үйлчилгээнд шилжүүлэх нь логик юм (энэ талаар дэлгэрэнгүй
Дараагийн өгүүллээр бид Active Restore үйлчилгээний өөр нэг бүрэлдэхүүн хэсэг болох UEFI драйверын талаар ярих болно. Дараагийн нийтлэлийг алдахгүйн тулд манай блогт бүртгүүлээрэй.
Эх сурвалж: www.habr.com