确实通过读取FS寄存器指定的内存可以获得很多系统关键信息, 主要是和进线程相关的很多信息,例如
代码:
lkd> u PsGetCurrentProcess nt!IoGetCurrentProcess: 804f0700 64a124010000 mov eax,dword ptr fs:[00000124h] 804f0706 8b4044 mov eax,dword ptr [eax+44h]
代码:
lkd> u PsGetCurrentThread nt!PsGetCurrentThread: 8052c42a 64a124010000 mov eax,dword ptr fs:[00000124h]
那么FS作为段寄存器到底指向的是什么呢? 我取了内核态和用户态FS的值,在内核态FS=0x30, 在用户态FS=0x3B。实际上如果你去看wrk中SwapContext 和 KiSystemServices的代码就会发现,FS 只会是前面提到的两个固定值。OK, 且看内核态FS 指向哪里:
代码:
kd> r gdtr gdtr=8003f000 kd> dq 8003f000 8003f000 00000000`00000000 00cf9a00`0000ffff 8003f010 00cf9200`0000ffff 00cffa00`0000ffff 8003f020 00cff200`0000ffff 80008b04`200020ab 8003f030 ffc092df`f0000001 0040f300`00000fff 8003f040 0000f200`0400ffff 00000000`00000000 8003f050 80008954`a3000068 80008954`a3680068 8003f060 00009202`2f30ffff 0000920b`80003fff 8003f070 ff0092ff`700003ff 80009a40`0000ffff
代码:
kd> .formats 0x30 Evaluate expression: Hex: 00000030 Decimal: 48 Octal: 00000000060 Binary: 00000000 00000000 00000000 00110000 Chars: ...0 Time: Thu Jan 01 08:00:48 1970 Float: low 6.72623e-044 high 0 Double: 2.37152e-322
RPL 0 TI 0 Index 6
意思是从GDTR中取第6项,here we go 从前面打印的内存中取第6项,内容为
ffc092df`f0000001。后面我们需要解释这个描述符项的含义。再次参照intel手册
代码:
kd> .formats ffc092df Binary: 11111111 11000000 10010010 11011111 kd> .formats f0000001 Binary: 11110000 00000000 00000000 00000001
按手册中规则的形式,拼接出段的基地址
11111111 11011111 11110000 00000000
把这个数值转换成16进制为 0xFFDFF000 。 如果你熟悉windows内核的话现在应该WOW了。是的,这是关于运行态最重要的信息PCR的地址,而且这个地址永远固定在0xFFDFF000(单核,XP)。这里是一个KPCR结构,这个结构里有关于当前线程的所有信息。
至此,我们搞清楚了在内核里面为什么可以通过FS方便的取得进程,线程信息,也解释前面列举的两个函数的原理。
在用户态,走一遍我刚才介绍的流程一切也就了然了。过程不再赘述。得到的FS 指向的虚拟内存地址为7ffdd000。这个地址正好是线程的TEB的地址。实际上,KPCR里有字段直接指向了线程的TEB。