Welcome

首页 / 软件开发 / 汇编语言 / 实战DeviceIoControl之六:访问物理端口

实战DeviceIoControl之六:访问物理端口2010-01-10 blog.csdn.net Q 在NT/2000/XP中,如何读取CMOS数据?

Q 在NT/2000/XP中,如何控制speaker发声?

Q 在NT/2000/XP中,如何直接访问物理端口?

A 看似小小问题,难倒多少好汉!

NT/2000/XP从安全性、可靠性、稳定性上考虑,应用程序和操作系统是分开的,操作系统代码运行在核心态,有权访问系统数据和硬件,能执行特权指令;应用程序运行在用户态,能够使用的接口和访问系统数据的权限都受到严格限制。当用户程序调用系统服务时,处理器捕获该调用,然后把调用的线程切换到核心态。当系统服务完成后,操作系统将线程描述表切换回用户态,调用者继续运行。

想在用户态应用程序中实现I/O读写,直接存取硬件,可以通过编写驱动程序,实现CreateFile、CloseHandle、 DeviceIOControl、ReadFile、WriteFile等功能。从Windows 2000开始,引入WDM核心态驱动程序的概念。

下面是本人写的一个非常简单的驱动程序,可实现字节型端口I/O。

#include <ntddk.h>#include "MyPort.h"// 设备类型定义// 0-32767被Microsoft占用,用户自定义可用32768-65535#define FILE_DEVICE_MYPORT0x0000f000// I/O控制码定义// 0-2047被Microsoft占用,用户自定义可用2048-4095 #define MYPORT_IOCTL_BASE 0xf00#define IOCTL_MYPORT_READ_BYTE CTL_CODE(FILE_DEVICE_MYPORT, MYPORT_IOCTL_BASE, METHOD_BUFFERED, FILE_ANY_ACCESS)#define IOCTL_MYPORT_WRITE_BYTECTL_CODE(FILE_DEVICE_MYPORT, MYPORT_IOCTL_BASE+1, METHOD_BUFFERED, FILE_ANY_ACCESS)// IOPM是65536个端口的位屏蔽矩阵,包含8192字节(8192 x 8 = 65536)// 0 bit: 允许应用程序访问对应端口// 1 bit: 禁止应用程序访问对应端口#define IOPM_SIZE8192typedef UCHAR IOPM[IOPM_SIZE];IOPM *pIOPM = NULL;// 设备名(要求以UNICODE表示)const WCHAR NameBuffer[] = L"\Device\MyPort";const WCHAR DOSNameBuffer[] = L"\DosDevices\MyPort";// 这是两个在ntoskrnl.exe中的未见文档的服务例程// 没有现成的已经说明它们原型的头文件,我们自己声明void Ke386SetIoAccessMap(int, IOPM *);void Ke386IoSetAccessProcess(PEPROCESS, int);// 函数原型预先说明NTSTATUS MyPortDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);void MyPortUnload(IN PDRIVER_OBJECT DriverObject);// 驱动程序入口,由系统自动调用,就像WIN32应用程序的WinMainNTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath){PDEVICE_OBJECT deviceObject;NTSTATUS status;UNICODE_STRING uniNameString, uniDOSString;// 为IOPM分配内存pIOPM = MmAllocateNonCachedMemory(sizeof(IOPM));if (pIOPM == 0){return STATUS_INSUFFICIENT_RESOURCES;}// IOPM全部初始化为0(允许访问所有端口)RtlZeroMemory(pIOPM, sizeof(IOPM));// 将IOPM加载到当前进程Ke386IoSetAccessProcess(PsGetCurrentProcess(), 1);Ke386SetIoAccessMap(1, pIOPM);// 指定驱动名字RtlInitUnicodeString(&uniNameString, NameBuffer);RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);// 创建设备status = IoCreateDevice(DriverObject, 0,&uniNameString,FILE_DEVICE_MYPORT,0, FALSE, &deviceObject);if (!NT_SUCCESS(status)){return status;}// 创建WIN32应用程序需要的符号连接status = IoCreateSymbolicLink (&uniDOSString, &uniNameString);if (!NT_SUCCESS(status)){return status;}// 指定驱动程序有关操作的模块入口(函数指针)// 涉及以下两个模块:MyPortDispatch和MyPortUnloadDriverObject->MajorFunction[IRP_MJ_CREATE] =DriverObject->MajorFunction[IRP_MJ_CLOSE]=DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyPortDispatch;DriverObject->DriverUnload = MyPortUnload;return STATUS_SUCCESS;}// IRP处理模块NTSTATUS MyPortDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){PIO_STACK_LOCATION IrpStack;ULONGdwInputBufferLength;ULONGdwOutputBufferLength;ULONGdwIoControlCode;PULONG pvIOBuffer;NTSTATUS ntStatus;// 填充几个默认值Irp->IoStatus.Status = STATUS_SUCCESS;// 返回状态Irp->IoStatus.Information = 0;// 输出长度IrpStack = IoGetCurrentIrpStackLocation(Irp);// Get the pointer to the input/output buffer and it"s length// 输入输出共用的缓冲区// 因为我们在IOCTL中指定了METHOD_BUFFERED,pvIOBuffer = Irp->AssociatedIrp.SystemBuffer;switch (IrpStack->MajorFunction){case IRP_MJ_CREATE:// 与WIN32应用程序中的CreateFile对应break;case IRP_MJ_CLOSE:// 与WIN32应用程序中的CloseHandle对应break;case IRP_MJ_DEVICE_CONTROL:// 与WIN32应用程序中的DeviceIoControl对应dwIoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;switch (dwIoControlCode){// 我们约定,缓冲区共两个DWORD,第一个DWORD为端口,第二个DWORD为数据// 一般做法是专门定义一个结构,此处简单化处理了case IOCTL_MYPORT_READ_BYTE:// 从端口读字节pvIOBuffer[1] = _inp(pvIOBuffer[0]);Irp->IoStatus.Information = 8;// 输出长度为8break;case IOCTL_MYPORT_WRITE_BYTE: // 写字节到端口_outp(pvIOBuffer[0], pvIOBuffer[1]);break;default:// 不支持的IOCTLIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;}}ntStatus = Irp->IoStatus.Status;IoCompleteRequest (Irp, IO_NO_INCREMENT);return ntStatus;}// 删除驱动void MyPortUnload(IN PDRIVER_OBJECT DriverObject){UNICODE_STRING uniDOSString;if(pIOPM){// 释放IOPM占用的空间MmFreeNonCachedMemory(pIOPM, sizeof(IOPM));}RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);// 删除符号连接和设备IoDeleteSymbolicLink (&uniDOSString);IoDeleteDevice(DriverObject->DeviceObject);}