# برنامه نویسی سطح پایین > توسعه‌ی هسته‌ی سیستم عامل >  آموزش درایور نویسی ویندوز

## kernel.programmer

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


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



*مقدمه*

مطمعنا اکثر افرادی که کارشون مرتبط به کامپیوتر هست یا با کامپیوتر دائما سرو کله می زنند و چیزهای جدید رو امتحان می کنند حتما یک آشنایی مختصری از درایورها دارند. خصوصا موقع نصب سیستم عامل که در انتها باید درایور دستگاهای مختلف مثل مانیتور،‌ کارت گرافیک،‌ کارت شبکه،‌ پرینتر و غیره را نصب کنید و می دانستیم که این کار برای این است که سیستم عامل دستگاه مذکور را بشناسد. البته درایورها فقط برای این هدف ساخته نمی شوند و کاربردهای مختلفی دارند که به غیر از درایور راه انداز دستگاه های متصل به سیستم هر جا نیاز به تغییر در سطح کرنل سیستم عامل باشد یا درایوری که در برخی نرم افزارها مانند  آنتی ویروس، فایروال،‌ IPS، *** Server و ... بکار می رود. برای دیدن بهتر کاربردهای درایورها بهتر است نکاهی به پوشه زیر روی سیستم خود بیاندازید.
c:\windows\system32\driversدر این مسیر می بینید که پر از فایل های با پسوند sys است. روی هر کدام خواستین کلیک راست کنید بعد properties بگیرید سربرگ Version قسمت Description یک توضیح مختصری داده که این درایور برای چیست. من برای اینکه راحتتر این کار انجام داده بشود یک برنامه ساده نوشتم که توضیحات همه درایورها را یکجا نشان بدهد، تصویر زیر بخشی از این توضیحات گرفته شده از آن فایل هاست. لازم به ذکره همه درایورها لزوما در این مسیر قرار نمی گیرندد، از این لینک می توانید این ابزار را دانلود کنید.

drv-tut1-sys_description.jpg


اینجا کلی درایور است که ماکروسافت برای دستگاه ها و چیپ های مختلف آماده کرده است. چون من این اطلاعات را داخل 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 را میگویم. در زیر تصویری از محیط این ابزار را می توانید مشاهده کنید
drv-tut1-osrloader.JPG
در این محیط ما تنظیمات خاصی نمی خواهیم انجام بدهیم فقط درایور خود را از طریق دکمه Browse انتخاب کنید. در آموزش های بعدی بیشتر در مورد این تنظیمات صحبت میکنم.
به طور کلی دو مرحله است که باید طی کنیم تا یک درایور لود شود،‌ اول درایور باید در سیستم ثبت شود. برای این کار بعد انتخاب درایور روی دکمه Register Service کلیک کنید. این کار باعث می شود  کلیدی در رجیستری سیستمتون در مسیر زیر همنام با اسم درایور ایجاد شود. که در مثال ما نام کلید hello می باشد.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Servic  es

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


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


خوب رسیدیم به پایان این آموزش، امیدوارم براتون مفید واقع شده باشد.

----------


## kernel.programmer

*آموزش درایور نویسی - قسمت دوم - ارتباط با سطح کاربر
*

در قسمت قبل طریقه ساخت یک درایور و لود کردن آن را یاد گرفتیم، و همچنین با ابزارهای ماند OSRLoader و DbgView آشنا شدیم. در این آموزش هم قصد داریم ببینیم چگونه میتوان درایوری نوشت که بتواند با یک برنامه سطح کاربر ارتباط برقرار کند در واقع اطلاعاتی به درایور بفرسیم و دریافت کنیم.

*مقدمه*

در ابتدا باید بگویم مثالی که برای این آموزش انتخاب کرده ام یکی از مثالهای بسته WDK به نام ioctl است که در مسیر زیر قرار گرفته.
[WDK path]\7600.16385.1\src\general\ioctl\اینجا دو پوشه به نامهای wdm و kmdf وجود دارد. این دو  در واقع معماری های متفاوت درایور نویسی در ویندوز است. معماری دیگری هم قبلا وجود داشت به نام VxD که بر می گردن به زمانی که ویندوزهای نسخه 95-98 وجود داشتند ولی الان دیگه استفاده نمی شود. آموزش هایی که من فعلا قرار می دهم بر پایه wdm است و فعلا با kmdf کاری نداریم. در این نمونه سورس روش های مختلفی که می توان میان سطح کاربر و سطح کرنل ارتباط برقرار کرد را نشان داده است. من قسمت هایی از این سورس را حذف کرده ام نسخه تغییر یافته را از این لینک می توانید بگیرید و ما در ادامه در مورد مفاهیمی مانند IOCTL, IRP, Driver Object, Device Name, Symbolic Link آشنا می شویم.


*Driver Object چیست؟*

هر درایوری که در سیستم لود شده باشد از دید سیستم عامل یک Driver Object  دیده می شود در واقع در سیستم عامل ویندوز خیلی چیز ها مثل فایل (File)، پوشه (Directory)، فرایند (Process)،‌ نخ (Thread) و ... را به صورت Object می بیند. هر کدام از این آبجکتها برای خود ساختار مشخصی دارند. برای آبجکت درایور در ویندوز ما ساختاری به نام _DRIVER_OBJECT داریم که در پایین محتویات آن را می بینید. من این محتویات را از فایل wdm.h داخل بسته WDK برداشته ام
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;شما در آموزش اول با DriverUnload اشنا شدید که تابعی تعریف کردیم که در هنگام Unload شدن درایور توسط سیستم عامل صدا زده می شود.  از این مقادیر یکسری از اسمشان تا حدودی مشخص است که چه هستند. باقی هم فعلا چون به کار ما نمیاید کاری با آنها  نداریم. چیزی که بیشتر مورد توجه ما است MajorFunction است.


*MajorFunction چیست؟*

اول با استفاده از مفاهیمی که از زبان C می دانیم قسمتی که مربوط به MajorFunciton است را شرح می دهیم. اینجا ما یک آرایه از اشارگرهایی که از نوع DRIVER_DISPATCH هستند داریم و طول آرایه ما IRP_MJ_MAXIMUM_FUNCTION + 1 می باشد.  مقادیر DRIVER_DISPATCH و IRP_MJ_MAXIMUM_FUNCTION  را هم از فایل wdm.h بدست می آوریم


 DRIVER_DISPATCH در واقع تعریف یک تابع است به این صورت
DRIVER_DISPATCH (
 _In_ struct _DEVICE_OBJECT *DeviceObject,
 _Inout_ struct _IRP *Irp
 );

typedef DRIVER_DISPATCH *PDRIVER_DISPATCH;

و IRP_MJ_MAXIMUM_FUNCTION که مقداری برابر 0x1b دارد که به صورت هگزادسیمال آمده که در واقع در مبنای دسیمال عدد ۲۷ می شود
#define IRP_MJ_MAXIMUM_FUNCTION 0x1bاین به معنی است که این آرایه می تواند ۲۸ (۲۷+۱) تابع از نوع  DRIVER_DISPATCH را در خود داشته باشد.
DriverObject->MajorFunction[0] = Function0
DriverObject->MajorFunction[1] = Function1
DriverObject->MajorFunction[2] = Function2
DriverObject->MajorFunction[3] = Function3
...
DriverObject->MajorFunction[27] = Function27از آنجایی که این توابع برای هر ایندکس از این آرایه قرار است کار مشخصی انجام دهد ایندکس های ۰-۲۷ خود به صورت یک نام قابل فهم تعریف شده اند.
#define IRP_MJ_CREATE 0x00
#define IRP_MJ_CREATE_NAMED_PIPE 0x01
#define IRP_MJ_CLOSE 0x02
#define IRP_MJ_READ 0x03
#define IRP_MJ_WRITE 0x04
...
...
#define IRP_MJ_PNP 0x1b

در نتیجه اگر ما بخواهیم از این تعاریف استفاده کنیم به این شکل استفاده میکنیم
DriverObject->MajorFunction[IRP_MJ_CREATE] = Function0
DriverObject->MajorFunction[IRP_MJ_CREATE_NAMED_PIPE] = Function1
DriverObject->MajorFunction[IRP_MJ_CLOSE] = Function2
DriverObject->MajorFunction[IRP_MJ_READ] = Function3
...
DriverObject->MajorFunction[IRP_MJ_PNP] = Function27

هر درایور برای اینکه ارتباط با دنیای بیرون داشته باشد که این ارتباط می تواند از طرف درایورهای دیگر یا از طرف برنامه های سطح کاربر باش. ملزم است آرایه MajorFunciton را مقدار دهی کند. بسته به نوع و کارکرد درایور شما مقداردهی این آرایه متفاوت خواهد بود. راهنمایی که همراه WDK نصب می شود برای هر نوع درایور مشخص کرده که مقدار دهی چه Major Funciton هایی واجب و کدام اختیاری است. 


اگر دقت کنید همه این تعاریف با عبارت IRP شروع می شوند. IRP مختصر شده I/O Request Packet است. در مورد IRP  در آموزش های بعدی توضیح میدهم وفتی به Filter Driver برسیم. ولی در همین حد بگویم که تمام درخواست هایی که به درایور میرسد به بصورت یک ساختار IRP در می آید.


برنامه های سطح کاربر با فراخوانی یکسری API ها می توانند درخواست خود را به درایور بفرستند. و تابع مربوط به درخواست در درایور اجرا شود  در پایین لیست API و در مقابل در خواستی که تولید می شود آورده ام. 
CreateFile ==> IRP_MJ_CREATE
ReadFile ==> IPR_MJ_READ
WriteFile ==> IRP_MJ_WRITE
CloseFile ==> IRP_MJ_CLOSE
DeviceIoControl ==> IRP_MJ_DEVICE_CONTROLدر استفاده از CreateFile برای اینکه با درایورمان مرتبط شود از نامی مشخص استفاده می کنیم در مورد این نام بعد در همین آموزش توضیح داده ام. باقی توابع از هندلی که از CreateFile به دست آمده استفاده میکنند. حالا در ادامه نمونه سورسی که آماده کرده ام بررسی میکنیم تا بیشتر با این موضوع آشنا بشویم.
توضیح در مورد این توابع و پارامترهای آنها را به عهده خودتان می گذارم با اینکه به جز DeviceIoControl که در ادامه در مورد آن توضیح می دهم باقی از نامشان می شود حدس زد کارشان چیست ولی اگر برایتان نامفهوم است  در موردشان جستجو کنید.
*بررسی Major Function در سورس کد sioctl.c*

نمونه سورسی که ما استفاده کرده ایم بخشی که Major Funciton ها را مقدار میدهد به این صورت است.
DriverObject->MajorFunction[IRP_MJ_CREATE] = SioctlCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SioctlCreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SioctlDeviceControl;در این درایور سه درخواست کنترل می شود که دو تا از این درخواست ها توسط یک تابع بررسی می شود. قبلا گفتم که یکسری درخواست ها هستند که کنترل برخی درخواست ها برایشان اجباری است. در واقع وقتی سیستم عامل درخواستی که مربوط به درایور ما می شود. برای ما میفرستد ( یا در واقع Major Function مربوط به آن درخواست را صدا می زند) انتظار دارد. به این درخواست پاسخی از طرف درایور ما به سیستم عامل  داده شود.


تابع SioctlCreateClose وظیفه کنترل درخواست های IRP_MH_CREATE و IRP_MJ_CLOSE  را دارد. کد مربوط به این تابع را در زیر می بینید. اگر دقت کنید تابع از نوع DRIVER_DISPATCH است که بالاتر دیدیم.
NTSTATUS
SioctlCreateClose(
 PDEVICE_OBJECT DeviceObject,
 PIRP Irp
 )
{
 UNREFERENCED_PARAMETER(DeviceObject);

 PAGED_CODE();

 Irp->IoStatus.Status = STATUS_SUCCESS;
 Irp->IoStatus.Information = 0;

 IoCompleteRequest( Irp, IO_NO_INCREMENT );

 return STATUS_SUCCESS;
}

اگر به کد دقت کنید می بینید تقریبا کاری انجام نمی دهد. در واقع به خاطر اجباری بودن پاسخ به این درخواست مجبوریم به این صورت عمل کنیم. به صورت کلی این کد به سیستم عامل می گوید که 


اولا:درخواست مربوطه توسط درایور بررسی شد و خطایی رخ نداد، با این کد
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;دوما: در این کد می گوییم این درخواست در این مرحله به اتمام رسیده و به سیستم عامل نیز با این تابع اعلام میکنیم. شاید الان این کد زیاد معنی نداشته باشد. ولی به هر حال این کار را باید در مورد درخواست هایی که کاری با آنها نداریم ولی باید پاسخی در قبال دریافت آنها به سیستم عامل بفرسیم انجام دهیم. من در آموزش دیگری که در مورد IRP است این مباحث را توضیح می دهم.
IoCompleteRequest( Irp, IO_NO_INCREMENT );حالا میرسیم به تابع SioctlDeviceControl که می شود گفت مهمترین قسمت از کد این درایور است. در این تابع درخواستی که از سمت کاربر می آید بررسی می شود و متناسب با در خواست کاری را انجام می دهد. بخشی که تصمیم می گیرد بر اساس دستور سطح کاربر چکار باید کند به این صورت تعریف شده است.
switch ( irpSp->Parameters.DeviceIoControl.IoControlCode )
{
    case IOCTL_SIOCTL_METHOD_BUFFERED:
    // کد مربوط به این دستور
    break;

    case IOCTL_SIOCTL_METHOD_NEITHER:
    // کد مربوط به این دستور
    break;

    case IOCTL_SIOCTL_METHOD_IN_DIRECT:
    // کد مربوط به این دستور
    break;

    case IOCTL_SIOCTL_METHOD_OUT_DIRECT:
    // کد مربوط به این دستور
    break;

    default:
 }مقدارهایی که برای هر دستور case مشخص شده فرمان هایی است که از طرف کاربر می آید این نام گذاری را توسط خودمان انجام می شود قابل تغییر هستند. به جای هر کدام از مقادیر IOCTL_SIOCTL_METHOD_BUFFERED یا IOCTL_SIOCTL_METHOD_NEITHER و ... هر نامی را می توان تعریف کزد.


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

از سه طریق می توانیم به بافر حاوی داده ارسالی دسترسی داشده باشیم این سه روش به قرار زیر است

*روش Buffered I/O* : در این روش سیستم عامل "حافظه سیستمی" به اندازه حافظه ی داده ای که می خواهید به درایور بفرستید ایجاد میکند و داده ما را در آن قرار می دهد. و بالعکس در موقع دریافت داده سیستم عامل داده را از "حافظه سیستمی" به حافظه کاربر انتقال می دهد*روش Direct I/O* : در این روش سیستم عامل آدرس سطح کاربر را قفل می کند و بعد از این آدرس حافظه اصطلاحا یک MDL (Memory Descritor List) می سازد و آن را به درایور می فرستد.*روش نه Buffered I/O و نه Direct I/O* : در این حالت آدرس مجازی که ما در سطح کاربر به داده خود اختصاص داده ایم را عینا در سطح کرنل دریافت می کنیم
این نمونه کد بخاطر اینکه همه حالت ها را پشتیبانی می کرد به صورت مشخص تعریف نکرده از کدام روش برای دسترسی به حافظه داده استفاده کند. ولی اگر بخواهیم مشخصا تعیین کنیم از کدام روش قرار است استفاده کنیم باید این مقدار را تغییر دهید
 pDeviceObject->Flags*بررسی بخش دسترسی به حافظه داده از کد sioctl.c مربوط به درایور*

برای دسترسی به حافظه داده ما سه کار باید انجام دهیم
*اول)* اندازه حافظه ورودی یا خروجی را می توانیم به صورت زیر بدست آوریم. از این لینک اطلاعات بیشتر در مورد ساختار  IO_STACK_LOCATION را دریافت کنید.
irpSp = IoGetCurrentIrpStackLocation( Irp );
inBufLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
outBufLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;*

دوم)* گرفتن حافظه داده مان که به این صورت بدست می آید. از این لینک اطلاعات بیشتر در مورد ساختار IRP
inBuf = Irp->AssociatedIrp.SystemBuffer;
outBuf = Irp->AssociatedIrp.SystemBuffer;*

سوم)* گرفتن کد IOCTL که از سمت برنامه کاربر رسیده و در دستور switch-case استفاده شده
irpSp->Parameters.DeviceIoControl.IoControlCode*NT Device Name و DOS Device Name*

تا الان در مورد اینکه چگونه با تعریف Major Funciton ها درخواست هایی که به درایور ما می آیند را کنترل کنیم و همچنین مختصر توضیحی هم در رابطه با روش های موجود برای دسترسی به حافظه داده ی درخواستیمان داده ایم. حالا مساله ی دیگری که باید با آن آشنا باشید Device Name است. در ویندوز برای دسترسی به یکسری چیزها یا دستگاه ها از API های مربوط به فایل استفاده می شود مثل Files, Hard Disk, Serial Port, Parallel Port, ... و اگر قبلا با C/C++‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎ و API های ویندوز کد سیستمی نوشته باشید احتمالا می دانید که با تابعی مثل CreateFile اول یک هندل از دستگاه مربوطه می گرفیتم بعد مثلا با توابعی مثل ReadFile یا WriteFile داده می خواندیم و می نوشتیم در هنگام هندل گرفتن با تابع CreateFile هم از نام هایی مانند \\.\PhysicalDrive0, \\.\PhysicalDrive1, \\.\COM1, ... استفاده می کردیم. حالا حتما می گویید این دستگاه ها چکار به درایور دارد. خوب در واقع کسی(برنامه سطح کربر و درایور های سطح کرنل دیگر) دسترسی مستقیم به درایور شما ندارند و برای اینکه دیگران بتوانند به شما درخواستی بفرستند اصطلاحا باید یک Device Name بسازید این Device Name خود دو نوع است NT Device Name که این نام فقط قابل دسترسی برای دیگر درایورها و کدهای سطح کرنل است و برنامه های سطح کربر دسترسی به این نام ندارن.د این نام ها با عبارت \Device\ شروع می شوند. دیگری DOS Device Name است که این نام امکان دسترسی برنامه های سطح کاربر را مهیا می کند که این نامها هم با عبارت \DosDevices\ شروع می شوند. کاری که شما باید انجام بدهید این است که اول NT Device Name را با تابع IoCreateDevice بسازید بعد باید یک رشته تعریف کنید که حاوی نام DOS شما می شود و در نهایت این دو با هم توسط تابعی به نام IoCreateSymbolicLink متصل می شوند. این کار حتما باید انجام شود یعنی باید نام NT شما ایجاد شده باشد و چون برنامه سطح کاربر دسترسی به این نام ندارد نیاز است نامی تعریف کنیم که در سطح کاربر قابل دسترسی باشد.این کدی است که این نام ها را ایجاد میکند. نام های Symbolic Link با عبارت \\.\ شروع می شوند که مثال هایی برای این نوع بالاتر زده ام 


#define NT_DEVICE_NAME L"\\Device\\SIOCTL"
#define DOS_DEVICE_NAME L"\\DosDevices\\IoctlTest"

...
...

RtlInitUnicodeString( &ntUnicodeString, NT_DEVICE_NAME );

ntStatus = IoCreateDevice(
     DriverObject,
     0, 
     &ntUnicodeString, 
     FILE_DEVICE_UNKNOWN, 
     FILE_DEVICE_SECURE_OPEN, 
     FALSE, 
     &deviceObject ); 

...
...

RtlInitUnicodeString( &ntWin32NameString, DOS_DEVICE_NAME );

ntStatus = IoCreateSymbolicLink(
     &ntWin32NameString, &ntUnicodeString );اکثر توابع کرنلی از رشته های نوع UNICODE_STRING استفاده میکنند. این رشته ساختاری است با تعریف زیر. در نتیجه رشته خود را با استفاده از تابع RtlInitUnicodeString تبدیل به UNICODE_STRING می کنیم.
typedef struct _LSA_UNICODE_STRING {
 USHORT Length;
 USHORT MaximumLength;
 PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;در مورد پارامترهای تابع IoCreateDevice
۱) *DriverObject*: این مقدار را از پارامتری که در DriverEntry است می توانید بگیرید
۲) *DeviceExtensionSize*: یک مقدار عددی است که به اندازه آن برایتان فضایی تخصیصی داده می شود و از این فضا می شود به عنوان نگهداری وضعیت های داخلی درایور استفاده کرد. ما چون نیاز نداریم صفر قرار می دهیم
۳) *DeviceName*: همان نام NT که بالاتر به آن اشاره کردیم که باید از نوع UNICODE_STRING باشد
۴) *DeviceType*: بسته به کاری که می خواهیم انجام دهیم می توانیم از نوع های تعریف شده برای device خود استفاده کنیم. این مقادیر را در این لینک می توانید ببینید. چون device ما با هیچ نوع خاصی از سخت افزار ارتباطی ندارد مقدار FILE_DEVICE_UNKNOWN را قرار می دهیم
۵) *DeviceCharacteristics*: یکسری خصوصیات دیگری که درایور ما می تواند داشته باشد
۶) *Exclusive*: این پارامتر را FALSE قرار می دهیم
۷) *DeviceObject*: اینجا device ایجاد شده بر می گردد


توضیحات بیشتر تابع IoCreateDevice را در این لینک ببینید. 


ابزاری به نام WinObj وجود دارد که Device Name و Symbolic Name های تعریف شده در سیستم را نمایش می دهد. مثلا در شکل زیر من بعد از اینکه درایور را با OSR Loader لود کردم اطلاعاتی که در شکل آمده اضافه شده اند.
 
drv-tut2-winobj.jpg
*بررسی تابع DriverUnload*

در این قسمت وقتی قرار است درایور unload شود باید یکسری تغییراتی که در سیستم داده ایم را به حالت قبل بر گردانیم در واقع Device و Symbolic Link ایجاد شده باید حذف گردد ما این کار را به این دو دستور انجام می دهیم


IoDeleteSymbolicLink( &uniWin32NameString );IoDeleteDevice( deviceObject );*بررسی برنامه سطح کاربر با سورس فایل testapp.c*

بخش سطح کاربر به صورت کلی سه کار انجام می دهد.


*اول)* نصب درایور که ما در آموزش اول این کار را با ابزار OSR Loader انجام می داده ایم. ولی اینبار بد نیست ببینید بدون استفاده از ابزارهای جانبی و از طریق کدنویسی چگونه می شود این کار را انجام داد. API های استفاده شده مربوط به کار با سرویسها در ویندوز می باشد که به غیر از ایجاد سرویس از آنها برای لود درایور هم می توان استفاده کرد. بررسی توابع را به عهده ی خودتان می گذارم چون هدفم از این آموزش ها بیشتر اشنایی به مباحث کرنلی است. به هر حال


*دوم)* گرفتن هندل با استفاده از CreateFile که قبلا گفتم نامی که به عنوان پارامتر به این تابع می دهیم Symbolic Link ایجاد شده برای device ما است. به این صورت
if ((hDevice = CreateFile( "\\\\.\\IoctlTest",
 GENERIC_READ | GENERIC_WRITE,
 0,
 NULL,
 CREATE_ALWAYS,
 FILE_ATTRIBUTE_NORMAL,
 NULL)) == INVALID_HANDLE_VALUE) {
    ...
    ...
}*سوم)* آماده کردن بافر خود و فرستادن دستورات IOCTL به درایور. برای فرستان یک دستور IOCTL به یک device از تابع DeviceIoControl باید استفاده کنیم. به غیر از اینکه IOCTL هایی که خودمان تعریف کرده ایم تا به device درایور خود بفرستیم. در ویندوز تعدادی IOCTL های عمومی نیز وجود دارد که کارهای خوب و بد زیادی می توان با آنها انجام داد. (به هر حال)
ما به این صورت از تابع استفاده کرده ایم
bRc = DeviceIoControl ( hDevice,
    (DWORD) IOCTL_SIOCTL_METHOD_BUFFERED,
    &InputBuffer,
    (DWORD) strlen ( InputBuffer )+1,
    &OutputBuffer,
    sizeof( OutputBuffer),
    &bytesReturned,
    NULL
 );توضیح پارامترها:
۱) *hDevice*: هندل خروجی که از CreateFile می گیرید را اینجا باید قرار دهید
۲) *dwIoControlCode*: کد IOCTL ما که پایین تر توضیح داده ام چگونه این کد تولید می شود
۳) *lpInBuffer*: بافری است که ما می خواهیم به درایور بفرستیم این بافر می تواند هر فرمتی مثلا یک رشته یا struct باشد
۴) *nInBufferSize*: طول بافر ورویدی
۵) *lpOutBuffer*: بافری که درایور قرار است اطلاعاتی در آن قرار دهد
۶) *nOutBufferSize*: طول بافر خروجی
۷) *lpBytesReturned*: اندازه اطلاعاتی که در بافر خروجی ریخته می شود. در واقع اندازه واقعی ممکن از کو چکتر از اندازه بافر باشد که این پارامتر مشخص می کند
۸) *lpOverlapped*: این پارامتر برای ما مهم نیست.


توضیحات بیشتر اینجا


*ساختار IOCTL چیست؟*

 تا الان باید بدانید ioctl چیست. یک کد که در واقع یک عدد است و  ما در سمت سطح کاربر می خواهیم بسازیم و از طریق آن به درایور خود بگوییم فلان کار را انجام بدهد. ساختاری که این کد ۳۲ بیتی دارد در شکل زیر نمایش داده شده است.
drv-tut2-ioctl.jpg
 ioctl را با این ماکرو ایجاد می کنند. که IOCTL_Device_Function نامی اختیاری است.
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)

توضیح پارامترها
۱) *DeviceType*: از آنجایی که درایور ما برای سخت افزار خاصی نیست کاری با مقدارهای تعریف شده نداریم. مقادیر که می دهیم باید مقداری بیشتر از 0x8000 داشته باشد چون مقادیر زیر 0x8000 برای ماکروسافت رزرو شده اند
۲) *FunctionCode*: این مقدار در واقع عملیاتی است که قرار است اتفاق بیافتد خود ما مقدار آن را مشخص میکنم و این عدد باید بیشتر از 0x800 باشد چون مقادیر کمتر رزرو  شده اند
۳) *TransferType*: نوع دسترسی به حافظه داده که قبلتر در مورد آن صحبت کردیم و شامل این مقادیر می تواند باشد
METHOD_BUFFERED
METHOD_IN_DIRECT
METHOD_OUT_DIRECT
METHOD_NEITHER۴) *RequiredAccess*: میزان دسترسی که در هنگام هندل گرفتن از device باید وارد کنیم ما در درایور خود همه دسترسی ها را گرفتیم یعنی هم خواندن و هم نوشتن


با استفاده از این کد ioctl را تعریف کرده ایم
#define IOCTL_SIOCTL_METHOD_BUFFERED \
 CTL_CODE( SIOCTL_TYPE, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS )*پایان*

----------


## kernel.programmer

*Device Stack چیست؟*


می خواهم در مورد Device Stack مطلبی بنویسم. من خودم زمانی که با این مفهوم آشنا شدم برایم کمی گنگ بود. تا اینکه بعد ها روی این موضوع بیشتر کار کردم و جزئیات بیشتری از چگونه نگهداری این ساختار در ویندوز و اینکهیک IRP چه مسیری را طی میکند تا به مقصد بررسد را متوجه شدم. در این پست این مسائل را شرح خواهم داد. این آموزش به دو بخش تقسیم شده یک بخش نگاه سطح بالا که بدون وارد شدن به جزئیات به موضوع پرداخته ام. بخش دوم نگاه سطح پایین که نشان داده ام چگونه با استفاده از Windbg ساختار مربوط به Deivce Stack را بیرون بکشیم. در ادامه با ما باشید... :لبخند: 


*بخش اول - نگاه سطح بالا به Device Stack*

 ما اصطلاحی داریم به نام Plug and Play (PnP) device tree یا به اختصار  Device Tree  در واقع هر Device یک گره از این درخت می شود. برای هر کدام از این گره ها ما یک Device Stack داریم. Device Stack طبق تعریف ماکروسافت"مجموعه مرتبی از Device Object ها به همراه درایور مرتبط به آن را Device Stack گویند".  جلوتر که در مورد ساختار این درخت توضیح می دهم این جمله برایتان بهتر معنی پیدا می کند. دلیل وجود چنین درختی برای این است که سیستم عامل بداند درخواست به یک Device باید از چه Device های دیگری بگذرد. الان در تصویر پایین می توانید بهتر تصویر گره های device در یک Device Stack را ببینید

dev-tree.png
طبق تصویر بالا می بینید که PCI Bus به تعداد زیادی device اشاره می کند. درایورهای Bus یکی از انواع تقسیم بندی درایور در سیستم عامل ویندوز است به طور کلی به device این نوع درایور PDO (Physical Device Object) می گویند. به غیر از Bus ما دو نوع درایور دیگر هم داریم به نام Fucniton Driver و Filter Driver. این طبقه بندی ای است که ماکروسافت تعریف کرده که و اینطور که من متوجه شده ام این تعریف برای وقتی است که یم device در device stack قرار گرفته باشد. اگر نباشد جزو هیچ کدام از این نوع ها قرار نمی گیرد.(اگر این مساله جور دیگری است و کسی اطلاعای دارد در کامنت اطلاع بدهد). به حر حال درایورهای Bus مستقیم با سخت افزار ارتباط دارند و در صورت متصل شدن دستگاه جدید از طریق PnP Manger از وجود device جدید مطلع می شوند. در هنگام بالا آمادن سیستم عامل نیز PnP Manager از درایورهای Bus می خواهد تمام device های متصل به خود را شناسایی کنند. و در این هنگام درایور PCI شروع به ایجاد گره device های متصل به خود می کند. 


قبل از اینکه ادامه بدهم یک نکته را جا داره اشاره کنم من در آموزش درایور نویسی دوم در مورد اینکه چگونه device بسازیم گفته ام. این کار را با استفاده از تابع IoCreateDevice انجام می دادیم. برای افرادی که تازه کار درایور نویسی را شروع کرده اند ممکن است استفاده از Device و Driver که من دائم به آن اشاره می کنم گیج کننده باشد. چرا یک جا می گوییم Device یک جا Driver. اصلا این دو چطور به هم مرتبط هستند؟ من در آموزش دوم گفته بودم که برای ارتباط با یک درایور دیگر باید از device ای که توسط آن درایور ایجاد شده استفاده کنیم. یک درایور میتواند چندین device برای خود ایجاد کند. device هم میتواند دارای نام (مانند نمونه ای که در آموزش دوم درایور نویسی آوردم) باشد یا بدون نام باشد. وقتی هم از Device Stack و ارسال درخواست به یک device صحبت می کنیم در واقع می گوییم که این درخواست از درایور های مرتبط با آن device ها گذشته است.


و اما ادامه مطلب  لازمه توضیحی هم در مورد ساختار پشته (stack) بدهم. همانطور که بالاتر گفتم ما در stack یک درایور از نوع Bus داریم این device همیشه در پایین stack قرار می گیرد به خاطر اینکه در نهایت باید درخواست دریافت شده را به سخت افزار مربوطه بفرستد.قبل از درایور Bus می توان دو نوع درایور دیگر قرار می گیرد. Function Driver و Filter Driver 


Funciton Driver Object یا FDO عملیات اصلی در آن stack توسط این درایور انجام می شود. مثلا کار با دیسک، شبکه، کیبورد، ماوس کلا هر چی همه برای خود یک درایور Function دارند که مسٔول پردازش های اصلی درخواست های وارد شده یا خارج شده را بر عهده دارند. هر stack برای خود حتما باید یک درایور Function داشته باشد.


Filter Driver Object یا FiDO این نوع درایورها که دو نوع است Upper Filter Driver که قبل یا بالای stack از Function Driver و Loewr Filter Driver که بعد از Function Driver یا پایین stack قرار می گیرد تقسیم بندی می شود. درایور Bus هم می تواند بالای خود Filter Driver داشته باشد. Filter Driver می تواند به هر تعدادی باشد. یا اصلا نباشد. این نوع درایور برای این است که اگر درخواستی لازم است قبل از رسیدن به Function Driver یا بعد از آن پردازشی رویش انجام شود به کار می آید. مثلا می توانیم با یک Upper Filter Driver روی درایور Disk تمام درخواست هایی که به این درایور فرستاده می شوند را یک جا LOG بگیریم (یک چیزی مثل IDS ها) یا مثلا اطلاعات آن را رمز گذاری کنیم (مثل BitLocket) .


 تصویر پایین سه stack را نمایش می دهد در دو تا از stack ها pci.sys به عنوان درایور Bus قرار گرفته  stack سمت چپ دارای Upper Filter Driver  است و سمت راستی دارای یک Lower Filter Driver می باشد. و در stack پایینی pci.sys به عنوان Function Driver و acpi.sys به عنوان Bus Driver قرار گرفته اند.
dev-stack.png
*بخش دوم - نگاه سطح پایین با استفاده از ابزار Windbg*

من این بخش را جدا کردم برای افرادی که علاقه مندند بیشتر با ساختار Device Stack اشنا بشوند. توضیحاتی که بالا آوردن را دیگر اینجا تکرار نمی کنم. یکسری دستورات جدید خواهم گفت که به کمک آنها بتوانیم اطلاعات مختلفی را از طریق Windbg بدست باوریم.


*دستور !object*

قبلا در آموزش دوم درایور نویسی در مورد Object توضیح داده ام در سیستم عامل ویندوز خیلی مفاهیم مثل File, Process, Driver, Device و چیزهای دیگر Object نامیده می شوند. با این دستور می توان از object ها اطلاعات بدست آورد. بگذارید با چند مثال کار با این دستور را یاد بگیریم.


 Object ها یک ساختار پوشه مانندی دارند که ریشه این ساختار پوشه ای به صورت یک حرف backslash یا "\" تعریف شده. برای مثال بخواهیم هر چیز داخل این پوشه را ببینیم این دستور را وارد میکنیم

lkd> !object \
Object: e1001108 Type: (825ed260) Directory
 ObjectHeader: e10010f0 (old version)
 HandleCount: 0 PointerCount: 40
 Directory Object: 00000000 Name: \
 130 symbolic links snapped through this directory

 Hash Address  Type          Name
 ---- -------  ----         ----
  00  e100d748  Directory    ArcName
      82224030  Device       Ntfs
  01  e15f1860  Port         SeLsaCommandPort
  02  81fc7368  Device       FatCdrom
  03  e1011490  Key          \REGISTRY
  05  e15e28d8  Port         ThemeApiPort
  06  e170e970  Port         XactSrvLpcPort
  09  e1615860  Directory    NLS
  10  e1008688  SymbolicLink DosDevices
...
...
  32  82338a20  WaitablePort NLAPrivatePort
      e1008540  Directory    Callback
  33  8220c0c0  Event        SeLsaInitEvent
      82494ae8  Event        UniqueSessionIdEvent
   35  e1917748  Directory    KnownDlls


اگر یادتان باشد در همان آموزش دوم یک ابزار به نام WnObj هم معرفی کردم که این اطلاعات را نمیاش می داد. منتها اینجا شما آدرس object ها را هم دارید
مثلا دوم: اگر بخواهیم لیست درایورهایی که در سیستم load شده اند را ببینیم با دستور زیر این کار را انجام می دهیم که همراه خروجی آمده است. به ساختار پوشه ای هم دقت کنید.

lkd> !object \Driver
    Object: e1022968 Type: (825ed260) Directory
    ObjectHeader: e1022950 (old version)
    HandleCount: 0 PointerCount: 86
    Directory Object: e1001108 Name: Driver

    Hash Address Type Name
    ---- ------- ---- ----
     00 82437818 Driver Beep
        823c0218 Driver NDIS
        823c0a40 Driver KSecDD
     01 8243ada8 Driver Mouclass
        8209c110 Driver Raspti
        82065ad0 Driver es1371
     02 824c98e8 Driver vmx_svga
     03 824c7e88 Driver Fips
        824caec8 Driver Kbdclass
     04 823c9f38 Driver VgaSave
        82085ae8 Driver NDProxy
    ...
    ...
     34 82397978 Driver AFD
        82011740 Driver Ndisuio
        35 82031168 Driver hidusb
        8243a2a0 Driver Parport
     36 820653e0 Driver i8042prt
        824ca238 Driver CmBatt
        82086c70 Driver IntelIde

مثال های دیگر، خروجی این مثال ها را خودتان امتحان کنید


گرفتن object های device روی سیستم

!object \Device



گرفتن اطلاعات از object درایور NDIS

  !object \Driver\NDIS

گرفتن اطلاعات از آدرس یک object این آدرس را از دستورات بالا می توان بدست آورد مثلا آنجا که object های مربوط به درایورها را نمایش دادیم
!object e102296

*دستور !drvobj*

با این دستور اطلاعات بیشتری در مورد object یک درایور می توانیم بدست می آوریم. بالاتر گفتم که یک درایور می تواند بیش از یک device برای خود ایجاد کند. این دستور لیست device های یک درایور را نیز نمایش می دهد.
به عنوان مثال لیست device های درایور PCI همانطور که مشاهده می کنید تعداد زیادی device object توسط این درایور ایجاد شده است.
lkd> !drvobj PCI
Driver object (825c4500) is for:
 \Driver\PCI
Driver Extension List: (id , addr)

Device Object list:
825e3378 8245ae50 823e80d0 8252f900
8252fac8 8252fc90 8252fe58 8252f020
825303a8 82530570 82530738 82530900
82530ac8 82530c90 82530e58 82530020
824983a8 82498570 82498738 82498900
82498ac8 82498c90 82498e58 82498020
8235d3a8 8235d570 8235d738 8235d900
8235dac8 8235dc90 8235de58 8235d020
82096178 82096340 82096508 820966d0
82096af8 825e49c0 825e4cf8 825e5628
825e5960 825e5c98 8253b2f0 8253b628
8252b9c0 8252bcf8 820a1578 820a18b0
820a1a90 825ad600 825ad7e0 823e7c70
823e7e50 825adcf8 825e6738 825e6a70
8252b378 824f88b0 824f8be8 825e5380
825ac220 825ac030 825ae370 825aecf8
823c2330 8245a518 825ae830 8253be50
824a9e50 823e7688 824f8620 8242f1f0
825ab2e8 825af338 825e62f8 825ab7b8
825b30c0 8245aba8*دستور !devstack*

با این دستور میتوانیم stack مربوط به یک device را نمایش بدهیم. مثلا من یکی از Device Object هایی که در دستور قبل بدست آمد را به این دستور می دهم تا stack آن استخراج شود
lkd> !devstack 825e3378 
  !DevObj !DrvObj !DevExt ObjectName
  82493790 \Driver\es1371 82493848 00000078
  8245aa88 \Driver\ACPI 825cc008 00000066
> 825e3378 \Driver\PCI 825e3430 NTPNP_PCI0042
!DevNode 8257e1c8 :
  DeviceInst is "PCI\VEN_1274&DEV_1371&SUBSYS_13711274&REV_02\4&47  b7341&0&1088"
  ServiceName is "es1371"در خروجی بالا می بینیم که دو درایور در بالای درایور PCI ما قرار کرفته. یکی ACPI و دیگری es1371


*استخراج اطلاعات با دستور dt*

من در بالا دستورات سطح بالاتر windbg را گفتم در این حالت ما مستقیم با ساختارهای داخلی سیستم عامل درگیر نیستیم ولی یک موقع هایی شاید لازم داشته باشیم با این ساختارها درگیر شویم. در آموزشی که در مورد windbg نوشته بودم توضیح دادم چگونه ساختارهای _DRIVER_OBJECT و _DEVICE_OBJECT را استخراج کنیم.


ساختار _DRIVER_OBJECT یک عضو به نام DeviceObject دارد. من اطلاعات مربوط به درایور Disk را اینجا آوردم.
lkd> dt _DRIVER_OBJECT 8242e940 
nt!_DRIVER_OBJECT
 +0x000 Type : 4
 +0x002 Size : 168
 +0x004 DeviceObject : 0x822259f0 _DEVICE_OBJECT
 +0x008 Flags : 0x12
 +0x00c DriverStart : 0xf86ca000 
 +0x010 DriverSize : 0x8e00
 +0x014 DriverSection : 0x825ed928 
 +0x018 DriverExtension : 0x8242e9e8 _DRIVER_EXTENSION
 +0x01c DriverName : _UNICODE_STRING "\Driver\Disk"
 +0x024 HardwareDatabase : 0x80671860 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM  "
 +0x028 FastIoDispatch : (null) 
 +0x02c DriverInit : 0xf86d18ad long +fffffffff86d18ad
 +0x030 DriverStartIo : (null) 
 +0x034 DriverUnload : 0xf86e14b4 void +fffffffff86e14b4
 +0x038 MajorFunction : [28] 0xf86e0bb0 long +fffffffff86e0bb0

این یکی از device های ساخته شده توسط درایور Disk است. حالا اطلاعات این device را ببینیم

lkd> dt _DEVICE_OBJECT 0x822259f0
nt!_DEVICE_OBJECT
 +0x000 Type : 3
 +0x002 Size : 0x368
 +0x004 ReferenceCount : 0
 +0x008 DriverObject : 0x8242e940 _DRIVER_OBJECT
 +0x00c NextDevice : 0x823c1ab8 _DEVICE_OBJECT
 +0x010 AttachedDevice : (null) 
 +0x014 CurrentIrp : (null) 
 +0x018 Timer : (null) 
 +0x01c Flags : 0xd0
 +0x020 Characteristics : 0x100
 +0x024 Vpb : 0x8245a248 _VPB
 +0x028 DeviceExtension : 0x82225aa8 
 +0x02c DeviceType : 7
 +0x030 StackSize : 4 ''
 +0x034 Queue : __unnamed
 +0x05c AlignmentRequirement : 0
 +0x060 DeviceQueue : _KDEVICE_QUEUE
 +0x074 Dpc : _KDPC
 +0x094 ActiveThreadCount : 0
 +0x098 SecurityDescriptor : 0xe10178d8 
 +0x09c DeviceLock : _KEVENT
 +0x0ac SectorSize : 0x200
 +0x0ae Spare1 : 0
 +0x0b0 DeviceObjectExtension : 0x82225d58 _DEVOBJ_EXTENSION
  +0x0b4 Reserved : (null)


از این عضوهای داده آنهایی NextDevice, AttackedDevice و DeviceObjectExtension به کار ما می آیند. اگر درایور دارای device های دیگری باشد NextDevice باید دارای مقدار غیر null باشد که در مثال ما این گونه است. اگر device دارای device هایی در بالای stack خود باشد این device ها از طریق عضو AttackedDevice مشخص می شود. مثلا این device هیچ device ای در بالای stack خود ندارد. لازم به ذکره ما نام درایور مربوط به device را از طریق استخراج اطلاعات عضو DriverObject می توانیم بدست آوریم
هر Device Object یک عضو به نام DeviceObjectExtension نیز دارد که نام ساختار آن _DEVOBJ_EXTENSION است. که از طریق آن می توانیم به device های پایین stack دسترسی پیدا کنیم. 
lkd> dt _DEVOBJ_EXTENSION 0x82225d58 
nt!_DEVOBJ_EXTENSION
 +0x000 Type : 13
 +0x002 Size : 0
 +0x004 DeviceObject : 0x822259f0 _DEVICE_OBJECT
 +0x008 PowerFlags : 0
 +0x00c Dope : 0x8242f900 _DEVICE_OBJECT_POWER_EXTENSION
 +0x010 ExtensionFlags : 0
 +0x014 DeviceNode : (null) 
 +0x018 AttachedTo : (null) 
 +0x01c StartIoCount : 0
 +0x020 StartIoKey : 0
 +0x024 StartIoFlags : 0
 +0x028 Vpb : (null)که در این ساختار عضو AttackedTo اشاره به device هایی دارد که در پایین device stack ما قرار گرفته. که در این مثال ما هیچ device stack ای در پایین نداریم.
همین کار با اگر با دستور !devstack انجام می دهیم که همین نتیجه ملاحظه می شود
lkd> !devstack 0x822259f0
 !DevObj !DrvObj !DevExt ObjectName
> 822259f0 \Driver\Disk 82225aa8 DP(1)0x7000-0x3bf2ca000+1حالا همین کار را می توانید با device دیگر که با NextDevice مشخص شده انجام دهید می بینید که برای آن device ما stack داریم. 
lkd> !devstack 0x823c1ab8 
  !DevObj !DrvObj !DevExt ObjectName
  82352930 \Driver\PartMgr 823529e8 
> 823c1ab8 \Driver\Disk 823c1b70 DR0
  8242ea38 \Driver\VMSCSI 8242eaf0 VMSCSI1Port2Path0Target0Lun0
!DevNode 8209e8e8 :
  DeviceInst is "SCSI\Disk&Ven_VMware_&Prod_VMware_Virtual_S&Rev_1  .0\4&5fcaafc&0&000"
  ServiceName is "disk"

همانطور که میبینید در بالا stack ما درایور PartMgr و در پایین stack درایور VMSCSI را داریم.
*پایان*

خوب به پایان آموزش رسیدیم امیدوارم این مطلب برایتان مفید واقع شده باشد.
موفق باشید

----------


## kernel.programmer

*روش های دسترسی به بافر داده در درایورهای ویندوزی*

در آموزش های قبلی به صورت خیلی خلاصه در مورد روش هایی که یک درایور می تواند با برنامه سطح کاربر ارتباط برقرار کند صحبت کردیم و برای یکی از این روش ها که  IOCTL بود مثالی آوردیم. در این آموزش سعی می کنم این روش ها را با جزئیات بیشتری به همراه مثال شرح بدهم. در طول آموزش موضوعات دیگری هم مطرح می شود و برای این که ساختار آموزش بهم نریزد توضیحات مربوطه را بصورت جداگانه در انتهای این آموزش آورده ام. 

سرفصل مطالب این بخش

راه های دسترسی به بافر داده
روش Buffered I/Oروش Direct I/Oروش نه Buffered I/O و نه Direct I/O مقدار دادن DEVICE_OBJECT.Flagsموضوعات جداگانه
درخواست هاحافظه از نوع Nonepaged 

لینک مثال ها

مثال Buffered I/Oمثال Direct I/O 

*راه های دسترسی به بافر داده*

در روش هایی که جلوتر توضیح خواهم داد I/O Manager نقش مهمی را ایفا میکند به این دلیل که بر اساس روش دسترسی به حافظه داده، I/O Manager  یکسری شرایط را قبل از اینکه درخواست کاربر را به درایور مقصد بفرستد برقرار می کند. حالا سوال مهم این است که I/O Manager چطور تشخیص می دهد که کدام روش مورد استفاده قرار گرفته است؟ در واقع این کار به دو طریق زیر انجام می شود.


 برای درخواست های IRP_MJ_READ و IRP_MJ_WRITE درایور نویس می تواند عضو داده Flags از ساختار DEVICE_OBJECT را به یکی از موارد زیر مقدار دهی کند.
DO_BUFFERED_IO DO_DIRECT_IO درخواسته هایی که نه DO_BUFFERED_IO و نه DO_DIRECT_IO در عضو داده Flags آنها مقدار دهی شده باشد 
در واقع درایور باید عضو Flags از DeviceObject را مقدار دهی کند. ما قبلا دیده بودیم که با تابع IoCreateDevice می توانیم یک DeviceObjectبسازیم.                برای درخواست های IRP_MJ_DEVICE_CONTROL  و IRP_MJ_INTERNAL_DEVICE_CONTROL ما به همراه درخوست یک کد IOCTL می فرستادیم. در مورد ساختار این کد من در آموزش دوم ساختار IOCTL را با جزئیات توضیح داده بودم ولی برای تکرار مجدد من مقادیر مربوط به پارامتر TransferType کد IOCTL را اینجا هم می آورم.
METHOD_BUFFERED METHID_IN_DIRECT و METHOD_OUT_DIRECT METHOD_NEITHER 

*نکته*: لازم به ذکر است که سورس مثال های این آموزش بر اساس تغییر Flags هست ولی با کمی تغییر می توان به روش IOCTL تبدیلش کرد. و برای اطلاعات بیشتر به آموزش دوم رجوع کنید که در مورد روش IOCTL مثال زده بودیم.
*
روش Buffered I/O*

این روش بیشتر در درایورهای عمومی مثل کیبورد، ماوس، گرافیک، دستگاه های بر پایه رابط Serial و Parallel مورد استفاده قرار می گیرد. و همچنین به دلیل اینکه دستگاه های مرتبط به این درایورها از نظر عملکرد کند هستند و اندازه اطلاعاتی که بین سیستم عامل و این دستگاه ها منتقل می شود حجم  کمی دارند ما از این روش استفاده می کنیم.


I/O Manager در صورتی که درخواست به صورت Buffered I/O (با توجه به یکی از راه هایی که بالاتر گفتم. مقدار دهی Flags یا از طریق IOCTL) فرستاده شده باشد، قبل از فرستادن این درخواست به درایور ما یک "حافظه ی سیستمی" (یک حافظه  Nonpaged) به اندازه حافظه داده ای که از سمت کاربر آمده تخصیص می کند. در صورتی که درخواست "نوشتن"(Write) باشد I/O Manager داده سطح کاربر را در این حافظه ی سیستمی کپی میکند و این حافظه در دسترس درایور ما قرار می گیرد، و در صورتی که درخواست "خواندن"(Read) باشد بعد از آنکه درایور ما و دیگر درایور ها کارشان را انجام دادند، داده بازگشت داده شده از سمت درایور در حافظه سطح کاربر کپی می شود در نتیجه داده برگشتی در دسترس برنامه سطح کاربر قرار خواهد گرفت.
*
مثال - سطح کرنل*

حالا برای اینکه مطلب بهتر جا بیافتد یک مثال میزنیم. لینک به سورس کامل این مثال را در ابتدای آموزش قرار داده ام.

*اول)* با IoCraeteDevice یک Device Object می سازیم بعد Flags را به DO_BUFFERED_IO مقدار می دهیم. (در یک بخش جدا از همین آموزش این موضوع را شرح داده ام). مقدار دادن Flags هم به این شکل (با حفظ مقادیر قبلی Flags)

objectDevice.Flags |= DO_BUFFERED_IO;

*نکته*: اگر بخواهید از روش IOCTL استفاده کنید ما نیاز به تغییر متغییری حاصی نداریم. در واقع چون برای فرستادن درخواست به روش IOCTL ما ار تابع DeviceIoControl از سمت کاربر استفاده میکنیم IO Manager به صورت اتوماتیک پیش نیاز های لازم را برقرار می کند.

*دوم)* درایور ما بسته به نیاز باید بتواند درخواست های IRP_MJ_READ و IRP_MJ_WRITE را پردازش کند. این دو را در کنار دیگر IRP هایی که پردازش می کنیم قرار می دهیم. در نتیجه در DriverEntry داریم



DriverEntry(
    __in PDRIVER_OBJECT   DriverObject,
    __in PUNICODE_STRING  RegistryPath
    )
{
  ...
  ...


  /* تعریف دیگر درخواست ها */ 
  DriverObject->MajorFunction[IRP_MJ_CREATE] = IrpCreateClose;
  DriverObject->MajorFunction[IRP_MJ_CLOSE] = IrpCreateClose;


  DriverObject->MajorFunction[IRP_MJ_READ] = IrpRead;
  DriverObject->MajorFunction[IRP_MJ_WRITE] = IrpWrite;


  ...
  ...
}



*نکته*: در روش IOCTL درایور ما بجای درخواست های IRP_MJ_READ  و IRP_MJ_WRITE باید درخواست های IRP_MJ_DEVICE_CONTROL  و IRP_MJ_INTERNAL_DEVICE_CONTROL را پردازش کند. در نتیجه تابع هایی که جلوتر می آودم کمی تغییر میکند.

*سوم)* و در نهایت پیاده سازی توابعی که درخواست های IRP_MJ_READ و IRP_MJ_WRITE را دریافت  می کنند


NTSTATUS
IrpRead(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    )
{
  ...
  ...


  irpSp = IoGetCurrentIrpStackLocation( Irp );
  outLength = irpSp->Parameters.Read.Length;


  ...
  ...


  /* بافر هم برای خواندن و نوشتن */
  outBuf = Irp->AssociatedIrp.SystemBuffer;


  /* اینجا داده لازم را داخل بافر حافظه می ریزیم*/
  RtlCopyBytes(outBuf, msg, sizeof(msg));
    
  Irp->IoStatus.Information = sizeof(msg);


  ...
  ...
}


NTSTATUS
IrpWrite(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    )
{
 ...
}


*چهارم)* اینجا چون پردازش این دو درخواست شبیه به هم است من یکی را توضیح می دهم (فقط درخواست خواندن). در این مرحله ما اطلاعات مربوط به درخواست را می توانیم از ساختار  IO_STACK_LOCATION که داخل ساختار IRP است بگیریم. فعلا به جزئیات این ساختار کار نداریم ولی همینقدر لازم است بدانیم که داخل این ساختار یک ساختار داده union است که داخل این union به ازای نوع درخواست یک ساختار مجزا وجود دارد.
مثلا نسبت تابعی که سطح کاربر صدا زده می شود و ساختاری که داخل IO_STACK_LOCATION مقدار دهی می شود در جدول زیر قابل مشاهده است.


UserMode Function | IO_STACK_LOCATION strcut
______________________________________________
  CreateFile       |        Create
  ReadFile         |        Read
  WriteFile        |        Write
  DeviceIOControl  |        DeviceIoControl 
  ...              |        ...          


در نتیجه در مورد مثال ما اگر کاربر از تابع ReadFile استفاده کند که درخواستی به درایور ما بفرستد. IO Manager ساختار مربوط به Read (که پایین محتویات آن را می بینید) را با مقادیر آمده از سطح کاربر مقدار دهی می کند. و در نهایت ما Irp که از طرف IO Manager آماده شده را داخل تابع مربوط به  IRP_MJ_READ دریافت می کنیم.
محتویات این ساختارها را من از اینجا گرفته ام.


  //
  // Parameters for IRP_MJ_READ 
  //
  struct {
    ULONG  Length;
    ULONG POINTER_ALIGNMENT  Key;
     LARGE_INTEGER  ByteOffset;
  } Read;


  //
  // Parameters for IRP_MJ_WRITE 
  //
  struct {
    ULONG  Length;
    ULONG POINTER_ALIGNMENT  Key;
    LARGE_INTEGER  ByteOffset;
  } Write;


برای دسترسی به مقادیر IO_STACK_LOCATION داخل IRP از IoGetCurrentIrpStackLocation استفاده می کنیم بعد مقدار Length را میخوانیم.


 irpSp = IoGetCurrentIrpStackLocation( Irp );
  outLength = irpSp->Parameters.Read.Length;


*نکته*: در روش IOCTL ما در آموزش دوم دیده بودیم که این قبیل پارامترها مربوط به IO_STACK_LOCATION را از ساختار DeviceIoControl (توجه به irpSp->Parameters.DeviceIoControl) می گرفتیم. برای اینکه این مطلب مروری شود کد مربوطه را اینجا هم کپی میکنم.


 irpSp = IoGetCurrentIrpStackLocation( Irp );
  inBufLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
  outBufLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;


برای درخواست "خواندن" ما باید اطلاعاتی را برای کاربر بفرستیم. چطور اینکار را انجام می دهیم؟ قبلا گفته بودیم که IO Manager یک بافر سیستمی تخصیص می کند و اگر درخواست "خواندن" بود داخل این حافظه داده لازم را می ریزیم و اگر درخواست "نوشتن" از آن حافظه سیستمی باید داده آمده از سمت کاربر را بخوانیم. به حافظه سیستمی هم به صورت زیر دسترسی داریم.



  outBuf = Irp->AssociatedIrp.SystemBuffer;


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

*روش Direct I/O*

این روش معمولا برای درایورهای مربوط به دستگاهای نگه داری اطلاعات است (مثل هارد دیسک، و...) یا اگر بخواهیم از DMA استفاده کنیم. در این موارد اندازه داده هایی که بین سیستم عامل و دستگاه منتقل می شود حجم زیادی دارند. 


I/O Manager در صورتی که درخواست به صورت Direct I/O فرستاده شده باشد، قبل از فرستادن درخواست به درایور ما از آدرس سطح کاربر را قفل می کند و از آن یک Mdl می سازد. Mdl همراه درخواست به درایور ما فر ستاده می شود و درایور با استفاده از یکسری تابع که در مثال خواهیم دید به حافظه سطح کاربر می تواند دسترسی پیدا کند. توجه شود که در این روش ما داده سطح کاربر را مانند روش قبلی کپی نمی کنیم در نتیجه سرعت کارایی و عملیات بیشتر می شود و همچنین ما بطور مستقیم هم به آدرس سمت کاربر دسترسی نداریم در واقع Mdl اینجا نقش یک واسط را بازی می کند که ارتباط درایور با آدرس سطح کاربر را ممکن می سازد.
*مثال - سطح کرنل*

توضیحات مربوط به این مثال تا حد زیادی مشابه با روش Buffered I/O است در نتیحه من فقط قسمت هایی که متفاوت هستند را اینجا ذکر می کنم.


*اول)* مقدار دادن Flags به این صورت می شود


objectDevice.Flags |= DO_DIRECT_IO;



*دوم)* رجوع به مثال Buffered I/O

*سوم)* پیاده سازی توابع مربوط به درخواست ها 


NTSTATUS
IrpRead(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    )
{
  ...
  ...


  irpSp = IoGetCurrentIrpStackLocation( Irp );
  outLength = irpSp->Parameters.Read.Length;


  ...
  ...


  outBuf = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, 
                                    NormalPagePriority);


  /* اینجا داده لازم را داخل بافر حافظه می ریزیم*/
  RtlCopyBytes(outBuf, msg, sizeof(msg));
    
  Irp->IoStatus.Information = sizeof(msg);


  ...
  ...
}


NTSTATUS
IrpWrite(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    )
{
 ...
}


*چهارم)* همانطور که می بینید خیلی مشابه روش Buffered I/O است با این تفاوت که اینجا IO Manager از حافظه سطح کاربر یک Mdl ساخته و آن را داخل ساختار IRP قرار داده است. ما برای استفاده از Mdl از تابع MmGetSystemAddressForMdlSafe استفاده کرده ایم. این تابع چکار می کند، در واقع یک Mdl می گیرد به شما خروجی آدرس حافظه ای را می دهد که با آن کار کنیم. به همین سادگی

*روش نه Buffered I/O و نه Direct I/O*

در این روش ما مستقیم به آدرس سطح کاربر دسترسی داریم. استفاده از این روش می تواند مشکلاتی ایجاد کند به این خاطر که اگر درایور یکسری نکات امنیتی را رعایت نکند می تواند باعث بهم ریختن امنیت سیستم شود (درایور های زیادی متاسفانه دارای این نقطه ضعف هستند). 
در روش های پیشین توضیح دادیم که I/O Manager یکسری کارها قبل از آمدن در خواست به ما انجام می دهد. ولی در این روش همه چی را خودمان باید انجام دهیم. در واقع ما باید مانند IO Manager عمل کینم.
توضیح این بخش به نظرم فعلا بی مورد است و حجم این آموزش را زیاد میکند. در آینده شاید بخشی جدا برایش ایجاد کنم چون نکته زیاد دارد.

*مقدار دادن DEVICE_OBJECT.Flags*

ما قبلا در مورد روش IOCTL صحبت کرده ایم (رجوع به آموزش دوم). حالا در مورد مقدار دادن Flags باید صحبت کنیم. عضو داده Flags مقادیر مختلفی می گیرد (سایت ماکروسایت مقادیر کامل تر را ببینید) و می توانیم این مقادیر را با هم OR (یکی از عملگرهای بولی) کنیم. کد DriverEntry داشتیم به این صورت



NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, 
                     IN PUNICODE_STRING regPath)
{
  PDEVICE_OBJECT  deviceObject = NULL;
  ...
  ...


  ntStatus = IoCreateDevice(...,
                            ...,
                            &deviceObject)


  deviceObject.Flags = مقدار های معتبر; /* DO_BUFFERED_IO
                                           DO_DIRECT_IO */


  ...
  ...


  return STATUS_SUCCESS;
}



ما داخل این قسمت با IoCreateDevice یک Device Object  می سازیم و حالا می توانیم مقدار Flags مربوط به ساختار این Object را تغییر دهیم.
اگر بخواهیم هم DO_BUFFERED_IO داشته باشیم و هم DO_DIRECT_IO  به این صورت می شود (که با هم OR شده اند).


deviceObject.Flags = DO_BUFFERED_IO | DO_DIRECT_IO;


یا اگر بخواهیم مقدار قبلی Flags را هم نگه داریم


deviceObject.Flags |= (DO_BUFFERED_IO | DO_DIRECT_IO)



اگرحالت سوم که نه Buffered I/O و نه Direct I/O بود را بخواهیم. دیگر با هیچکدام از این دو مقدار Flags را مقدار دهی نمی کنیم.

*موضوعات جداگانه*

*درخواست ها*

ما در طول آموزش در مورد درخواست هایی مثل IRP_MJ_READ, IRP_MJ_WRITE و ... صحبت کردیم. وقتی صحبت از "درخواست" میکنیم منظور ما ساختاری است به نام IRP. داخل این ساختار یک ساختار دیگر وجود دارد به نام IO_STACK_LOCATION که یک جایی داخل این ساختار عضو داده ای وجود دارد به نام MajorFunction. مقادیری که این عضو داده می تواند داشته باشد به شرح زیر است.


IRP_MJ_CREATE
IRP_MJ_PNP
IRP_MJ_POWER
IRP_MJ_READ
IRP_MJ_WRITE
IRP_MJ_FLUSH_BUFFERS
IRP_MJ_QUERY_INFORMATION
IRP_MJ_SET_INFORMATION
IRP_MJ_DEVICE_CONTROL
IRP_MJ_INTERNAL_DEVICE_CONTROL
IRP_MJ_SYSTEM_CONTROL
IRP_MJ_CLEANUP
IRP_MJ_CLOSE
IRP_MJ_SHUTDOWN


در نتیجه وقتی می گوییم درخواست IRP_MJ_READ منظور ما این است که یک ساختار IRP.IO_STACK_LOCATION داریم که عضو داده MajorFunction آن IRP_MJ_READ است.

یک درخواست یا همان IRP وقتی به یک درایور می رسد. تابعی با توجه به آرایه ی MajorFunction داخل DriverObject مربوط به آن درایور صدا زده می شود. (در مورد آرایه ای از MajorFunction رجوع به آموزش دوم کنید).


*حافظه از نوع Nonpaged*

در سیستم عامل یک مفهومی داریم به نام Swapping که قابلیتی است که سیستم عامل می تواند از یک فایل یا پارتیشن به عنوان حافظه اضافی روی دیسک در کنار حافظه اصلی (RAM) استفاده کند (در سیستم عامل ویندوز این فایل pagefile.sys نام دارد). در نتیجه فضایی که در کل خواهید داشت بیشتر از فضای حافظه اصلی سیستم می شود. و همچنین یک مفهومی داریم به نام Paging (این امکان از طرف CPU باید پشتیبانی شود) که در این حالت حافظه اصلی تقسیم شده به واحد های کوچکتر و یک اندازه به نام Page. اندازه Page ها در حالت پیش فرض 4 KiB می باشد. سیستم عامل با یکسری از روش ها تصمیم می گیرد که یکسری از Page ها که مورد نیاز نیستن به دیسک منتقل شوند(به این عمل اصطلاحا میگن swap-out).
اگر حافظه را به صورت Nonpaged تخصیص کنیم در واقع به سیستم عامل می گوییم که حافظه ای که می گیریم همیشه در حافظه اصلی RAM باقی بماند و به دیسک منتقل نشود. جدا از موضوع آموزش این پست این کار یکسری مزایا و معایب دارد. مزیت این کار این است که چون آن ناحیه همیشه در حافظه است سرعت دسترسی هم بیشتره، در واقع زمانی برای منتقل کردن Page های مربوطه به دیسک صرف نمی شود. عیب این کار این است که وقتی یک فضایی از حافظه اصلی می گیریم در نتیجه حافظه فیزیکی کمتری خواهیم داشت، در نتیجه سیستم عامل در صورت مواجه شدن با کمبود حافظه مجبور است از حافظه اضافی که روی دیسک است استفاده کند که این کار تاثیر روی کارایی سیستم می گذارد.
در ویندوز ما یک جاهایی مجبوریم حافظه را از نوع Nonpaged بگیریم و راستش من نمی خواهم وارد جزئیات این مساله بشوم چون مارو خیلی از موضوع آموزش این پست دور خواهم شد. ولی بعد سعی می کنم پستی در این رابطه بنویسم.
*نکته*: زمانی که یک حافظه از نوع Nonpaged گرفتین و دیگر کارتان با این حافظه تمام شد حتما آنرا آزاد کنید که تاثیر روی کاراریی سیستم نگذارد.

----------


## kernel.programmer

رزروووووووووووو

----------


## kernel.programmer

پست رزوووووووو

----------


## kernel.programmer

پست رزرووووووووو

----------


## kernel.programmer

پست رزروووووووووو

----------


## heballi1

خيلي ممنون از پست خوبتون، ميشه لطفا در مورد مثلا يك داده اي كه بايد روي فلش نوشته بشه از چه درايورهايي براي رسيدگي به اين درخواستش استفاده مي شه و فرمت درخواست يا همان IRP به چه شكلي هست و تصويرش در حافظه را توضيح بديد؟ 

يك سوال ديگه هم داشتم اينكه،‌ آيا درايور ما بايد حتما به يك سخت افزار متصل باشه؟  خود درايور ما تنها مي تونه توي كرنل اجرا بشه؟ كلا  يك درايو  از رجيتسرير چه استفاده اي مي كند؟ اگر يك درايور بخواهد به چندين درخواست همزمان پاسخ بدهد،‌تابع هاي Major را چگونه بايد پياده سازي كنيم؟ يك كتاب خوب به زبان اصلي و يا فارسي اگر دارين ميشه آپلود كنيد؟
من hello رو توي سيستم با OSLoader register كردم،‌ميگه بايد sign بشه،‌ويندوز 7 هست. چه كار كنم؟

----------


## بهروز عباسی

سلام
به برنامه نویس خوش آمدی.





> دوست دارم فیدبک بیشتری داشته باشم






> *پیش نیازها*
> 
> لیستی از دانشها و ابزارهایی که برای توسعه یک درایور نیاز دارید
> 
> 
> آشنایی خوب با زبان برنامه نویسی 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 کامپایل کنید نیاز است تنظیماتی را انجام دهید*.


پیش نیازهای برنامه نویسی کرنل این مواردی که ذکر کردین نیستن !
اینا فقط چندتا ابزارن

برای شروع برنامه نویسی کرنل شما باید اول از همه با معماری ویندوز آشنا باشی، برای آشنایی هم باید کتاب های Windows Internals و RootKit Arsenal رو بخونید ، باید به مباحث مهندسی معکوس آشنایی داشته باشید چون برخی مواقع مجبورید کدتون رو به روش های عجیبی دیباگ کنید و ... در آخر باید برنامه نویس باشین :d


در مورد WDK 8.1 هم اگه اشتباه نکنم همچنان میشه Command line باهاش کامپایل کرد(دقیق یادم نیست)، اما در کل خیلی خوب با VS مچ میشه و به راحتی میشه کار کرد باهاش.
وقتی WDK 8.1 رو نصب میکنید خودش با VS مچ میشه و توی VS شما میتونی نوع پروژتو درایور انتخاب کنی  (البته از نوع *KMDF* ) و مثل قدیم نیازی نیست چیزی رو تغییر بدین. حتما امتحان کنید :D




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


میشه BSOD رو غیر فعال کرد ولی این کاری که شما انجام میدی بهتره .

موفق باشید.

----------


## بهروز عباسی

> من hello رو توي سيستم با OSLoader register كردم،‌ميگه بايد sign بشه،‌ويندوز 7 هست. چه كار كنم؟


چون ویندوز شما 64بیتی این مشکل به وجود میاد، شما برای تست/توسعه باید TestSign رو فعال کنید (قبلا هم بحث شده)

----------


## kernel.programmer

> ميشه لطفا در مورد مثلا يك داده اي كه بايد روي فلش نوشته بشه از چه درايورهايي براي رسيدگي به اين درخواستش استفاده مي شه


 تو آموزش سوم که در مورد device stack است یکسری دستور در windbg گفتم که با اونا خیلی راحت می شه این قبیل اطلاعات رو در آورد. 

 برای مثال لیست driver object ها رو بگیرین و درایور هایی که مرتبط با usb هستند را شروع کنید استک ابجکت device هاشون رو بگیرین. که در نهایت تمام درایورهای مرتبط در میاد
این لاگ windbg که من روی ویندوز xp تست کردم

lkd> !drvobj usbhub
Driver object (823baf38) is for:
 \Driver\usbhub
Driver Extension List: (id , addr)

Device Object list:
82380690  824277e0  821c37e0  820392f8
---------------------------------------------
lkd> !devstack 82380690  
  !DevObj   !DrvObj            !DevExt   ObjectName
> 82380690  \Driver\usbhub     82380748  0000007f
  824277e0  \Driver\usbhub     82427898  USBPDO-2
!DevNode 81f41698 :
  DeviceInst is "USB\Vid_0e0f&Pid_0002\6&63561a1&0&2"
  ServiceName is "usbhub"
---------------------------------------------
lkd> !devstack 824277e0  
  !DevObj   !DrvObj            !DevExt   ObjectName
  82380690  \Driver\usbhub     82380748  0000007f
> 824277e0  \Driver\usbhub     82427898  USBPDO-2
!DevNode 81f41698 :
  DeviceInst is "USB\Vid_0e0f&Pid_0002\6&63561a1&0&2"
  ServiceName is "usbhub"
---------------------------------------------
lkd> !devstack 821c37e0  
  !DevObj   !DrvObj            !DevExt   ObjectName
  8242b6f8  \Driver\usbccgp    8242b7b0  0000007e
> 821c37e0  \Driver\usbhub     821c3898  USBPDO-1
!DevNode 81f417b8 :
  DeviceInst is "USB\Vid_0e0f&Pid_0003\6&63561a1&0&1"
  ServiceName is "usbccgp"
---------------------------------------------
lkd> !devstack 820392f8
  !DevObj   !DrvObj            !DevExt   ObjectName
> 820392f8  \Driver\usbhub     820393b0  0000007c
  82212030  \Driver\usbuhci    822120e8  USBPDO-0
!DevNode 823d7ee8 :
  DeviceInst is "USB\ROOT_HUB\5&19fe899c&0"
  ServiceName is "usbhub"
---------------------------------------------
---------------------------------------------
lkd> !drvobj usbuhci
Driver object (823ba380) is for:
 \Driver\usbuhci
Driver Extension List: (id , addr)

Device Object list:
82212030  8206e028  
---------------------------------------------
lkd> !devstack 82212030  
  !DevObj   !DrvObj            !DevExt   ObjectName
  820392f8  \Driver\usbhub     820393b0  0000007c
> 82212030  \Driver\usbuhci    822120e8  USBPDO-0
!DevNode 823d7ee8 :
  DeviceInst is "USB\ROOT_HUB\5&19fe899c&0"
  ServiceName is "usbhub"
---------------------------------------------
lkd> !devstack 8206e028  
  !DevObj   !DrvObj            !DevExt   ObjectName
> 8206e028  \Driver\usbuhci    8206e0e0  USBFDO-0
  823f5198  \Driver\ACPI       825cdce8  00000067
  825ace50  \Driver\PCI        825acf08  NTPNP_PCI0040
!DevNode 82095270 :
  DeviceInst is "PCI\VEN_15AD&DEV_0774&SUBSYS_197615AD&REV_00\4&47  b7341&0&0088"
  ServiceName is "usbuhci"
---------------------------------------------
---------------------------------------------
lkd> !drvobj usbccgp
Driver object (823d4170) is for:
 \Driver\usbccgp
Driver Extension List: (id , addr)

Device Object list:
8202b6e8  823c1860  8242b6f8  
---------------------------------------------
lkd> !devstack 8202b6e8  
  !DevObj   !DrvObj            !DevExt   ObjectName
  824da720  \Driver\hidusb     824da7d8  _HID00000001
> 8202b6e8  \Driver\usbccgp    8202b7a0  00000081
!DevNode 81f418e0 :
  DeviceInst is "USB\Vid_0e0f&Pid_0003&MI_01\7&1127f114&0&0001  "
  ServiceName is "HidUsb"
---------------------------------------------
lkd> !devstack 823c1860  
  !DevObj   !DrvObj            !DevExt   ObjectName
  824d8720  \Driver\hidusb     824d87d8  _HID00000000
> 823c1860  \Driver\usbccgp    823c1918  00000080
!DevNode 823ce420 :
  DeviceInst is "USB\Vid_0e0f&Pid_0003&MI_00\7&1127f114&0&0000  "
  ServiceName is "HidUsb"
---------------------------------------------
lkd> !devstack 8242b6f8  
  !DevObj   !DrvObj            !DevExt   ObjectName
> 8242b6f8  \Driver\usbccgp    8242b7b0  0000007e
  821c37e0  \Driver\usbhub     821c3898  USBPDO-1
!DevNode 81f417b8 :
  DeviceInst is "USB\Vid_0e0f&Pid_0003\6&63561a1&0&1"
  ServiceName is "usbccgp"
---------------------------------------------
---------------------------------------------
lkd> !drvobj hidusb
Driver object (82506468) is for:
 \Driver\hidusb
Driver Extension List: (id , addr)
(f87b0402 821d7630)  
Device Object list:
824d5720  81f43720  824da720  824d8720
---------------------------------------------
lkd> !devstack 824d5720  
  !DevObj   !DrvObj            !DevExt   ObjectName
  81f40840  \Driver\Mouclass   81f408f8  PointerClass3
  8237b818  \Driver\mouhid     8237b8d0  
> 824d5720  \Driver\hidusb     824d57d8  00000083
!DevNode 821bf618 :
  DeviceInst is "HID\Vid_0e0f&Pid_0003&MI_01\8&51f168b&0&0000"
  ServiceName is "mouhid"
---------------------------------------------
lkd> !devstack 81f43720  
  !DevObj   !DrvObj            !DevExt   ObjectName
  824d7658  \Driver\Mouclass   824d7710  PointerClass2
  82428818  \Driver\mouhid     824288d0  
> 81f43720  \Driver\hidusb     81f437d8  00000082
!DevNode 821c1618 :
  DeviceInst is "HID\Vid_0e0f&Pid_0003&MI_00\8&28f6544d&0&0000  "
  ServiceName is "mouhid"
---------------------------------------------
lkd> !devstack 824da720  
  !DevObj   !DrvObj            !DevExt   ObjectName
> 824da720  \Driver\hidusb     824da7d8  _HID00000001
  8202b6e8  \Driver\usbccgp    8202b7a0  00000081
!DevNode 81f418e0 :
  DeviceInst is "USB\Vid_0e0f&Pid_0003&MI_01\7&1127f114&0&0001  "
  ServiceName is "HidUsb"
---------------------------------------------
lkd> !devstack 824d8720
  !DevObj   !DrvObj            !DevExt   ObjectName
> 824d8720  \Driver\hidusb     824d87d8  _HID00000000
  823c1860  \Driver\usbccgp    823c1918  00000080
!DevNode 823ce420 :
  DeviceInst is "USB\Vid_0e0f&Pid_0003&MI_00\7&1127f114&0&0000  "
  ServiceName is "HidUsb"
 دونه دونه باید استک ها رو در آورد مثلا یک جا میرسه به ACPI و PCI این یعنی به پایین استک رسیدین از بالا هم رسید به hidsub و یکسری درایور مربوط به موس
خلاصش این درایورا به usb مربوطه

\Driver\hidusb
\Driver\usbccgp
\Driver\usbhub
\Driver\usbuhci
\Driver\ACPI
\Driver\PCI




> و فرمت درخواست يا همان IRP به چه شكلي هست و تصويرش در حافظه را توضيح بديد؟


باید سوالتون رو دقیق تر بپرسین چیکار می خواین بکنین




> ‌ آيا درايور ما بايد حتما به يك سخت افزار متصل باشه؟ خود درايور ما تنها مي تونه توي كرنل اجرا بشه؟


اگه از سخت افزار منظورتون device object نه لزومی به این نیست و درایور تنها هم مشکلی ایجاد نمی کنه




> كلا يك درايو از رجيتسرير چه استفاده اي مي كند؟


استفاده خاصی نمی کنه  :لبخند گشاده!:  
مثلا یک استفده از رجیستری اینه اگه بخواهید یه اطلاعاتی رو بعد از راه اندازی دوباره سیستم نگه دارید. 




> كلا يك درايو از رجيتسرير چه استفاده اي مي كند؟ اگر يك درايور بخواهد به چندين درخواست همزمان پاسخ بدهد،‌تابع هاي Major را چگونه بايد پياده سازي كنيم


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

http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx




> يك كتاب خوب به زبان اصلي و يا فارسي اگر دارين ميشه آپلود كنيد؟


فارسی که نداریم (بجز وبلاگ من  :بامزه:  )
زبان اصلی اینا خوبن (کتاب سوم که در مورد WDF است معماری جدیدتر برای توسعه درایور در ویندوز است)

1. MSDN
2. Programming the Microsoft Windows Driver Model (2nd Edition)
3. Developing Drivers with the Windows Driver Foundation
...

----------


## kernel.programmer

سلام




> پیش نیازهای برنامه نویسی کرنل این مواردی که ذکر کردین نیستن !
>  اینا فقط چندتا ابزارن


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




> برای شروع برنامه نویسی کرنل شما باید اول از همه با معماری ویندوز آشنا باشی، برای آشنایی هم باید کتاب های Windows Internals و RootKit Arsenal رو بخونید ، باید به مباحث مهندسی معکوس آشنایی داشته باشید چون برخی مواقع مجبورید کدتون رو به روش های عجیبی دیباگ کنید و ... در آخر باید برنامه نویس باشین :d


با این جملتون یه بخش هاییش مخالفم و یه جاهاییش مخالفتی نیست.

جاهاییش که به نظرم ایراد داره
اول) Windows Internal  :لبخند گشاده!:  این کتاب که معرفی کردین در مورد معماری ویندوز است ولی در مورد درایور نویسی که نیست. (با اینکه خود من اول این کتاب خوندم بعد رفتم سمت درایور نویسی ولی ... حالا :لبخند: ) 
دوم) Rootkit Arsenal این کتاب هم همینطور یه بخش هایی مستقیم به درایور نویسی و معماری ویندوز وخیل یچیزای دیگه داره ولی برای کسی که نمی خواد کار روتکیتی کنه چی؟ 

اینجوری یکی بخواد C یاد بگیره هم میشه بگیم برو Rootkit Arsenal بخون یا اگه بخواد اسمبلی یاد بگیره می گیم اول برو Bios Disassembly Ninjutsu  رو بخون پایت قوی شد بعد بیا اسمبلی رو ادامه بده  :لبخند: 




> در مورد WDK 8.1 هم اگه اشتباه نکنم همچنان میشه Command line باهاش کامپایل کرد(دقیق یادم نیست)،


نه متاسفانه تو اون ورژن هم نداره باید حتما VS نصب کرد.

----------


## heballi1

ببخشيد توي كجاي windbg اين دستوراتو مينويسيد؟ من امتحان كردم ميگه connect نيست. چه جوري متصل بشم؟

----------


## kernel.programmer

> ببخشيد توي كجاي windbg اين دستوراتو مينويسيد؟ من امتحان كردم ميگه connect نيست. چه جوري متصل بشم؟


این لینک رو ببینید

http://binthought.blog.ir/1393/01/27...dbg%20debugger

----------


## haniyeh.ghassami

سلام
با اینکه این پست خیلی قدیمی هست اما سوالی داشتم. امیدوارم یکی از دوستان پاسخ منو بتونه بده
من کمی این پست را مطالعه کردم . هم چنین درایور نویسی که خود ماکروسافت گفته نیز مطالعه کردم. که بصورت مقدماتی توضیحاتی را داده اند.
مشکا اصلی من درست کردن پورت مجازی به تعداد بی نهایت (255) در ویندوز هست.
می خواهم بتوانم با برنامه نویسی تمام 255 پورت درون ویندوز را فراخوانی و به حالت فعال در بیاورم که در Device Manager قابل مشاهده باشند. سورس خود ماکروسافت را دانلود کردم . در ان فایل مربوط به سریال پورت و نال مودم و ویرچوال پورت نیز می باشد، اما کدها خطا میدهند. WDK8.1 Visual Studio 2013 Ultimate را در سیستم نصب دارم.
چون اموزش های ماکروسافت روی ویژوال 2015 حرفه ای هست تصمیم گرفتم نسخه 2015 حرفه ای را خریداری کنم تا شاید خطاهای کد های اماده ماکروسافت از بین برود.
ایا کسی راجب درایور نویسی برای پورت سریال اشنایی دارد؟
من یک مرجع ( کتاب یا ..) برای اموزش کدنویسی درایور در ویژوال استدیو نیاز دارم.
ممنون میشوم من را راهنمایی کنید


                                                                                                                                                     با تشکر

----------

