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

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

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

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

آخرین نظرات

نوشتن یک File System Filter Driver

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

در ادامه سری آموزش هایی که در مورد درایور نویسی در ویندوز نوشته ام، این بار آموزشی در مورد نوشتن یک Filesystem Filter Driver آماده کرده ام. از عنوان آموزش مشخص است که این درایور از نوع Filter Driver است که قبلا توضیح مختصری در مورد این نوع درایورها در آموزش مربوط به Device Stack داده ام، این درایور قرار است Filesystem هایی که روی سیستم رجیستر شده یا بعدا رجیستر می شوند را مانیتور کند. در انتهای این آموزش بخشی اختصاص داده ام در مورد کار با WinDbg. تمام فرمان هایی که در این بخش آمده را قبلا در آموزش مربوط به Device Stack در موردشان توضیح داده ام. دلیل آوردن چنین بخشی این بود که قسمت هایی از این آموزش هست که به موضوعات دیگری و مفصلی مرتبط هستند. برای اینکه وارد این توضیحات مفصل نشوم ترجیح دادم با نشان دادن آن مساله در WinDbg مساله را برایتان روشن کنم. 

قبل ادامه مروری هم روی آموزش های زیر داشته باشید.

سرفصل های این آموزش

مقدمه

همانطور که بالاتر اشاره کرده ام در این آموزش قرار است یک Filter Driver بنویسیم. این Filter Driver در واقع یک Upper Filter Driver است. همانطور که در آموزشهای قبلی گفته بودم این نوع درایور قبل یک Function Driver قرار می گیرد.  در آموزش ما Function Driver هایی که ما با آنها سروکار داریم مربوط به Filesystem ها می شوند. هدف از نوشتن این فیلتر درایور این است که بتوانیم تمام درخواست هایی که به یک Filesystem فرستاده میشود را دریافت کنیم. این درخواست ها برای مثال می تواند ایجاد (Create)، خواندن (Read)، نوشتن (Write) یک فایل روی یک Filesystem باشد. ما در این مثال بعد دریافت همه درخواست ها فقط درخواست های ایجاد (Create) را با DbgPrint به بافر Debug می فرستیم که بعد با برنامه DbgView بتوانیم این درخواست ها را مشاهده کنیم.

پایین تصویری است از Stack یکسری Filesystem رجیستر شده روی یک سیستم. این Filesystem ها عبارتند از NTFS, UDFS, MUP, .... درایور ما اصطلاحا باید به Stack یک Filesystem متصل (Attach) شود. همانطور که می بینید درایور ما که به رنگ قرمز مشخص شده در بالای Stack هر کدام از Filesystem ها قرار گرفته. 

Device Stack

اگر خواستین روی آموزشهای قبلی که در مورد Device Stack و فرمان های WinDbg بود مروری داشته باشید می توانید، به انتهای این آموزش بخش "کار با Windbg - قسمت اول" رجوع کنید.

مفهوم Stack Location

یکسری مفاهیم است که لازمه قبل از ادامه آموزش آنها را توضیح بدهم. یکی از این مفاهیم Stack Location است. در آموزش های قبلی گفته بودم که یک ساختار  IO_STACK_LOCATION داریم که همراه ساختار IRP است که وقتی برنامه سطح کاربر یک درخواست دارد (مثلا خواندن محتویات یک فایل) پارامترهای مربوط به این درخواست داخل این ساختار قرار می گیرد. لازم به ذکر است که استفاده از این ساختار فقط محدود به برنامه سطح کاربر نیست، و در ارتباط درایور با یک درایور دیگر نیز این ساختار مورد استفاده قرار می گیرد.

بطور دقیقتر هر ساختار IRP می تواند آرایه ای از ساختار IO_STACK_LOCATION به همراه خود داشته باشد. این آرایه دقیقا بعد از ساختار IRP قرار گرفته است. (مانند تصویر زیر). 

IRP and Stack Location

دلیل داشتن آرایه ای از IO_STACK_LOCATION این است که یک IRP از Stack ای از درایورها می گذرد. در نتیجه به ازای هر درایور در مسیر IRP یک IO_STACK_LOCATION وجود دارد. تعداد Stack Location ها از عضو داده ای به نام StackSize مربوط به بالاترین Device Object داخل Stack مشخص می شود. 

از عضو داده های این ساختار که در آموزش های قبلی دیده ایم، ما با Parameters بیشتر سروکار داشته ایم (رجوع به اینجا و اینجا). در این آموزش هم در مورد عضو داده دیگری به نام CompletionRoutine صحبت خواهیم کرد.

درایور می تواند با ماکروی زیر به ساختار Stack Location خود دسترسی داشته باشد.

  PIO_STACK_LOCATION irpSp;

  irpSp = IoGetCurrentIrpStackLocation( Irp );

یک درایور می تواند از پردازش یک درخواست (IRP) صرف نظر کند (Skip). این کار را با این ماکرو انجام می دهد. در این حالت ما نباید هیچ عضو داده ای از Stack Location را تغییر دهیم. 

  IoSkipCurrentIrpStackLocation( Irp );

اگر بخواهیم تغییر در Stack Location بدهیم. مثلا بخواهیم عضو داده CompletionRoutine را مقدار بدهیم این ماکروها را صدا می زنیم. در واقع اول یک کپی از Stack Location می گیریم و بعد CompletionRoutine را مقدار می دهیم.

  IoCopyCurrentIrpStackLocationToNext( Irp );
  IoSetCompletionRoutine( Irp, CompletionRoutine, ...);

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

مفهوم Fast I/O

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

داخل ساختار DRIVER_OBJECT یک عضو داده به نام FastIoDispatch وجود دارد. این عضو داده خود یک ساختار است با این محتویات

typedef struct _FAST_IO_DISPATCH
{
    ULONG32  SizeOfFastIoDispatch;
    PVOID FastIoCheckIfPossible;
    PVOID FastIoRead;
    PVOID FastIoWrite;
    PVOID FastIoQueryBasicInfo;
    PVOID FastIoQueryStandardInfo;
    PVOID FastIoLock;
    PVOID FastIoUnlockSingle;
    PVOID FastIoUnlockAll;
    PVOID FastIoUnlockAllByKey;
    PVOID FastIoDeviceControl;
    PVOID AcquireFileForNtCreateSection;
    PVOID ReleaseFileForNtCreateSection;
    PVOID FastIoDetachDevice;
    PVOID FastIoQueryNetworkOpenInfo;
    PVOID AcquireForModWrite;
    PVOID MdlRead;
    PVOID MdlReadComplete;
    PVOID PrepareMdlWrite;
    PVOID MdlWriteComplete;
    PVOID FastIoReadCompressed;
    PVOID FastIoWriteCompressed;
    PVOID MdlReadCompleteCompressed;
    PVOID MdlWriteCompleteCompressed;
    PVOID FastIoQueryOpen;
    PVOID ReleaseForModWrite;
    PVOID AcquireForCcFlush;
    PVOID ReleaseForCcFlush;
} FAST_IO_DISPATCH, *PFAST_IO_DISPATCH;

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

تحلیل کد درایور

درایوری که برای این آموزش انتخاب کرده ام،‌ یکی از نمونه درایورهای WDK (نسخه های قدیمی ترش) است به نام sfilter. این کد برای ویندوزهای 2000 به بالا نوشته شده و همچنین موضوعات مختلفی را پوشش داده است. من برای اینکه پیچیدگی این کد را کمتر کنم خیلی قسمت ها را از کد حذف و همچنین کد را به چند سورس فایل کوچکتر تقسیم کرده ام. لینک هر دو کد را اینجا قرار می دهم اگر خواستین کد اصلی را هم داشته باشید.

تحلیل کد تابع DriverEntry

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

اول ایجاد یک Device Object

    RtlInitUnicodeString( &nameString, L"\\FileSystem\\Filters\\SFilter" );

    status = IoCreateDevice( DriverObject,
                             0,
                             &nameString,
                             FILE_DEVICE_DISK_FILE_SYSTEM,
                             FILE_DEVICE_SECURE_OPEN,
                             FALSE,
                             &gSFilterControlDeviceObject );

در مورد پارامتر های بالا قبلا در آموزش دوم توضیحاتی داده ام و فقط پارامتر چهارم تغییر کرده. این پارامتر در واقع DeviceType بود که به FILE_DEVICE_DISK_FILE_SYSTEM مقدار داده ایم که نشان می دهد نوع درایور از نوع Filesystem است.

دوم مقدار دادن آرایه MajorFunction

از همه مقادیر این آرایه فقط IRP_MJ_CREATE برای ما مهم است و باقی ایندکس ها که Irp مربوط به آنها را نمی خواهیم پردازش کنیم با SfPassThrough مقدار داده ایم. 

    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {

        DriverObject->MajorFunction[i] = SfPassThrough;
    }

    DriverObject->MajorFunction[IRP_MJ_CREATE] = SfCreate;

توضیح تابع SfPassThrough

این تابع را به صورت زیر تعریف کرده ایم و کارش این است که اولا، از پردازش درخواست صرف نظر کند با IoSkipCurrentIrpStackLocation و دوما، Irp را با به درایور پایین تر از خود در Stack می فرستد.

NTSTATUS
SfPassThrough (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
  ...
  ...

  IoSkipCurrentIrpStackLocation( Irp );

  return IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject, 
                         Irp )
}

تابع IoCallDriver که در کد بالا استفاده کرده ایم به صورت زیر تعریف شده است. کار این تابع ارسال یک Irp به یک درایور دیگر است.

NTSTATUS IoCallDriver(
  _In_    PDEVICE_OBJECT DeviceObject,
  _Inout_ PIRP           Irp
);

توضیح پارامترهای این تابع به نظرم خیلی واضح است ولی با این حال یک توضیحی می دهیم.

DeviceObject: مربوط به درایوری است که می خواهیم Irp را به آن بفرستیم. اگر یادتان باشد قبلا گفته ام برای ارتباط با یک درایور باید با Device Object ای که آن درایور ایجاد کرده ارتباط برقرار کنیم.

Irp: این هم که معلوم است Irp مربوطه که قرار است ارسال شود.

حالا در مثالی که زدم این Device Object از کجا آمده، این مطلب را جلوتر وقتی به Attach شدن به یک Device Object و توضیح ساختا SFILTER_DEVICE_EXTENSION رسیدیم شرح خواهم داد.

توضیح تابع SfCreate

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

NTSTATUS
SfCreate (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
  NTSTATUS status;

  PAGED_CODE();
if (IS_MY_CONTROL_DEVICE_OBJECT(DeviceObject)) { /**** (1) شرط اول *****/ Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Information = 0; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return STATUS_INVALID_DEVICE_REQUEST; } ... if (!FlagOn( SfDebug, SFDEBUG_DO_CREATE_COMPLETION | SFDEBUG_GET_CREATE_NAMES| SFDEBUG_DISPLAY_CREATE_NAMES )) { /**** (2) شرط دوم ****/
IoSkipCurrentIrpStackLocation( Irp );
return IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject, Irp ); } else { /**** (3) شرط سوم ****/ ...
...
IoCopyCurrentIrpStackLocationToNext( Irp ); IoSetCompletionRoutine( Irp, SfCreateCompletion, &waitEvent, TRUE, TRUE, TRUE ); status = IoCallDriver( ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject, Irp ); ... ... return status; } }

 این تابع کی صدا زده می شود،؟وقتی یک برنامه سطح کاربر یک فایل بخواهد ایجاد/باز (Create/Oprn) کند (البته باز اشاره کنم فرستادن یک درخواست فقط محدود به برنامه سطح کاربر نمی شود). یادتان باشد که ما تمام در خواست های ایجاد یا باز کردن فایل روی سیستم را خواهیم گرفت. یعنی هر برنامه ای بخواهد یک فایل ایجاد کند در این تابع ما درخواست را دریافت میکنیم. (خیلی از Antivirus ها و IDS ها یک درایور دارند که دقیقا از همین تکنیک برای مانیتور کردن سیستم استفاده می کنند. شاید یکم با جزئیات بیشتر ولی تکنیک همین است). 

حالا تابع ما چکار می کند. در واقع بر اساس سه شرط کارهای مختلفی انجام می دهیم

  1. (شرط اول): اگر درخواست به Device Object درایور ما بود یعنی همانی که در DriverEntry ساخته ایم. همینجا در خواست را خاتمه می دهیم. اگر یادتان باشد در آموزش دوم هم کدی مشابه این داشتیم.
  2. (شرط دوم): این درایور یک امکانی داخل خودش پیاده کرده است. در واقع از طریق یک کلید رجیستری ما می توانیم پیام هایی که در بافر Debug چاپ می کنیم کنترل کنیم. اینجا دراین شرط گفتیم اگر هیچ کدام از مقدارهای SFDEBUG_DO_CREATE_COMPLETION یا SFDEBUG_GET_CREATE_NAMES یا SFDEBUG_DISPLAY_CREATE_NAMES تعریف نشده دستورات این شرط را اجرا کند (در مورد این کلید رجیستری و این مقدارها جلوتر توضیح داده ام). حالا این دستورات چکار می کنند؟ اول اینکه از Stack Location صرف نظر و درخواست را به درایور پایین Stack ارسال می کند.
  3. (شرط سوم): اگر یکی از مقادیری که اشاره کردیم تعریف شده بود دستورات این شرط اجرا می شوند. این دستورات چکار می کنند؟ یک CompletionRoutine برای درایور ما تعریف می کند. چطوری؟ بالاتر گفته بودیم که ساختار IO_STACK_LOCATION یک عضو داده دارد به نام CompletionRoutine که یک اشاره گر به تابع است. برای اینکه بتوانیم عضو داده های Stack Location را تغییر دهیم گفتیم اول از ماکروی IoCopyCurrentIrpStackLocationToNext و بعد برای تعریف یک CompletionRoutine از ماکروی IoSetCompletionRoutine استفاده میکنیم. تابع CompletionRoutine ای که در درایور خود تعریف کرده ایم (تابع SfCreateCompletion) فقط یک Event را به حالت signal در می آورد (با تابع KeSetEvent). به این طریق متوجه می شویم که پردازش درخواست Irp به پایان رسیده و همچنین تابعی که تعریف کرده ایم توسط I/O Manager صدا زده شده است.

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

در مورد مقدار دادن عضو داده CompletionRoutine زیاد توضیح داده ام ولی در مورد اینکه کی این تابع صدا زده می شود هنوز توضیحی نداده ام. این تابع کی صدا زده می شود؟ وقتی یکی از درایورهای داخل Stack با تابع IoCompleteRequest مشخص کند که کارش با درخواست به پایان رسیده است. در این حالت I/O Manager شروع می کند تمام درایورهای داخل Stack را پیمایش می کند و در صورتی که تابع CompletionRoutine در درایور تعریف شده باشد آن را صدا می زند.

از آنجایی که این تابع در IRQL <= DISPATCH_LEVEL اجرا می شود (IRQL در سطح DPC و پایین تر،‌ برای اطلاعات بیشتر در مورد IRQL به این آموزش رجوع کنید ).

اولا) تابع ممکن است در IRQL سطح DPC اجرا بشود،‌ در نتیجه کد آن ناحیه باید از نوع Nonpaged باشد (یعنی باید همیشه در حافظه باقی بماند). چطور مطمعن بشیم کد آن تابع از نوع Nonpaged باشد؟ در واقع بطور پیشفرض حافظه مربوط به کدهای درایور بصورت Nonpaged است مگر اینکه خودمان این مساله را تغییر بدهیم. چطور؟ با استفاده از کدهای زیر که در ابتدای فایل sfilter.c وجود دارد.

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)

#if DBG
#pragma alloc_text(PAGE, DriverUnload)
#endif

#pragma alloc_text(PAGE, SfFsNotification)
#pragma alloc_text(PAGE, SfCreate)
#pragma alloc_text(PAGE, SfCleanupClose)
...
...
#endif

دوما) تابع ممکن است در IRQL سطوح پایین تر از DPC اجرا بشود، در نتیجه در انتخاب توابع باید دقت کنید که این توابع قابل اجرا در سطوح IRQL پایین تر از DPC نیز باشند.

مقدار دهی توابع مربوط به Fast I/O

بالاتر در مورد Fast I/O توضیح داده ام. در این مرحله باید عضو داده FastIoDispatch که یک ساختار از نوع FAST_IO_DISPATCH است و داخل ساختار DRIVER_OBJECT قرار دارد را مقدار دهی کنیم.

  fastIoDispatch = ExAllocatePoolWithTag( NonPagedPool, 
                                          sizeof( FAST_IO_DISPATCH ), 
                                          SFLT_POOL_TAG );

  if (!fastIoDispatch) {

    IoDeleteDevice( gSFilterControlDeviceObject );
    return STATUS_INSUFFICIENT_RESOURCES;
  }

  RtlZeroMemory( fastIoDispatch, sizeof( FAST_IO_DISPATCH ) );

  fastIoDispatch->SizeOfFastIoDispatch = sizeof( FAST_IO_DISPATCH );
  fastIoDispatch->FastIoCheckIfPossible = SfFastIoCheckIfPossible;
  fastIoDispatch->FastIoRead = SfFastIoRead;
  ...
  ...

  DriverObject->FastIoDispatch = fastIoDispatch;

این کد یک حافظه Nonpaged به اندازه ساختار FAST_IO_DISPATCH با استفاده از تابع ExAllocatePoolWithTag تخصیص میکند. و جلوتر این حافظه را با توابعی که قبلا در فایل fastio.c تعریف کرده ایم مقداردهی می کنیم، و در نهایت این مغییر را درون DriverObject->FastIoDispatch قرار می دهیم.

همه توابعی که تعریف کرده ایم یک کار یکسان را انجام می دهند. کاری که این توابع انجام می دهند صدا زدن تابع FastIoDispatch مشابه در درایور پایین Stack خود است . من برای نمونه تابع SfFastIoRead را اینجا قرار داده ام.

BOOLEAN
SfFastIoRead (
    IN PFILE_OBJECT FileObject,
    IN PLARGE_INTEGER FileOffset,
    IN ULONG Length,
    IN BOOLEAN Wait,
    IN ULONG LockKey,
    OUT PVOID Buffer,
    OUT PIO_STATUS_BLOCK IoStatus,
    IN PDEVICE_OBJECT DeviceObject
    )
{
  PDEVICE_OBJECT nextDeviceObject;
  PFAST_IO_DISPATCH fastIoDispatch;

  PAGED_CODE();

  if (DeviceObject->DeviceExtension) {

    ASSERT(IS_MY_DEVICE_OBJECT( DeviceObject ));

    nextDeviceObject = ((PSFILTER_DEVICE_EXTENSION) DeviceObject->DeviceExtension)->AttachedToDeviceObject;
    ASSERT(nextDeviceObject);

    fastIoDispatch = nextDeviceObject->DriverObject->FastIoDispatch;

    if (VALID_FAST_IO_DISPATCH_HANDLER( fastIoDispatch, FastIoRead )) {

      return (fastIoDispatch->FastIoRead)(
                  FileObject,
                  FileOffset,
                  Length,
                  Wait,
                  LockKey,
                  Buffer,
                  IoStatus,
                  nextDeviceObject );
      }
  }
  return FALSE;
}

مطلع شدن از Register/Unregister شدن Filesystem ها

بعد همه کارهایی که بالا گفتیم می رسیم به مرحله نهایی که مطلع شدن از Filesystem هایی که روی سیستم رجیستر شده اند یا بعد رجیستر می شوند. این کار را با تابع زیر انجام می دهیم.

  status = IoRegisterFsRegistrationChange( DriverObject, SfFsNotification );

به طور دقیقتر ما یک تابع برای سیستم عامل تعریف می کنیم که اینجا تابع ما SfFsNotification است، که هر زمان یک درایور خود را به عنوان یک Filesystem با استفاده از تابع IoRegisterFileSystem رجیستر کند یا خود را با تابع IoUnregisterFileSystem حذف کند، تابعی که تعریف کرده ایم صدا زده می شود. همانطور که قبلا گفتم برای Filesystem هایی که قبلا رجیستر شده اند نیز تابع که تعریف کرده ایم صدا زده می شود.

نکته: استفاده از تابع IoRegisterFsRegistrationChange حتما باید بعد از تمام کارهایی باشد که بالاتر اشاره کردم.

مقدار دهی DriverUnload

مساله DriverUnload چیز جدیدی نیست و در آموزش های پیشین مثال هایی دیده ایم که چطور DriverObject->DriverUnload را مقدار دهی کنیم. اما در مورد فیلتر درایور یک نکته وجود دارد‌، برای فیلتر درایور مرسوم نیست که تابع  DriverUnload تعریف بشود. اگر هم میخواهید تعریف کنید بهتر است فقط برای تست باشد و نه در یک محصول جدی. بر همین اساس کد درایور ما عبارت DBG را بررسی میکند اگر معتبر بود تابع DriverUnload مقدار دهی می شود،‌ و اگر نه که هیچ اتفاقی نمی افتد. این کدی است که ما برای رسیدن به این مقصود نوشته ایم.

#if DBG
  gSFilterDriverObject->DriverUnload = DriverUnload;
#endif

برای تعریف عبارت DBG هم کافی است این خط را به فایل sources اضافه کنیم که من به طور پیش فرض این مقدار را اضافه کرده ام.

C_DEFINES= $(C_DEFINES) -DDBG=1

بررسی کد این تابع را به عهده خودتان می گذارم لبخند. واضح  است که باید عکس تمام کارهایی را که کرده ایم انجام دهیم. یعنی اول تابع Notification ای که روی سیستم ثبت کردیم برای مطلع شدن از Register/Unregister شدن یک Filesystem را از سیستم حذف کنیم. این کار با تابع IoUnregisterFsRegistrationChange انجام می شود. و شروع کنیم تمام Device Object هایی که Attach کرده ایم را Detach کنیم. 

خواندن کلید رجیستری درایور

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

این کدی است که داخل DriverEntry صدا زده می شود

  SfReadDriverParameters( RegistryPath );

در این تابع ما چکار می کنیم؟ از کلید رجیستری زیر

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\sfilter

مقدار DebugFlags را می خوانیم،‌ این مقدار را باید خودمان ایجاد کنیم و آن را با یک عدد مقدار دهی کنیم. که این مقدار از OR کردن مقادیر زیر بدست می آید.

#define SFDEBUG_DISPLAY_ATTACHMENT_NAMES    0x00000001  //display names of device objects we attach to
#define SFDEBUG_DISPLAY_CREATE_NAMES        0x00000002  //get and display names during create
#define SFDEBUG_GET_CREATE_NAMES            0x00000004  //get name (don't display) during create
#define SFDEBUG_DO_CREATE_COMPLETION        0x00000008  //do create completion routine, don't get names

در جاهای مختلف کد بر اساس مقادیری که از کلید رجیسری ذکر شده است خروجی های DbgPrint کنترل می شود. اینطوری می توانیم بگوییم فقط Register/Unregter شدن Filesystem گزارش (در بخش های پایینی در این مورد توضیح داده ام) شود، یا فقط گزارش اینجا/باز کردن فایل ها را داشته باشیم، یا ترکیبی از این مقدار ها باشد (OR بشوند). 

تحلیل کد تابع SfFsNotification

کل کاری که این تابع باید انجام دهد این است که بررسی کند آیا درایور مربوط به Filesystem می خواهد خود را Register کند یا Unregister. کد زیر نشان می دهد که چگونه این تابع را پیاده سازی کنیم.

VOID
SfFsNotification (
    IN PDEVICE_OBJECT DeviceObject,
    IN BOOLEAN FsActive
    )
{
  ...
  ...

  if (FsActive) {
    SfAttachToFileSystemDevice( DeviceObject, &name );

  } else {
SfDetachFromFileSystemDevice( DeviceObject ); } }

تحلیل کد تابع SfAttachToFileSystemDevice

برای Attack شدن به Device Object یک درایور چه مراحلی باید انجام شوم؟

اول ایجاد یک Device Object

با تابع IoCreateDevice یک Device Object می سازیم،‌ در واقع به ازای هر Filesystem ای که رجیستر می شود ما باید این کار را انجام دهیم. برای درک بهتر به شکلی که اول آموزش قرار دادم  مراجعه کنید،‌ Upper Filter که به رنگ قرمز مشخص شده در واقع همین Device Object ای است که اینجا می سازیم. البته در این مرحله هنوز به Stack مربوطه اضافه نشده است.

  PDEVICE_OBJECT newDeviceObject;

...
...

status = IoCreateDevice( gSFilterDriverObject, sizeof( SFILTER_DEVICE_EXTENSION ), NULL, DeviceObject->DeviceType, 0, FALSE, &newDeviceObject ); if (!NT_SUCCESS( status )) { return status; }

ایجا در مورد پارامترها یکسری نکته وجود دارد. 

پارامتر دوم DeviceExtensionSize: در آموزش های قبلی این پاراتر را صفر مقدار می دادیم اما این بار اندازه ساختاری به نام SFILTER_DEVICE_EXTENSION را داده ایم. این ساختاری است که درایور نویس میتواند تعریف کند و این تابع فقط حافظه ای برای این ساختار که ما اندازه آن را داده ایم تخصیص می کند. این حافظه از طریق DeviceObject->DeviceExtension قابل دسترسی است. هدف از وجود چنین ساختاری نگهداری یکسری اطلاعات است که درایور نویس میخواهد همیشه همراه یک Device Object باشد. پایین تر وقتی به Attach به یک Device Object برسیم این مساله را بیشتر شرح می دهم.

پارامر سوم DeviceName: در فیلتر درایور دیگر احتیاجی به تعریف نام (Nt Device Name) نداریم. در نتیجه NULL مقدار دهی میکنیم.

پارامتر چهارم DeviceType: نوع Device Object را هم از Device Object مربوط به Filesystem می گیریم.

دوم مقدار دهی Flags

در این مرحله عضو داده Flags مربوط به Device Object ای که ایجاد کرده ایم را با مقدادیر Flags مربوط به Filesystem رجیستر شده مقدار دهی می کنیم.

  if ( FlagOn( DeviceObject->Flags, DO_BUFFERED_IO )) {

      SetFlag( newDeviceObject->Flags, DO_BUFFERED_IO );
  }

  if ( FlagOn( DeviceObject->Flags, DO_DIRECT_IO )) {

      SetFlag( newDeviceObject->Flags, DO_DIRECT_IO );
  }

  if ( FlagOn( DeviceObject->Characteristics, FILE_DEVICE_SECURE_OPEN ) ) {

      SetFlag( newDeviceObject->Characteristics, FILE_DEVICE_SECURE_OPEN );
  }

سوم Attach شدن به Filesystem

در این مرحله به Stack مربوط به Filesystem اصطلاحا Attach می شویم. این تابع Stack مربوط به Filesystem ای که رجیستر شد را رو به بالا پیمایش می کند و Device Object ای را که ایجاد کرده بودیم را به بالاترین Device Object این Stack اضافه میکند.

  devExt = newDeviceObject->DeviceExtension;

  status = IoAttachDeviceToDeviceStackSafe( newDeviceObject, 
                                            DeviceObject, 
                                            &devExt->AttachedToDeviceObject );

تابع بالا بصورت زیر تعریف شده

NTSTATUS IoAttachDeviceToDeviceStackSafe(
  _In_  PDEVICE_OBJECT SourceDevice,
  _In_  PDEVICE_OBJECT TargetDevice,
  _Out_ PDEVICE_OBJECT *AttachedToDeviceObject
);

توضیح پارامترها:

SourceDevice: این پارامتر Device Object ای است که ما با IoCreateDevice بعد رجیستر شدن Filesystem ایجاد کردیم.

TargetDevice: این پارامتر Device Object مربوط به Filesystem است. در واقع این تابع از این گره روی Stack شروع به پیمایش می کند تا به بالای Stack برسد.

AttachedToDeviceObject: این پارامتر باید آدرس SourceDevice->DeviceExtension->AttachedToDeviceObject باشد. ما قبلا از طریق تابع IoCreateDevice اندازه ساختار DeviceExtension را داده بودیم و I/O Manager برای ما حافظه به اندازه ساختار مربوطه تخصیص کرده است. چون این ساختار را درایور نویس تعریف میکند هر عضو داده ای میتواند داشته باشد فقط در مورد فیلتر درایور ها این ساختار حتما باید عضو داده ای به نام AttachedToDeviceObject نیز داشته باشد. حالا این تابع چه مقداری داخل این عضو داده نگه می دارد؟ تابع بعد از پیمایش Stack آخرین Device Object ای که روی Stack پیدا کرده را در این عضو داده قرار می دهد. در واقع این Device Object درایور پایین تر ما می شود. در جاهای مختلف درایور خود ما لازم داریم درخواست Irp ای را به درایور پایین خود ارسال کنیم، در نتیجه لازم است Device Object درایور پایین خود را یک جا نگه داری کنیم.

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

typedef struct _SFILTER_DEVICE_EXTENSION {

    //
    //  Pointer to the file system device object we are attached to
    //

    PDEVICE_OBJECT AttachedToDeviceObject;

    //
    //  Pointer to the real (disk) device object that is associated with
    //  the file system device object we are attached to
    //

    PDEVICE_OBJECT StorageStackDeviceObject;

    //
    //  Name for this device.  If attached to a Volume Device Object it is the
    //  name of the physical disk drive.  If attached to a Control Device
    //  Object it is the name of the Control Device Object.
    //

    UNICODE_STRING DeviceName;

    //
    //  Buffer used to hold the above unicode strings
    //

    WCHAR DeviceNameBuffer[MAX_DEVNAME_LENGTH];

} SFILTER_DEVICE_EXTENSION, *PSFILTER_DEVICE_EXTENSION;

پاک کردن فلگ DO_DEVICE_INITIALIZING

بعد از Attach شدن به Stack لازم است فلگ DO_DEVICE_INITIALIZING از روی Device Object پاک شود. این فلگ را تابع IoCreateDevice روی DeviceObject جدید مقدار دهی می کند. اگر این فلگ بعد از Attach شدن پاک نشود دیگر هیچ درایوری نمی تواند به Stack مربوط به Filesystem ذکر شده Attach شود (اجرای تابع IoAttachDeviceToDeviceStackSafe ناموفق خواهد بود)

برای حل این مساله از کد زیر استفاده کرده ایم.

  ClearFlag( newDeviceObject->Flags, DO_DEVICE_INITIALIZING )

Attach شدن به Volume های یک Filesystem

بعد از Attach شدن به Filesystem حالا نوبت به Attach شدن به Volume های Filesystem مربوطه می شود. تابعی تعریف کرده ایم به نام SfEnumerateFileSystemVolumes که این کار را برای ما انجام می دهد.

  status = SfEnumerateFileSystemVolumes( DeviceObject, &fsName );

  if (!NT_SUCCESS( status )) {
    IoDetachDevice( devExt->AttachedToDeviceObject );
    goto ErrorCleanupDevice;
  }

تحلیل کد تابع SfEnumerateFileSystemVolumes

این تابع کار اصلیش این است که تمام Device Object هایی که درایور مربوط به Filesystem مذکور ایجاد کرده استخراج کند و بر اساس یکسری شرایط آن هایی که مورد نظر ما هستند به آنها Attach شود.

مراحلی که این تابع انجام می دهد

اول استخراج تمام Device Object های Filesystem

در دو مرحله تابع IoEnumerateDeviceObjectList ار اجرا می کنیم تا لیست Device Object ها بدست آید. این دو بار اجرا کردن به این دلیل است که بار اول تعداد Device Object ها بدست می آید، حافظه تخصیص می شود، و بار دوم اطلاعات مربوط به Device Object ها گرفته می شود.

  /* اجرای بار اول برای گرفتن تعداد آبجکت ها */

status = IoEnumerateDeviceObjectList ( FSDeviceObject->DriverObject, NULL, 0, &numDevices); if (!NT_SUCCESS( status )) { ASSERT(STATUS_BUFFER_TOO_SMALL == status); numDevices += 8; //grab a few extra slots
/* تحصیص حافظه */ devList = ExAllocatePoolWithTag( NonPagedPool, (numDevices * sizeof(PDEVICE_OBJECT)), SFLT_POOL_TAG ); if (NULL == devList) { return STATUS_INSUFFICIENT_RESOURCES; }
/* گرفتن اطلاعات از آبجکت ها */ status = IoEnumerateDeviceObjectList ( FSDeviceObject->DriverObject, devList, (numDevices * sizeof(PDEVICE_OBJECT)), &numDevices); if (!NT_SUCCESS( status )) { ExFreePool( devList ); return status; } ... ... }

بعد گرفتن این اطلاعات داخل یک حلقه این Device Object ها بررسی می شوند اگر با یکسری شرایط مطابق بودند بهآن  Attach می شویم

شرایط بررسی Device Object ها

اول) بررسی می کنیم در صورت برقرار بودن شرایط زیر از Attach شدن به Device Object صرف نظر می کنیم

  1. Device Object برابر Device Object مربوط به Filesystem مذکور باشد، 
  2. نوع Device Object با نوع Device Object مربوط به Filesystem مطابق نباشد.
  3. بررسی میکند که قبلا به Stack مربوط به Device Object گرفته شده Attach شده ایم یا خیر.
  if ((devList[i] == FSDeviceObject) ||
      (devList[i]->DeviceType != FSDeviceObject->DeviceType) ||
      SfIsAttachedToDevice( devList[i], NULL )) {

    leave;
  }

دوم) اینجا یک تابع استفاده کرده ایم که خروجی به ما یک رشته بر می گرداند. اگر رشته مقدار داشت (طولش بزرگتر از صفر بود) از Attack شدن به Device Object صرف نظر میکنیم، چون Object Device مربوط به Volume که اینجا Enumerate شده قالبا بی نام هستند. حالا تابع SfGetBaseDeviceObjectName چکار میکند؟ اول بگم به سورس کد رجوع کنید و کد کامل این تابع را ببینید. این تابع اول، با استفاده از تابع IoGetDeviceAttachmentBaseRef پایینترین Device Object در Stack مربوط به Device Object ای که Enumerate کردیم را به ما بر می گرداند. (در خیلی از موارد همین Device Object پایین ترین گره در Stack است). دوم، تابع SfGetObjectName را اجرا میکند که این تابع نیز ObQueryNameString را اجرا میکند تا نام Object را بدست آورد. انتهای همین آموزش بخش "کار با WinDbg - قسمت دوم" نشان داده ام با فرمان های WinDbg چطور نام یک Object را بدست آورید.

  SfGetBaseDeviceObjectName( devList[i], Name );

  if (Name->Length > 0) {

    leave;
  }

سوم) بررسی میکنیم اگر هیچ Disk Device Object با این Device Object مرتبط نیست از Attach شدن به Device Object صرف نظر میکنیم. در صورت وجود Disk Device Object این مقدار داخل storageStackDeviceObject قرار می گیرد. به انتهای آموزش بخش "کار با WinDbg - قسمت سوم" بروید تا ببینید دقیقا این تابع چه ساختارهایی را و در نهایت چه مقداری به عنوان Disk Device Object بر می گرداند.

  status = IoGetDiskDeviceObject (devList[i], 
                                  &storageStackDeviceObject );

  if (!NT_SUCCESS( status )) {

    leave;
  }

دوم Attach شدن به Volume

در نهایت برای Attach شدن به Volume تابع SfAttachToMountedDevice را صدا می زنیم. Attach شدن به یک Volume مانند Attach شدن به Filesystem است که بالا توضیح داده ام. فقط این تابع سعی میکند در ۸ مرتبه اقدام به Attach شدن به Volume کند. چرا؟ چون Mount شدن یک Volume روی یک سیستم کمی زمان می برد. 

تحلیل کد تابع SfDetachFromFileSystemDevice

در بخش های قبل تر بررسی کردیم اگر یک Filesystem روی سیستم Register شد چه کارهایی باید انجام دهیم. حالا اگر یک Filesystem از روی سیستم Unregister شود چه باید کنیم؟

در این حالات کار ما خیلی ساده تر است. فقط کافیه از Device Object ای که داریم شروع کنیم به پیمایش و هر جا به Device Object مربوط به درایور خود رسیدیم. تابع IoDetachDevice اجرا و بعد Device Object را نیز با IoDeleteDevice پاک کنیم.

VOID
SfDetachFromFileSystemDevice (
    IN PDEVICE_OBJECT DeviceObject
    )
{
  PDEVICE_OBJECT ourAttachedDevice;
  PSFILTER_DEVICE_EXTENSION devExt;

  PAGED_CODE();

  ourAttachedDevice = DeviceObject->AttachedDevice;

  while (NULL != ourAttachedDevice) {

    if (IS_MY_DEVICE_OBJECT( ourAttachedDevice )) {

      devExt = ourAttachedDevice->DeviceExtension;

      ...
      ...

      SfCleanupMountedDevice( ourAttachedDevice );
      IoDetachDevice( DeviceObject );
      IoDeleteDevice( ourAttachedDevice );

      return;
    }

    DeviceObject = ourAttachedDevice;
    ourAttachedDevice = ourAttachedDevice->AttachedDevice;
  }
}

کار با WinDbg

قسمت اول

لیست گرفتن Filesystem های رجیستر شده روی سیستم

kd> !object \FileSystem
Object: 88e4d260  Type: (84f446a0) Directory
    ObjectHeader: 88e4d248 (new version)
    HandleCount: 0  PointerCount: 29
    Directory Object: 88e05ed0  Name: FileSystem

    Hash Address  Type          Name
    ---- -------  ----          ----
     00  86b33388 Driver        srvnet
         8518e670 Driver        Ntfs
     01  85ab58e8 Driver        NetBIOS
...
...

گرفتن اطلاعات از Driver Object مربوط به Ntfs،‌ 

kd> !drvobj 8518e670
Driver object (8518e670) is for:
 \FileSystem\Ntfs
Driver Extension List: (id , addr)

Device Object list:
85af6020  85a28020  8519b640 

خواندن Device Stack یکی از Device Object های Ntfs که از فرمان بالا به دست آمد می گیریم

kd> !devstack 8519b640
  !DevObj   !DrvObj            !DevExt   ObjectName
  85384620  \Driver\LiveKd     853846d8  
  856a4d20  \FileSystem\FltMgr 856a4dd8  
> 8519b640  \FileSystem\Ntfs   00000000  Ntfs

خواندن Device Stack مربوط به Device Object فایل سیستم Ntfs بعد از نصب فیلتر درایوری که قرار است بنویسیم. فیلتر درایور ما با نام sfilter در بالای stack قرار گرفته. در این مرحله می توانید این نتایج را با تصویری که از Device Stack مروبط به Filesystem در ابتدای آموزش ارائه دادیم مطابقت دهید.

kd> !devstack 8519b640  
  !DevObj   !DrvObj            !DevExt   ObjectName
  86542e78  \Driver\sfilter    86542f30  
  85384620  \Driver\LiveKd     853846d8  
  856a4d20  \FileSystem\FltMgr 856a4dd8  
> 8519b640  \FileSystem\Ntfs   00000000  Ntfs

قسمت دوم

در قسمت اول همین بخش نشان دادم چطور Object Device های درایور Ntfs را بگیریم. حالا در این قسمت می خواهیم نام مروبط به این Object ها را بدست بیاوریم.

یک بار دیگه اطلاعات در مورد درایور Ntfs را می گیریم. (آدرس Device Object ها با قسمت اول فرق دارد که طبیعی است چون در هر بار روشن خاموش شدن سیستم این آدرس ها تغییر می کند)

lkd> !drvobj 85396670 
Driver object (85396670) is for:
 \FileSystem\Ntfs
Driver Extension List: (id , addr)

Device Object list:
85cf7020  85c01020  85b4c460  

اول اینکه خودتون Stack هر سه Object را بگیرید. می بینید که هر سه در پایین ترین بخش Stack قرار دارند.

حالا با دستورات زیر اطلاعات در مورد Object ها می گیریم. دستور !object بطور عمومی برای گرفتن اطلاعات از هر نوع Object ای است. ما این اطلاعات را با دستور !devobj نیز می توانستیم بگیریم که مخصوص Object های از نوع Device بود. دو Object اول بی نام هستند و فقط Object سوم نام دارد که Ntfs است. (نکته: این نام مربوط به یک Device Object  است و با درایور Ntfs فرق دارد)

lkd> !object 85cf7020
Object: 85cf7020  Type: (851e7a38) Device
    ObjectHeader: 85cf7008 (new version)
    HandleCount: 0  PointerCount: 1
lkd> !object 85c01020 Object: 85c01020 Type: (851e7a38) Device ObjectHeader: 85c01008 (new version) HandleCount: 0 PointerCount: 1
lkd> !object 85b4c460 Object: 85b4c460 Type: (851e7a38) Device ObjectHeader: 85b4c448 (new version) HandleCount: 0 PointerCount: 2 Directory Object: 89005ed0 Name: Ntfs

قسمت سوم

هدف از این بخش این است که نشان بدهیم تابع IoGetDiskDeviceObject چطور عمل میکند. کاری این تابع انجام می دهد خواندن یک ساختار به نام VPB است. این مفهوم مربوط می شود به مساله Volume Manager و Mount Manager در ویندوز که کلا مباحثی جدا از موضوع این آموزش است. در نتیجه اینجا من فقط یکسری دستور نشانتان می دهم که این تابع دقیقا چه مراحلی طی می کند و چه مقداری را بر میگرداند.

در قسمت دوم سه Device Object از درایور Ntfs بدست آمد. من آدرس Device Object اول را برای این مثال آماده کرده ام.

اول) گرفتن اطلاعات از ساختار DEVICE_OBJECT

lkd> dt _DEVICE_OBJECT 85cf7020
nt!_DEVICE_OBJECT
   +0x000 Type             : 0n3
   +0x002 Size             : 0xf90
   +0x004 ReferenceCount   : 0n0
   +0x008 DriverObject     : 0x85396670 _DRIVER_OBJECT
   +0x00c NextDevice       : 0x85c01020 _DEVICE_OBJECT
   +0x010 AttachedDevice   : 0x85cf2a18 _DEVICE_OBJECT
   +0x014 CurrentIrp       : (null) 
   +0x018 Timer            : (null) 
   +0x01c Flags            : 0x40000
   +0x020 Characteristics  : 0
   +0x024 Vpb              : (null) 
   +0x028 DeviceExtension  : 0x85cf70d8 Void
   +0x02c DeviceType       : 8
   +0x030 StackSize        : 8 ''
   +0x034 Queue            : 
   +0x05c AlignmentRequirement : 0
   +0x060 DeviceQueue      : _KDEVICE_QUEUE
   +0x074 Dpc              : _KDPC
   +0x094 ActiveThreadCount : 0
   +0x098 SecurityDescriptor : (null) 
   +0x09c DeviceLock       : _KEVENT
   +0x0ac SectorSize       : 0x200
   +0x0ae Spare1           : 1
   +0x0b0 DeviceObjectExtension : 0x85cf7fb0 _DEVOBJ_EXTENSION
   +0x0b4 Reserved         : (null) 

دوم) خواندن ساختار DEVOBJ_EXTENSION از ساختار قبلی

lkd> dt _DEVOBJ_EXTENSION 0x85cf7fb0 
nt!_DEVOBJ_EXTENSION
   +0x000 Type             : 0n13
   +0x002 Size             : 0
   +0x004 DeviceObject     : 0x85cf7020 _DEVICE_OBJECT
   +0x008 PowerFlags       : 0
   +0x00c Dope             : (null) 
   +0x010 ExtensionFlags   : 0x800
   +0x014 DeviceNode       : (null) 
   +0x018 AttachedTo       : (null) 
   +0x01c StartIoCount     : 0n0
   +0x020 StartIoKey       : 0n0
   +0x024 StartIoFlags     : 0
   +0x028 Vpb              : 0x85bbe408 _VPB
   +0x02c DependentList    : _LIST_ENTRY [ 0x85cf7fdc - 0x85cf7fdc ]
   +0x034 ProviderList     : _LIST_ENTRY [ 0x85cf7fe4 - 0x85cf7fe4 ]

سوم) خواندن ساختار VPB که مخفف Volume Parameter Block است از ساختار قبلی

lkd> dt _VPB 0x85bbe408 
nt!_VPB
   +0x000 Type             : 0n10
   +0x002 Size             : 0n88
   +0x004 Flags            : 1
   +0x006 VolumeLabelLength : 0x1e
   +0x008 DeviceObject     : 0x85cf7020 _DEVICE_OBJECT
   +0x00c RealDevice       : 0x85bbfe20 _DEVICE_OBJECT
   +0x010 SerialNumber     : 0x6e3267e9
   +0x014 ReferenceCount   : 0x16
   +0x018 VolumeLabel      : [32]  "System Reserved"

تابع مذکور بعد از چک کردن یکسری از عضو داده، عضو داده RealDevice را بر می گرداند. این Device Object در واقع توسط درایور دیگری ایجاد شده و برعکس Device Object ای که بالاتر Enumerate شده بود و بی نام بود، این Device Object دارای نام می باشد که با دستور !devobj به راحتی به این اطلاعات می توانیم بررسیم.

lkd> !devobj 0x85bbfe20 
Device object (85bbfe20) is for:
 HarddiskVolume1 \Driver\volmgr DriverObject 85ad7328
Current Irp 00000000 RefCount 22 Type 00000007 Flags 00203050
Vpb 85bbe408 Dacl 891a947c DevExt 85bbfed8 DevObjExt 85bbffc0 Dope 85b9b9b0 DevNode 85bccd78 
ExtensionFlags (0x00000800)  
                             Unknown flags 0x00000800
AttachedDevice (Upper) 85bcc880 \Driver\fvevol
Device queue is not busy.

از اطلاعات بدست آمده می توانیم بفهمیم که اولا نام Device Object برابر است با HarddiskVolume1 و توسط درایور volmgr ایجاد شده است.

ابزارهای دیگر

آیا در مورد گرفتن اطلاعات از  Stack مربوط به Device Object ها ابزاری ساده تر از WinDbg وجود دارد. بله خوشبختانه لبخند. یکی از این ابزارها VrtuleTree نام دارد و کار باهاش خیلی ساده است. تصویری از این برنامه که Device Stack همه درایور های روی سیستم را در آورده است. (برای نمایش بزرگتر تصویر روی آن کلیک کنید)

تعصویر برنامه VrtuleTree

پایان

نظرات (۴)

holy shit، عالی بود.
یه پیغام خصوصی هم واست فرستادم چک بکن..
  • عماد رضوانی
  • بوی روت کیت میاد .... اینطور نیست ؟! به مطالب خوبی پرداختین و انشاا.. ادامه دار باشه. یکی از بازدیدکنندگان شما در پستی به دنبال کتاب Undocumented NT میگشت. اگه پیدا نکردین ایمیل بزنید تا ارسال کنم. موقف باشید.
    پاسخ:
    ممنون
    راستش مطلب رو به هدف روت ... ننوشتم، بسته به شامه فردی که می خونه داره :). الان نگاه می کنم جزئیاتی که دادم بدرد روت کیت کار هم میخوره :)
    در مورد کتاب یادمه یکی یک کتاب خواسته بود ولی اون کتاب جژء کتاب هایی بود که در یک پست معرفی کرده بودم و می دونم این کتاب نبودش.
    خودم این کتاب رو ندارم اگر لینکی ازش دارین خوشحال می شم خودم بگیرمش :)
    موفق باشید
    سلام میخوام شروع به درایور نویسی کنم از کجا شروع کنم ، چه پیش زمینه ایی لازمه و اینکه کار در این در این حوزه وجود داره؟! 
    پاسخ:
    سلام

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

    http://binthought.blog.ir/page/windows-driver-development-tutorials

    اگر این آموزش ها مبهم هستن یا به نظرتون ساده نیستن خوشحال می شم دقیقا بدونم که چه بخشهایی مشکل دارن
    اگر هم آموزش های بیشتر می خواهید به این لینک ها می توانید مراجعه کنید

    http://www.codeproject.com/Articles/9504/Driver-Development-Part-Introduction-to-Drivers
    http://www.catch22.net/tuts/introduction-device-drivers

    چه پیش زمینه ایی لازمه: آدم به آدم این مساله ممکنه متفاوت باشه یکی ممکنه خیلی پیش نیاز ها رو بعد تو کار یاد بگیره ولی به صورت کلی به نظرم میاد اگر کسی می خواد درایور نویس خوبی باشه باید این ویژگی ها رو داشته باشه
    ۱- برنامه نویس خوب باشه، خصوصا تجربه با زبون های C/C++ و ترجیحا برنامه نویسی سیستمی،
    ۲- بازم اشاره می کنم تسط به زبان C‌، خیلی مهم که با این زبون راحت باشین، با مشکلات زبان C اشنا باشه،‌خصوصا باگ های خیلی رایج
    ۳- یکم آشنایی با اسمبلی و معماری پردازنده ای که می خواهید روش کد بزنید هر چی بیشتر بهتر
    ۴- آشنایی با سیستم عامل، کسی که برنامه نویسی سیستمی کرده باشه تا حدودی این ویژگی رو داره ولی هر کی در این زمینه اطلاعات بیشتر داشته باشه موفق تر خواهد بود
    ۵- اگر هدف درایور نیوسی برای دیوایسی است آشنایی با دیوایس و کمی سخت افزار لازمه

    حالا اگر یکی یکسری از ویژگی های بالا نداشته باشه چی:مشکلی وجود نداره، به نظرم این فرد باید صبور باشه، آهسته آهسته با گذشت زمان به این موارد مسلط بشه، حسابی وقت بذاره، و از یکی که بلده سوال بپرسه زیادم سوال بپرسه

    کار در این در این حوزه وجود: راستش جواب دادن به این سوال سخته چون شرکت ها در این کشور خیلی بسته کار می کنن اطلاعات درست و حسابی از شرکت ها و پروژه هاشون در دسترس نیست، اطلاع دقیقی نمی تونم بدم،‌

    موفق باشید
    سلام، ممنون از مطالب خوبت.
    سوالی از خدممتتون دارم. بنده قصد نوشتن درایوری را دارم که بتواند ایجاد یک پروسس را به سایر فرآیندهای در حال اجرای سیستم در سطح یوزر مد هشدار دهد. با مطالعاتی که داشتم روتین PsSetCreateProcessNotifyRoutine امکان خوبی برای این مورد هست. در حال حاضر یک ابهام دارم، چه نوع درایوری را برای این پیاده سازی انتخاب کنم؟ فیلتر درایور، WDM یا WDF
    قصد دارم یک درایور هشداردهنده در سطح کرنل و یک برنامه هشدارگیرنده در سطح یوزر پیاده کنم. ممنون میشم راهنمایی کنید.
    پاسخ:
    سلام
    خوشحالم که مطالب سایت بدردتون خورده.

    در مورد سوالتون اول بگم که WDM و WDF در یک طبقه بندی قرار می گیرند یعنی با هر دوی اینا میشه برای ویندوز درایور نوشت ولی سبک نوشتن اینها یکم فرق داره و با هر دوی هم میشه فیلتر درایور نوشت و هم میشه از تابعی که ذکر کردین استفاده کرد. 

    خوب اگه بخواین از سورس هایی که اینجا قرار دادم استفاده کنید و از آنجایی که آموزشهای من همه بر اساس WDM است  WDM انتخاب بهتری براتون خواهد بود.

    خواندن این لینک هم پیشنهاد میشه در در مورد این مساله که کاربر هم باخبر بشه از ایجاد پروسه

    شاد باشید.



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