实战DeviceIoControl之六:访问物理端口
2010-01-10 09:37:25 来源:WEB开发网核心提示:Q 在NT/2000/XP中,如何读取CMOS数据?Q 在NT/2000/XP中,实战DeviceIoControl之六:访问物理端口,如何控制speaker发声?Q 在NT/2000/XP中,如何直接访问物理端口?A 看似小小问题,缓冲区共两个DWORD,第一个DWORD为端口,难倒多少好汉!NT/2000/XP从安
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_MYPORT 0x0000f000
// 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_BYTE CTL_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_SIZE 8192
typedef 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应用程序的WinMain
NTSTATUS 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和MyPortUnload
DriverObject->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;
ULONG dwInputBufferLength;
ULONG dwOutputBufferLength;
ULONG dwIoControlCode;
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; // 输出长度为8
break;
case IOCTL_MYPORT_WRITE_BYTE: // 写字节到端口
_outp(pvIOBuffer[0], pvIOBuffer[1]);
break;
default: // 不支持的IOCTL
Irp->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);
}
Tags:实战 DeviceIoControl 访问
编辑录入:爽爽 [复制链接] [打 印][]
- ››实战案例分析:高质量软文对网站百度排名的影响
- ››实战经验浅谈网站搬家后的优化工作
- ››实战Active Directory站点部署与管理,Active Dir...
- ››实战操作主机角色转移,Active Directory系列之十...
- ››实战经验:巧用微博推广淘宝网店
- ››实战iPhone GPS定位系统
- ››实战Linux环境配置DBD:Oracle模块
- ››实战DeviceIoControl系列之一:通过API访问设备驱...
- ››实战DeviceIoControl系列之二:获取软盘/硬盘/光盘...
- ››实战DeviceIoControl系列之三:制作磁盘镜像文件
- ››实战DeviceIoControl系列之四:获取硬盘的详细信息...
- ››实战DeviceIoControl之五:列举已安装的存储设备
更多精彩
赞助商链接