Callgate to user : nt!KeUserModeCallback & ROP / MDL

Sometimes in kernel developement is needed to process some user mode data. But some of data – structs are internal and not so well documented, and due to this are available functions which work with these structures, but these are often exported just for user mode only. What are options in that case ?

  • user mode component – service / application
  • find kernel mode alternative function – often not exported
  • reverse structure – parse it by yourself
  • nt!KeUserModeCallback

In this blog post I will describe last mentioned method, you do not need additional resources or reversing undocomented structures. Some articles related to nt!KeUserModeCallback ring0 – ring3 – ring0 gate :

Seems it is no so well documented function, and it is used primary by win32k to call user32 methods. But still it is exported and available to use!

Nice feature to call to user mode, but seems it have some preprocessing :

nt!KeUserModeCallback expect apiNumber! Next interesting information for us will be what kind of api calls can be invoked via this callback.


lets take a look at one of them, my favorite user32!_fnDWORD

You can 3 times guess why is this api interesing to me so much – an interesting call on the board!*

As definition of nt!KeuserModeCallback routine uncover, you provide to choosen api your own buffer and also its length. Interesting is also how is inputBuffer provided to usermode api itself :

So data are copied onto stack, nice! And when you take closer look at user32!_fnDWORD function, you can see that 5 parameters which are passed to well looked call are stored in inputBuffer. And in addition address of this call is stored in inputBuffer as well!

Seems all count for us, but there are one more thing left. How to provide cpl3 code address for calling ?

  • ROP – all you need you already have!
  • MDL & PTE – get less privilages to your code

ROP technique :

I personaly like this technique because it is fun to play with it, but for developers it is most probably not so cool sollution, because it need to carry on with various OS version for compatibility, and same time optionally have and ROP gadgets tool (OptiRop) / compiler (ROPC) to spare your time at finding appropriate ROP sled.

I was lucky enough and I rellatively easly find suffictient ROP sled for rulling over control flow, after user32!_fnDWORD magic call was invoked. Indeed it was due to fact of low complexity of needed code

PoC for ROP method :

Seems ROP comes handy not just in exploit case, and its main pros is that it is non-invasive method, where you use already present ring3 code. This method is transparent, but on the other side, when it comes to developement, it is needed to keep eye on different versions of OS, where binaries are changed to implement correct ROP gadgets. This can be pain in the ass – maybe when you have available ROP tool at runtime it can solve this problem more genericaly.


Another option how to obtain our goal, is developing more friendly method – share kernel mode code with usermode. This can be done by documented methods – memory descriptor list (MDL).

MmProbeAndLockPages performs the following operations:

  1. If the specified memory range is paged to a backing store (disk, network, and so on),MmProbeAndLockPages makes it resident.
  2. The routine then confirms that the pages permit the operation specified by the Operation parameter.
  3. If the memory range permits the specified operation, the routine locks the pages in memory so that they cannot be paged out. Use the MmUnlockPages routine to unlock the pages.
  4. Finally, the routine updates the page frame number (PFN) array in the MDL to describe the locked physical pages.

Cool, almost done! But one problem is present here, and that you can share this code with cpl3 – but with no exec privilages!

But when we look at 4th point of processing MmProbeAndLockPages routine, we can get an inspiration, and alter PTE and its NoExec flag itself!

OK, everything is ready, Poc of MDL/PTE based method:

Final implementation of invoking cpl3 callback and using ring3 api – PoC for invoking one parameter API “KERNELBASE!FreeLibrary” :

Hope this article can be to someone usefull, and code you can find at GitHub :
Callback2User, and that is part of my kernel shareds

* also other apis include call to address stored at inputBuffer, but this api have almost no processing, is really straightforward and do not move RSP to far from original so inputBuffer is on the shot for ROP technique!

Leave a comment


  1. Nice concept with ROP in callbacks
    But about sharing user buffer and modifying PTE’s NX bit – isn’t it easier to just use documented ZwAllocateVirtualMemory() to allocate executable pages in user process?

Leave a Reply

[ Ctrl + Enter ]

Go To Top

Get every new post delivered to your Inbox

Join other followers: