Author: Yu
Email: tombkeeper [0x40] nsfocus [0x2e] com
Tombkeeper [0x40] xfocus [0x2e] org
Completed at: 2004.07.30
Keywords: psloadedmodulelist, psactiveprocesshead, ntsystemdebugcontrol
Psntosimagebase, kdversionblock, kddebuggerdatablock, kernel variable
Psloadedmodulelistand other important internal kernel changes are not exported by ntoskrnl.exe, and no public letter is provided.
Number can be obtained. These kernel variables are used for rootkit, anti-rootkit, and kernel overflow.
Is crucial.
The following describes how to use psloadedmodulelist, psactiveprocesshead, and so on.
Variable Method.
For Windows NT 4.0 and Windows 2000, there is no "gentle" way to get these variables,
The ideal method is feature.CodeSearch. Although this method is violent, it is usually effective and generally
There is no problem; for Windows XP and Windows 2003, we have found some more elegant options.
The following describes the feature code search method.
[DWORD kernelbase]
To search for the kernel code, you must first set the loading address of ntoskrnl.exe in the kernel kernelbase.
This address can be used through systemmoduleinformation of zwquerysysteminformation class 10.
. The related code is provided in resource [1. In fact, the value of kernelbase applies to the same operating system.
In general, it is very fixed and can be used as constants:
Windows NT: 0x80100000
Windows 2000: 0x80400000
Windows XP: 0x804d1000
Windows 2003: 0x804e0000
Optionalheader of Windows NT 4.0 ntoskrnl.exe-> imagebase = 0x80100000,
Ntldr also loads the kernel according to this value, but this is not the case since Windows 2000. Possible basis
For this historical reason, the * (DWORD *) psntosimagebase of each system is always initialized to 0x80100000.
In addition, kernel variables such as psntosimagebase and kdpntosimagebase also point to kernelbase:
Kernelbase = * (DWORD *) psntosimagebase
Kernelbase = * (DWORD *) kdpntosimagebase
[List_entry psloadedmodulelist]
The global variable psloadedmodulelist points to a bidirectional linked list that stores the loaded driver information.
It can enumerate all the driver modules in the system.
Although many kernel functions use psloadedmodulelist, most of them are not exported.
It takes a lot of time to start searching from the base address. For Windows 2000, it is a good idea to start from the following:
NT! Mmgetsystemroutineaddress + 0x66:
804f0ed0 8b35f0e84680 mov ESI, [nt! Psloadedmodulelist (8046e8f0)]
804f0ed6 81fef0e84680 cmp esi, 0x8046e8f0
The process is as follows:
1. imagebase = loadlibrarya ("ntoskrnl.exe ")
2. getprocaddress (imagebase, "mmgetsystemroutineaddress ")
3. Search feature code:
* (Word *) (mmgetsystemroutineaddress + I) = 0x358b &&/
* (Word *) (mmgetsystemroutineaddress + I + 6) = 0xfe81
&&
* (DWORD *) (mmgetsystemroutineaddress + I + 2) =/
* (DWORD *) (mmgetsystemroutineaddress + I + 8)
4. Locate the address in the kernel:
Psloadedmodulelist =/
* (DWORD *) (mmgetsystemroutineaddress + I + 2) + (kernelbase-imagebase)
From sp0 to SP4, the I value is not the same, but it must not be greater than 0x100.
For Windows NT, there is no such good luck, and there is no ideal API for locating.
Start searching. The following code at least applies to SP1 ~ SP6a has good stability and uniqueness:
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
If you are using a driver to do this, you don't have to perform a brute force search, fuzen_op (fuzen_op@yahoo.com)
Use a clever code in fu_rootkit2.0 (refer to resource [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]
In theory, psactiveprocesshead can also be retrieved using the code search method, but it is simpler.
Method.
The psinitialsystemprocess exported by ntoskrnl.exe is a peprocess, pointing
. The eprocess structure member eprocess. activeprocesslinks. Blink is
Psactiveprocesshead:
Kd> dt _ eprocess activeprocesslinks. Blink poi (psinitialsystemprocess)
+ 0x0a0 activeprocesslinks: [0x81356900-0x8046e728]
+ 0x004 Blink: 0x8046e728 [0x81a2fb00-0xff5a4ce0]
Kd>? Psactiveprocesshead
Evaluate expression:-2142836952 = 8046e728
The eprocess structure varies with operating systems and needs to be treated separately.
[Struct _ kddebugger_data64 kddebuggerdatablock]
In Windows 2000, The kddebuggerdatablock variable is introduced. Including a large number
Kernel variable. If it can be obtained, it can solve many problems. Unfortunately, Windows NT does not
This variable. The wdbgexts. H of the windbg SDK contains its structure:
Typedef struct _ kddebugger_data64
Because it is long, it will not be referenced here.
According to the reverse engineering results of 5.0.2195.690220.ntoskrnl.exe, only two functions are used.
And the two functions are not exported, and there are no obvious features before and after the code, you cannot directly search for this variable.
.
However, we found that ntoskrnl.exe exported kdenabledebugger, and kdenabledebugger will
Call kdinitsystem, and kdinitsystem references 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 270 H // 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 + 18 h]
68 4B 44 42 47 push 4742444bh // "kdbg", which can be used as a search marker
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 290 H
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 318 H
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
We can see that the features of the above Code are very unique. There is no problem with searching. At the same time
The code for listing the three systems is only for comparison. In fact, for Windows XP and Windows 2003
There is no need to take such violent means.
The following describes the methods for Windows XP and Windows 2003.
[Struct _ dbgkd_get_version64 kdversionblock]
Opc0de and Edgar Barbosa mentioned in reference resource [3] That Windows XP and Windows 2003 introduce
A new kernel variable: kdversionblock, whose structure contains psloadedmodulelist.
The wdbgexts. H of the windbg SDK contains the kdversionblock structure:
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 is a member of 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
Kpcr for Windows 2000 and NT does not have this member:
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: 0 xffffffff
+ 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
The value of kpcr in each version of Windows system is fixed 0xffdff000, which provides us with another
A method to obtain psloadedmodulelist:
# Define kpcr 0xffdff000
Psloadedmodulelist = * (DWORD *) (kpcr + 0x34) + 0x18)
The debuggerdatalist of the kdversionblock structure is actually kdpdebuggerdatalisthead,
And:
Kdpdebuggerdatalisthead. flink = kddebuggerdatablock
Kdpdebuggerdatalisthead. Blink = kddebuggerdatablock
In other words, the obtained kdversionblock also obtains kddebuggerdatablock.
[Obtain the kdversionblock using ntsystemdebugcontrol]
On Windows XP and Windows 2003, when KD runs locally using the "-KL" parameter
The correct psloadedmodulelist can still be provided when the symbol table is loaded:
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
Obviously, Windows 5.1 and later versions provide machines for obtaining psloadedmodulelist and kernelbase.
. Debug kD with windbg (boiled beans and beans ......) It is found that dbgeng. dll calls an undocumented
The native API ntsystemdebugcontrol of gets the kdversionblock mentioned above. Called
The process is as follows:
Dbgeng! Debugclient: waitforevent
Dbgeng! Rawwaitforevent
Dbgeng! Waitforanytarget
Dbgeng! Locallivekerneltargetinfo: waitforevent
Dbgeng! Livekerneltargetinfo: initfromkdversion
Dbgeng! Locallivekerneltargetinfo: gettargetkdversion
Ntdll! Ntsystemdebugcontrol
For ntsystemdebugcontrol, in addition to a vulnerability report of BugTraq ID 9694
No relevant information is found online. (In fact, the issues reported by the author cannot be called vulnerabilities, because,
To execute this API, you must have the sedebugprivilege privilege. Normally, only the administrator user
. For details about this vulnerability, refer to resource [4].
The results of reverse engineering show that ntsystemdebugcontrol on Windows XP and Windows 2003
Function 7 calls the internal function 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
Using ntsystemdebugcontrol, you can obtain the kdversionblock elegantly:
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 );
In addition to obtaining the kdversionblock, ntsystemdebugcontrol has many powerful functions.
This document will be detailed.
To sum up.
For Windows 2000, the most important thing is to search for code, get kddebuggerdatablock, and get
In fact, psloadedmodulelist and psactiveprocesshead are obtained.
For Windows XP and Windows 2003, the best way is to directly use ntsystemdebugcontrol
Go to the kdversionblock and obtain the kddebuggerdatablock.