تفکرات صفر و یکی

نوشته های من در مورد سیستم عامل، درایور نویسی، مهندسی معکوس، امنیت و هر چیز سطح پایین دیگر

تفکرات صفر و یکی

نوشته های من در مورد سیستم عامل، درایور نویسی، مهندسی معکوس، امنیت و هر چیز سطح پایین دیگر

آخرین نظرات

آموزش درایور نویسی - قسمت اول - سلام دنیا

دوشنبه, ۷ بهمن ۱۳۹۲، ۰۲:۰۷ ق.ظ

از این به بعد بخشی از این وبلاگ به آموزش درایور نویسی در ویندوز اختصاص خواهد یافت. مهمترین دلیل این کارم نبودن منابع فارسی در این زمینه است.  و دلیل دیگر این که خودم دوست دارم بهتر این موضوع را یاد بگیرم. سعی می کنم در ابتدا یکسری آموزش های ساده بگذارم بعد وارد مباحث پیچیده تر بشوم.

خوب بریم سر اصل مطلب ...

مقدمه

مطمعنا اکثر افرادی که کارشون مرتبط به کامپیوتر هست یا با کامپیوتر دائما سرو کله می زنند و چیزهای جدید رو امتحان می کنند حتما یک آشنایی مختصری از درایورها دارند. خصوصا موقع نصب سیستم عامل که در انتها باید درایور دستگاهای مختلف مثل مانیتور،‌ کارت گرافیک،‌ کارت شبکه،‌ پرینتر و غیره را نصب کنید و می دانستیم که این کار برای این است که سیستم عامل دستگاه مذکور را بشناسد. البته درایورها فقط برای این هدف ساخته نمی شوند و کاربردهای مختلفی دارند که به غیر از درایور راه انداز دستگاه های متصل به سیستم هر جا نیاز به تغییر در سطح کرنل سیستم عامل باشد یا درایوری که در برخی نرم افزارها مانند  آنتی ویروس، فایروال،‌ IPS، VPN Server و ... بکار می رود. برای دیدن بهتر کاربردهای درایورها بهتر است نکاهی به پوشه زیر روی سیستم خود بیاندازید.

c:\windows\system32\drivers

در این مسیر می بینید که پر از فایل های با پسوند sys است. روی هر کدام خواستین کلیک راست کنید بعد properties بگیرید سربرگ Version قسمت Description یک توضیح مختصری داده که این درایور برای چیست. من برای اینکه راحتتر این کار انجام داده بشود یک برنامه ساده نوشتم که توضیحات همه درایورها را یکجا نشان بدهد، تصویر زیر بخشی از این توضیحات گرفته شده از آن فایل هاست. لازم به ذکره همه درایورها لزوما در این مسیر قرار نمی گیرند، از این لینک می توانید این ابزار را دانلود کنید.

drv-tut1-sys_description

اینجا کلی درایور است که ماکروسافت برای دستگاه ها و چیپ های مختلف آماده کرده است. چون من این اطلاعات را داخل VM  گرفتم یکسری درایورهای Vmware هم هست. اگر روی سیستمون فایروال یا آنتی ویروس داشته باشین درایور این محصولات نیز داخل این لیست قرار می گیرند. 

پیش نیازهای این آموزش

لیستی از دانشها و ابزارهایی که برای توسعه یک درایور برای این آموزش نیاز دارید.

  • آشنایی خوب با زبان برنامه نویسی C
  • دانلود بسته 7.1 WDK از این آدرس، این نسخه برای سیستم عامل های Windows 7, Windows Vista, Windows XP, Windows Server 2008 R2, Windows Server 2008, and Windows Server 2003. کاربرد دارد این بسته شامل تمام ابزارها، کتابخانه ها و هدرهایی که برای درایور نویسی نیاز دارید را همراه خود دارد. حجم این بسته حدود ۶۰۰-۷۰۰ مگابایت است. اگر برای ویندوز ۸ می خواهید به این صفحه مراجعه کنید. بسته ای که برای ویندوز ۸.۱ است فاقد ابزارهای کامپایل است در نتیجه ابتدا باید Visual Studio نصب کرده باشید بعد WDK 8.1 را نصب کنید. کلا از بعضی کارهای ماکروسافت در عجب می مانم.
  • یک ماشین مجازی برای اجرا کردن و تست درایور نوشته شده (اختیاری بخش نکات مهم را ببینید) می توانید از VirtualBox یا Vmware استفاده کنید. VirtualBox اپس سورس است ولی متاسفانه سایت بر روی ایران بسته است ولی میتوانید از سایت www.filehippo.com ابزار را دانلود کنید. VMware تجاری است که آن هم با کمی جستجو پیدا می شود.
  • ابزار OSRLoader برای لود کردن درایور،‌ از این لینک دانلود کنید
  • ابزار DbgView که از این لینک قابل دریافت است
  • یک ادیتور مانند ++Notepad یا Vim یا هر چیز دیگری که با آن راحت هستین،‌ البته از Visual Studio هم می توانید استفاده کنید اگر بخواهید از محیط Visual Studio کامپایل کنید نیاز است تنظیماتی را انجام دهید. 

نکات مهم

می خواستم نکات مهم رو در انتها بگذارم ولی بهتر دیدم همین اول باشد قبل از اینکه ادامه بدهیم.

دلیل اینکه گفتم از یک ماشین مجازی استفاده کنید این است که کلا درایور به خاطر اینکه در سطح کرنل سیستم عامل اجرا می شود  دارای بالاترین حق دسترسی نیز است و در این سطح می توان هر کاری کرد (استفاده از این قبیل دسترسی ها برای یک فرد بداندیش می تواند منجر به تولید انواع بدافزارهای مختلف با اهداف پلید شود چشمک)،‌ در واقع کوچکترین خظایی منجر به بهم ریختن روند کار سیستم عامل و به نمایش در آمدن صفحه آبی مرگ (Blue Screen of Death) که کم بیش همه باهاش روبرو شدن و آشنا هستن می شود. در نتیجه برای جلوگیری از این اتفاق و صدمه ندیدن سیستم عاملی که خود شما داخلش هستین و توسعه راحت تر و تست درایور باید یک ماشین مجازی داشته باشید خصوصا که بعد برای دیباگ درایور از ماشین مجازی استفاده میکنیم. دلیل دیگر استفاده از ماشین مجازی این است که شما بعد نیاز دارید درایور خود را با سیستم عامل های مختلف سازگار کنید. در نتیجه باید سیستم عامل های مختلف یکجا کنار دستتان باشد.

شروع کد نویسی

خوب میرسیم به کد نویسی،‌ عادت و رسمی است که اکثر برنامه نویس ها در شروع یادگیری یک زبان یا تکنولوژی جدید ابتدا اقدام به نوشتن برنامه "سلام دنیا" میکنند. ما هم از این رسم تبعیت میکنیم. یک برنامه ساده که شما را با مفاهیم اولیه درایور نویسی آشنا سازد. حالا برای شروع این فایل ها را بسازید همه را در یک پوشه مثلا به نام hello قرار دهید.

فایل makefile با این محتویات. توجعه کنید که این فایل نباید هیچ پسوندی داشته باشد. بعضی ادیتورها اگر فایل بدون پسوند وارد شود پسوند .txt به آن اضافه میکنند.  ویندوز به طور پیشفرض پسوندها را نشان نمی دهد ولی تنظیماتی دارد برای نمایششون. در نتیجه بعد از ایجاد فایل حتما باید پسوند را از فایل حذف کنید.

!INCLUDE $(NTMAKEENV)\makefile.def

فایل بعدی sources است که باز این فایل هم مانند فایل قبلی پسوند ندارد. این فایل تمام اطلاعات لازم برای ایجاد درایور شما توسط ابزار build را دربر می گیرد.

TARGETNAME = hello
TARGETPATH = obj
TARGETTYPE = DRIVER

INCLUDES   = %BUILD%\inc
LIBS       = %BUILD%\lib

SOURCES    = hello.c

خوب از اطلاعات بالا

TARGETNAME نام درایور شما موقع ایجاد توسط build است

TARGETPATH پوشه ای که درایور داخلش قرار می گیرد.

TARGETTYPE نوع فایلی که قرار است ایجاد شود که خوب از آنجایی که ما درایور می سازیم واضح است که نوع هم DRIVER است

INCLUDES و LIBS به ابزار build می گوید کجا دنبال هدرها و کتابخانه های مورد نیاز بگردد که خیلی مهم نیست برامون

SOURCES: نام فایل سورس ما اینجا قرار می گیرد اگر بیشتر از یک فایل باشد باید نامها با فاصله از هم جدا شوند.

فایلی به نام hello.c هم میسازیم با این محتویات

#include <ntddk.h>

VOID Unload(IN PDRIVER_OBJECT pDriverObject)
{
DbgPrint("Driver Unloaded.\n");
return;
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING regPath)
{
DbgPrint("Hello World!.\n");
pDriverObject->DriverUnload = Unload;

return STATUS_SUCCESS;
}

build کردن کد

قبل اینکه توضیحی در مورد کد بدهم نحوه biuild کردنش را می گویم. برای build کردن باید از منوی start و پوشه ای که مربوط به WDK است x86 Free Build را انتخاب کنید.یک صفحه cmd برایتان باز می شود به مسیر پروژه ای که ایجاد کرده اید بروید

C:\WinDDK\7600.16385.1>cd C:\Users\[your_user]\src\Drivers\hello

بعد دستور build را وارد کنید

C:\Users\[your_user]\src\Drivers\hello>build

این دستور کمی زمان می برد بعد اگر مشکلی نباشد درایور شما در پوشه ای به نام objfre_wxp_x86\i386  یا اگر نام پوشه را تغییر داده اید در پوشه مربوطه قرار می گیرد

می توان کمی روند build رو با وارد کردن پارامتر Z تصریح کرد، من پارامتر g و c را هم برای رنگی کردن پیامها و پاک کردن آبجکت قبلی وارد میکنم. که به این صورت می شود

C:\Users\[your_user]\src\Drivers\hello>build /gcZ

توضیح در مورد کد

اگر فردی باشید که با C بیشتر برنامه های کنسولی نوشته باشید، چه برای دل خودتون یا پروژه های دانشگاهی یا پروژه های کاری و با اصطلاحاتی مانند Win32 API, DLL, MFC و غیره سر و کار نداشته اید. احتمالا این کد برایتان کمی عجیب خواهد بود. کدی که نه تابع main دارد و نه از انواع داده های استاندار C خبری است. کلا در دنیای ویندوز این روال همیشه اینگونه بوده و ماکروسافت علاقه زیادی دارد که همه چی را با عناوین دیگری تعریف کند. در واقع برای تمام انواع داده ها عناوین دیگری تعریف کرده است که به مرور با آنها آشنا خواهید شد برای نمونه در کد بالا برای void نوع جدید VOID با حروف بزرگ تعریف شده است. لازم به ذکر است در کنار این داده های جدید شما همچنان می توانید از تمام امکانات زبانی زبان C استفاده کنید. 

درایور هم مانند برنامه های کنسولی که نوشته اید دارای تابعی مانند main است که بعد از لود شدن درایور صدا زده می شود. این تابع DriverEntry نام دارد. که به صورت زیر تعریف شده

NTSTATUS DriverEntry(
_In_ struct _DRIVER_OBJECT *DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{ ... }

این تابع دو پارامتر ورودی می گیرد، توضیحات بیشتر این تابع را در سایت ماکروسافت ببینید

DriverObject: ساختاری است که مقادیر متعددی دارد که در آموزش های بعدی در موردشان بیشتر توضیح خواهم دارد. در اینجا ما فقط از مقدار DriverUnload در این ساختار استفاده کرده ایم. این مقدار به عنوان ورودی نام تابعی را می گیرد که در هنگام Unload شدن درایور صدا زده می شود. نکته ای که باید به آن توجه کرد این است که اگر تابعی برای Unload تعیین نکنیم درایور ما بعد از لود شدن دیگر Unload نمی شود.

RegistryPathاین پارامتر مسیر درایور ما در رجیستری بعد ثبت شدن را در خود دارد که برای ما کاربردی ندارد و فعلا از آن صرف نظر میکنم،  خوب است خودتان در مورد نوع PUNICODE_STRING کمی تحقیق کنید ببینید به چه صورت تعریف شده است.

مقدار برگشتی: مقداری که تابع DriverEntry بر می گرداند باید از نوع NTSTATUS باشد. این نوع داخل فایل ntstatus.h تعریف شده و مقادیر بسیار زیادی دارد. ما در این مثال مقدار STATUS_SUCCESS برگردانده ایم. به این معنی که درایور بدون مشکلی لود شده است. برای دیدن تمام مقادیر این صفحه را ببینید

حالا میرسیم به چاپ یک متن مانند "سلام دنیا"، خوب از آنجایی که دنیای کرنل زمین تا آسمان با برنامه هایی که در سطحی که به آن  User-Mode یا سطح کاربر گفته می شود فرق دارد. چاپ کردن یک پیام روی صفحه مانند سطح کاربر که یک متن با printf روی کنسول بفرستیم نیست یعنی اصلا همچین چیزی نداریممهر شده، پس حتما می گویید ما را سره کار گذاشته ای با این آموزشت (شایدزبان درازی). در واقع قابلیتی وجود دارد که با استفاده از توابع DbgPrint یا KdPrint و امثالهم می توان پیامی به بافری فرستاد که بعد یکسری ابزار هستن که می تواند این بافر را بخواند. این ابزار برای مثال می تواند یک دیباگر یا ابزاری مثل DbgView باشد. همانطوری هم که در کد می بینید ما در دو قسمت از DbgPrint استفاده کرده ایم یکی در تابع DriverEntry برای چاپ "Hello World!\n" و دیگری در تابع Unload . متنی که قرار می دهید می تواند فرمتی مشابه printf در C داشته باشد،‌ در ضمن می توانید همراه متن کاراکترهای کنترلی مثل "n\" هم بفرستید تا پیام ها در هنگام نمایش در خطوط مجزا قرار گیرند برای اطلاعات بیشتر هم می توانید  صفحه مربوط به تابع در سایت ماکروسافت را ببینید.

نکته: در ویندوزهای بعد از ویستا تابع DbgPrint چیزی را به بافر دیباگ نمی فرستد و باید از تابعی جایگزین که DbgPrintEx است استفاده کنید. به این صورت

DbgPrintEx(DPFLTR_DEFAULT_ID, 
DPFLTR_ERROR_LEVEL,
"YOUR MESSAGE");

ثبت و لود درایور

بعد از اینکه درایور ایجاد شد باید لودش کنیم. برای لود کردن درایور راه های مختلفی وجود دارد،‌ یک راه استفاده از ابزارهای آماده برای این منظور است که من یک نمونه آن را در این آموزش آورده ام به نام OSRLoader و راه دیگر استفاده از API هایی است که ماکروسافت در ویندوز قرار داده تا با آنها بتوان یک درایور را لود کرد (البته راه هایی دیگر و غیر مستند دیگری وجود دارد که فعلا به آن اشاره نمیکنم). مسلما اگر پروژه ای داشته باشید و بخواهید با برنامه نویسی درایوری لود کنید باید از API های ویندوز استفاده کنید. داخل نمونه سورس هایی که همراه WDK است نمونه کد برای این منظور زیاد است. من اینجا نحوه استفاده از OSRLoader را میگویم. در زیر تصویری از محیط این ابزار را می توانید مشاهده کنید

OSR Loader

در این محیط ما تنظیمات خاصی نمی خواهیم انجام بدهیم فقط درایور خود را از طریق دکمه Browse انتخاب کنید. در آموزش های بعدی بیشتر در مورد این تنظیمات صحبت میکنم.

به طور کلی دو مرحله است که باید طی کنیم تا یک درایور لود شود،‌ اول درایور باید در سیستم ثبت شود. برای این کار بعد انتخاب درایور روی دکمه Register Service کلیک کنید. این کار باعث می شود  کلیدی در رجیستری سیستمتون در مسیر زیر همنام با اسم درایور ایجاد شود. که در مثال ما نام کلید hello می باشد.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services

حالا که درایور ثبت شده می توان درایور را اجرا کرد. ولی قبل از اجرا کردن ابزار DbgView که بالا لینک دانلودش را قرار داده ام بگیرین و اجرا کنید. و بعد از باز کردن ابزار از منوی Capture گزینه Capture Kernel را انتخاب کنید که یک تیک کنارش بخورد. با این کار این ابزار پیامهایی که در سطح کرنل با استفاده از تاابع DbgPrint فرستاده می شود را دریافت میکند. حالا با کلیک بر دکمه Start Service درایور اجرا می شود و شما باید پیامهایی که در درایورتون با DbgPrint گداشته اید ببینید.

بعد برای اتمام کار درایور و متوقف کردن آن به ترتیب اول دکمه Stop Service و بعد Unregister Service را کلیک کنید. در این مرحله نیز پیامی که در تابع Unload قرار داده اید باید نمایش داده شود.

خوب رسیدیم به پایان این آموزش، امیدوارم براتون مفید واقع شده باشد. اگر ایراد،‌ ابهام یا هر سوالی دارید حتما بپرسید این کار را هم از طریق کامنت یا از طریق قسمت "تماس با من" می توانید مطرح کنید

  • موافقین ۵ مخالفین ۰
  • ۹۲/۱۱/۰۷
  • ۵۸۸۲ نمایش
  • آرش

آموزش درایور نویسی

ویندوز

کرنل

نظرات (۱۴)

  • غلامعلی حسینی بهجانی
  • سلام.
    شما با افتخار لینک شدید.
    به وبلاگ من (اثر پروانه ای - برنامه نویسی حرفه ای سی شارپ) تشریف بیارید و اگه مایل بودید لینک کنید.
    متشکرم.
    سلام 

    از مطلب خوبی که گذاشتید متشکریم

    یا علی
    عالی بود خیلی شیوا و کامل توضحی دادید ! امیدوارم به کارتون ادامه بدید تا ما هم استفاده کنیم !
    پاسخ:
    ممنون، امیدوارم بتونم ادامه بدم :)
    سلام می تونید کتابهای فارسی و نمونه برنامه های کاربردی  را معرفی کنید.ممنون
    پاسخ:
    سلام
    در مورد کتاب فارسی من کتابی نمی شناسم و فکر هم نمیکنم کتابی وجود داشته باشه حداقل با جستجویی که من در چند سایت کتاب و گوگل انجام دادم چیزی پیدا نکردم
    در مورد نمونه برنامه بسته WDK کلی مثال همراهش است که می تونید ازشون استفاده کنید.
    عالی بود
    نیازی به نوشتن ابزار نبود!
    با دستور driverquery  در خط فرمان می شه اطلاعات کل درایور ها رو بدست آورد
    فکر کنم منبع شما برای این آموزش کتاب Rootkits  Subverting the Windows Kernel باشه!

    پاسخ:
    این ابزار رو نمی شناختم، جالب :)، ممنون بابت معرفی

    نمی دونم چطور صاف گفتین این کتاب و کتاب دیگه یا سایت دیگه نه :) !!
    با اینکه این کتاب رو خیلی وقته پیش خودندم،  ولی نه این کتاب منبعم نبوده. این مثال رو تو خیلی سایت ها و کتابها می شه پیدا کرد. برای مطالب اینجا من از منابع مختلفی استفاده می کنم محض اطلاع اینا مهماشن

    • وبسایت ماکروسافت
    • Programming the Microsoft Windows Driver Model 
    • Windows Internals
    • ....
    سلام دوست عزیز ممنون از پست خوبتون بنده یه تازه وارد هستم و میخوام وارد حوضه Kernelmod بشم باید از چی شروع کنم ممنون میشم کمکم کنید :)
    پاسخ:
    سلام

    به نظرم آموزش های اینجا برای شروع بد نیستن و برای ادامه می توانید پستی که جدید در این رابطه قرار داده ام یک نگاهی بیاندازید
    باز سوالی بود بپرسیددر خدمتم. :)
    اونایی که با زبان سی آشنایی ندارن چی؟
    پاسخ:
    اون عده اگر بخوان می تونن با سی آشنا بشن 
    اگرم نخوان که همون زندگی که در پیش گرفتن رو ادامه میدن :)
    بسیار کمک کرد. ممنون
    با سلام
    داداش این کارها رو در چه ویندوزی (ایکس پی با 7 یا ...؟)
    انجام بدیم؟
    اگه  بیتی باشه از  64x  برای  build  باید استفاده کنیم؟

    پاسخ:
    سلام
    ۱- کدها روی ایکس پی تست شده،‌برای اینکه روی 7 کار کنه باید DbgPrint رو به DbgPrintEx تغییر داد تو همون آموزش اول گفتم چطوریه. روی بقیه ورژن ها من تست نکردم ولی احتمال میدم اونی که روی 7 کار کنه روی ویندوز های جدیدتر هم بدون مشکل اجرا بشه
    ۲- بله برای 64 باید build 64 رو انتخاب کنید. و این نکته رو هم در نظر داشته باشید که یک محدودیت های وجود داره یکی اینکه از ویندوز ۷ به بعد درایور باید اصطلاحا sign شده باشه. 
    سلام
    عالی بود
    اگه میشه چند تا منبع برای آموزش برنامه نویسی درایور ویندوز معرفی کنید (از مبتدی ترین سطح ممکن تا حرفه ای) لطفا
    بهشون نیاز دارم
    خییییلی ممنون
     لودش با موفقیت انجام شد ولی توی debug view چیزی نشون نمیده بهم. ویندوز 7 سی دوبیت نصبه. به ماشین مجازی ه ربط نداره؟؟ چون نصب نکزدم :)

    پاسخ:
    در ویندوز ۷ به بعد این تابع باید با تابع DbgPrintEx جایگزین بشه که داخل همین آموزش توضیح داده ام چه پارامترهایی م  خواهد

    این لینک توضیخ بیشتری داده اگر خواستین
     
    http://www.osronline.com/article.cfm?article=295
  • میلاد علیدوست
  • اقا تشکر فکر میکنم دیگه سایت نبود نگشته باشم....دستتون در نکنه
    سلاممممممممممممممممممم ترو خدا ادامه بده کارت رو
    واقعا دستت درد نکنه
    ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
    شما میتوانید از این تگهای html استفاده کنید:
    <b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
    تجدید کد امنیتی