kernel 获取ntoskrnl.exe基址
标题: 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
显然,这个算法是经验性的。
作者:IBinary
坚持两字,简单,轻便,但是真正的执行起来确实需要很长很长时间.当你把坚持两字当做你要走的路,那么你总会成功. 想学习,有问题请加群.群号:725864912(收费)群名称: 逆向学习小分队 群里有大量学习资源. 以及定期直播答疑.有一个良好的学习氛围. 涉及到外挂反外挂病毒 司法取证加解密 驱动过保护 VT 等技术,期待你的进入。
详情请点击链接查看置顶博客 https://www.cnblogs.com/iBinary/p/7572603.html
本文来自博客园,作者:iBinary,未经允许禁止转载 转载前可联系本人.对于爬虫人员来说如果发现保留起诉权力.https://www.cnblogs.com/iBinary/p/11984305.html
欢迎大家关注我的微信公众号.不定期的更新文章.更新技术. 关注公众号后请大家养成 不白嫖的习惯.欢迎大家赞赏. 也希望在看完公众号文章之后 不忘 点击 收藏 转发 以及点击在看功能. QQ群: