获取Windows 系统的内核变量
關鍵字: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 系统的内核变量的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 吐血整理!这可能是最全的机器学习工具手册
- 下一篇: 数据结构与算法必备的 50 个代码实现