در ادامه بحث های مطرح شده در آنالیز ساختار ویندوز و روت کیت های سطح هسته-بخش اول ادامه بحث را دنبال خواهیم کرد.
سطح هسته همانند سطح کاربر دارای هوک های خاص خودش است که معروفترین آن ایجاد تغییرات دلخواه در جداولی است که آدرس توابع مورد نیاز سیستم را در خود نگهداری می کنند:
- System Service Dispatch Table (SSDT)
- Interrupt Descriptor Table (IDT)
آدرس توابع سطح کرنل درون جدول SSDT نگهداری می شود(توابع nt*). هنگامیکه روند اجرا یک برنامه سطح کاربر میخواهد به سمت کرنل هدایت شود ID مرتبط با تابع کرنل درون رجیستر EAX قرار می گیرد و رجیستر EDX به لیست پارامترهای که بایستی به تابع ارجاع داده می شود اشاره می کند، سپس با اجرای دستور int 2e و یا sysenter روند اجرا پروسه به سطح کرنل خواهد رفت. هنگامیکه وقفه ای رخ می دهد سیستم عامل با جستجو در IDT ، روتین مرتبط با مدیریت آن وقفه را بدست میاورد. این جدول تمامی وقفه ها را درون خود نگهداری می کند. روتین هندل کننده وقفه 0x2e تابع KiSystemService() از ntoskrnl است. برای بازگشت به سطح کاربر از دستور iret استفاده می شود. در صورتی که از دستور sysenter استفاده شود برای بازگشت به سطح کاربر از دستور sysexit استفاده خواهد شد. برای پشتیبانی از دستور sysenter ویندوز در زمان بوت آدرس روتین مرتبط را بجای IDT در رجیستر MSR[۱] ذخیره می کند. . اسکریپت زیر را می توانید از این آدرس[۲] دریافت نمایید. با دستور زیر در windbg میبینیم که handler این وقفه تابع KiSystemService() است :
kd> !@display_idt #################################### # Interrupt Descriptor Table (IDT) # #################################### Processor 00 Base : 8003F400 Limit : 07FF Int Type Sel : Offset Attributes Symbol/Owner ---- -------- ------------- ---------- ------------ ۰۰۲A IntG32 0008:8053DA7E DPL=3 P nt!KiGetTickCount (8053da7e) ۰۰۲B IntG32 0008:8053DB80 DPL=3 P nt!KiCallbackReturn (8053db80) ۰۰۲C IntG32 0008:8053DD20 DPL=3 P nt!KiSetLowWaitHighThread (8053dd20) ۰۰۲D IntG32 0008:8053E660 DPL=3 P nt!KiDebugService (8053e660) ۰۰۲E IntG32 0008:8053D521 DPL=3 P nt!KiSystemService (8053d521) ۰۰۲F IntG32 0008:80540838 DPL=0 P nt!KiTrap0F (80540838) ۰۰۳۰ IntG32 0008:806D7D50 DPL=0 P hal!HalMakeBeep+0x130 (806d7d50)
یا از دستور زیر استفاده نمایید:
kd> !idt 2e Dumping IDT: ۲e: ۸۰۵۳d521 nt!KiSystemService
اما در سیستم های x64 بحث متفاوت تر است و از دستور syscall استفاده می شود :
اولین تکنیک از هوک های کرنل، هوک IDT است که می توانیم Handler مرتبط با وقفه 0x2e را تغییر دهیم. روت کیت می تواند با ایجاد تغییر در IDT بجای nt!KiSystemService آدرس روتین خود را قرار دهد و همچنین SSDT جدیدی را ایجاد نماید و کاملا روند اجرا را تحت کنترل داشته باشد. روت کیت ها می توانند IDTR[۳] را نیز تغییر دهد. این رجیستر آدرس ابتدای IDT را در خود نگه میدارد، حال روت کیت می تواند IDT مورد نیاز خود را ایجاد نماید و آدرس درون IDTR را نیز تغییر دهد. برای مشاهده رجیستر MSR می توان از rdmsr و از دستور wrmsr برای نوشتن در این رجیستر استفاده کرد ، ادرس روتین Dispatcher در 0x176 قرار دارد:
برای سیستم های x64 بجای 0x176 بایستی 0xC0000082 استفاده نماییم. براحتی روت کیت می تواند این رجیستر را بازنویسی نماید با کامند !address
می توانیم آدرس را بعنوان پارامتر به آن دهیم تا بفهمیم در محدوده چه پروسه ای است:
همچنین می توانیم از کامند !!display_current_msrs
استفاده کنیم تا رجیستر MSR را بطور کامل مشاهده نماییم.
یکی دیگر از هوک های سطح کرنل ایجاد تغییرات در جدول SSDT[۴] است. همانطور که قبلا گفتیم شماره مربوط به تابع سطح کرنل درون رجیستر EAX که رجیستر ۳۲بیتی است ذخیره می گردد. این فضای برای ذخیره شدن یک شماره تابع ساده بسیار بزرگ است. بنابراین مقدار درون این رجیستر به ۳ بخش تقسیم می شود.
- بیت های ۰-۱۱ مربوط به SSN[۵] است درواقع همان ایندکس داده موردنیاز درون جدول را مشخص می کند. بنابراین سایز جدول ۴۰۹۶ بایت است.
- بیت های ۱۲-۱۳ نیز مربوط به انتخاب جدول است، پس می توانیم ۴ جدول داشته باشیم.
- بیت های ۱۴-۳۱ نیز استفاده نمی شوند.
تصویر زیر این ساختار را بدرستی نمایش میدهد:
برای بیت های ۱۲-۱۳ گفتیم که جدول مورد استفاده را انتخاب می کند. ما تنها دو جدول داریم مقدار 0x00 نشان دهنده جدول KeServiceDescriptorTable که جدول پیش فرض و اصلی است و مقدار 0x01 نشان دهنده جدول KeServiceDescriptorTableShadow است. این دو جدول همانند هم هستند ولی با این تفاوت که در KeServiceDescriptorTableShadow دارای توابع GDI سطح کرنل که در Win32k.sys پیاده سازی شده اند می باشد. هنگامی که NTOSKNRL.EXE بارگذاری می شود اولین عنصر این دو جدول را مقدار دهی می کند، سپس Win32k .sys بارگذاری می شود و با استفاده از تابع KeAddSystemServiceTable از NTOSKNRL ، دومین عنصر از آرایه KeServiceDescriptorTableShadow را مقدار دهی می کند. ساختار این دو جدول بصورت زیر است :
typedef struct ServiceDescriptorTable { SDE ServiceDescriptor[4]; }
ساختار KeServiceDescriptorTable در ntoskrnl است. هر کدام از این جداول یک آرایه ۴ عنصری است که هرکدام از عناصر آرایه دارای ساختاری از نوع SDE [۶] است. خوب حال ساختار SDE را می بینیم که به چه صورت است:
typedef struct ServiceDescriptorEntry { PDWORD KiServiceTable; //address of the SSOT PDWORD CounterBaseTable; //not used DWORD nSystemCalls; //number of system call PDWORD KiArgumentTable; //byte array (each byte = size of arg stack) } SOE, *PSDE;
روتین KiSystemService آرگومان ها را از پشته سطح کاربر به درون پشته سطح کرنل کپی می کند و سپس تابع سطح کرنل اجرا می گردد. کرنل با استفاده از جدول Argument Table می داند که چند بایت از پشته بایستی کپی گردد. این جدول آرایه ای از نوع بایت است و هربایت اندازه آرگومان های درون پشته را مشخص می کند. در سیستم های x64 اطلاعات Service Table توسط System Call Table Compaction بصورت کد شده ذخیره می گردد. ابتدا جداول مرتبط با دستور x جستجو می کنیم:
آدرس این دو جدول را بدست آوردیم، حال با توجه به ساختار SDE که گفتیم بایستی تعداد ۱۶ عدد DWORD برای هرکدام از جداول داشته باشیم. اختلاف بین دو جدول ۶۴بایت است ، هر DWORD سایز ۴بایت دارد، بنابراین در فاصله بین دو جدول میتوانیم ۱۶ عدد مقدار Dword داشته باشیم. پس ابتدا جدول دوم را با طول ۱۶ مشاهده می کنیم:
تا به اینجا با ساختار این دو جدول آشنا شدیم حال میخواهیم محتوای SSDT را مشاهده کنیم. جدولی که روت کیت ها مقادیر درون آن را تغییر می دهند تا توابع حساس را هوک نمایند و بتوانند خود را پنهان نمایند. با استفاده از کامند زیر می توانیم محتوای این جدول را مشاهده نماییم.
با استفاده از کامند بالا جدول SSDT را بطور کامل مشاهده می کنیم، L نشان دهنده طول می باشد که ما طول جدول را نیز مشخص کرده ایم. دستور dps برای نمایش حافظه است که بطور خودکار تصمیم به اندازه آن گرفته می شود (۱۶-۶۴ bit) و همچنین اگر دارای سیمبول است نشان داده شود. اگر توابعی در این جدول هوک شود آدرس آن خارج از محدوده nt می باشد و براحتی می توان آن را تشخیص داد. برروی سیستم های X64 با استفاده از مکانیزم Kernel Patch Protection جداول درحال مانیتور شدن می باشند اگر تغییری در این جداول بوجود آید سیستم کرش می کند. روش دیگر هوک کردن توابع بصورت in-line است تا تغییری در ساختار جداول ایجاد نشود. جدول SSDT در حافظه read-only قرار دارد که این امر مشکل اساسی در بازنویسی نمودن این جدول است. فلگ WP در رجیستر CR0 می تواند مقدار ۰,۱ داشته باشد. اساسا این فلگ، از حافظه read-only در برابر بازنویسی محافظت می کند. این فلگ تاثیرش در سطح کرنل است :
- ۰ : کرنل اجازه دارد تا در حافظه های read-only بنویسد صرفنظر از فلگ های R/W , U/S در PDE و PTE
- ۱: کرنل اجازه نوشتن ندارد. فلگ های R/W , U/S در PDE و PTE استفاده می شود تا بتوان نحوه دسترسی کرنل به حافظه را مشخص نماید.
برای مشاهده رجیستر CR0 در قالب های مختلف از کامند .formats استفاده می کنیم. با استفاده از آن بیت ۱۶ رجیستر که همان WP[۷] را میبینیم که چه مقداری دارد:
در زیر به راه حل ها میپردازیم که به اما اجازه نوشتن در SSDT را میدهد:
- تغییر مقدار WP : هنگامی که مقدار این فلگ را در رجیستر CR0 به مقدار ۰ ست نماییم دیگر مقادیر PDE,PTE نادیده گرفته می شود.
- تغییر در کلید رجیستری “HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory\Management\EnforceWriteProtection” که به ما اجازه نوشتن میدهد.
- با استفاده از MDL می توانیم SSDT را قابل بازنویسی نماییم. MDL خود را در جایی که SSDT وجود دارد ایجاد می نماییم.
در ساختار nt!KTHREAD فیلد ServiceTable در واقع آدرس همان دو جدول است:
+0x0e0 ServiceTable : Ptr32 Void
با دستور زیر می توانیم این فیلد را در تمامی ترید های فعال مشاهده کنیم که مقدار آن بایستی به یکی از دو جدولی که قبلا ذکر شده اشاره کند در غیر اینصورت جدولی جعلی درون سیستم وجود دارد و هوک انجام شده است:
!for_each_thread ".echo Thread: @#Thread; dt nt!_kthread ServiceTable @#Thread"
در بخش بعدی به سایر تکنیک ها خواهیم پرداخت.
[۱] Machine-Specific Register
[۲] http://www.laboskopia.com/download/SysecLabs-Windbg-Script.zip
[۳] Interrupt Descriptor Table Register
[۴] System Service Dispatch Table
[۵] System Service Number
[۶] ServiceDescriptorTable
[۷] Write Protect
دیدگاهتان را بنویسید لغو پاسخ