DeviceIoControl VS WriteFile and ReadFile

Status
Not open for further replies.

Zhou He

Level 1
Thread author
Mar 13, 2017
7
When communicating userland and kernel which method is better?
In better I mean have a significant advantage in any case.

I'm a noob and when reading tutorials and codes, they use different methods to communicate.
 
Last edited:
D

Deleted member 65228

There is a lot within this topic due to how many different methods there really are, some are however naturally unsupported. It is best to stick to supported, documented techniques though (for stability and maintenance purposes).

Functions like DeviceIoControl require a handle. You can acquire this handle through using CreateFile(A/W). You can use this combination to send IOCTL (Input & Output Control) codes to the device driver which will handle the requests through IRP_MJ_XXXXXX callback routines.

You can also use Shared Events (trigger an event to cause a routine elsewhere execute an operation), Inverted Calls, or even shared named pipes (although named pipes is not supported in kernel-mode by default - using this technique would be "hacky" and not recommended unless you are certain you know what you are doing and have a good reason for doing so). One of the most preferred methods for kernel-mode to user-mode communication transitioning is through Ports (an example for this is demonstrated by Microsoft in the file-system mini-filter device driver samples).

Regarding named pipes in kernel-mode... You can re-create your own wrapper for Win32 API pipe functions yourself. NtCreateNamedPipeFile (NTDLL) is called when CreateNamedPipe (KERNEL32) is called however it is not exported by NTOSKRNL but since NTDLL performs a system call, this means there is an address to the routine within the System Service Dispatch Table (SSDT). NtCreateNamedPipeFile will call IoCreateFile (a function which is exported and accessible) therefore it would be much easier to just rely on IoCreateFile to create a kernel-mode equivalent of NtCreateNamedPipeFile. Regarding some of the other functions, they will internally call functions like NtFsControlFile so you could use FltFsControlFile, and the alike.

Alternatively, you can go old-school and store data within protected files or protected registry keys (e.g. under the values for specific entries) (which only your device driver or user-mode program can access).

OSR have a great article surrounding Inverted Calls, you can find it here: The Inverted Call Model in KMDF - OSR

OSR also have a great article surrounding Shared Events, although it was posted back in 2002, it is still extremely beneficial IMO. You can find it here: The NT Insider:Sharing Is Caring - Sharing Events Between Kernel-User Mode

I wish you great luck with accomplishing your goals, I am sure you will manage to achieve them! :)
 
D

Deleted member 65228

I know of a method which is not "supported" but will work if implemented correctly.

1. User-Mode DLL contains a function stub which can be executed within the context of your user-mode process.
2. Kernel-Mode device driver will read the contents of the DLL into memory and extract the bytes for the target function stub.
3. Kernel-Mode device driver will attach to the user-mode process.
4. Kernel-Mode device driver will allocate memory (within the context of the attached process) to store the shell-code (correct size).
5. Kernel-Mode device driver will copy the bytes for the function stub which was previously extracted from the User-Mode DLL into the allocated memory.
6. Kernel-Mode device driver will get a remote thread created within the attached process (NtCreateThreadEx -> retrieve the address from the SSDT) and point the staring address for the new thread to the same address you allocated/wrote to in memory, you can pass an argument if the function requires data (e.g. in a pointer structure).
7. Kernel-Mode device driver detaches from the process, therefore it is no longer executing within the context of the targeted process.

NtWriteVirtualMemory is not exported and finding the address from the SSDT is not worth the hassle (especially for x64 systems where ntoskrnl.exe does not export the SSDT). You can call memcpy while executing within the context of the targeted process (after having attached to it) to fill the allocated memory.

The above technique is basically code-cave injection from kernel-mode. You can hard-code shell-code in a byte array instead if that is preferred, up to you. You must ensure the code will work within the target process though, and you can perform testing using a user-mode injector beforehand. There are many guidelines for code-cave injection (e.g. usage of strings, you cannot call functions normally since you are not connected to the Import Address Table (therefore you'd need to scan the Export Address Table and resolve imports manually), etc.). Remote thread creation is optional, Asynchronous Procedure Calls work too (however you must find an alert-able thread to target preferably).

Other implementations similar to the above should work as well. It is a lot more hassle than using supported methods though, therefore unrecommended unless there is a genuine need to do this over documented and supported techniques for communication. No matter what implementation you end up working with, you must ensure it is secure otherwise your product will be potentially abused for unauthorised mischief more than others.
 
Status
Not open for further replies.

About us

  • MalwareTips is a community-driven platform providing the latest information and resources on malware and cyber threats. Our team of experienced professionals and passionate volunteers work to keep the internet safe and secure. We provide accurate, up-to-date information and strive to build a strong and supportive community dedicated to cybersecurity.

User Menu

Follow us

Follow us on Facebook or Twitter to know first about the latest cybersecurity incidents and malware threats.

Top