Hal hazırda yazdığım kernelin üzərində işləyirəm… Demək normal halda OS üzərində System call mexanizmini NT Kernel tərzində SSDT – SSDTS bənzəri etdim birinci fikirləşdim ki, standart IRQ (interrupt request) göndərərək – Trap handler vasitəsi ilə (int 0x2e) birbaşa KiSystemService dispatcher çağırmaq ancaq sonra qərarımdan döndüm çünki SYSENTER/SYSEXIT instruction-larından istifadə etməyə qərar verdim və maraqlanmağa başladım.
SYSENTER/SYSEXIT Interrupt çoxluqundan qaçınmaq üçün hazırlanmış Syscall based instruction-dır.
NT architecture native api istifadə zamanı Zw və Nt prefixlərindən istifadə edir. Windows OS privilige level yoxlanılması üçün previous mode istifadə edir. Belə ki, previous mode dəyəri (user or kernel) hər dəfə trap handling icra edildiyində kernel `thread` içində bunu saxlayır və daxil olan exception,trap,syscall imtiyaz səviyyəsini yoxlayır. Əgər system call kernel driver tərəfindən gəlirsə `probing` və `capturing` prosessləri ötülür. Həmçinin bu native APİ üçün interrupt və yaxud sysenter ehtiyyac yoxdur çünki CPU artıq doğru privilige level-dədir. NTDLL modulu tərəfindən NT və Zw prefixləri altında funksiyalar export edilir. Yalnız əgər birbaşa NtCreateFile (Native APİ) kernel mode address çağrılarsa previous mode bu əməliyyatı sonlandırar.
User mode application-lar NTDLL.LIB ilə `link` edildikdə System servis dispatcher vasitəsi ilə Kernel mode (context switching – mode transition) keçid edilir. Ümumiyyətlə NT kernel `System servic call stub` üçün ayrıca instruction `hardcode` əvəzinə bunu shared strukturada saxlayır (KUSER_SHARED_DATA).
_KUSER_SHARED_DATA+(0x300) offsetində yerləşir System servis çağırıcısı
sysenter instruction-dan danışım: CPL = 0 (privilige level 0) olan system prosedurlarını və rutinlərini sürətli çağırışını təmin edir. Bu instruction CPL = 0 (privilige level 0) üzərində olan əməliyyat sistemi və yaxud icrad edici prosedurların CPL = 3 (privilige level 3) üzərində çalışdırmaq üçün maksimum performans təşkil edir.
SYSENTER instruction icra edilmədən öncə, proqram təminatı privilige level 0 (CPL 0) code segment və code entry point təyin etməlidir, və privilige level 0 stack segment və stack pointer təyin etmək gərəktir.
İnterrupt və yaxud Exception generasiya edildiyində, (context switching) prosessor maşının cari vəziyyətini kernel steki üzərində saxlayır ki, interrupt icra edildikdən sonra proqram axışı cari nöqtəyə qayıdaraq icra davam etsin. Əgər thread istifadəçi modunda çalışırsa, OS threadi kernel moda keçirtmək istərsə. O zaman OS kernel stekdə trap frame yaradaraq onun üzərinə cari thread vəziyyətini (thread state) qeyd edir.
1. trap frame yaradaraq user moda döndükdə lazım olacaq bütün informasiyaları saxlayır.
2. `System service descriptor table` üzərində sistem servis funksiyasını taparaq çağırır.
`MSR` (model-specific register) register dəyərinə baxaq.
PVOID ServiceTable;
e.g
mov eax,0x102
102 – ci sistem servis nömrəsinə məxsus funksiya = NtReadFile
bit 0-11: sistem servis nömrəsi çağırış üçün
bit 12-13: service descriptor table üçün istifadə edilir
bit 13-31: istifadə edilmir
Keçək dəfə mövzuda bəhs etmişdim Win iki System Descriptor Table istifadə etdiyindən.
KeServiceDescriptorTableShadow
——————————
KeServiceDescriptorTable
sistem servis nömrələrdindən 0x0-0xFFF aralıqında bütün nömrələr KeServiceDescriptorTable indexləri olaraq istifadə edilir.
0x1000-0x1FFF aralıqları isə KeServiceDescriptorTableShadow indexləri olaraq istifadə edilir.
SSN(SYSTEM SERVİCE NUMBER) – SSDT (System Descriptor Table)
ecx = 10 olarsa Shadow Table qeyd edilir əks halda SSDT regular table qeyd edilir.
SYSENTER/SYSEXIT Interrupt çoxluqundan qaçınmaq üçün hazırlanmış Syscall based instruction-dır.
NT architecture native api istifadə zamanı Zw və Nt prefixlərindən istifadə edir. Windows OS privilige level yoxlanılması üçün previous mode istifadə edir. Belə ki, previous mode dəyəri (user or kernel) hər dəfə trap handling icra edildiyində kernel `thread` içində bunu saxlayır və daxil olan exception,trap,syscall imtiyaz səviyyəsini yoxlayır. Əgər system call kernel driver tərəfindən gəlirsə `probing` və `capturing` prosessləri ötülür. Həmçinin bu native APİ üçün interrupt və yaxud sysenter ehtiyyac yoxdur çünki CPU artıq doğru privilige level-dədir. NTDLL modulu tərəfindən NT və Zw prefixləri altında funksiyalar export edilir. Yalnız əgər birbaşa NtCreateFile (Native APİ) kernel mode address çağrılarsa previous mode bu əməliyyatı sonlandırar.
User mode application-lar NTDLL.LIB ilə `link` edildikdə System servis dispatcher vasitəsi ilə Kernel mode (context switching – mode transition) keçid edilir. Ümumiyyətlə NT kernel `System servic call stub` üçün ayrıca instruction `hardcode` əvəzinə bunu shared strukturada saxlayır (KUSER_SHARED_DATA).
_KUSER_SHARED_DATA+(0x300) offsetində yerləşir System servis çağırıcısı
0:001> dt ntdll!_KUSER_SHARED_DATA
+0x000 TickCountLowDeprecated : Uint4B
+0x004 TickCountMultiplier : Uint4B
+0x008 InterruptTime : _KSYSTEM_TIME
[…]
+0x300 SystemCall : Uint4B <========
+0x304 SystemCallReturn : Uint4B
+0x308 SystemCallPad : [3] Uint8B
[…]
WINDOWS Əməliyyat sistemi üzərində bu Structure (_KUSER_SHARED_DATA) statik adrese sahibdir = 0x7FFE0000.0: kd> u ntdll!NtReadFile
ntdll!NtReadFile:
77f761e8 b8b7000000 mov eax,0xb7
77f761ed ba0003fe7f mov edx,0x7ffe0300 <=====
77f761f2 ffd2 call edx
77f761f4 c22400 ret 0x24
lkd> u poi(0x7ffe0300)
ntdll!KiFastSystemCall:
770b64f0 8bd4 mov edx,esp
770b64f2 0f34 sysenter <========
ntdll!KiFastSystemCallRet:
770b64f4 c3 ret
770b64f5 8da42400000000 lea esp,[esp]
770b64fc 8d642400 lea esp,[esp]
ntdll!KiIntSystemCall:
770b6500 8d542408 lea edx,[esp+8]
770b6504 cd2e int 2Eh
770b6506 c3 ret
Win7 üzərində system call dispatcher olaraq sysenter istifadə edildiyini gördük.sysenter instruction-dan danışım: CPL = 0 (privilige level 0) olan system prosedurlarını və rutinlərini sürətli çağırışını təmin edir. Bu instruction CPL = 0 (privilige level 0) üzərində olan əməliyyat sistemi və yaxud icrad edici prosedurların CPL = 3 (privilige level 3) üzərində çalışdırmaq üçün maksimum performans təşkil edir.
SYSENTER instruction icra edilmədən öncə, proqram təminatı privilige level 0 (CPL 0) code segment və code entry point təyin etməlidir, və privilige level 0 stack segment və stack pointer təyin etmək gərəktir.
intel ref:
•IA32_SYSENTER_CS — Contains a 32-bit value, of which the lower 16 bits are the segment selector for the privilege level 0 code segment. This value is also used to compute the segment selector of the privilege level 0 stack segment.
•IA32_SYSENTER_EIP — Contains the 32-bit offset into the privilege level 0 code segment to the first instruction of the selected operating procedure or routine.
•IA32_SYSENTER_ESP — Contains the 32-bit stack pointer for the privilege level 0 stack.These MSRs can be read from and written to using RDMSR/WRMSR. Register addresses are listed in Table 4-17. The addresses are defined to remain fixed for future Intel 64 and IA-32 processors.
[öz hazırladıqım kernel üzərində İVT 0x2e qeyd etmişim vector onu ləğv edərək `MSR` register daxilində dispatcher funksiya adresini qeyd etdim.]İnterrupt və yaxud Exception generasiya edildiyində, (context switching) prosessor maşının cari vəziyyətini kernel steki üzərində saxlayır ki, interrupt icra edildikdən sonra proqram axışı cari nöqtəyə qayıdaraq icra davam etsin. Əgər thread istifadəçi modunda çalışırsa, OS threadi kernel moda keçirtmək istərsə. O zaman OS kernel stekdə trap frame yaradaraq onun üzərinə cari thread vəziyyətini (thread state) qeyd edir.
lkd> dt nt!_ktrap_frame
+0x000 DbgEbp : Uint4B
+0x004 DbgEip : Uint4B
+0x008 DbgArgMark : Uint4B
+0x00c DbgArgPointer : Uint4B
+0x010 TempSegCs : Uint2B
+0x012 Logging : UChar
+0x013 Reserved : UChar
+0x014 TempEsp : Uint4B
+0x018 Dr0 : Uint4B
+0x01c Dr1 : Uint4B
+0x020 Dr2 : Uint4B
+0x024 Dr3 : Uint4B
+0x028 Dr6 : Uint4B
+0x02c Dr7 : Uint4B
+0x030 SegGs : Uint4B
+0x034 SegEs : Uint4B
+0x038 SegDs : Uint4B
+0x03c Edx : Uint4B
+0x040 Ecx : Uint4B
+0x044 Eax : Uint4B
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B
+0x07c V86Es : Uint4B
+0x080 V86Ds : Uint4B
+0x084 V86Fs : Uint4B
+0x088 V86Gs : Uint4B
KiFastCallEntry iki əməliyyatı yerinə yetirir.1. trap frame yaradaraq user moda döndükdə lazım olacaq bütün informasiyaları saxlayır.
2. `System service descriptor table` üzərində sistem servis funksiyasını taparaq çağırır.
`MSR` (model-specific register) register dəyərinə baxaq.
IA32_SYSENTER_EIP (0x176) – The kernel’s EIP for SYSENTER. This is the address of your SYSENTER entry point.
0: kd> rdmsr 176msr[176] = 00000000`8053a270
0: kd> ln 8053a270
(8053a270) nt!KiFastCallEntry | (8053a2fb) nt!KiSystemService
nt!KiFastCallEntry:
b923000000 mov ecx,23h
6a30 push 30h
0fa1 pop fs
8ed9 mov ds,cx
8ec1 mov es,cx
648b0d40000000 mov ecx,dword ptr fs:[40h]
8b6104 mov esp,dword ptr [ecx+4]
6a23 push 23h
52 push edx
9c pushfd
6a02 push 2
83c208 add edx,8
9d popfd
804c240102 or byte ptr [esp+1],2
6a1b push 1Bh
ff350403dfff push dword ptr ds:[0FFDF0304h]
6a00 push 0
55 push ebp
53 push ebx
56 push esi
57 push edi
648b1d1c000000 mov ebx,dword ptr fs:[1Ch]
6a3b push 3Bh
8bb324010000 mov esi,dword ptr [ebx+124h]
ff33 push dword ptr [ebx]
c703ffffffff mov dword ptr [ebx],0FFFFFFFFh
8b6e28 mov ebp,dword ptr [esi+28h]
6a01 push 1
83ec48 sub esp,48h
81ed9c020000 sub ebp,29Ch
c6863a01000001 mov byte ptr [esi+13Ah],1
3bec cmp ebp,esp
7597 jne nt!KiFastCallEntry2+0x49 (82e7a2fb)
Super məlumat taparaq ingiliscəyə tərcümə etmişəm Azərbaycan dilində tərcümə çox çətin olduğundan.// When a thread executes in kernel mode data segment selector is 23h
// When a thread executes in kernel mode, loaded in the FS register is to select the sub-30,
// Structure for addressing PCR 804de6f0 mov ecx, 23h
804de6f5 push 30h
804de6f7 pop fs
// 23h-> ds; 23h-> es
804de6f9 mov ds, cx
804de6fb mov es, cx
// Dword ptr fs: [40h] == dword ptr ds: [0FFDFF040h] {TSS}
// Dword ptr [ecx + 4] for the current thread’s kernel-mode stack pointer value 804de6fd mov ecx, dword ptr ds: [0FFDFF040h]
804de703 mov esp, dword ptr [ecx + 4]
// ==========================================
// The current (the value of the registers before execution Sysenter) pushed onto the system stack context,
// _KTRAP_FRAME.HardwareSegSs
804de706 push 23h
// _KTRAP_FRAME.HardwareEsp
804de708 push edx
// _KTRAP_FRAME.EFags
804de709 pushfd
804de70a push 2
804de70c add edx, 8
// EFlags = 2
804de70f popfd
804de710 or byte ptr [esp + 1], 2
// _KTRAP_FRAME.SegCs
804de715 push 1Bh
// Ds: [0FFDF0304h] is SystemCallReturn (_KUser_Shared_Data)
// _KTRAP_FRAME.Eip
804de717 push dword ptr ds: [0FFDF0304h]
//_KTRAP_FRAME.ErrorCode
804de71d push 0
//_KTRAP_FRAME.Ebp
804de71f push ebp
//_KTRAP_FRAME.Ebx
804de720 push ebx
//_KTRAP_FRAME.Esi
804de721 push esi
//_KTRAP_FRAME.Edi
804de722 push edi
//ebx<-_kpcr -="" .selfpcr804de723="" 0ffffffffh="" 124h="" 18h="" 1="" 3bh="" 804de729="" 804de72b="" 804de731="" 804de733="" 804de739="" 804de73c="" _ktrap_frame.segfs="" ds:="" dword="" ebp="" ebx="" esi="" esp="" h="" initialization="" initialstack="" list="" mov="" ptr="" push="" reviouspreviousmode="1" xceptionlist=""> _ KTRAP_FRAME
804de73e sub esp, 48h
// Ebp -> _ KTRAP_FRAME
804de741 sub ebp, 29Ch
// CurrentThread +124 = 1
804de747 mov byte ptr [esi + 140h], 1
// Compare 804de74e cmp ebp, esp
// Not equal, the occurrence of abnormal 804de750 jne nt! KiFastCallEntry2 + 0x24 (804de6c8)
// _KTRAP_FRAME.Dr7 = 0
804de756 and dword ptr [ebp + 2Ch], 0
// [Esi + 2Ch] is KTHREAD.DebugActive current thread // debug flag is not equal to 0xFF indicates debugging?
804de75a test byte ptr [esi + 2Ch], 0FFh
804de75e mov dword ptr [esi + 134h], ebp
804de764 jne nt! Dr_FastCallDrSave (804de5b0)
// Ebx = _KTRAP_FRAME.Ebp804de76a mov ebx, dword ptr [ebp + 60h]
// Edi = _KTRAP_FRAME.EIP
804de76d mov edi, dword ptr [ebp + 68h]
// _KTRAP_FRAME.DbgArgPointer
804de770 mov dword ptr [ebp + 0Ch], edx
// _KTRAP_FRAME.DbgArgPointer = DbgArgMark
804de773 mov dword ptr [ebp + 8], 0BADB0D00h
//_KTRAP_FRAME.DbgEbp
804de77a mov dword ptr [ebp], ebx
//_KTRAP_FRAME.DbgEip = _KTRAP_FRAME.EIP
804de77d mov dword ptr [ebp + 4], edi
// ==================================
-_kpcr>
Doğru System Descriptor Table tapmaq üçün KiFastCallEntry daxilində bəzi hesablama əməliyyatları aparılır.mov ecx,23h
mov ds,cx //load the new ds
mov ebx,dword ptr ds:[0FFDFF01Ch] //EBX=KPCR.SelfPcr, ie, KPCR itself
mov esi,dword ptr [ebx+124h] //ESI=KTHREAD
mov edi,eax //EDI=system service number
shr edi,8
mov ecx, edi ; ecx now has the actual index for the SSDT
and edi,30h //EDI=SSDT index
add edi,dword ptr [esi+0E0h] //EDI=SSDT
mov edi,dword ptr [edi] //EDI=SSDT->ServiceTableBase
mov ebx,dword ptr [edi+eax*4] //EBX=service function address
Kernel Process Control Block təqdim edirəm.typedef struct _KPCR
{
union
{
NT_TIB NtTib;
struct
{
PEXCEPTION_REGISTRATION_RECORD Used_ExceptionList;
PVOID Used_StackBase;
PVOID Spare2;
PVOID TssCopy;
ULONG ContextSwitches;
ULONG SetMemberCopy;
PVOID Used_Self;
};
};
PKPCR SelfPcr;
PKPRCB Prcb;
UCHAR Irql;
ULONG IRR;
ULONG IrrActive;
ULONG IDR;
PVOID KdVersionBlock;
PKIDTENTRY IDT;
PKGDTENTRY GDT;
PKTSS TSS;
WORD MajorVersion;
WORD MinorVersion;
ULONG SetMember;
ULONG StallScaleFactor;
UCHAR SpareUnused;
UCHAR Number;
UCHAR Spare0;
UCHAR SecondLevelCacheAssociativity;
ULONG VdmAlert;
ULONG KernelReserved[14];
ULONG SecondLevelCacheSize;
ULONG HalReserved[16];
ULONG InterruptMode;
UCHAR Spare1;
ULONG KernelReserved2[17];
KPRCB PrcbData;
} KPCR, *PKPCR;
on = typedef struct _KTHREADPVOID ServiceTable;
e.g
mov eax,0x102
102 – ci sistem servis nömrəsinə məxsus funksiya = NtReadFile
lkd> ln poi(KiServiceTable + 102 * 4)
(82193023) nt!NtReadFile
32 bit register üzərindəki (eax) sistem servis nömrəsi bu parçalardan ibarətdir:bit 0-11: sistem servis nömrəsi çağırış üçün
bit 12-13: service descriptor table üçün istifadə edilir
bit 13-31: istifadə edilmir
Keçək dəfə mövzuda bəhs etmişdim Win iki System Descriptor Table istifadə etdiyindən.
KeServiceDescriptorTableShadow
——————————
KeServiceDescriptorTable
sistem servis nömrələrdindən 0x0-0xFFF aralıqında bütün nömrələr KeServiceDescriptorTable indexləri olaraq istifadə edilir.
0x1000-0x1FFF aralıqları isə KeServiceDescriptorTableShadow indexləri olaraq istifadə edilir.
SSN(SYSTEM SERVİCE NUMBER) – SSDT (System Descriptor Table)
eax = 0x100F
mov edi,eax //EDI=system service number
shr edi,8 // edi>>0x8 = 10h = shifting rulezzz
and 0x10,0x30 = (0x10)d = (10000)b & (110000)b = (10000)b = (0x10)d
nt!KiFastCallEntry+0xaf:82e7a3af 83f910 cmp ecx,10h
82e7a3b2 751a jne nt!KiFastCallEntry+0xce (82e7a3ce)
Burada check edir SSDT table yoxsa Shadow Table olduğunu.ecx = 10 olarsa Shadow Table qeyd edilir əks halda SSDT regular table qeyd edilir.
No comments:
Post a Comment