|
![]() | 作者: bluecat_ [bluecat_]
![]() |
登录 |
<font color=red>本帖由 [日月双星] 从 << 菜鸟乐园>> 转移而来</font><p></p>获取Windows 系统的内核变量 创建时间:2004-08-05 文章属性:原创 文章提交:tombkeeper (t0mbkeeper_at_hotmail.com) 获取Windows 系统的内核变量 作 者:于�D 邮 件:tombkeeper[0x40]nsfocus[0x2e]com tombkeeper[0x40]xfocus[0x2e]org 完成于:2004.07.30 关键字: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。 参考资源: [1]内核级HOOK的几种实现与应用 http://www.xfocus.net/articles/200303/499.html [2]FU_Rootkit 2.0 https://www.rootkit.com/vault/fuzen_op/FU_Rootkit.zip [3] Finding some non-exported kernel variables in Windows XP http://www.rootkit.com/vault/Opc0de/GetVarXP.pdf [4]Microsoft Windows NtSystemDebugControl() Kernel API Function Privilege Escalation Vulnerability http://www.securityfocus.com/bid/9694 对大家应该有用把 ![]() [此贴被 bluecat_(bluecat_) 在 08月12日21时21分 编辑过] |
地主 发表时间: 04-08-12 21:16 |
![]() | 回复: bluecat_ [bluecat_] ![]() |
登录 |
怎么没人顶哦?![]() |
B1层 发表时间: 04-08-12 21:20 |
![]() | 回复: bug_me [bug_me] ![]() |
登录 |
暂时看不懂 下半夜的,你叫谁来顶? 我该去zZ了,88 ![]() |
B2层 发表时间: 04-08-13 05:04 |
![]() | 回复: bluecat_ [bluecat_] ![]() |
登录 |
![]() |
B3层 发表时间: 04-08-13 07:27 |
![]() | 回复: bluecat_ [bluecat_] ![]() |
登录 |
没人定啊 |
B4层 发表时间: 04-08-13 11:00 |
![]() | 回复: lxlmf [boadi] ![]() |
登录 |
不知道你写这个对我门有什么好处,但如果把你理解堆栈的思路告诉我的话,我会帮你顶 |
B5层 发表时间: 04-08-15 11:19 |
![]() | 回复: bug_me [bug_me] ![]() |
登录 |
楼上的你真的把我逗乐了![]() ![]() ![]() 我得诚恳地学习你的那种精神 ![]() |
B6层 发表时间: 04-08-15 14:24 |
![]() | 回复: bluecat_ [bluecat_] ![]() |
登录 |
顶............. |
B7层 发表时间: 04-08-21 20:27 |
![]() | 回复: hackerjune [hackerjune] ![]() |
登录 |
我都已经写过了啊````````` |
B8层 发表时间: 04-08-23 08:38 |
![]() | 回复: bluecat_ [bluecat_] ![]() |
登录 |
我比你早写啊......... 你看看日期...........你是翻版的 |
B9层 发表时间: 04-08-23 21:32 |
|
20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon
粤ICP备05087286号