IoGetDeviceObjectPointer()返回的设备对象上的I / O使用IRP

可以使用IoCallDriver()与由IoBuildAsynchronousFsdRequest()创build的IoGetDeviceObjectPointer()返回的设备对象上的IRP? 我目前失败的蓝屏(BSOD)0x7E(未处理的exception),当捕获显示访问冲突(0xc0000005)。 设备堆叠时使用相同的代码(使用由IoAttachDeviceToDeviceStack()返回的设备对象)。

所以我有以下几点:

status = IoGetDeviceObjectPointer(&device_name, FILE_ALL_ACCESS, &FileObject, &windows_device); if (!NT_SUCCESS(status)) { return -1; } offset.QuadPart = 0; newIrp = IoBuildAsynchronousFsdRequest(io, windows_device, buffer, 4096, &offset, &io_stat); if (newIrp == NULL) { return -1; } IoSetCompletionRoutine(newIrp, DrbdIoCompletion, bio, TRUE, TRUE, TRUE); status = ObReferenceObjectByPointer(newIrp->Tail.Overlay.Thread, THREAD_ALL_ACCESS, NULL, KernelMode); if (!NT_SUCCESS(status)) { return -1; } status = IoCallDriver(bio->bi_bdev->windows_device, newIrp); if (!NT_SUCCESS(status)) { return -1; } return 0; 

device_name是根据WinObj.exe存在的\ Device \ HarddiskVolume7。

缓冲区有足够的空间,可以读/写。 偏移和io_stat在堆栈上(也尝试堆,没有帮助)。 捕获exception(SEHexception)时,它不会蓝屏,但显示访问冲突是exception的原因。 io是IRP_MJ_READ。

我想念一些明显的东西吗? 通常比使用ZwCreateFile / ZwReadFile / ZwWriteFile API(这可能是一个选项,但速度并不慢)使用IRP会更好吗? 我也尝试了一个ZwCreateFile有一个额外的参考,但这也没有帮助。

感谢您的任何见解。

你在这个代码中至少犯了2个严重错误。

  1. 我可以问 – 从哪个文件中尝试读取(或写入)数据? 从FileObject你说? 但如何处理这个请求的文件系统驱动程序知道这一点? 您不传递任何文件对象到newIrp 。 查找IoBuildAsynchronousFsdRequest – 它没有文件对象参数(不可能从设备对象获取文件对象 – 反之亦然 – 因为在设备上可以打开多个文件)。 所以它不能用newIrp的这个api newIrp 。 你必须自己设置它:

      PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation( newIrp ); irpSp->FileObject = FileObject; 

    我猜错误就是当文件系统尝试访问从你的情况是0的IRP的FileObject 。 还阅读了IRP_MJ_READ文档 – IrpSp-> FileObject指向与DeviceObject关联的文件对象的指针

  2. 你通过我猜局部变量io_stat (和offsetIoBuildAsynchronousFsdRequest 。 因为io_stat必须有效,直到newIrp完成 – 当操作完成时,I / O子系统将最终结果写入它。 但是不要等到函数完成(如果STATUS_PENDING返回),而只是退出函数。 作为后来的I / O子系统的结果,如果操作完成异步,将数据写入任意地址&io_stat (在函数退出后它变成了任意的)。 所以你需要或检查返回的STATUS_PENDING并在这种情况下等待(实际上有同步IO请求)。 但在这种情况下更合理地使用IoBuildSynchronousFsdRequest 。 或者不从栈中分配io_stat ,而是在你的对象中对应于文件。 在这种情况下,您不能在此时使用此对象具有多个单一的IO请求。 或者如果你想要完全异步的I / O – 你可以做下一个技巧 – newIrp->UserIosb = &newIrp->IoStatus 。 结果你iosb总是会有效的newIrp 。 以及您在DrbdIoCompletion检查/使用的实际操作状态

也可以解释一下(不是为了我自己)下一个代码行吗?

 status = ObReferenceObjectByPointer(newIrp->Tail.Overlay.Thread, THREAD_ALL_ACCESS, NULL, coreelMode); 

谁在哪里解引用线程和这个有什么意义?

可以使用…

我们可以全部使用,但是有条件 – 我们理解我们在做什么,并深入理解系统内部。

使用IRP通常比ZwCreateFile / ZwReadFile / ZwWriteFile API更好

表现 – 是的,更好。 但是这需要更多的代码和更复杂的代码来比较api调用。 并需要更多的知识。 如果你知道以前的模式是内核模式 – 你可以使用NtCreateFile,NtWriteFile,NtReadFile – 这当然会慢一点(需要每次引用文件对象的句柄),但更快的比较ZW版本


只是想补充一点, ObReferenceObjectByPointer是需要的,因为IRP引用当前线程,在请求完成之前退出。 它在完成程序中被取消。 另外作为一个提示,完成例程必须返回STATUS_MORE_PROCESSING_REQUIRED如果它释放IRP (花了我几天的时间来解决这个问题)。

在这里你再犯几个错误。 我如何理解你在完成日常工作下一步:

 IoFreeIrp(Irp); return StopCompletion; 

但调用简单的调用IoFreeIrp这里是错误 – 资源泄漏。 我建议你检查(DbgPrint) Irp->MdlAddress在这一点上。 如果从文件系统对象读取数据并请求完成异步 – 文件系统总是在任意上下文中为访问用户缓冲区分配Mdl 。 现在的问题 – 谁免费这个MdlIoFreeIrp – 简单的免费Irp内存 – 仅此而已。 你自己做这个? 怀疑。 但是Irp是复杂的对象,内部拥有很多资源。 因为结果不仅需要释放它的内存,而且还需要调用“析构函数”。 这个“析构函数”是IofCompleteRequest 。 当你返回=STATUS_MORE_PROCESSING_REQUIRED=STATUS_MORE_PROCESSING_REQUIRED )时,你将=STATUS_MORE_PROCESSING_REQUIRED破解这个析构函数。 但你必须再次调用IofCompleteRequest来继续Irp (和它的资源)正确的销毁。

关于引用Tail.Overlay.Thread – 你在做什么 – 没有任何意义:

它在完成程序中被取消。

  1. IofCompleteRequest访问Tail.Overlay.Thread 调用完成例程(如果不返回StopCompletion )。 结果是你的引用/解引用线程失去了意义 – 因为系统实际访问它之前 ,你提前引用它。
  2. 如果你返回IofCompleteRequest而不是更多的为这个Irp调用IofCompleteRequest – 系统根本不访问Tail.Overlay.Thread 。 在这种情况下你不需要参考它。
  3. 并存在其他原因之一,为什么参考线程是没有意义的。 系统访问Tail.Overlay.Thread仅用于向他插入Apc – 用于在原始线程上下文中调用Irp IopCompleteRequest最后部分( IopCompleteRequest )。 实际上这只需要用户模式Irp的请求,其中缓冲区和iosb位于用户模式,并且仅在进程(原始线程)的上下文中有效。 但是如果线程被终止 – 调用KeInsertQueueApc 失败 – 系统不允许将apc插入到死亡线程。 因为IopCompleteRequest将不会被调用,资源也不会被释放。

所以你或者解除引用Tail.Overlay.Thread太早,或者你根本不需要这样做。 反正死亡线程的参考无济于事。 在任何情况下你所做的都是错误的。

你可以在这里尝试下一步:

 PETHREAD Thread = Irp->Tail.Overlay.Thread; IofCompleteRequest(Irp, IO_NO_INCREMENT);// here Thread will be referenced ObfDereferenceObject(Thread); return StopCompletion; 

IofCompleteRequest的第二次调用导致I / O管理器继续调用IRP的完成。 这里io经理和访问Tail.Overlay.Thread插入Apc给他。 最后你调用ObfDereferenceObject(Thread); 已经系统访问它并返回IofCompleteRequest中断第一次调用IofCompleteRequest 。 看起来像正确但是..如果线程已经终止,我怎么解释3这将是错误的,因为KeInsertQueueApc失败。 对于扩展测试 – 从单独的线程调用IofCallDriver ,并从中退出。 并在完成运行下一个代码:

 PETHREAD Thread = Irp->Tail.Overlay.Thread; if (PsIsThreadTerminating(Thread)) { DbgPrint("ThreadTerminating\n"); if (PKAPC Apc = (PKAPC)ExAllocatePool(NonPagedPool, sizeof(KAPC))) { KeInitializeApc(Apc, Thread, 0, coreelRoutine, 0, 0, coreelMode, 0); if (!KeInsertQueueApc(Apc, 0, 0, IO_NO_INCREMENT)) { DbgPrint("!KeInsertQueueApc\n"); ExFreePool(Apc); } } } PMDL MdlAddress = Irp->MdlAddress; IofCompleteRequest(Irp, IO_NO_INCREMENT); ObfDereferenceObject(Thread); if (MdlAddress == Irp->MdlAddress) { // IopCompleteRequest not called due KeInsertQueueApc fail DbgPrint("!!!!!!!!!!!\n"); IoFreeMdl(MdlAddress); IoFreeIrp(Irp); } return StopCompletion; //--------------- VOID coreelRoutine (PKAPC Apc,PKNORMAL_ROUTINE *,PVOID *,PVOID *,PVOID *) { DbgPrint("coreelRoutine(%p)\n", Apc); ExFreePool(Apc); } 

你必须得到下一个调试输出:

 ThreadTerminating !KeInsertQueueApc !!!!!!!!!!! 

coreelRoutine将不会被调用(like和IopCompleteRequest ) – 不从它打印。

那么正确的解决方案是什么 这当然没有记录在任何地方,但基于深刻的内部理解。 你不需要参考原始线程。 你需要做下一个:

  Irp->Tail.Overlay.Thread = KeGetCurrentThread(); return ContinueCompletion; 

你可以安全地改变Tail.Overlay.Thread – 如果你没有任何指针只在原始的进程上下文有效。 这对于内核模式请求是正确的 – 在内核模式下你所有的缓冲区在任何情况下都是有效的。 当然你不需要破坏Irp破坏,但是继续它。 为正确的免费mdl和所有irp资源。 最后系统为你调用IoFreeIrp

再次为iosb指针。 我怎么说通过本地变量地址,如果你从IRP完成(和这个iosb访问)之前退出函数是错误的。 如果你破坏Irp破坏,当然不会访问iosb,但在这种情况下更好的传递0指针作为iosb。 (如果你后来有什么改变,iosb指针将被访问 – 将是最糟糕的错误 – 任意的内存损坏 – 具有不可预知的效果,而研究的崩溃将非常困难)。 但如果你完成例程 – 你不需要单独的iosb – 你有完成irp,并可以直接访问它的内部iosb – 你需要什么其他的? 所以最好的解决方案将是下一步:

 Irp->UserIosb = &Irp->IoStatus; 

完全正确的例子如何读取文件异步:

 NTSTATUS DemoCompletion (PDEVICE_OBJECT /*DeviceObject*/, PIRP Irp, BIO* bio) { DbgPrint("DemoCompletion(p=%x mdl=%p)\n", Irp->PendingReturned, Irp->MdlAddress); bio->CheckResult(Irp->IoStatus.Status, Irp->IoStatus.Information); bio->Release(); Irp->Tail.Overlay.Thread = KeGetCurrentThread(); return ContinueCompletion; } VOID DoTest (PVOID buf) { PFILE_OBJECT FileObject; NTSTATUS status; UNICODE_STRING ObjectName = RTL_CONSTANT_STRING(L"\\Device\\HarddiskVolume2"); OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE }; if (0 <= (status = GetDeviceObjectPointer(&oa, &FileObject))) { status = STATUS_INSUFFICIENT_RESOURCES; if (BIO* bio = new BIO(FileObject)) { if (buf = bio->AllocBuffer(PAGE_SIZE)) { LARGE_INTEGER ByteOffset = {}; PDEVICE_OBJECT DeviceObject = IoGetRelatedDeviceObject(FileObject); if (PIRP Irp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ, DeviceObject, buf, PAGE_SIZE, &ByteOffset, 0)) { Irp->UserIosb = &Irp->IoStatus; Irp->Tail.Overlay.Thread = 0; PIO_STACK_LOCATION IrpSp = IoGetNextIrpStackLocation(Irp); IrpSp->FileObject = FileObject; bio->AddRef(); IrpSp->CompletionRoutine = (PIO_COMPLETION_ROUTINE)DemoCompletion; IrpSp->Context = bio; IrpSp->Control = SL_INVOKE_ON_CANCEL|SL_INVOKE_ON_ERROR|SL_INVOKE_ON_SUCCESS; status = IofCallDriver(DeviceObject, Irp); } } bio->Release(); } ObfDereferenceObject(FileObject); } DbgPrint("DoTest=%x\n", status); } struct BIO { PVOID Buffer; PFILE_OBJECT FileObject; LONG dwRef; void AddRef() { InterlockedIncrement(&dwRef); } void Release() { if (!InterlockedDecrement(&dwRef)) { delete this; } } void* operator new(size_t cb) { return ExAllocatePool(PagedPool, cb); } void operator delete(void* p) { ExFreePool(p); } BIO(PFILE_OBJECT FileObject) : FileObject(FileObject), Buffer(0), dwRef(1) { DbgPrint("%s<%p>(%p)\n", __FUNCTION__, this, FileObject); ObfReferenceObject(FileObject); } ~BIO() { if (Buffer) { ExFreePool(Buffer); } ObfDereferenceObject(FileObject); DbgPrint("%s<%p>(%p)\n", __FUNCTION__, this, FileObject); } PVOID AllocBuffer(ULONG NumberOfBytes) { return Buffer = ExAllocatePool(PagedPool, NumberOfBytes); } void CheckResult(NTSTATUS status, ULONG_PTR Information) { DbgPrint("CheckResult:status = %x, info = %p\n", status, Information); if (0 <= status) { if (ULONG_PTR cb = min(16, Information)) { char buf[64], *sz = buf; PBYTE pb = (PBYTE)Buffer; do sz += sprintf(sz, "%02x ", *pb++); while (--cb); sz[-1]= '\n'; DbgPrint(buf); } } } }; NTSTATUS GetDeviceObjectPointer(POBJECT_ATTRIBUTES poa, PFILE_OBJECT *FileObject ) { HANDLE hFile; IO_STATUS_BLOCK iosb; NTSTATUS status = IoCreateFile(&hFile, FILE_READ_DATA, poa, &iosb, 0, 0, FILE_SHARE_VALID_FLAGS, FILE_OPEN, FILE_NO_INTERMEDIATE_BUFFERING, 0, 0, CreateFileTypeNone, 0, 0); if (0 <= (status)) { status = ObReferenceObjectByHandle(hFile, 0, *IoFileObjectType, coreelMode, (void**)FileObject, 0); NtClose(hFile); } return status; } 

和输出:

 BIO::BIO<FFFFC000024D4870>(FFFFE00001BAAB70) DoTest=103 DemoCompletion(p=1 mdl=FFFFE0000200EE70) CheckResult:status = 0, info = 0000000000001000 eb 52 90 4e 54 46 53 20 20 20 20 00 02 08 00 00 BIO::~BIO<FFFFC000024D4870>(FFFFE00001BAAB70) 

eb 52 90 4e 54 46 53 read ok