مقامي ايپليڪيشنن ۾، داخلا پوائنٽ مکيه يا 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 فنڪشن ناهي (ان کي رن ٽائم سي لائبريرين جي ضرورت آهي). يقينا، توهان صرف هڪ اسٽيڪ استعمال ڪري سگهو ٿا. پر جيڪڏهن اسان کي متحرڪ طور تي ميموري مختص ڪرڻ جي ضرورت آهي، اسان کي اهو ڪرڻو پوندو ڍير تي (يعني هيپ). سو اچو ته پنهنجي لاءِ هڪ ڍير ٺاهيون ۽ جڏهن به ضرورت هجي ان مان يادگيري وٺون.
فنڪشن هن ڪم لاء مناسب آهي 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 ڍانچي جو اعلان ڪنداسين، جيڪو ڪيبورڊ ڊيٽا جي نمائندگي ڪري ٿو. اهو اسان جي ڪم کي آسان بڻائي ڇڏيندو.
اصلي ايپليڪيشن هڪ فنڪشن ڪال سان ختم ٿئي ٿي 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);
}
پي اسان آساني سان استعمال ڪري سگھون ٿا DbgBreakPoint() فنڪشن اسان جي ڪوڊ ۾ ان کي ڊيبگر ۾ روڪڻ لاءِ. سچ، توهان کي ڪنيبل ڊيبگنگ لاءِ WinDbg کي ورچوئل مشين سان ڳنڍڻ جي ضرورت پوندي. اهو ڪيئن ڪجي تي هدايتون ملي سگهن ٿيون هتي يا صرف استعمال ڪريو ورچوئل ڪي ڊي.
تاليف ۽ اسيمبلي
هڪ مقامي ايپليڪيشن ٺاهڻ جو آسان طريقو استعمال ڪرڻ آهي ڊي ڊي (ڊرائيور ڊولپمينٽ کٽ). اسان کي قديم ستين ورزن جي ضرورت آهي، ڇاڪاڻ ته بعد ۾ ورزن ۾ ٿورو مختلف طريقو آهي ۽ ويزوئل اسٽوڊيو سان ويجهي ڪم ڪري ٿو. جيڪڏهن اسان 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
توهان جو ميڪ فائل بلڪل ساڳيو هوندو، پر اچو ته ذريعن کي ٿورو وڌيڪ تفصيل سان ڏسو. هي فائل توهان جي پروگرام جا ذريعا بيان ڪري ٿي (سي فائلون)، اختيارن جي تعمير، ۽ ٻيا پيرا ميٽر.
TARGETNAME - قابل عمل فائل جو نالو جيڪو آخر ۾ پيدا ڪيو وڃي.
TARGETTYPE - executable فائل جو قسم، اهو ڊرائيور (.sys) ٿي سگهي ٿو، پوءِ فيلڊ جي قيمت DRIVER هجڻ گهرجي، جيڪڏهن لائبريري (.lib)، ته پوءِ قيمت لائبريري آهي. اسان جي حالت ۾، اسان کي هڪ قابل عمل فائل (.exe) جي ضرورت آهي، تنهنڪري اسان پروگرام کي قيمت مقرر ڪيو.
UMTYPE - هن فيلڊ لاءِ ممڪن قدر: ڪنسول ايپليڪيشن لاءِ ڪنسول، ونڊوز موڊ ۾ ڪم ڪرڻ لاءِ ونڊوز. پر اسان کي وضاحت ڪرڻ جي ضرورت آهي nt هڪ ڏيهي ايپليڪيشن حاصل ڪرڻ لاء.
BUFFER_OVERFLOW_CHECKS - بفر اوور فلو لاءِ اسٽيڪ چيڪ ڪرڻ، بدقسمتي سان اسان جو معاملو ناهي، اسان ان کي بند ڪري ڇڏيون ٿا.
شامل آهن - هيڊر فائلون جيڪي اسيمبليءَ لاءِ گهربل آهن. هتي اهي عام طور تي ظاهر ڪن ٿا فائلن ڏانهن رستو جيڪي DDK سان گڏ ايندا آهن، پر توهان اضافي طور تي وضاحت ڪري سگهو ٿا ڪنهن ٻئي کي.
TARGETLIBS - لائبريرين جي فهرست جنهن کي ڳنڍڻ جي ضرورت آهي.
سيشن مينيجر هن فهرست مان هڪ هڪ ڪري پروگرامن کي انجام ڏئي ٿو. سيشن مينيجر پاڻ کي سسٽم 32 ڊاريڪٽري ۾ قابل عمل فائلن کي ڳولي ٿو. رجسٽري جي اهم قدر فارميٽ هن ريت آهي:
انسٽاليشن ۽ ريبوٽ کان پوء، جيتوڻيڪ صارف جي چونڊ اسڪرين کي ظاهر ٿيڻ کان اڳ، اسان کي هيٺ ڏنل تصوير ملندي:
نتيجو
اهڙي ننڍڙي ايپليڪيشن جي مثال کي استعمال ڪندي، اسان کي يقين ڏياريو ويو ته ونڊوز جي مقامي سطح تي ايپليڪيشن کي هلائڻ بلڪل ممڪن آهي. اڳيون، انوپوليس يونيورسٽي جا ماڻهو ۽ مان هڪ خدمت ٺاهيندا رهنداسين جيڪا اسان جي منصوبي جي پوئين ورزن جي ڀيٽ ۾ ڊرائيور سان رابطي جي عمل کي شروع ڪندي. ۽ Win32 شيل جي اچڻ سان، اهو منطقي هوندو ته ڪنٽرول کي هڪ مڪمل خدمت تي منتقل ڪرڻ لاء جيڪو اڳ ۾ ئي ترقي ڪئي وئي آهي (وڌيڪ هن تي هتي).
ايندڙ آرٽيڪل ۾ اسان هڪ ٻئي جزو تي رابطو ڪنداسين فعال بحالي سروس، يعني UEFI ڊرائيور. اسان جي بلاگ تي رڪنيت حاصل ڪريو ته جيئن توهان ايندڙ پوسٽ کي نه وڃايو.