上一篇说到内核里面用户态这边差不多的函数, 比如ZwCreateFile, ZwReadFile什么之类的, 内核里面其实还有些更加暴力的东西存在的, 比如说用户态这边打开设备只有一个句柄, 但是内核里面是可以直接触摸到指针的, 用户态那边只可以调用几个固定的函数来发送Irp. 那么内核里面可以自定义Irp的发送, 非常的暴力, 好用!
首先我们也不用去调用什么ZwCreateFile了, 直接用这个IoGetDeviceObjectPointer函数, 这个函数可以通过设备名获得文件对象指针, 和设备对象指针, 指针啊, 不是句柄了..和ZwCreateFile一样, 末尾记得调用ObDereferenceObject函数, 来解引用.
打开以后就是发送Irp消息了, 基本上有很多的选择, 看下面代码就知道了, 有同步的又异步的, 非常的暴力好用, 看自己喜欢了. 其实看下面代码就知道了, 同步的和异步的其实区别不是很大, 无论如何还是需要一个同步事件来同步的. 只是放在了不同的地方了. 稍微搞一下就明白了, 这个很简单的嘛.
重点说说这个, ObReferenceObjectByName这号函数是未公开的函数, 一般未公开的函数就是暴力啊, 其可以获得各类对象的指针, 而不是和IoGetDeviceObjectPointer这样只能够获取设备对象的指针, ObReferenceObjectByName可以获取各种对象的指针, 非常好用了. ObReferenceObjectByName只是引用下对象的指针, 并没有打开操作, 但是IoGetDeviceObjectPointer内部是有打开操作的. 也就是相当于向设备发送了Irp_Mj_Create的Irp. 明白了他们之间的区别, 那么我们就可以适时的在二者中选择某个函数来做一些事情了.
后面还有一个模拟IoGetDeviceObjectPointer内核函数的实现, 首先要打开设备对象, 获得设备对象的句柄,然后ObReferenceObjectByHandle内核函数将设备对象的句柄, 转换设备对象相关的文件对象的指针. 然后再调用IoGetBaseFileSystemDeviceObject函数, 可以将文件对象指针得到设备对象针! 有点晕了. 主要要把握住, 设备, 文件, 句柄. 这是关键字!
下面就上代码, 测试驱动已经在上一篇中说到了, 那么这边看看代码的情况:
/* Windows 内核下驱动程序通过设备指针调用其他驱动程序 调用驱动程序 编译方法参见makefile. TAB = 8 */ #include <ntddk.h> //=========================================================================== #define SYSLINK_NAME L"\\??\\SysLinkTestDriver" #define MAX_PATH 260 #ifdef __cplusplus extern "C" { #endif #include <NTDDK.h> NTKERNELAPI NTSTATUS ObReferenceObjectByName( IN PUNICODE_STRING ObjectName, IN ULONG Attributes, IN PACCESS_STATE PassedAccessState OPTIONAL, IN ACCESS_MASK DesiredAccess OPTIONAL, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, IN OUT PVOID ParseContext OPTIONAL, OUT PVOID *Object ); NTKERNELAPI PDEVICE_OBJECT NTAPI IoGetBaseFileSystemDeviceObject ( IN PFILE_OBJECT FileObject ); extern POBJECT_TYPE IoDeviceObjectType; #ifdef __cplusplus } #endif //=========================================================================== //模拟系统内部IoGetDeviceObjectPointer实现 //=========================================================================== _IoGetDeviceObjectPointer( PUNICODE_STRING pObjName, ACCESS_MASK DesiredAccess, PFILE_OBJECT* ppFileObj, PDEVICE_OBJECT* ppDeviceObj ) { HANDLE hDevice = NULL; NTSTATUS Status; PFILE_OBJECT pFileObj = NULL; OBJECT_ATTRIBUTES ObjAttr; IO_STATUS_BLOCK Status_block; //初始化要打开的设备名称 InitializeObjectAttributes( &ObjAttr, pObjName, OBJ_KERNEL_HANDLE, NULL, NULL ); //打开设备 对象 Status = ZwOpenFile( &hDevice, DesiredAccess, &ObjAttr, &Status_block, 0, FILE_NON_DIRECTORY_FILE ); if ( !NT_SUCCESS( Status ) ) { return Status; } //通过设备对象句柄得到设备对象指针 Status = ObReferenceObjectByHandle( hDevice, 0, *IoFileObjectType, KernelMode, &pFileObj, NULL ); if ( !NT_SUCCESS( Status ) ) { return Status; } *ppFileObj = pFileObj; //通过设备相关文件对象指针得到设备对象指针 *ppDeviceObj = IoGetBaseFileSystemDeviceObject( pFileObj ); if ( hDevice ) { ZwClose(hDevice); } return STATUS_SUCCESS; } //=========================================================================== //打开设备 //=========================================================================== NTSTATUS OpenDevice() { NTSTATUS Status; HANDLE hSysLink; HANDLE hDevice = NULL; ULONG ulStrLen, i; UCHAR ucBuf[10]; KEVENT Event; PIRP pNewIrp = NULL; OBJECT_ATTRIBUTES ObjAttr; UNICODE_STRING USzDeviceName = {0}; PIO_STACK_LOCATION pStack = NULL; IO_STATUS_BLOCK Status_Block; LARGE_INTEGER liOffset; PDEVICE_OBJECT pDevice = NULL; PFILE_OBJECT pFileObj = NULL; UNICODE_STRING USzDeviceLinkName = RTL_CONSTANT_STRING( SYSLINK_NAME ); do { //这边是内核的对象, 加了个内核属性 InitializeObjectAttributes( &ObjAttr, &USzDeviceLinkName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL ); //获取符号链接句柄 Status = ZwOpenSymbolicLinkObject( &hSysLink, FILE_ALL_ACCESS, &ObjAttr ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "打开符号链接失败!\n" ) ); break; } USzDeviceName.Buffer = ExAllocatePool( PagedPool, MAX_PATH ); USzDeviceName.MaximumLength = MAX_PATH; ASSERT( USzDeviceName.Buffer ); //通过符号名称得到设备名称 Status = ZwQuerySymbolicLinkObject( hSysLink, &USzDeviceName, &ulStrLen ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "符号名称转换成链接名称失败!\n" ) ); break; } KdPrint( ( "设备名称%wZ\n", &USzDeviceName ) ); //这个打开设备比ZwCreateFile好多了, 参数简单, //还可以直接获取到文件对象指针, 设备对象指针 //这边还模拟了一个系统这个部分的实现. Status = _IoGetDeviceObjectPointer( &USzDeviceName, FILE_ALL_ACCESS, &pFileObj, &pDevice ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "设备名转文件对象指针失败!\n" ) ); break; } //--------------------------------------------------------------------------- //创建同步IRP写入请求, 这里的话, 其实同步和异步差不多 //--------------------------------------------------------------------------- liOffset.QuadPart = 0i64; KeInitializeEvent( &Event, NotificationEvent, FALSE ); RtlFillMemory( ucBuf, sizeof( ucBuf ), 'a' ); //创建同步IRP请求 pNewIrp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE, pDevice, &ucBuf[0], sizeof( ucBuf ), &liOffset, &Event, &Status_Block ); //得到下一层的I/O堆栈(如果驱动不是只有一层, 应该怎么做呢?) pStack = IoGetNextIrpStackLocation( pNewIrp ); //设置I/O堆栈的文件对象指针 pStack->FileObject = pFileObj; //调用下一层驱动 Status = IoCallDriver( pDevice, pNewIrp ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "调用驱动失败!\n" ) ); break; } if ( Status == STATUS_PENDING ) { KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); Status = Status_Block.Status; if ( NT_SUCCESS( Status_Block.Status ) ) { KdPrint( ( "写入设备成功, 总共写入了%d字节!\n", Status_Block.Information ) ); } else { KdPrint( ( "写入设备失败!\n" ) ); break; } } //--------------------------------------------------------------------------- //创建异步Irp读取请求, //--------------------------------------------------------------------------- pNewIrp = NULL; pStack = NULL; KeResetEvent( &Event ); liOffset.QuadPart = 0i64; RtlZeroBytes( &ucBuf[0], sizeof( ucBuf ) ); //创建异步Irp pNewIrp = IoBuildAsynchronousFsdRequest( IRP_MJ_READ, pDevice, &ucBuf[0], sizeof( ucBuf ), &liOffset, &Status_Block ); //Irp完成后会通过这里通知这边 pNewIrp->UserEvent = &Event; //获取下一层I/O堆栈 pStack = IoGetNextIrpStackLocation( pNewIrp ); pStack->FileObject = pFileObj; Status = IoCallDriver( pDevice, pNewIrp ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "读取设备请求失败!" ) ); break; } if ( Status == STATUS_PENDING ) { KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); Status = Status_Block.Status; if ( NT_SUCCESS( Status_Block.Status ) ) { KdPrint( ( "读取设备成功!\n" ) ); for( i = 0; i < Status_Block.Information; i++ ) { KdPrint( ( "%c\t", ucBuf[i] ) ); } KdPrint( ( "\n" ) ); } else { KdPrint( ( "读取设备失败!\n" ) ); break; } } //--------------------------------------------------------------------------- //手工创建Irp //--------------------------------------------------------------------------- pNewIrp = NULL; pStack = NULL; KeResetEvent( &Event ); liOffset.QuadPart = 0i64; RtlZeroBytes( &ucBuf[0], sizeof( ucBuf ) ); //这个创建出来的是最原始的IRP pNewIrp = IoAllocateIrp( pDevice->StackSize, FALSE ); //设置同步事件 pNewIrp->UserEvent = &Event; pNewIrp->UserIosb = &Status_Block; //填写新Irp的线程号 pNewIrp->Tail.Overlay.Thread = PsGetCurrentThread(); //填写缓冲区 pNewIrp->AssociatedIrp.SystemBuffer = ucBuf; //获取下一层堆栈 pStack = IoGetNextIrpStackLocation( pNewIrp ); //填写功能号 pStack->MajorFunction = IRP_MJ_READ; pStack->MinorFunction = IRP_MN_NORMAL; //填写文件对象 pStack->FileObject = pFileObj; //填写缓冲区长度和偏移 pStack->Parameters.Read.Length = sizeof( ucBuf ); pStack->Parameters.Read.ByteOffset = liOffset; Status = IoCallDriver( pDevice, pNewIrp ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "Irp发送失败!\n" ) ); break; } if ( Status == STATUS_PENDING ) { KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); Status = Status_Block.Status; if ( NT_SUCCESS( Status_Block.Status ) ) { KdPrint( ( "读取设备成功!\n" ) ); for( i = 0; i < Status_Block.Information; i++ ) { KdPrint( ( "%c\t", ucBuf[i] ) ); } KdPrint( ( "\n" ) ); } else { KdPrint( ( "读取设备失败!\n" ) ); break; } } } while ( FALSE ); //--------------------------------------------------------------------------- if ( USzDeviceName.Buffer ) { ExFreePool( USzDeviceName.Buffer ); } if ( hSysLink ) { ZwClose( hSysLink ); } if ( pFileObj ) { ObDereferenceObject( pFileObj ); } return Status; } //=========================================================================== //驱动入口 //=========================================================================== NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pUSzRegPath ) { NTSTATUS Status; PDEVICE_OBJECT pDevice = NULL; PFILE_OBJECT pFileObj = NULL; UNICODE_STRING USzDeviceName = RTL_CONSTANT_STRING( SYSLINK_NAME ); //这号函数可以通过名字获取设备指针, 可以获取各种对象的指针(事件, 互斥量等) Status = ObReferenceObjectByName( &USzDeviceName, OBJ_CASE_INSENSITIVE, NULL, FILE_ALL_ACCESS, IoDeviceObjectType, KernelMode, NULL, &pDevice ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "通过名称获取对象指针失败!\n" ) ); return Status; } else { KdPrint( ( "通过名称获取对象指针成功!\n" ) ); } if ( pDevice ) { ObDereferenceObject( pDevice ); pDevice = NULL; } Status = OpenDevice(); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "操作设备失败!\n" ) ); return Status; } else { KdPrint( ( "操作设备成功!\n" ) ); } return STATUS_UNSUCCESSFUL; }