Which IRQL demands ZwQueryVirtualMemory()?

I try and did not success to find information which IRQL asks ZwQueryVirtualMemory().
DDK has not information about.

PASSIVE_LEVEL is OK - it is kernel callback of VirtualQuery().
But is it may be used on DISPATCH_LEVEL too?

IIRC, all Zwxxx functions in existence are callable strictly at PASSIVE_LEVEL…

Anton Bassov

Anton,
Thanks!

If so what is about NtQueryVirtualMemory() with same spec?

Regards,
MG.

what is about NtQueryVirtualMemory() with same spec?

What makes you believe there may be any difference between ZwXXX and NtXXX versions of the same service??? Look - they both
implement high-level services that take handles as parameters. You can think of handles as of logical indices to the tables that keep pointers
to the actual objects. These tables reside in pageable memory and, hence, cannot get accessed either at elevated IRQL or on the paging IO path to the pagefile.

Therefore, PASSIVE_LEVEL constraint applies to both versions…

Anton Bassov

To further expand on this, system service (Nt/Zw) calls are generally designed to be called from user mode (from which the environment is IRQL == PASSIVE_LEVEL, interrupts enabled, APCs enabled and no kernel locks held).

Unless a particular system service explicitly promises otherwise with its documented interface contract for the PreviousMode == KernelMode (i.e., Zw-call) case, it

Anton,
I guess (dream!) that MEMORY_BASIC_INFORMATION is used not only for Application, but by kernel too. It means that it may be needed on DPC and placed in non paged space.
For example, ExAllocatePoolWithTag may be called on DISPATCH_LEVEL and it needs access this information too.

I guess (dream!) that MEMORY_BASIC_INFORMATION is used not only for Application, but by kernel too. It means that it may
be needed on DPC and placed in non paged space.

__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryVirtualMemory(
HANDLE ProcessHandle,
PVOID BaseAddress,
MEMORY_INFORMATION_CLASS MemoryInformationClass,
PVOID MemoryInformation,
SIZE_T MemoryInformationLength,
PSIZE_T ReturnLength
);

Look at the very first parameter to the function, and then read my previous post again. This parameter is a logical index to the process handle table, which happens to be located in the pageable memory. Therefore, you cannot call it at IRQL > PASSIVE_LEVEL. The same applies to any other function that takes handles, regardless of object type that a given handle refers to. Once NtXXX and ZwXXX functions take handles as parameters, they are subjects to PASSIVE_LEVEL constraint …

For example, ExAllocatePoolWithTag may be called on DISPATCH_LEVEL and it needs access this information too.

MEMORY_BASIC_INFORMATION structure describes range of pages in the virtual address space of a process.

https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_memory_basic_information

Therefore, ExAllocatePoolWithTag(), which allocates memory to the kernel address space, most definitely does not need it.
Furthermore, even if it was used by ExAllocatePoolWithTag(), one would still be unable to use NtQueryVirtualMemory()
at IRQL > PASSIVE_LEVEL because of the handle parameter…

Anton Bassov

Anton,
I understand you consideration and logic, thanks for lesson - really interesting logic! :slight_smile:

Anton, check me please.
Really I want to do stack analyzing and log (dump) executable stack for some internal critical situations inside low level NDIS miniport.

  1. I plan using as handle the ZwCurrentProcess() macros, not application level process.:slight_smile:
  2. Driver works on both PASSIVE and DPC - I’d like to keep my code universal.
  3. In this low level driver I did not meet application layer addresses inside the stack. Even if it would meet - not interested, ignore!

Question:
If I write the test: try calling ZwQueryVirtualMemory() with ZwCurrentProcess() as handle on DPC and check it under Driver Verify.
If Driver Verify would answer OK, is it correct to keep this code inside Driver?

Probably you advice me other way for this job, not ZwQueryVirtualMemory()?
I obviously have option to Log (dump) long part of stack and analyze it after, by hand. But it may take Kilo bytes, I don’t like it…

If Driver Verify would answer OK, is it correct to keep this code inside Driver?

Oh, come on… no, absolutely not. If you close your eyes and drive across a bridge, and you make it to the other side safely, does that means it’s OK and that you should do it every time you cross that bridge?

There’s a contract for the interface. The contract is that you call it at no IRQL higher than IRQL PASSIVE_LEVEL. You keep that contract, or you break it at your own peril. It might work, it might not work, it might depend on many factors so that sometimes it will work and sometimes it will not.

Is that so very difficult to understand? As I said in another thread, you need to decide if you want engineering or if gambling is your game. If the latter, by all means and close your eyes every time you drive across that bridge. Heck, it just may work out for you.

Peter

Really I want to do stack analyzing and log (dump) executable stack for some internal critical situations inside low level NDIS miniport.

Well,in such case I can assure you that ZwQueryVirtualMemory() is the last function that you may want to rely upon. Your miniport code may run in an arbitrary context, which means all virtual address space info is simply irrelevant here…

Question:
If I write the test: try calling ZwQueryVirtualMemory() with ZwCurrentProcess() as handle on DPC and check it under Driver Verify.
If Driver Verify would answer OK, is it correct to keep this code inside Driver?

Well, not really, at least not in theory…

If a function is callable strictly at PASSIVE_LEVEL, it may call pageable code or access pageable data. Verifier is going to ensure that all pageable memory that your driver accesses is not physically resident in RAM. Therefore, the very first conclusion that one may make is that the very fact of passing Verifier test automatically implies that the code in question never touches pageable memory, and, hence, is 100% safe.

However, things are not necessarily as simple as they seem to be at the first glance. The problem is that, once the target function is written in an imperative language (i.e. in C), its precise code paths and data access patterns may differ from one another on separate invocations, depending on the external factors like the state of global variables. This,in turn,implies that you may be (at least in theory) lucky enough to pass Verifier tests when running potentially unsafe code - as long as it avoids “problematic” code paths and data access patters, everything is going to work just fine.

Certainly, this concern is more of a theoretical nature, but I believe it is still a valid one…

Anton Bassov

Well,in such case I can assure you that ZwQueryVirtualMemory() is the last function that you may want to rely upon. Your miniport code may run in an arbitrary context, which means all virtual address space info is simply irrelevant here….

Anton,
really I’m not interesting addresses in virtual space, even if their would is in the stack (I did not meet, but…). I’m interesting only filtering return (executable) addresses inside my driver and output them to Log with goal output call stack for following analyzing.

What other way may recommend in kernel instead of ZwQueryVirtualMemory()?
I used in the same situation on application layer VirtualQuery() and try to use it’s kernel analog. Probably it’s bad idea…:frowning:

I’m interesting only filtering return (executable) addresses inside my driver and output them to Log with goal output call stack
for following analyzing. What other way may recommend in kernel instead of ZwQueryVirtualMemory()?

I dunno - to be honest, I am not aware of any “supported” way of doing it…

Many moons ago ZwQuerySystemInformation() with ‘ModuleInformation’ information class would do the job just fine, but, according to MSDN, this function is no longer available.

https://docs.microsoft.com/en-us/windows/desktop/sysinfo/zwquerysysteminformation

To make it even worse, the above mentioned info class does not seem to be not documented anyway (I am going to leave the job
of warning you about the ramifications of using undocumented stuff and all the possible “consequences” that may ensue, to the usual suspects).

Once we are at it,why don’t you want to log the info in the kernel, and pass it to the userland for the further processing? It seems to be the right way to go here…

Anton Bassov

There’s no API that will give you arbitrary VirtualQuery-like behavior for kernel addresses. AuxKlibQueryModuleInformation will give you the loaded module list with the start/end address of each module if that’s all you need.

Scott,
Thanks!

I did not hear about AUX and want to try it.
If I have to limit my solution by PASSIVE only, it may really help. :slight_smile:

By the way, I don’t understand Microsoft.
They don’t write which level is possible for lot of kernel APIs (AUX too).
Way?!?

They don’t write which level is possible for lot of kernel APIs (AUX too).
Way?!?

File a bug. Right from the doc page. If you don’t do it, nobody else will.

Peter

Ok, Peter,
I’ve started do this job and remained note. :slight_smile:

But there are lot of errors in the DDK Documentation.
Еspecially in NDIS, where I’m working now.