في هذا الدرس سنتعلم كيف نكتب driver بسيط جداً لا يقوم بشئ تقريباً ، أعتقد أن المتابع حصل على الأدوات التي تم الإشارة إليها في الدرس السابق وهي حزمة الـddk و DbgView و محرر كود .
بعد تنصيب الـDDK ستجد انه يوجد له قائمة في Start->Programs تحت اسم Development Kits ، ما يهمنا هو الملف التالي وهو سطر الأوامر الذي سنعمل منه :
Build Environments -> Windows XP -> Windows XP Free Build Environment
قم بعمل shortcut للملف المذكور في مكان سهل الوصول إليه .
لعمل driver بسيط عن طريق سطر الأوامر هذا لابد من توافر ثلاث أشياء أساسية و هي :
1- makefile : وهو ملف يقوم بعمل إعادة توجيه لملف الـmake file الحقيقي الآتي مع حزمة الـDDK ، يحتوي هذا الملف على هذا السطر فقط :
!INCLUDE $(NTMAKEENV)\makefile.def
لا تقم بتغيير محتوى هذا الملف .
2- sources : وهو الملف المسئول عن عناصر المشروع الأساسية مثل الملفات التي سيتم ترجمتها و ونوع الملف الناتج و اسمه واي خيارات اخرى للمشروع :
TARGETNAME = test_driver هنا نعرف اسم الملف الناتج بأنه "test_driver" .
TARGETTYPE = DRIVER نصرح بان الملف الناتج هو عبارة عن Driver وليس Exe او Dll .
INCLUDES=..\..\inc لتحديد مجلد الـIncludes الخاص بالـDDK .
SOURCES = driver.c هنا نقوم بوضع ملفات السورس التي سيتم ترجمتها بحيث يفصل بين كل ملف وآخر مسافة " " .
حسنا الآن سنقوم بشرح الكود الخاص بالدرايفر ، الدرايفر لا يقوم بفعل أي شئ ، فقط يتم تحميله فيقوم بإرسال جملة للـDebug output لنعرف انه تم تحميله ، أيضا سيتم طباعة جملة أخرى أثناء عمل Unload للدرايفر .
كما نعرف لابد من نقطة بداية للدرايفر لأنه يعتبر PE File ، في برامج الـConsole كنا نعرف نقطة البداية باسم main وفي برامج الـGUI نعرفها باسم WinMain بينما في المكتبات DLL يتم تعريفها باسم DllEntryPoint او DllMain ، في الدرايفرات نعرفها باسم DriverEntry وهو بالشكل التالي :
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath );
DriverObject : مؤشر الى DRIVER_OBJECT وهو structure يمثل اي درايفر في النظام و تركيبه كالتالي :
typedef struct _DRIVER_OBJECT {
CSHORT Type;
CSHORT Size;
PDEVICE_OBJECT DeviceObject;
ULONG Flags;
PVOID DriverStart;
ULONG DriverSize;
PVOID DriverSection;
PDRIVER_EXTENSION DriverExtension;
UNICODE_STRING DriverName;
PUNICODE_STRING HardwareDatabase;
PFAST_IO_DISPATCH FastIoDispatch;
PDRIVER_INITIALIZE DriverInit;
PDRIVER_STARTIO DriverStartIo;
PDRIVER_UNLOAD DriverUnload;
PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT, *PDRIVER_OBJECT;العناصر المهمة :
DeviceObject : وهي مؤشر إلى سلسلة الـdevices التي ترتبط بهذا الدرايفر ، سنتعرف المزيد عنها لاحقاً .
DriverStart : عنوان لـDriverEntry الخاص بالدرايفر .
DriverSize : حجم الدرايفر بعد تحميله في الذاكرة الخاصة بالكرنل .
DriverSection : هذا العنصر غير مصرح به من قبل مايكروسوفت لكنه مهم جداً وسنتكلم عنه أكثر عند الحديث عن طرق جرد الدرايفرز .
DriverName : هو structure يحتوي على اسم الدرايفر ، تركيب الـstructure كالتالي :
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;حيث Buffer تشير إلى النص كـunicode وليس ansi ، ولتكن قاعدة لديك ان كل تعاملنا مع روتينات النظام نستخدم فيها دائما نصوص Unicode ، لاحظ أيضا أن النص ليس بالضرورة Null-Terminated أي انه لابد من الاستعانة بالطول Length أثناء التعامل مع النص كي لا نقع في مشاكل خطيرة .
DriverUnload : وهو عنوان للدالة التي سيتم استدعاؤها أثناء عمل Unload للدرايفر ، لاحظ انك اذا لم يتم بوضع هذا العنصر فانك لن تتمكن من عمل Unload للدرايفر إلى ان تقوم بعمل reset للنظام .
MajorFunction : مصفوفة من عناوين للـdispatch routines الخاصة بالدرايفر وهي مهمة جدا وسنقوم بتفصيل المهم منها عند الحديث عن الـIRP Handling .
RegistryPath : وهو مؤشر الى UNICODE_STRING يحتوي على اسم مفتاح الرجستري الخاص بالدرايفر ، لاحظ أن أي درايفر يتم تحميله لابد من وجود مفتاح رجستري خاص به و يكون عادة تحت هذا المسار :
\Registry\Machine\System\CurrentControlSet\Services\Xxx
حيث Xxx هو اسم الدرايفر .
DriverEntry لابد أن نقوم فيها بإرجاع NTSTATUS تحدد فشل العملية من نجاحها ، في Win32 API كنا دائما نجد ان معظم الدوال تقوم بإرجاع إما true للدلالة على نجاح العملية او false للدلالة على فشلها ، في Kernel-mode لا يتم التعامل بهذا الشكل ولكن عن طريق الـNTSTATUS والتي تسمح لنا بعدد كبير من النواتج لدقة أفضل ، ويمكن تقسيم الـNTSTATUS إلى مجموعتين أساسيتين :
SUCCESS : وتأخذ القيم الموجبة من 0 الى 0x7FFFFFFF .
ERROR : وتأخذ القيم السالبة من 0xC0000000 الى 0xFFFFFFFF .
يمكنك الإطلاع على قيم NTSTATUS في ملف ntstatus.h الموجود بالـDDK .
في DriverEntry إذا قمنا بإرجاع قيمة تدل على ERROR فانه سيتم عمل Unload تلقائيا للدرايفر ، في حالتنا سنقوم بإرجاع قيمة STATUS_SUCCESS لتدل على نجاح العملية .
اذن الكود الخاص بالـDriverEntry سيكون بالشكل التالي :
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPathName
)
{
DbgPrint("Test Driver :: DriverEntry");
DriverObject->DriverUnload = Unload;
return STATUS_SUCCESS;
};في الكود السابق الدالة DbgPrint تقوم بعمل format للنص (اعتبرها كـprintf) ثم إرساله إلى الـDebug output والذي يمكننا مراقبته عن طريق DbgView ، قمنا أيضا بتحديد الدالة Unload ليتم استدعائها أثناء عمل Unloading للدرايفر .
الدالة Unload تأخذ دائما الشكل التالي :
VOID Unload( IN PDRIVER_OBJECT DriverObject );
حيث DriverObject هو مؤشر للدرايفر الذي سيتم عمل Unload له وهو نفسه الذي يتم تمريره في DriverEntry ، اذن دالة Unload الخاصة بنا تكون بالشكل التالي :
VOID
Unload(
IN PDRIVER_OBJECT DriverObject
)
{
DbgPrint("Test Driver :: Unload");
};حسنا هكذا انتهينا من شرح الكود ، نأتي الآن لعملية الترجمة
في حالتي هذه فإن المشروع موجود في المجلد C:\driver ، قم بفتح سطر الاوامر الخاص بالـddk :
Start->Programs->Development Kit->Windows DDK->Build Environments->Windows XP->Windows XP Free Build Environment
انتقل إلى مجلد المشروع ثم أعطي الأمر build ، ستجد في المجلد الناتج ملفات الـobject/sys/pdb .

لتحميل الدرايفر يلزمنا loader ، طبعا في البرامج الحقيقية سنقوم بكتابة الـloader بأنفسنا لكن في حالتنا هذه وللتسهيل يمكن الاستعانة بأي لودر عام مثل الخاص بـOSR (ستجده بالمرفقات) ، قم بتشغيل الـLoader ثم اختر ملف الدرايفر test_driver.sys واترك الباقي كما هو ، هناك خطوتين لتحميل الدرايفر وخطوتين لإنهائه ، قم أولا بتشغيل DbgView كي نلتقط الـDebug Output ثم :
1- Register Service : هذه العملية لا تقوم بتحميل الدرايفر ولكنها تضع المفاتيح اللازمة لبدأ الدرايفر في الرجستري + إعلام الـService Manager به .
2- Start Service : هنا نقوم فعليا بتحميل الدرايفر وبالتالي يتم استدعاء DriverEntry و ظهور رسالة الـDebug Output في DbgView كما اتفق.
3- Stop Service : هنا يتم عمل Unload للدرايفر واستدعاء الدالة Unload وظهور الرسالة الخاصة بها أيضا في DbgView أيضاً.
4- Unregister Service : يتم هنا إزالة القيم الخاصة بالدرايفر من الرجستري والـService Manager .

في الدرس القادم سنتعلم بعض المفاهيم المهمة قبل أن نتمكن من التعمق في الموضوع !
GamingMasteR / AT4RE
Microsoft Developer Network
الملفات المرفقة
-
Kernel_mode_programming__2_Files.rar (344.2K)
عدد مرات التحميل: 294
تم تعديل هذه المشاركة بواسطة GamingMasteR: 15 February 2009 - 07:03 PM


تسجيل الدخول
التسجيل
المساعدة



ارجع للأعلى
متعدد الإقتباسات