6

Essentially I am looking for a function that could do for kernel mode what VirtualProtect does for user mode.

I am allocating memory using a logic exemplified by the following simplified code.

    PMDL mdl = MmAllocatePagesForMdl    
    (
        LowAddress,
        HighAddress,
        SkipAddress,
        size
    );

    ULONG flags = NormalPagePriority | MdlMappingNoExecute | MdlMappingNoWrite;
    PVOID ptr = MmGetSystemAddressForMdlSafe
    (
        mdl, 
        flags
    );

The MdlMappingNoExecute and MdlMappingNoWrite flags will have effect only on Win8+.
Moreover, using only MmGetSystemAddressForMdlSafe I cannot assign for example NoAccess protection for the memory region.

Are there any additional or alternative API-s I could use so that I can modify the page protection of the allocated memory?
A hack would do too since currently this functionality would not be in use in production code.

Roland Pihlakas
  • 4,246
  • 2
  • 43
  • 64

2 Answers2

2
C:\Windows\System32>dumpbin /exports ntdll.dll | find "Protect"
        391  17E 0004C030 NtProtectVirtualMemory
       1077  42C 000CE8F0 RtlProtectHeap
       1638  65D 0004C030 ZwProtectVirtualMemory

I think you can call Zw functions from kernel mode, and the args are generally the same as for the corresponding Nt functions. And while ZwProtectVirtualMemory is undocumented, there is a documented ZwAllocateVirtualMemory that accepts protection flags.

Another approach might be to allocate and protect virtual memory in user-mode, pass the buffer down to your driver, then create the corresponding MDL there.

asynchronos
  • 583
  • 2
  • 14
  • Thanks. Do You happen do know, can these API-s be applied to non-paged and system (not process associated) memory too? – Roland Pihlakas Aug 02 '16 at 19:54
  • *The `ZwAllocateVirtualMemory` routine reserves, commits, or both, a region of pages within the user-mode virtual address space of a specified process* - https://msdn.microsoft.com/en-us/library/windows/hardware/ff566416%28v=vs.85%29.aspx While Your answer is still useful, the question currently somewhat implicitly asks for a solution to nonpaged memory since `MmAllocatePagesForMdl` generates nonpaged memory allocation - https://msdn.microsoft.com/en-us/library/windows/hardware/ff554482(v=vs.85).aspx – Roland Pihlakas Aug 02 '16 at 20:12
  • It would still be interesting to know whether the undocumented `ZwProtectVirtualMemory` API is capable of working on nonpaged memory and how it differs from `MmProtectMdlSystemAddress`? I think I understand that `NtProtectVirtualMemory` is for user-mode memory and will fail with arguments pointing to system memory range. The difference between `Zw` and `Mm` APIs is interesting since in the internet one can find various hacks to call `ZwProtectVirtualMemory` API. If not for certain advantages... then I assume these hacks would not have been made? – Roland Pihlakas Aug 02 '16 at 20:21
2

The code I currently ended up using is below.
All used APIs are official.
Here I create another mdl for subrange of the allocated memory and change protection of that subrange.

If You trip over memory protected with this method below then:

  • at IRQL < DISPATCH_LEVEL You will get PAGE_FAULT_IN_NONPAGED_AREA fault (Invalid system memory was referenced. This cannot be protected by try-except, it must be protected by a Probe. Typically the address is just plain bad or it is pointing at freed memory.)
  • at IRQL == DISPATCH_LEVEL You will get DRIVER_IRQL_NOT_LESS_OR_EQUAL fault (An attempt was made to access a pageable (or completely invalid) address at an interrupt request level (IRQL) that is too high. This is usually caused by drivers using improper addresses.)

Note that changing the protection might fail if the subrange is part of large page allocation. Then the status will be likely STATUS_NOT_SUPPORTED.
Large page allocations can happen if the originally allocated memory region's size and alignment (which depends on SkipAddress variable in the question) are suitable and some additional preconditions are fulfilled with which I am not familiar with (perhaps starting from certain OS version).

        PMDL guard_mdl = IoAllocateMdl
        (
            NULL, 
            PAGE_SIZE * guardPageCount, 
            FALSE,           
            FALSE,  
            NULL        
        );

        if (guard_mdl)
        {
            IoBuildPartialMdl
            (
                mdl,    
                guard_mdl,  
                (PVOID)(0),  // **offset** from the beginning of allocated memory ptr
                PAGE_SIZE * guardPageCount
            );

            status = MmProtectMdlSystemAddress
            (
                guard_mdl,
                PAGE_NOACCESS
            );
        }
Roland Pihlakas
  • 4,246
  • 2
  • 43
  • 64