由_SEGMENT结构的变化引出的PAE问题
具体如下:
Names of kernel
《深入解析Windows操作系统 第四版》第43页表2.2列出了与上述内容一致的信息。
《深入解析Windows操作系统 第四版》第436页最后一段说明,要想选择支持PAE的内核,必须在boot.ini中通过/PAE开关来引导系统。但是通过用WinDbg调试在VMWare中的Winows XP sp2 32bit发现,在boot.ini并未使用/PAE开关时,系统加载的内核竟然是ntkrnlpa.exe。这说明,某种原因造成/PAE开关默认开启。
感谢Wikipedia,感谢
Physical_Address_Extension:
Windows XP Service Pack 2 and later, by default, on processors with the no-execute (NX) or execute-disable (XD) feature, runs in PAE mode in order to allow NX.[11] The NX (or XD) bit resides in bit 63 of the page table entry and, without PAE, page table entries only have 32 bits; therefore PAE mode is required if the NX feature is to be exploited. However, desktop versions of Windows (Windows XP, Windows Vista) limit physical address space to 4 GiB for driver compatibility and licensing[12] reasons.
记忆中NX bit貌似是DEP(数据执行保护)功能中用到的。考虑到DEP是XP sp2中加入的新功能,也就顺理成章了。也就是说,开启DEP功能的系统默认开启PAE支持。这一推论在MSDN中一篇关于PAE的文章中得到证实。
由此看来,_SEGMENT中BaseAddress成员偏移需要根据CPU数量和是否开启PAE来获得。
但是,在ntddk.h中发现如下定义
#if (NTDDI_VERSION >= NTDDI_VISTA)
extern NTSYSAPI volatile CCHAR KeNumberProcessors;
#else
#if (NTDDI_VERSION >= NTDDI_WINXP)
extern NTSYSAPI CCHAR KeNumberProcessors;
#else
extern PCCHAR KeNumberProcessors;
#endif
#endif
而且,MSDN中KeNumberProcessors的说明中也说:
Windows Server 2008 includes support for Dynamic Hardware Partitioning (DHP) in the Windows Datacenter and Enterprise Edition SKUs. As part of DHP, Windows Server 2008 supports hot adding CPUs at runtime. In a hot-add CPU environment, the number of processors may not remain constant during runtime.
Accordingly, in Windows Server 2008, code that can determine the number of processors must use KeQueryActiveProcessors instead of direct references to the kernel variable, KeNumberProcessors.
Requirements
Versions: Obsolete in Windows Vista with Service Pack 1 (SP1), Windows Server 2008, and later operating systems.
也就是说,获取CPU数目想要做到Compile Once, Run Any-version NT Platforms可不太容易。
那么我们能不能直接获取当前系统使用的内核文件的名称呢?
经过调试得到一个重要的信息:
kd> dd PsLoadedModuleList
805541a0 821fc3a8 820430c0 00000000 00000000
805541b0 00000000 00000000 00000000 00000000
kd> dt _LDR_DATA_TABLE_ENTRY 821fc3a8
nt!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x821fc340 - 0x805541a0 ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x018 DllBase : 0x804d8000
+0x01c EntryPoint : 0x8068e6dc
+0x020 SizeOfImage : 0x1f6100
+0x024 FullDllName : _UNICODE_STRING "WINDOWSsystem32ntkrnlpa.exe"
+0x02c BaseDllName : _UNICODE_STRING "ntoskrnl.exe"
+0x034 Flags : 0xc004000
+0x038 LoadCount : 1
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x0 - 0x1f6612 ]
+0x03c SectionPointer : (null)
+0x040 CheckSum : 0x1f6612
+0x044 TimeDateStamp : 0
+0x044 LoadedImports : (null)
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : 0x0074006e
通过FullDllName就能获得当前使用的内核文件的准确路径和名称了。代码如下:
typedef enum _ENUM_KERNEL_FILE
{
EKF_unknown = 0,
EKF_ntoskrnl,
EKF_ntkrnlpa,
EKF_ntkrnlmp,
EKF_ntkrpamp,
} ENUM_KERNEL_FILE;
#define PAE_ENABLED(a) ((a)==EKF_ntkrnlpa || (a)==EKF_ntkrpamp)
#define PAE_DISABLED(a) ((a)==EKF_ntoskrnl || (a)==EKF_ntkrnlmp)
// 在DriverEntry中调用
ENUM_KERNEL_FILE GetKernelFileType(PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING usNtoskrnl;
UNICODE_STRING usNtkrnlpa;
UNICODE_STRING usNtkrnlmp;
UNICODE_STRING usNtkrpamp;
UNICODE_STRING usFileName;
WCHAR szNtoskrnl[] = L"ntoskrnl.exe";
WCHAR szNtkrnlpa[] = L"ntkrnlpa.exe";
WCHAR szNtkrnlmp[] = L"ntkrnlmp.exe";
WCHAR szNtkrpamp[] = L"ntkrpamp.exe";
PUNICODE_STRING pusFileFullName = NULL;
PLIST_ENTRY PsLoadedModuleList = ((PLIST_ENTRY)(DriverObject->DriverSection))->Flink;
// _LDR_DATA_TABLE_ENTRY
// +0x024 FullDllName
// 2000到Win7都一样
pusFileFullName = (PUNICODE_STRING)RVATOVA(PsLoadedModuleList->Flink, 0x24);
RtlInitUnicodeString(&usFileName, &pusFileFullName->Buffer[pusFileFullName->Length/2-12]); //24字节=12宽字符
RtlInitUnicodeString(&usNtkrnlpa, szNtkrnlpa);
RtlInitUnicodeString(&usNtkrnlmp, szNtkrnlmp);
RtlInitUnicodeString(&usNtkrpamp, szNtkrpamp);
RtlInitUnicodeString(&usNtoskrnl, szNtoskrnl);
if (0 == RtlCompareUnicodeString(&usFileName, &usNtoskrnl, TRUE))
{
return EKF_ntoskrnl;
}
if (0 == RtlCompareUnicodeString(&usFileName, &usNtkrnlpa, TRUE))
{
return EKF_ntkrnlpa;
}
if (0 == RtlCompareUnicodeString(&usFileName, &usNtkrnlmp, TRUE))
{
return EKF_ntkrnlmp;
}
if (0 == RtlCompareUnicodeString(&usFileName, &usNtkrpamp, TRUE))
{
return EKF_ntkrpamp;
}
return EKF_unknown;
}