获取Windows 系统的内核变量

来源:互联网 发布:mac鼠标怎么连接 编辑:程序博客网 时间:2024/06/06 02:52

获取Windows 系统的内核变量     

创建时间:2004-08-05

文章属性:原创

文章提交:tombkeeper (t0mbkeeper_at_hotmail.com)

获取Windows 系统的内核变量

   者:于旸

   件:tombkeeper[0x40]nsfocus[0x2e]com

        tombkeeper[0x40]xfocus[0x2e]org

 完成于:2004.07.30

 关键字:PsLoadedModuleListPsActiveProcessHeadNtSystemDebugControl

        PsNtosImageBaseKdVersionBlockKdDebuggerDataBlock、内核变量


    PsLoadedModuleList等重要内核变量并未被ntoskrnl.exe导出,也没有公开的函

 数可以获取。而这些内核变量对于RootkitAnti-Rootkit 以及内核溢出的利用等都

 是至关重要的。

     下面我们以PsLoadedModuleListPsActiveProcessHead 等为例,介绍得到这些

 变量的方法。

     对于Windows NT 4.0Windows 2000,尚没有“温柔”的办法可以获取这些变量,

 比较理想的办法也就是特征代码搜索,这种方法虽然暴力,但通常都很有效,一般也

 不会出问题;对于Windows XPWindows 2003,我们找到了一些更加优雅的选择。

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


 [DWORD KernelBase]

     要进行特征代码搜索,首先要定位ntoskrnl.exe在内核的加载地址KernelBase

 这个地址可以通过ZwQuerySystemInformation Class 10SystemModuleInformation

 来得到。参考资源[1]中给出了相关代码。事实上,KernelBase 这个值对同一操作系

 统来说非常固定,可以作为常量来用:

 Windows NT  0x80100000

 Windows 20000x80400000

 Windows XP  0x804d1000       //HDM: My computer XP SP3, KernelBase0x804d8000so,还是不要用常量了吧

 Windows 2003:0x804e000

     Windows NT4.0 ntoskrnl.exe OptionalHeader->ImageBase = 0x80100000

 ntldr 也会按照这个值来加载内核,但是从Windows 2000开始就不是这样了。可能基

 于这个历史原因,各系统的*(DWORD *)PsNtosImageBase始终初始化为0x80100000

     另外,内核变量PsNtosImageBaseKdpNtosImageBase等也指向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

 流程如下:

 1ImageBase= LoadLibraryA("ntoskrnl.exe")

 2GetProcAddress(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)

     SP0SP4i值并不相同,但是肯定不大于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 450C           mov       eax,dword ptr [ebp+0Ch]

 801CEB24: 8910              mov       dword ptr [eax],edx

 801CEB26: 8B36              mov       esi,dword ptr [esi]

 801CEB28: 81 FE70 0B 15 80  cmp       esi,80150B70h  //PsLoadedModuleList

     如果是用驱动做这件事情,就不必暴力搜索了,fuzen_opfuzen_op@yahoo.com)

 FU_Rootkit2.0(参考资源[2])中使用了一段比较巧妙的代码:

 DWORDFindPsLoadedModuleList (IN PDRIVER_OBJECT DriverObject)

 {

    PMODULE_ENTRY pm_current;

     if(DriverObject == NULL)

         return0;

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

     if(pm_current == NULL)

         return0;

    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 ]

       +0x004Blink               : 0x8046e728  [ 0x81a2fb00 - 0xff5a4ce0 ]

 kd> ?PsActiveProcessHead

 Evaluateexpression: -2142836952 = 8046e728

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

 [struct_KDDEBUGGER_DATA64 KdDebuggerDataBlock]

     Windows2000 开始,系统引入了变量KdDebuggerDataBlock。其中包含了大量的

 内核变量。如果能够获取到的话,可以解决许多问题。遗憾的是,Windows NT上没有

 这个变量。WinDBGSDKwdbgexts.h中包含了它的结构:

     typedef struct_KDDEBUGGER_DATA64

 因为比较长,这里就不引用了。

     从对5.0.2195.6902版本ntoskrnl.exe 的逆向工程结果来看,只有两个函数使用

 了该变量,并且,两个函数都未导出,且代码前后没有明显特征,无法靠直接搜索代

 码来获取。

     但是,我们发现,ntoskrnl.exe导出了KdEnableDebuggerKdEnableDebugger

 调用KdInitSystem,而KdInitSystem 中引用了KdDebuggerDataBlock

 n < 100

 Windows 2000

 KdEnableDebugger + n:

 6A 00                 push    0

 6A 00                 push    0

 C6 05 28 41 4800 01  mov     _PoHiberInProgress, 1

 E8 1C DC 10 00        call   _KdInitSystem@8 ; KdInitSystem(x,x)

 KdInitSystem +n:

 68 70 02 0000        push    270h     // sizeof(KdDebuggerDataBlock)

 B9 50 D1 5400        mov     ecx, offset _KdpDebuggerDataListHead

 68 D8 FA 4600        push    offset KdDebuggerDataBlock

 8B 40 18              mov     eax,[eax+18h]

 68 4B 44 4247        push    4742444Bh // "KDBG",可以用作搜索的定位标志

 A3 3C D1 54 00        mov    ds:_KdpNtosImageBase, eax

 89 0D 54 D1 5400     mov     ds:dword_54D154, ecx

 89 0D 50 D1 5400     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 1300        call    _KdInitSystem@8 ; KdInitSystem(x,x)

 KdInitSystem +n:

 68 90 02 0000        push    290h

 68 E0 9D 4600        push    offset KdDebuggerDataBlock

 BE 74 96 5900        mov     esi, offset _KdpDebuggerDataListHead

 68 4B 44 4247        push    4742444Bh

 89 35 78 96 5900     mov     ds:dword_599678, esi

 89 35 74 96 5900     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 1500        call   _KdInitSystem@8 ; KdInitSystem(x,x)

 KdInitSystem +n:

 68 18 03 0000        push    318h

 68 D0 A3 47 00        push   offset KdDebuggerDataBlock

 BE 18 10 5D00        mov     esi, offset _KdpDebuggerDataListHead

 68 4B 44 4247        push   4742444Bh

 89 35 1C 10 5D 00     mov    ds:dword_5D101C, esi

 89 35 18 10 5D00     mov     ds:_KdpDebuggerDataListHead, esi

     可以看出,上面代码特征的唯一性很好。用于搜索是没有问题的。我在上面同时

 列出了三个系统的代码,仅仅只是为了比较,事实上,对Windows XPWindows 2003

 是完全没有必要采取如此暴力手段的。

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

 [struct_DBGKD_GET_VERSION64 KdVersionBlock]

     Opc0deEdgarBarbosa在参考资源[3]中提到,Windows XPWindows 2003引入

 的一个新内核变量:KdVersionBlock,其结构中包含了PsLoadedModuleList

     WinDBG SDKwdbgexts.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;

     ULONG64DebuggerDataList;

 }DBGKD_GET_VERSION64, *PDBGKD_GET_VERSION64;

 KdVersionBlockKPCR的一个成员

 lkd> dt_kpcr ffdff000

 nt!_KPCR

    +0x000NtTib            : _NT_TIB

    +0x000Used_ExceptionList : 0xf717dbcc

    +0x004Used_StackBase   : (null)

    +0x008PerfGlobalGroupMask : (null)

    +0x00c TssCopy          : 0x80042000

    +0x010ContextSwitches  : 0x1f8b07a

    +0x014SetMemberCopy    : 1

    +0x018Used_Self        : 0x7ffde000

    +0x01c SelfPcr          : 0xffdff000

    +0x020Prcb             : 0xffdff120

    +0x024Irql             : 0x2 ''

    +0x028IRR              : 0

    +0x02c IrrActive        : 0

    +0x030IDR              : 0xffff24e0

    +0x034KdVersionBlock   : 0x8055a3a8     <--

    +0x038IDT              : 0x8003f400

    +0x03c GDT              : 0x8003f000

    +0x040TSS              : 0x80042000

    +0x044MajorVersion     : 1

    +0x046MinorVersion     : 1

    +0x048SetMember        : 1

    +0x04c StallScaleFactor : 0x64

    +0x050SpareUnused      : 0 ''

    +0x051Number           : 0 ''

    +0x052Spare0           : 0 ''

    +0x053SecondLevelCacheAssociativity : 0x8 ''

    +0x054VdmAlert         : 0

    +0x058KernelReserved   : [14] 0

    +0x090SecondLevelCacheSize : 0x80000

    +0x094HalReserved      : [16] 0

    +0x0d4InterruptMode    : 0

    +0x0d8Spare1           : 0 ''

    +0x0dcKernelReserved2  : [17] 0

    +0x120PrcbData         : _KPRCB

 Windows 2000NTKPCR是没有这个成员的:          

 kd> dt _kpcrffdff000

 nt!_KPCR

    +0x000NtTib            : _NT_TIB

    +0x01c SelfPcr          : 0xffdff000

    +0x020Prcb             : 0xffdff120

    +0x024Irql             : 0 ''

    +0x028IRR              : 0

    +0x02c IrrActive        : 0

    +0x030IDR              : 0xffffffff

    +0x034Reserved2        : 0              <--

    +0x038IDT              : 0x80036400

    +0x03c GDT              : 0x80036000

    +0x040TSS              : 0x802a4000

    +0x044MajorVersion     : 1

    +0x046MinorVersion     : 1

    +0x048SetMember        : 1

    +0x04c StallScaleFactor : 0x64

    +0x050DebugActive      : 0 ''

    +0x051Number           : 0 ''

    +0x052VdmAlert         : 0 ''

    +0x053Reserved         : [1]  ""

    +0x054KernelReserved   : [15] 0

    +0x090SecondLevelCacheSize : 0

    +0x094HalReserved      : [16] 0

    +0x0d4InterruptMode    : 0

    +0x0d8Spare1           : 0 ''

    +0x0dcKernelReserved2  : [17] 0

    +0x120PrcbData         : _KPRCB

 

     KPCR 在各版本Windows系统上的值都是固定的0xffdff000,这就给我们提供了另

 一个获得PsLoadedModuleList的方法:

 

 #define KPCR0xffdff000

 PsLoadedModuleList = *(DWORD *)( *(DWORD*)(KPCR+0x34)+0x18 )

    KdVersionBlock的结构成员DebuggerDataList实际就是KdpDebuggerDataListHead

 而:

 KdpDebuggerDataListHead.Flink =KdDebuggerDataBlock

 KdpDebuggerDataListHead.Blink =KdDebuggerDataBlock

     也就是说,得到KdVersionBlock也就获得了KdDebuggerDataBlock

 [利用NtSystemDebugControl获取KdVersionBlock]

     Windows XP Windows2003上,kd在使用“-kl”参数本地运行的时候,即使没

 有加载符号表,依然能够给出正确的PsLoadedModuleLis

 Windows Server2003 Kernel Version 3790 UP Free x86 compatible

 Product:Server, suite: TerminalServer SingleUserTS

 Built by:3790.srv03_rtm.030324-2048

 Kernel base =0x804e0000 PsLoadedModuleList = 0x8056ac08

     显然,Windows5.1 以上版本提供了获取PsLoadedModuleListKernelBase的机

 制。用WinDBGkd进行调试(煮豆燃豆萁……)发现,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 XPWindows 2003上,NtSystemDebugControl

 的功能号7将调用内部函数KdpSysGetVersion

 ; __stdcallKdpSysGetVersion(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,得到

 了这个,实际上也就得到了PsLoadedModuleListPsActiveProcessHead等。

     WindowsXPWindows 2003,最佳的办法是直接利用NtSystemDebugControl

 KdVersionBlock,然后取得KdDebuggerDataBlock

 参考资源:

 [1]内核级HOOK的几种实现与应用

http://www.xfocus.net/articles/200303/499.html

 [2]FU_Rootkit2.0

https://www.rootkit.com/vault/fuzen_op/FU_Rootkit.zip

 [3] Findingsome non-exported kernel variables in Windows XP

http://www.rootkit.com/vault/Opc0de/GetVarXP.pdf

 [4]MicrosoftWindows NtSystemDebugControl() Kernel API Function Privilege

    EscalationVulnerability

http://www.securityfocus.com/bid/9694


原创粉丝点击