Published
- 4 min read
IOCTLs on Windows vs Linux
IOCTL: Windows vs Linux
IOCTLs (Input/Output Control) are crucial in both Windows and Linux for performing device-specific operations beyond what’s available through standard system drivers. Despite serving similar functions, their implementation and usage vary significantly between the two operating systems.
1. Concept & Usage
- Windows: Primarily used in kernel-mode drivers to communicate with and to control hardware devices
- Linux: Commonly used in both kernel modules and user-space applications for device interactions
2. Background 6 Implementation
Feature | Windows | Linux |
---|---|---|
Interface | DeviceIoControl function | ioctl system call |
Header Files | winioctl.h, ntddk.h | linux/ioctl.h, asm/ioctl.h |
Definition Method | CTL_CODE macro | _IO, _IOW, _IOR, _IOWR macros |
Security | Managed via security descriptors | Controlled by file permissions and capability checks |
Usage Context | Kernel-mode drivers and user-mode applications | Primarily user-space applications, also used in kernel modules |
Common Uses | Device management (e.g., ejecting USB devices), custom driver commands | Device configuration, special file operations, custom driver commands |
Permission Handling | Requires administrative privileges for direct device access | Root access or appropriate permissions needed for device files |
Error Handling | GetLastError() to retrieve detailed error information | Return codes and errno for error identification |
Data Transfer | Input/Output buffers specified in call | Data passed directly through ioctl parameters or via pointers to data structures |
Documentation | MSDN documentation and driver development kits | Linux man pages and kernel documentation |
3. Practical Coding Example
Finding Device Number on Windows
For USB devices, we can use Get-Disk
in PowerShell to list all disks and find the correct device number.
$ Get-Disk
Ejecting a USB Device with IOCTLs
Here’s how we can invoke an IOCTL to eject a USB device using a hybrid PowerShell/C# approach.
Demo
Source
$code = @'
using System;
using System.Runtime.InteropServices;
public class IOCTLInvoker {
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);
public const uint GENERIC_READ = 0x80000000;
public const uint GENERIC_WRITE = 0x40000000;
public const uint OPEN_EXISTING = 3;
public static bool SendIOCTL(string devicePath, uint ioctlCode) {
IntPtr device = CreateFile(devicePath, GENERIC_READ | GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (device.ToInt64() == -1) {
Console.WriteLine("Failed to open device. Error: " + Marshal.GetLastWin32Error());
return false;
}
uint bytesReturned;
bool result = DeviceIoControl(device, ioctlCode, IntPtr.Zero, 0, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
if (!result) {
Console.WriteLine("DeviceIoControl failed. Error: " + Marshal.GetLastWin32Error());
}
return result;
}
}
'@
Add-Type -TypeDefinition $code -Language CSharp
$devicePath = "\\.\PhysicalDriveN" # Replace N with the actual device number
$ioctlCode = 0x002D4808 # IOCTL code for ejecting media
if ([IOCTLInvoker]::SendIOCTL($devicePath, $ioctlCode)) {
"USB device ejected successfully."
} else {
"Failed to eject USB device."
}
IOCTL on Linux
Like Named Pipes
, IOCTL exist on Linux and Windows, but work quite differently. We added two simple IOCTL Linux examples to provide some foundational knowledge.
Blinking Keyboard LEDs using IOCTL
For a concise and direct demonstration of IOCTL in Linux, we do another simple example: using IOCTL
to blink a keyboard LEDs on and off. This is a demonstration of IOCTL’s capability to interact with devices at a low level.
C Example
This C program turn Caps Lock on and off (depending on the keyboard, it may blink). It requires root permissions to run.
#include <stdio.h>
#include <stdlib.h>
#include <linux/kd.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int console_fd = open("/dev/console", O_NOCTTY);
if(console_fd == -1) {
perror("Cannot open /dev/console");
return EXIT_FAILURE;
}
// Blink the Caps Lock LED
for(int i = 0; i < 5; i++) {
ioctl(console_fd, KDSETLED, LED_CAP);
sleep(1);
ioctl(console_fd, KDSETLED, 0);
sleep(1);
}
close(console_fd);
return EXIT_SUCCESS;
}
$ gcc -o blink_led blink_led.c
$ sudo ./blink_led`
Python Example
Using the fcntl
module in Python to perform a similar operation.
import fcntl
import os
import time
# Constants for the ioctl commands
KDSETLED = 0x4B32
LED_CAP = 0x04
# Open the console device
console_fd = os.open('/dev/console', os.O_NOCTTY)
# Blink the Caps Lock LED
for i in range(5):
fcntl.ioctl(console_fd, KDSETLED, LED_CAP)
time.sleep(1)
fcntl.ioctl(console_fd, KDSETLED, 0)
time.sleep(1)
os.close(console_fd)
Notes
- Permissions: Both examples require root access due to the direct hardware manipulation.
- Device Path: This uses
/dev/console
for simplicity, but actual device paths may vary. - Practical Use: While these examples demonstrate basic IOCTL usage in Linux, real-world applications often involve more complex operations and error handling.
Outlook
We’ve been demonstrating the ejection
of a USB device on Windows, to show the practical application of IOCTLs in managing hardware directly from scripts and applications.
We made this small exercise
as preparation to understand an evasion method using LOL Drivers
as process killers, which we’ll look at in an upcoming article.
https://www.loldrivers.io/
https://alice.climent-pommeret.red/posts/process-killer-driver/
https://github.com/h0mbre/ioctl.py
https://github.com/xalicex/Killers/