Keywords: psloadedmodulelist, psactiveprocesshead, ntsystemdebugcontrol psntosimagebase, kdversionblock, kddebuggerdatablock, kernel variable
The important kernel variables such as PsloadedModuleList are not exported by ntoskrnl.exe, nor without disclosing functions can be obtained. These kernel variables are critical to the use of rootkit, anti-rootkit, and kernel overflow.
Here we take PslineDModuleList, PSActiveProcessHead, etc., which describes how to get these variables.
For Windows NT 4.0 and Windows 2000, there is no "gentle" approach to obtain these variables, and the ideal method is the feature code search. Although violence, it is usually very effective, and it will not be problematic; Windows XP and Windows 2003, we found some more elegant options.
The method of the feature code search is first introduced.
[DWORD KERNELBASE]
To perform a feature code search, first position Ntoskrnl.exe in the loading address of the kernel KERNELBASE. This address can be obtained by the SystemModuleInformation of the ZwQuerySystemInformation Class 10. Reference resource [1] is given. In fact, the value of kernelbase is very fixed to the same operating system, which can be used as a constant:
Windows NT: 0x80100000Windows 2000: 0x80400000Windows XP: 0x804d1000Windows 2003: 0x804e0000
Windows NT 4.0 ntoskrnl.exe OptionalHeader-> ImageBase = 0x80100000, NTLDR will also load the kernel according to this value, but it is not the case from Windows 2000. It may be based on this historical reason, the * (dword *) PSNTOSIMAGEBASE of each system is always initialized to 0x80100000.
In addition, the kernel variables PSNTOSIMAGEBASE, KDPNTOSIMAGEBASE, etc. also points to kernelbase:
KeernelBase = * (dword *) psntosimagebasekernelbase = * (dword *) KdpntosImageBase
[LIST_ENTRY PSLOADMODULIST]
PSLoadedModuLIST This global variable points to a bidirectional linked list that holds the loaded drive information. Through it, it can enumerate all drive modules in the system.
Although many kernel functions have been used in PslineDModuleList, most of them have not been exported, and the search starts from the base address. For Windows 2000, it is a good idea from the following place:
NT! mmgetsystemRoutineaddress 0x66: 804f0ed0 8b35f0e84680 MOV ESI, [NT! PslineedModuleList (8046e8f0)] 804f0ed6 81FEF0E84680 CMP ESI, 0x8046E8F0
Process is as follows: 1, ImageBase = LoadLibraryA ( "ntoskrnl.exe") 2, GetProcAddress (ImageBase, "MmGetSystemRoutineAddress") 3, searches the feature codes: * (WORD *) (MmGetSystemRoutineAddress i) = 0x358b && / * (WORD *) (MmGetSystemRoutineAddress i 6) = 0xfe81 && * (DWORD *) (MmGetSystemRoutineAddress i 2) == / * (DWORD *) (MmGetSystemRoutineAddress i 8) 4, the positioning in the kernel address: PsLoadedModuleList = / * (DWORD * (MMgetsystemRoutineaddress i 2) (kernelbase - imagebase) is not the same from SP0 to SP4, I value, but it is certainly no more than 0x100.
For Windows NT, there is no such good luck, there is no ideal API that can be used for positioning, can only start searching from the head. The following code has a good stability and uniqueness for SP1 ~ SP6A: 801CEB1C: 8B 4D 08 MOV ECX, DWORD PTR [EBP 8] 801ceb1f: 89 01 MOV DWORD PTR [ECX], EAX801CEB21 : 8B 45 0C MOV EAX, DWORD PTR [EBP 0CH] 801CEB24: 89 10 MOV DWORD PTR [ES], EDX801CEB26: 8B 36 MOV ESI, DWORD PTR [ESI] 801CEB28: 81 Fe 70 0B 15 80 CMP ESI, 80150B70H / / PsloadedModuleList
If you do this with the driver, you don't have to search, Fuzen_op (Fuzen_op@yahoo.com) used a smart code in fu_rootkit 2.0 (reference resource [2]):
DWORD FINDPSLOADMODULIST (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 taken by search code, but there is a simpler method.
Ntoskrnl.exe exported PSinitialSystemProcess is a peprocess, pointing to EPRocess of the System process. This 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> PsActiveProcessHeadEvaluate expression: -2142836952 = 8046e728
This structure is different in different operating systems and needs to be treated separately.
[struct _kddebugger_data64 kddebuggerdatablock]
Windows 2000 starts, the system introduces the variable kddebuggerdatablock. It includes a large number of core variables. If you can get it, you can solve many problems. Unfortunately, there is no variable on Windows NT. The WDBGEXTS.H of Windbg SDK contains its structure: typedef struct _kddebugger_data64 is not quoted here because it is relatively long.
From the reverse engineering result of the 5.0.2195.6902 version Ntoskrnl.exe, only two functions use this variable, and the two functions are not exported, and there is no obvious feature before and after the code, and it is not possible to search directly.
However, we found that ntoskrnl.exe exports KDenableDebugger, KDenableDebugger calls Kdinitsystem, and KDDebuggerDataBlock is referenced in Kdinitsystem:
N <100
Windows 2000:
KDENABLEDEBUGGER N: 6A 00 PUSH 06A 00 PUSH 0C6 05 28 41 48 00 01 MOV _POHIBERINPROGRESS, 1E8 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 _KdpDebuggerDataListHead68 D8 FA 46 00 push offset KdDebuggerDataBlock8B 40 18 mov eax, [eax 18h] 68 4B 44 42 47 push 4742444BH / / "KDBG", can be used as a searcharge flag A3 3C D1 54 00 MOV DS: _kdpntosimagebase, EAX89 0D 54 D1 54 00 MOV DS: DWORD_54D154, ECX89 0D 50 D1 54 00 MOV DS: _kdpdebuggerDataListhead, ECXWindows XP
KDENABEDEBUGGER N: 6A 00 PUSH 06A 00 PUSH 0C6 05 8C 98 47 00 01 MOV _POHIBERINPROGRESS, 1E8 2B 17 13 00 Call _kdinitsystem @ 8; Kdinitsystem (x, x)
KdInitSystem n: 68 90 02 00 00 push 290h68 E0 9D 46 00 push offset KdDebuggerDataBlockBE 74 96 59 00 mov esi, offset _KdpDebuggerDataListHead68 4B 44 42 47 push 4742444Bh89 35 78 96 59 00 mov ds: dword_599678, esi89 35 74 96 59 00 mov DS: _kdpdebuggerdatalisthead, ESI
WINDOWS 2003
KDENABEDEBUGGER N: 56 PUSH ESI56 PUSH ESIC6 05 0C 08 49 00 01 MOV POHIBERINPROGRES, 1E8 CB AD 15 00 Call _kdinitsystem @ 8; Kdinitsystem (x, x)
KdInitSystem n: 68 18 03 00 00 push 318h68 D0 A3 47 00 push offset KdDebuggerDataBlockBE 18 10 5D 00 mov esi, offset _KdpDebuggerDataListHead68 4B 44 42 47 push 4742444Bh89 35 1C 10 5D 00 mov ds: dword_5D101C, esi89 35 18 10 5D 00 mov DS: _kdpdebuggerdatalisthead, ESI
It can be seen that the uniqueness of the above code characteristics is very good. It is no problem for searching for search. I also listed three system code at the same time, just for comparison, in fact, it is not necessary to take such a violent means for Windows XP and Windows 2003.
The following describes how to make Windows XP and Windows 2003. [struct _dbgkd_get_version64 kdversionblock]
OPC0DE and EDGAR BARBOSA mentioned in Reference Resources [3], a new core variable introduced by Windows XP and Windows 2003: KDVersionBlock, which contains psloadedModuleList.
The structure of KdversionBlock in WDBGEXTS.H of Windbg SDK:
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 ffdff000nt _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: _KPRCBWindows 2000 and NT KPCR is not this member:
kd> dt _kpcr ffdff000nt _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: _kprcbkpcr on each version of the value on the Windows system is fixed 0xffdff000, which gives another PslineDModuleli ST method:
#define kpcr 0xffdff000psloadedModuleList = * (DWORD *) (* (DWORD *) (KPCR 0x34) 0x18)
KdVersionBlock structure members of DebuggerDataList is actually KdpDebuggerDataListHead, but: KdpDebuggerDataListHead.Flink = KdDebuggerDataBlockKdpDebuggerDataListHead.Blink = KdDebuggerDataBlock In other words, get KdVersionBlock also won KdDebuggerDataBlock.
[NTSYSTEMDEBUGCONTROL to get KDVERSIONBLOCK]
On Windows XP and Windows 2003, KD is running when using the "-kl" parameter, even if there is no load symbol table, it is still possible to give the correct PSLoadedModuleList:
Windows Server 2003 Kernel Version 3790 UP Free x86 compatibleProduct: Server, suite: TerminalServer SingleUserTSBuilt by: 3790.srv03_rtm.030324-2048Kernel base = 0x804e0000 PsLoadedModuleList = 0x8056ac08 Obviously, Windows 5.1 or later provides a mechanism for obtaining and KernelBase of PsLoadedModuleList. Using Windbg to debug KD (boiled bean burning beans ...) found that dbGeng.dll calls an undunctional Native API NTSystemDebugControl to get the KDVersionBlock mentioned above. The calling process is as follows:
dbgeng! DebugClient :: WaitForEventdbgeng! RawWaitForEventdbgeng! WaitForAnyTargetdbgeng! LocalLiveKernelTargetInfo :: WaitForEventdbgeng! LiveKernelTargetInfo :: InitFromKdVersiondbgeng! LocalLiveKernelTargetInfo :: GetTargetKdVersionntdll! NtSystemDebugControl
For NTSystemDebugControl, there is no relevant information on the Internet in addition to a vulnerability report of BugTraq ID 9694. (In fact, the question of the author is not a vulnerability, because, to perform this API, you must have the sedebugprivilege privilege, and under normal circumstances, only the administrator user has the privilege. About this vulnerability, see the reference resource [ 4]).
The results of the reverse engineering show that on Windows XP and Windows 2003, NTSystemDebugControl's function number 7 will call internal functions 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
With NTSystemdebugControl, you can get KDVERSIONBLOCK very elegant.
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 ("PslineDModuleList: 0x% .8X / N", KDVERSIONBLOCK.PSLOADMODULIST); Printf ("DebuggerDataList: 0x% .8x / n", kdversionblock . DebuggerDataList); In addition to getting KDVERSIONBLOCK, NtsystemDebugControl has a lot of powerful features, I will introduce in another document.
Summary now. For Windows 2000, the most important thing is to search code, get KddebuggerDataBlock, get this, actually get PsLoadedModuleList, PSActiveProcesshead, etc.
For Windows XP and Windows 2003, the best way is to use NTSystemDebugControl to get KDVERSIONBLOCK, then get KddebuggerDataBlock.