分享
 
 
 

获取Windows 系统的内核变量

王朝system·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

关键字: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。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有