关键字:PsLoadedModuleList、PsActiveProcessHead、NtSystemDebugControl
        PsNtosImageBase、KdVersionBlock、KdDebuggerDataBlock、内核变量

PsLoadedModuleList等重要内核变量并未被ntoskrnl.exe导出,也没有公开的函
数可以获取。而这些内核变量对于Rootkit、Anti-Rootkit 以及内核溢出的利用等都
是至关重要的。

下面我们以PsLoadedModuleList、PsActiveProcessHead 等为例,介绍得到这些
变量的方法。

对于Windows NT 4.0和Windows 2000,尚没有“温柔”的办法可以获取这些变量,
比较理想的办法也就是特征代码搜索,这种方法虽然暴力,但通常都很有效,一般也
不会出问题;对于Windows XP和Windows 2003,我们找到了一些更加优雅的选择。

下面首先介绍特征代码搜索的方法。

[DWORD KernelBase]

要进行特征代码搜索,首先要定位ntoskrnl.exe在内核的加载地址KernelBase。
这个地址可以通过ZwQuerySystemInformation Class 10的SystemModuleInformation
来得到。参考资源[1]中给出了相关代码。事实上,KernelBase 这个值对同一操作系
统来说非常固定,可以作为常量来用:

Windows NT:  0x80100000
Windows 2000:0x80400000
Windows XP:  0x804d1000
Windows 2003: 0x804e0000

Windows NT 4.0 ntoskrnl.exe 的OptionalHeader->ImageBase = 0x80100000,
ntldr 也会按照这个值来加载内核,但是从Windows 2000开始就不是这样了。可能基
于这个历史原因,各系统的*(DWORD *)PsNtosImageBase始终初始化为0x80100000。

另外,内核变量PsNtosImageBase、KdpNtosImageBase等也指向KernelBase:

KernelBase = *(DWORD *)PsNtosImageBase
KernelBase = *(DWORD *)KdpNtosImageBase

[LIST_ENTRY PsLoadedModuleList]

PsLoadedModuleList这个全局变量指向一个保存着所加载驱动信息的双向链表。
通过它可以枚举系统中所有的驱动模块。

虽然很多内核函数都用到了PsLoadedModuleList,但是大部分并没有被导出,而
从基址开始搜会很花时间。对于Windows 2000来说,从下面这个地方入手是个好主意:

nt!MmGetSystemRoutineAddress+0x66:
804f0ed0 8b35f0e84680     mov     esi,[nt!PsLoadedModuleList (8046e8f0)]
804f0ed6 81fef0e84680     cmp     esi,0x8046e8f0

流程如下:
1、ImageBase = LoadLibraryA("ntoskrnl.exe")
2、GetProcAddress(ImageBase,"MmGetSystemRoutineAddress")
3、搜索特征代码:
*(WORD *)(MmGetSystemRoutineAddress + i) = 0x358b && /
*(WORD *)(MmGetSystemRoutineAddress + i + 6) = 0xfe81
&&
*(DWORD *)(MmGetSystemRoutineAddress + i + 2) == /
*(DWORD *)(MmGetSystemRoutineAddress + i + 8)
4、定位内核中的地址:
PsLoadedModuleList = /
*(DWORD *)(MmGetSystemRoutineAddress + i + 2) + (KernelBase - ImageBase)

从SP0到SP4,i值并不相同,但是肯定不大于0x100。

对Windows NT来说,就没这么好运气了,没有理想的可用于定位的API,只能从头
开始搜索。下面这段代码至少对SP1~SP6a的来说都具有很好的稳定性和唯一性:
801CEB1C: 8B 4D 08           mov       ecx,dword ptr [ebp+8]
801CEB1F: 89 01              mov       dword ptr [ecx],eax
801CEB21: 8B 45 0C           mov       eax,dword ptr [ebp+0Ch]
801CEB24: 89 10              mov       dword ptr [eax],edx
801CEB26: 8B 36              mov       esi,dword ptr [esi]
801CEB28: 81 FE 70 0B 15 80  cmp       esi,80150B70h  //PsLoadedModuleList

如果是用驱动做这件事情,就不必暴力搜索了,fuzen_op(fuzen_op@yahoo.com)
在FU_Rootkit2.0(参考资源[2])中使用了一段比较巧妙的代码:

DWORD FindPsLoadedModuleList (IN PDRIVER_OBJECT DriverObject)
{
    PMODULE_ENTRY pm_current;
    if (DriverObject == NULL)
        return 0;

pm_current = *((PMODULE_ENTRY*)((DWORD)DriverObject + 0x14));
    if (pm_current == NULL)
        return 0;

gul_PsLoadedModuleList = pm_current;

while ((PMODULE_ENTRY)pm_current->le_mod.Flink != gul_PsLoadedModuleList)
    {
        if ((pm_current->unk1 == 0x00000000) && /
        (pm_current->driver_Path.Length == 0))
        {
            return (DWORD) pm_current;
        }
        pm_current = (MODULE_ENTRY*)pm_current->le_mod.Flink;
    }

return 0;
}

[LIST_ENTRY PsActiveProcessHead]

理论上PsActiveProcessHead 也可以用搜索代码的方法来取,但是还有更简单的
方法。

ntoskrnl.exe导出的PsInitialSystemProcess 是一个PEPROCESS,指向system进
程的EPROCESS。这个EPROCESS的结构成员EPROCESS.ActiveProcessLinks.Blink 就是
PsActiveProcessHead:

kd> dt _EPROCESS ActiveProcessLinks.Blink poi(PsInitialSystemProcess)
   +0x0a0 ActiveProcessLinks  :  [ 0x81356900 - 0x8046e728 ]
      +0x004 Blink               : 0x8046e728  [ 0x81a2fb00 - 0xff5a4ce0 ]
kd> ? PsActiveProcessHead
Evaluate expression: -2142836952 = 8046e728

EPROCESS这个结构在不同的操作系统上各不相同,需要分别对待。

[struct _KDDEBUGGER_DATA64 KdDebuggerDataBlock]

Windows 2000 开始,系统引入了变量KdDebuggerDataBlock。其中包含了大量的
内核变量。如果能够获取到的话,可以解决许多问题。遗憾的是,Windows NT上没有
这个变量。WinDBG SDK的wdbgexts.h中包含了它的结构:
    typedef struct _KDDEBUGGER_DATA64
因为比较长,这里就不引用了。

从对5.0.2195.6902版本ntoskrnl.exe 的逆向工程结果来看,只有两个函数使用
了该变量,并且,两个函数都未导出,且代码前后没有明显特征,无法靠直接搜索代
码来获取。

但是,我们发现,ntoskrnl.exe导出了KdEnableDebugger,KdEnableDebugger会
调用KdInitSystem,而KdInitSystem 中引用了KdDebuggerDataBlock:

n < 100

Windows 2000:

KdEnableDebugger + n:
6A 00                 push    0
6A 00                 push    0
C6 05 28 41 48 00 01  mov     _PoHiberInProgress, 1
E8 1C DC 10 00        call    _KdInitSystem@8 ; KdInitSystem(x,x)

KdInitSystem + n:
68 70 02 00 00        push    270h      // sizeof(KdDebuggerDataBlock)
B9 50 D1 54 00        mov     ecx, offset _KdpDebuggerDataListHead
68 D8 FA 46 00        push    offset KdDebuggerDataBlock
8B 40 18              mov     eax, [eax+18h]
68 4B 44 42 47        push    4742444Bh // "KDBG",可以用作搜索的定位标志
A3 3C D1 54 00        mov     ds:_KdpNtosImageBase, eax
89 0D 54 D1 54 00     mov     ds:dword_54D154, ecx
89 0D 50 D1 54 00     mov     ds:_KdpDebuggerDataListHead, ecx

Windows XP

KdEnableDebugger + n:
6A 00                 push    0
6A 00                 push    0
C6 05 8C 98 47 00 01  mov     _PoHiberInProgress, 1
E8 2B 17 13 00        call    _KdInitSystem@8 ; KdInitSystem(x,x)

KdInitSystem + n:
68 90 02 00 00        push    290h
68 E0 9D 46 00        push    offset KdDebuggerDataBlock
BE 74 96 59 00        mov     esi, offset _KdpDebuggerDataListHead
68 4B 44 42 47        push    4742444Bh
89 35 78 96 59 00     mov     ds:dword_599678, esi
89 35 74 96 59 00     mov     ds:_KdpDebuggerDataListHead, esi

Windows 2003

KdEnableDebugger + n:
56                    push    esi
56                    push    esi
C6 05 0C 08 49 00 01  mov     PoHiberInProgres, 1
E8 CB AD 15 00        call    _KdInitSystem@8 ; KdInitSystem(x,x)

KdInitSystem + n:
68 18 03 00 00        push    318h
68 D0 A3 47 00        push    offset KdDebuggerDataBlock
BE 18 10 5D 00        mov     esi, offset _KdpDebuggerDataListHead
68 4B 44 42 47        push    4742444Bh
89 35 1C 10 5D 00     mov     ds:dword_5D101C, esi
89 35 18 10 5D 00     mov     ds:_KdpDebuggerDataListHead, esi

可以看出,上面代码特征的唯一性很好。用于搜索是没有问题的。我在上面同时
列出了三个系统的代码,仅仅只是为了比较,事实上,对Windows XP和Windows 2003
是完全没有必要采取如此暴力手段的。

下面介绍针对Windows XP和Windows 2003的方法。

[struct _DBGKD_GET_VERSION64 KdVersionBlock]

Opc0de和Edgar Barbosa在参考资源[3]中提到,Windows XP和Windows 2003引入
的一个新内核变量:KdVersionBlock,其结构中包含了PsLoadedModuleList。

WinDBG SDK的wdbgexts.h中有KdVersionBlock的结构:

typedef struct _DBGKD_GET_VERSION64 {
    USHORT  MajorVersion;
    USHORT  MinorVersion;
    USHORT  ProtocolVersion;
    USHORT  Flags;
    USHORT  MachineType;
    UCHAR   MaxPacketType;
    UCHAR   MaxStateChange;
    UCHAR   MaxManipulate;
    UCHAR   Simulation;
    USHORT  Unused[1];
    ULONG64 KernBase;
    ULONG64 PsLoadedModuleList;
    ULONG64 DebuggerDataList;

} DBGKD_GET_VERSION64, *PDBGKD_GET_VERSION64;

KdVersionBlock是KPCR的一个成员:

lkd> dt _kpcr ffdff000
nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x000 Used_ExceptionList : 0xf717dbcc
   +0x004 Used_StackBase   : (null)
   +0x008 PerfGlobalGroupMask : (null)
   +0x00c TssCopy          : 0x80042000
   +0x010 ContextSwitches  : 0x1f8b07a
   +0x014 SetMemberCopy    : 1
   +0x018 Used_Self        : 0x7ffde000
   +0x01c SelfPcr          : 0xffdff000
   +0x020 Prcb             : 0xffdff120
   +0x024 Irql             : 0x2 ''
   +0x028 IRR              : 0
   +0x02c IrrActive        : 0
   +0x030 IDR              : 0xffff24e0
   +0x034 KdVersionBlock   : 0x8055a3a8     <--
   +0x038 IDT              : 0x8003f400
   +0x03c GDT              : 0x8003f000
   +0x040 TSS              : 0x80042000
   +0x044 MajorVersion     : 1
   +0x046 MinorVersion     : 1
   +0x048 SetMember        : 1
   +0x04c StallScaleFactor : 0x64
   +0x050 SpareUnused      : 0 ''
   +0x051 Number           : 0 ''
   +0x052 Spare0           : 0 ''
   +0x053 SecondLevelCacheAssociativity : 0x8 ''
   +0x054 VdmAlert         : 0
   +0x058 KernelReserved   : [14] 0
   +0x090 SecondLevelCacheSize : 0x80000
   +0x094 HalReserved      : [16] 0
   +0x0d4 InterruptMode    : 0
   +0x0d8 Spare1           : 0 ''
   +0x0dc KernelReserved2  : [17] 0
   +0x120 PrcbData         : _KPRCB

Windows 2000和NT的KPCR是没有这个成员的:

kd> dt _kpcr ffdff000
nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x01c SelfPcr          : 0xffdff000
   +0x020 Prcb             : 0xffdff120
   +0x024 Irql             : 0 ''
   +0x028 IRR              : 0
   +0x02c IrrActive        : 0
   +0x030 IDR              : 0xffffffff
   +0x034 Reserved2        : 0              <--
   +0x038 IDT              : 0x80036400
   +0x03c GDT              : 0x80036000
   +0x040 TSS              : 0x802a4000
   +0x044 MajorVersion     : 1
   +0x046 MinorVersion     : 1
   +0x048 SetMember        : 1
   +0x04c StallScaleFactor : 0x64
   +0x050 DebugActive      : 0 ''
   +0x051 Number           : 0 ''
   +0x052 VdmAlert         : 0 ''
   +0x053 Reserved         : [1]  ""
   +0x054 KernelReserved   : [15] 0
   +0x090 SecondLevelCacheSize : 0
   +0x094 HalReserved      : [16] 0
   +0x0d4 InterruptMode    : 0
   +0x0d8 Spare1           : 0 ''
   +0x0dc KernelReserved2  : [17] 0
   +0x120 PrcbData         : _KPRCB

KPCR 在各版本Windows系统上的值都是固定的0xffdff000,这就给我们提供了另
一个获得PsLoadedModuleList的方法:

#define KPCR 0xffdff000
PsLoadedModuleList = *(DWORD *)( *(DWORD *)(KPCR+0x34)+0x18 )

KdVersionBlock的结构成员DebuggerDataList实际就是KdpDebuggerDataListHead,
而:
KdpDebuggerDataListHead.Flink = KdDebuggerDataBlock
KdpDebuggerDataListHead.Blink = KdDebuggerDataBlock
   
    也就是说,得到KdVersionBlock也就获得了KdDebuggerDataBlock。

[利用NtSystemDebugControl获取KdVersionBlock]

Windows XP 和Windows 2003上,kd在使用“-kl”参数本地运行的时候,即使没
有加载符号表,依然能够给出正确的PsLoadedModuleList:

Windows Server 2003 Kernel Version 3790 UP Free x86 compatible
Product: Server, suite: TerminalServer SingleUserTS
Built by: 3790.srv03_rtm.030324-2048
Kernel base = 0x804e0000 PsLoadedModuleList = 0x8056ac08

显然,Windows 5.1 以上版本提供了获取PsLoadedModuleList和KernelBase的机
制。用WinDBG对kd进行调试(煮豆燃豆萁……)发现,dbgeng.dll调用一个未文档化
的Native API NtSystemDebugControl,获取了上文提到的 KdVersionBlock。调用过
程如下:

dbgeng!DebugClient::WaitForEvent
dbgeng!RawWaitForEvent
dbgeng!WaitForAnyTarget
dbgeng!LocalLiveKernelTargetInfo::WaitForEvent
dbgeng!LiveKernelTargetInfo::InitFromKdVersion
dbgeng!LocalLiveKernelTargetInfo::GetTargetKdVersion
ntdll!NtSystemDebugControl

对于NtSystemDebugControl,除了BUGTRAQ ID 9694 的一个漏洞报告外,在互联
网上找不到任何相关信息。(事实上,作者报告的问题是不能称之为漏洞的,因为,
要执行这个API,必须拥有SeDebugPrivilege 特权,而正常情况下,只有管理员用户
才有该特权。关于该漏洞,见参考资源[4])。

逆向工程的结果显示,在Windows XP和Windows 2003上,NtSystemDebugControl
的功能号7将调用内部函数KdpSysGetVersion:
; __stdcall KdpSysGetVersion(x)

arg_0           = dword ptr  0Ch

push    esi
                push    edi
                mov     edi, [esp+arg_0]
                push    0Ah
                pop     ecx
                mov     esi, offset _KdVersionBlock
                rep movsd
                pop     edi
                pop     esi
                retn    4

利用NtSystemDebugControl,可以非常优雅地得到KdVersionBlock:

typedef enum _DEBUG_CONTROL_CODE {
    DebugGetKdVersionBlock = 7
} DEBUG_CONTROL_CODE;

EnablePrivilege(SE_DEBUG_NAME);

ZwSystemDebugControl(
    DebugGetKdVersionBlock,
    NULL,
    0,
    &KdVersionBlock,
    sizeof(KdVersionBlock),
    NULL
    );

printf ("KernBase:           0x%.8x/n",KdVersionBlock.KernBase);
printf ("PsLoadedModuleList: 0x%.8x/n",KdVersionBlock.PsLoadedModuleList);
printf ("DebuggerDataList:   0x%.8x/n",KdVersionBlock.DebuggerDataList);

除了获取KdVersionBlock之外,NtSystemDebugControl还有很多强大的功能,我
将在另外一篇文档中详细介绍。

现在总结一下。
   
    对Windows 2000来说,最重要的是搜索代码,得到 KdDebuggerDataBlock,得到
了这个,实际上也就得到了PsLoadedModuleList、PsActiveProcessHead等。

对Windows XP和Windows 2003,最佳的办法是直接利用NtSystemDebugControl得
到KdVersionBlock,然后取得KdDebuggerDataBlock。

获取Windows 系统的内核变量相关推荐

  1. 通过PowerShell获取Windows系统密码Hash

    当你拿到了系统控制权之后如何才能更长的时间内控制已经拿到这台机器呢?作为白帽子,已经在对手防线上撕开一个口子,如果你需要进一步扩大战果,你首先需要做的就是潜伏下来,收集更多的信息便于你判断,便于有更大 ...

  2. 这段百度问答,对我相关有对啊!!!----如何获取Windows系统登陆用户名

    如何获取Windows系统登陆用户名 http://zhidao.baidu.com/link?url=Hva9PkVwYZv8KSEWftSqTWe8fqM1dhoq59BurnfADmcOvFjF ...

  3. 【操作系统】Linux内核和Windows系统的内核有什么区别?

    本文内容转载自"拉勾教育"的讲义,更多课程信息请关注拉勾教育.本人在学习之余记记笔记,顺便当当搬运工! 目录 Linux内核和Windows系统的内核有什么区别? 什么是内核? 内 ...

  4. VC++ 获取Windows系统开机时间

    以下提供两种方法获取Windows系统的开机时间 第一种是使用C++的函数,该方法使用当前时间减去系统运行时间,秒级的,偶尔存在1秒的差异 第二种是使用Windows的API,该方法获取到的时间与cm ...

  5. Unity实现获取Windows系统声音并实现可视化

    效果 简介 该项目通过使用CSCore.dll来获取Windows系统声音的输出.并使用Rhythm Visualizator Pro 2.2b插件来实现上面的效果. 脚本简介(这里就只介绍一些重要的 ...

  6. [笔记]使用API函数 GetACP 获取Windows系统当前代码页

    代码页是字符集编码的别名,也称"内码表",是特定语言的字符集的一张表. 代码页分为两种:一种是ANSI代码页:另一种是OEM代码页. ⑴OEM代码页主要是用于Windows系统中的 ...

  7. C# 获取Windows系统ICON图标的四种方式-可提取各种文件夹、文件等等图标

    本文介绍的是提取Windows系统内部Icon图标的方法,就是系统资源管理器里面显示的图标,包括文件夹.文件,如:常规文件夹的图标.特定文件夹图标(磁盘根目录.收藏夹.网络共享目录等).各文件类型图标 ...

  8. 获取windows系统ip、计算机名、当前用户名

    1.用gethostname函数获取计算机名,用gethostbyname函数获取当前系统ip #include <iostream>using namespace std;#includ ...

  9. 使用API函数 GetACP 获取Windows系统当前代码页(字符编码)

    代码页是字符集编码的别名,也称"内码表",是特定语言的字符集的一张表. 代码页分为两种:一种是ANSI代码页:另一种是OEM代码页. ⑴OEM代码页主要是用于Windows系统中的 ...

最新文章

  1. 西部985,进军北京!
  2. python定义浮点数数组_tensorflow之tf.record实现存浮点数数组
  3. 软件架构解读与架构师角色培养——希赛嘉宾聊天实录
  4. TCP的粘包和拆包及Netty中的解决方案
  5. 《那些年啊,那些事——一个程序员的奋斗史》——39
  6. JAVA 文件监控 WatchService
  7. 用于地址解析的协议是服务器,tcp/ip协议和http协议
  8. ​“统治”移动处理器市场的Arm为何明年Q1才发布AI处理器?
  9. SCSI、FC、iSCSI三大协议概述
  10. 查看docker容器日志
  11. 配置多个ssh-key
  12. Shader之基础雾效 U3D-PostProcessing
  13. 【解决方案修复谷歌翻译 Windows、macOS】谷歌翻译退出了中国市场不能用了
  14. 最新黑客攻防实战从入门到精通(第二版)_学习笔记(一)
  15. C#--反汇编工具ildasm.exe
  16. 用计算机算账老是出负数是怎么回事,结存数量为负数是什么意思
  17. 阿里 P6 到底应该具备哪些核心能力?
  18. 数据仓库 OLAP
  19. 山西省大学计算机专业排名,山西省:排名前14的大学!山西的大学分为5档,前2档最难考!...
  20. 永续合约系统开发功能亮点

热门文章

  1. JAVA面试中问及HIBERNATE与 MYBATIS的对比,在这里做一下总结
  2. 还没做2022年计划?这个超赞工具送给你
  3. 再好的产品经理跑不过一半的A/B测试
  4. 中国游戏公司研运一体发展专题分析2020
  5. 产品经理必备的两种心态
  6. strlen函数_四种好用的PHP自定义加密函数(可逆/不可逆)
  7. python如何输出两列数据_Python-如何将一列分为两列?
  8. 作者:​邓波(1973-),男,博士,北京系统工程研究所研究员。
  9. 【操作系统】操作系统的设计与实现
  10. 进程间通信之3----信号量