kernel 獲取ntoskrnl.exe基址
- 2019 年 12 月 11 日
- 筆記
標題: kernel shellcode之尋找ntoskrnl.exe基址
http://scz.617.cn:8/windows/201704171416.txt
以64-bits為例,這是Eternalblue所用函數:
-------------------------------------------------------------------------- 0000000000000961 Private_find_MZ proc near 0000000000000961 53 push rbx /* * 起始位置 */ 0000000000000962 65 48 8B 04 25 38 00 00+ mov rax, gs:qword_38 ; KPCR.IdtBase 000000000000096B 48 8B 40 04 mov rax, [rax+4] 000000000000096F 48 C1 E8 0C shr rax, 0Ch ; 右移左移目的是對齊在頁邊界上(0x1000) 0000000000000973 48 C1 E0 0C shl rax, 0Ch 0000000000000977 0000000000000977 loop_977: 0000000000000977 48 8B 18 mov rbx, [rax] /* * 尋找"MZ" */ 000000000000097A 66 81 FB 4D 5A cmp bx, 5A4Dh 000000000000097F 74 08 jz short ok_989 0000000000000981 48 2D 00 10 00 00 sub rax, 1000h ; 以頁為單位遞減 0000000000000987 EB EE jmp short loop_977 0000000000000989 0000000000000989 ok_989: 0000000000000989 5B pop rbx 000000000000098A C3 retn 000000000000098A Private_find_MZ endp --------------------------------------------------------------------------
x64內核態GS:0指向nt!_KPCR。對於x64,必須從相應的MSR中讀取GS段基址:
kd> rdmsr 0xC0000101 msr[c0000101] = fffff800`019f9d00
在kd中有個辦法變相獲取GS段基址:
kd> !cpuinfo CP F/M/S Manufacturer MHz PRCB Signature MSR 8B Signature Features 0 6,42,7 GenuineIntel 3392 0000002300000000 0000002300000000 21193ffe Cached Update Signature 0000002300000000 Initial Update Signature 0000002300000000
kd> !pcr 0 KPCR for Processor 0 at fffff800019f9d00: ... kd> dt -v nt!_KPCR fffff800`019f9d00 struct _KPCR, 27 elements, 0x4e80 bytes +0x000 NtTib : struct _NT_TIB, 8 elements, 0x38 bytes +0x000 GdtBase : 0xfffff800`02cae000 union _KGDTENTRY64, 7 elements, 0x10 bytes +0x008 TssBase : 0xfffff800`02caf080 struct _KTSS64, 8 elements, 0x68 bytes +0x010 UserRsp : 0x2ef398 +0x018 Self : 0xfffff800`019f9d00 struct _KPCR, 27 elements, 0x4e80 bytes +0x020 CurrentPrcb : 0xfffff800`019f9e80 struct _KPRCB, 242 elements, 0x4d00 bytes +0x028 LockArray : 0xfffff800`019fa4f0 struct _KSPIN_LOCK_QUEUE, 2 elements, 0x10 bytes +0x030 Used_Self : 0x000007ff`fffdd000 Void +0x038 IdtBase : 0xfffff800`02cae080 union _KIDTENTRY64, 11 elements, 0x10 bytes +0x040 Unused : [2] 0 +0x050 Irql : 0 '' +0x051 SecondLevelCacheAssociativity : 0x10 '' +0x052 ObsoleteNumber : 0 '' +0x053 Fill0 : 0 '' +0x054 Unused0 : [3] 0 +0x060 MajorVersion : 1 +0x062 MinorVersion : 1 +0x064 StallScaleFactor : 0xd40 +0x068 Unused1 : [3] (null) +0x080 KernelReserved : [15] 0 +0x0bc SecondLevelCacheSize : 0x800000 +0x0c0 HalReserved : [16] 0xca337550 +0x100 Unused2 : 0 +0x108 KdVersionBlock : (null) +0x110 Unused3 : (null) +0x118 PcrAlign1 : [24] 0 +0x180 Prcb : struct _KPRCB, 242 elements, 0x4d00 bytes
GS:[0x38]是IdtBase,等於0xfffff800`02cae080,指向nt!_KIDTENTRY64:
kd> dt -v nt!_KIDTENTRY64 union _KIDTENTRY64, 11 elements, 0x10 bytes +0x000 OffsetLow : Uint2B +0x002 Selector : Uint2B +0x004 IstIndex : Bitfield Pos 0, 3 Bits +0x004 Reserved0 : Bitfield Pos 3, 5 Bits +0x004 Type : Bitfield Pos 8, 5 Bits +0x004 Dpl : Bitfield Pos 13, 2 Bits +0x004 Present : Bitfield Pos 15, 1 Bit +0x006 OffsetMiddle : Uint2B +0x008 OffsetHigh : Uint4B +0x00c Reserved1 : Uint4B +0x000 Alignment : Uint8B
kd> db 0xfffff800`02cae080 l 0x10 fffff800`02cae080 00 81 10 00 00 8e 87 01-00 f8 ff ff 00 00 00 00 ................
kd> dt -v nt!_KIDTENTRY64 0xfffff800`02cae080 union _KIDTENTRY64, 11 elements, 0x10 bytes +0x000 OffsetLow : 0x8100 +0x002 Selector : 0x10 +0x004 IstIndex : Bitfield 0y000 +0x004 Reserved0 : Bitfield 0y00000 (0) +0x004 Type : Bitfield 0y01110 (0xe) +0x004 Dpl : Bitfield 0y00 +0x004 Present : Bitfield 0y1 +0x006 OffsetMiddle : 0x187 +0x008 OffsetHigh : 0xfffff800 +0x00c Reserved1 : 0 +0x000 Alignment : 0x1878e00`00108100
Offset是ISR的入口地址,在中斷描述符中被分成三部分,上例中Offset實際值是:
( OffsetHigh << 32 ) | ( OffsetMiddle << 16 ) | OffsetLow = 0xfffff80001878100
對比下面的命令輸出:
kd> !idt 0 Dumping IDT: 00: fffff80001878100 nt!KiDivideErrorFault
Private_find_MZ()從IDT[0]的偏移0x4處取8位元組指針:
kd> ? poi(0xfffff800`02cae080+4) Evaluate expression: -8796067361280 = fffff800`01878e00
0xfffff80001878e00本身沒有意義,因為低16位跟Offset沒關係。但是,該值對齊在 頁邊界(0x1000)上後將位於nt模塊中。考慮兩個事實,一是高48位對應OffsetHigh、 OffsetMiddle,二是bit-15是KIDTENTRY64.Present,一般等於1。
kd> u 0xfffff80001878000 nt!KiUnexpectedInterrupt+0x1e0:
Private_find_MZ()從0xfffff80001878000開始向低址方向尋找"MZ",以頁為單位遞 減,以此定位nt模塊基址。其實際值是:
kd> lm m nt start end module name fffff800`0180b000 fffff800`01df2000 nt
顯然,這個算法是經驗性的。