Use deviceiocontrol to implement application and driver communication
1.
Readfile and writefile can communicate with the application and driver, and the other Win32 API is deviceiocontrol.
The application customizes the IO control code and then calls the deviceiocontrol function. The IO manager generates an irp_mj_device_control majorfunction, And the minorfunction is the IRP of the Control Code defined by the user, the system calls the dispatch function to process irp_mj_device_control. You can determine the minorfunction in the dispatch letter number. If the minorfunction is a custom control code, you can process it accordingly.
2.
First, introduce the deviceiocontrol function.
Bool winapi deviceiocontrol (_ in _ HANDLE hdevice, // The opened device handle _ in _ DWORD dwiocontrolcode, // custom control code, how to define _ in_opt _ lpvoid lpinbuffer later, // input buffer _ in _ DWORD ninbuffersize, // input buffer size _ out_opt _ lpvoid lpoutbuffer, // output buffer _ in _ DWORD noutbuffersize, // output buffer size _ out_opt _ lpdword lpbytesreturned, // the actual number of returned bytes, corresponding to the driver's pirp-> iostatus. information. _ Inout_opt _ lpoverlapped // overlapping operation structure pointer. If the synchronization is set to null, deviceiocontrol will block the call; otherwise, it should be designed asynchronously during programming );
For example:
Uchar inputbuffer [10]; uchar outputbuffer [10]; // set all input buffers to 0 xbbmemset (inputbuffer, 0xbb, 10); DWORD dwoutput; // input buffer as input, output buffer as output bool Bret = deviceiocontrol (hdevice, ioctl_test, inputbuffer, 10, & outputbuffer, 10, & dwoutput, (lpoverlapped) null );
3.
Define Io control code
# Define ioctl_device_function ctl_code (devicetype, function, method, access)
Ioctl_device_function: minorfunction of the generated IRP
Devicetype: the type of the device object. Device type reference: http://blog.csdn.net/liyun123gx/article/details/38058965
Function: Custom Io control code. Take 0x800 to 0 xfff for your own definition, because 0x0 to 0x7ff are reserved by Microsoft.
Method: data operation mode.
Method_buffered: Buffer Mode
Method_in_direct: Direct Write mode
Method_out_direct: Direct Read mode
Method_neither: Neither Mode
Access: access permission, which can be set:
File_any_access: indicates that the user has all permissions.
File_read_data: indicates that the permission is read-only.
File_write_data: indicates that the permission is writable.
Or file_write_data | file_read_data: indicates that the permission is readable and writable, but the permission of file_any_access has not been reached.
Example: # define ioctl_test ctl_code (file_device_unknown, 0x800, method_buffered, file_any_access)
4. The following describes different data operation modes.
(1). method_buffered: Buffer Mode
The user-provided input buffer content is copied to the pirp-> associatedirp. systembuffer memory address in the IRP. The replication length is the number of input bytes specified by deviceiocontrol.
When the driver outputs data, it can also be written to pirp-> associatedirp. systembuffer. The operating system will copy the data of this address to the output buffer of deviceiocontrol.
You can specify the number of bytes to be copied by setting pir-> iostatus. Information.
The following code shows the buffer size and IOCTL of the input buffer output in the dispatch letter.
// Obtain the current stack pio_stack_location stack = iogetcurrentirpstacklocation (pirp); // obtain the input buffer size ulong cbin = stack-> parameters. deviceiocontrol. inputbufferlength; // obtain the output buffer size ulong cbout = stack-> parameters. deviceiocontrol. outputbufferlength; // obtain the ioctl code ulong code = stack-> parameters. deviceiocontrol. iocontrolcode;
Operate pirp-> associatedirp. systembuffer to input and output data.
Uchar * inputbuffer = (uchar *) pirp-> associatedirp. systembuffer; For (ulong I = 0; I <cbin; I ++) {kdprint ("% x \ n", inputbuffer [I]);} // operation output buffer. The output buffer and input buffer are two buffers: uchar * outputbuffer = (uchar *) pirp-> associatedirp. systembuffer; memset (outputbuffer, 0xaa, cbout); // you can specify the length of the buffer pirp> iostatus. information = cbout;
(2) Direct Memory mode of method_in_direct and method_out_direct
In the same buffer mode, the user-provided input buffer content is copied to the pirp-> associatedirp. systembuffer memory address in the IRP. The replication length is the number of input bytes specified by deviceiocontrol.
In direct memory mode, the operating system locks the output buffer specified by deviceiocontrol, and remaps an address in kernel mode.
In the dispatch letter, the pirp-> mdladdress In the IRP records the output buffer specified by deviceiocontrol. The dispatch function should map the memory to the memory address in kernel mode using mmgetsystemaddressformdlsafe.
The size of the input and output buffer and the ioctl mode are the same as those in the buffer mode.
In addition, pay attention to the ctl_code permission. If the device is opened in read-only mode, the ioctl operation of method_in_direct will fail.
Dispatch letter count to process Direct Memory mode:
// Display the input buffer data uchar * inputbuffer = (uchar *) pir-> associatedirp. systembuffer; For (ulong I = 0; I <cbin; I ++) {kdprint ("% x \ n", inputbuffer [I]);} // pirp-> mdladdress is the same as the deviceiocontrol output buffer address kdprint ("User address: 0x % 08x \ n", mmgetmdlvirtualaddress (pirp-> mdladdress ))); uchar * outputbuffer = (uchar *) mmgetsystemaddressformdlsafe (pir-> mdladdress, normalpagepriority); // inputbuffer is mapped to the memory address in kernel mode and must be in memset (outputbuffer, 0xaa, cbout );
(3) method_neither: Neither Mode
Because this mode directly accesses the user mode address, this mode is very dangerous, so this mode is rarely used.
The user mode address must ensure that the thread that calls deviceiocontrol runs in the same thread context as the dispatch function.
The input buffer obtained by the dispatch function is different from the first two methods. In this mode, the input buffer is obtained through the stack-> parameters. deviceiocontrol. type3inputbuffer; of the IO stack.
The driver uses pirp-> userbuffer to obtain the output buffer.
The length of the input and output buffer is the same as that of the first two IOCTL methods.
Because the dispatch function of the driver cannot guarantee that the user address passed in is a valid address, you must make a read/write judgment on the incoming user mode address. This requires the probeforread function and probeforwrite function to be used in combination with _ Try _ execpt.
The following shows the neither mode in the dispatch letter number drive.
// Display the input buffer data uchar * userinputbuffer = (uchar *) Stack-> parameters. deviceiocontrol. type3inputbuffer; kdprint ("userinputbuffer: 0x % 0x \ n", userinputbuffer); // obtain the user mode address pvoid useroutputbuffer = pirp-> userbuffer; kdprint ("useroutputbuffer: 0x % 0x \ n ", useroutputbuffer) ;__ try {kdprint (" Enter _ Try block \ n "); // judge whether the pointer is readable probeforread (userinputbuffer, cbin, 4); // display the input buffer content for (ulong I = 0; I <cbin; I ++) {kdprint ("% x \ n", userin Putbuffer [I]);} // determines whether the pointer can write probeforwrite (useroutputbuffer, cbout, 4); // operate the output buffer memset (useroutputbuffer, 0xaa, cbout ); // if an exception is thrown above, the statement will not be executed in the future! Pirp-> iostatus. information = cbout; kdprint ("Leave _ Try block \ n"); }__ expect T (exception_execute_handler) {kdprint ("Catch the exception \ n ")); kdprint ("the program will keep going \ n"); status = status_unsuccessful;} pir-> iostatus. information = cbout;
NOTE: If something is wrong, please correct it.
Use deviceiocontrol to implement application and driver communication