Cool, but what is an IRP?

February 12th, 2007 - Fernando Roberto

My friend Slug, has just read the title of my last post, he laughed and said that most people who read this would ask, “What is an IRP?” Well, thinking about what he said and taking into consideration the a non-simplified explanation DDK reference offers us, I will write a superficial description about IRP, rid my conscience of this weight and be able to sleep again.

Let’s leave the frightening and horrifying DDK diagrams aside to try to see things in a somewhat simpler way. After that, you may refer to the sacred documentation to reinforce the concepts and take any questions about something you have already known what it is, or at least imagined.

Let’s rely on a very practical example. The File Systems drivers for example, NTFS and FAT are implemented as kernel drivers which are called File System Drivers. I imagine that many of you have had the opportunity to open and write to a file.

To start talking to a driver, which we initially make a connection with it, that is done using the CreateFile() function. Contrary to it seems, this function is not restricted to file creation, indeed, the fact of opening a file is a specific way to open the connection to the File System driver (Details about this the next time). The CreateFile function will return to us a handle which will be used to interact with the driver through functions such as ReadFile(), WriteFile() and DeviceIoControl(). By the way, they are not restricted to file operations. When an application calls ReadFile() function, the Win32 subsystem forwards this request to NtReadFile() native API, which in turn, makes the transition to Kernel-Mode and invokes the IoManager, which also in turn, will marshal the request parameters in a structure called IRP (I/O Request Packet).

IoManager sends this packet to the responsible driver, passing through the filters that might be installed on it. An anti-virus driver is a perfect example about the scenario we’re talking about. Anti-virus drivers are implemented as File System Filters. When some application writes to a file, the write IRP gets into the anti-virus before it reaches the File System driver, giving it the opportunity that it needs to check if what is being written contains any known virus signature. For a reading request, the IRP passes through the anti-virus, which installs a CompletionRoutine on it, and thus, it gains access to the IRP’s data when the reading is finished by the target driver.

The IRP is basically divided into two parts, the Header and the Stack Locations. In the IRP Header there is general information, such as status, a pointer to the thread to which the IRP belongs to, user’s buffer addresses, this IRP’s cancellation routine address and so on. In the Stack Locations there are specific request parameters. In case of a file reading, there will be the offset, size and buffer alignment.

Several Stack Locations might exist within an IRP. One for each device belonging to the chain of layers that follow until the IRP reaches the target driver. In other words, there is a Stack Location for the target driver and an additional one for each filter driver installed on it. As the IRP goes down the layers, each driver passes the received parameters from its Stack Location to the one belonging to the next driver using theIoCopyCurrentIrpStackLocationToNext() routine. Once copied, the filter can make the desired changes. If there are no changes being made in the parameters from one layer to another, then the IoSkipCurrentIrpStackLocation() routine you can be used.

The IRPs are going from one layer to another when the filter that received it forwards it to the device in which it is attached, using the IoCallDriver() function. When the IRP gets into the target driver, the driver has basically two options. If the request can be answered immediately, the driver does the desired action and completes the IRP using IoCompleteRequest() routine. But if drivers need to communicate to a device, or even to another driver, the IRP is placed in a queue, it is marked as pending and it will only be finalized when all processing is done.

Returning to our example, when a File System driver receives an writing IRP and depending on many other circumstances, the driver sets the current IRP as pending and creates a new IRP for a volume device representing the partitions, which in turn passes the requests to storage devices. Thus, it is easy to understand that disk drivers know nothing about NTFS or FAT. Storage drivers may have additional storage filters, such as a RAID for disk mirroring among other things. These new IRPs are also created by the IoManager and the whole cycle is redone for each new request.

Watching IRPs

IrpTracker is a tool capable of monitoring IRP’s activities for a particular driver or device. See the figure below where I’m selecting all the devices from Kbdclass driver, which are responsible for reading the keyboard. Notice that my machine has many keyboards. That’s because I’ve been working on anti-key-logger solutions. Selecting all these devices will be easy seeing the IRP’s activities to get the keys you press on the keyboard.

For each key you hit on the keyboard, four lines are issued on the IrpTracker. Each strike key represents two movements, the descent of the key and its release. Each movement is taken with an IRP, which comes with its Completion that means, a line is identified as Call and the other as Comp, representing, respectively, the going IRP and its return. Notice that in this example, the line Comp always comes before the line Call.

Does this mean that the IRP had its completion before being sent to the keyboard driver?

Indeed, the IRP responsible for reading the keyboard gets pended until a key is received. Thus, the system sends an IRP to the driver which waits keyboard events. When this event occurs, the driver receives the key, completes the IRP and the system receives its completion. Soon after, the system launches a new IRP to wait for the next event. Thus, we will always see pair lines, these being the previous Comp IRP and the Call for the next IRP.

If you double click one of these lines, you can see the details of each field in the IRP as shown in the figure below.

I am preparing an evolution driver of Useless.sys, which was used as a starting point in a previous post. The changes will allow it not to be so Useless and be able to demonstrate  the IRPs handling in practice, but it will be for another time.

See you there…

Leave a Reply