在IoGetDeviceObjectPointer()

时间:2017-10-14 20:54:29

标签: windows kernel storage driver device

可以在IoGetDeviceObjectPointer()返回的设备对象上使用IoCallDriver()和IoBuildAsynchronousFsdRequest()创建的IRP吗?我目前失败的蓝屏(BSOD)0x7E(未处理的异常),当捕获时显示访问冲突(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。

缓冲区有足够的空间并且是可读/写的。 offset和io_stat在堆栈上(也尝试使用堆,没有帮助)。捕获异常(SEH异常)时,它不会显示蓝屏,但会显示访问冲突作为异常的原因。 io是IRP_MJ_READ。

我是否会错过一些明显的东西?使用IRP通常比使用ZwCreateFile / ZwReadFile / ZwWriteFile API更好(这可能是一个选项,但不是那么慢吗?)?我也尝试过ZwCreateFile以获得额外的参考,但这也没有帮助。

感谢您的任何见解。

1 个答案:

答案 0 :(得分:2)

您在此代码中指出了至少2个严重错误。

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

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

    我猜错误就是文件系统尝试访问时FileObject 来自irp,在你的情况下为0。还阅读了文档 IRP_MJ_READ - IrpSp-> FileObject - 指向与DeviceObject关联的文件对象的指针

  2. 你通过了我认为局部变量io_stat(和offsetIoBuildAsynchronousFsdRequest。因此io_stat必须有效 直到newIrp完成 - I / O子系统将最终结果写入其中 操作完成后。但你不要等到功能,直到请求 将完成(如果STATUS_PENDING返回),但只是退出 从功能。结果是后来的I / O子系统,如果操作完成 异步,将数据写入任意地址&io_stat(它变成了 退出函数后任意选择)。所以你需要或检查 for STATUS_PENDING返回并在这种情况下等待(实际上 同步io请求)。但更合乎逻辑的用途 在这种情况下IoBuildSynchronousFsdRequest。或分配io_stat 不是来自堆栈,而是在您的对象中说出与文件对应的内容。在 在这种情况下,你不能只有单一的io请求 对象在时间。或者如果你想要完全异步的I / O - 你可以做到 下一招 - newIrp->UserIosb = &newIrp->IoStatus。结果你 iosb始终对newIrp有效。和实际运行状态 您在DrbdIoCompletion
  3. 中查看/使用

    你也可以解释(不适合我 - 为自己)下一个代码行吗?:

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

    谁在哪里取消引用线程和什么意义呢?

      

    可以使用......

    我们可以使用所有,但有条件 - 我们了解我们正在做什么,并在内部深入理解系统。

      

    使用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 。现在问题 - 谁解放了这个 Mdl IoFreeIrp - 只需免费 Irp 内存 - 仅此而已。你自己这样做?怀疑。但是 Irp 是一个复杂的对象,它在内部拥有许多资源。因此,不仅需要释放内存,而且需要调用"析构函数"为了它。这个"析构函数"是IofCompleteRequest。当你返回StopCompletion=STATUS_MORE_PROCESSING_REQUIRED)时,你会在开始时打破这个析构函数。但你必须再次呼叫IofCompleteRequest以继续 Irp (并且它的资源)正确销毁。

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

      

    在完成例程中将其取消引用。

    1. IofCompleteRequest 后访问Tail.Overlay.Thread 打电话给你的完成例程(如果你没有回来 StopCompletion)。结果你的引用/解引用线程丢失了 感觉 - 因为你过早地尊重它,之前系统 实际访问它。
    2. 如果您返回StopCompletion而不是更多电话 此 Irp IofCompleteRequest - 系统无法访问 Tail.Overlay.Thread一点都没有。你不需要参考它 情况下。
    3. 并存在其他一个原因,为什么参考线程毫无意义。系统 访问Tail.Overlay.Thread仅用于向他插入Apc - 用于呼叫 原始的Irp破坏的最后部分(IopCompleteRequest) 线程上下文。这真的只需要用户模式Irp的请求, 其中缓冲区和 iosb 位于用户模式且仅在 进程的上下文(原始线程)。但如果线程终止 - 调用KeInsertQueueApc 失败 - 系统不允许插入apc 死了。结果IopCompleteRequest将不会被调用 资源没有释放。
    4. 所以你或者过早引用Tail.Overlay.Thread或者你根本不需要这样做。并参考死亡线程无论如何没有帮助。在所有情况下,你所做的都是错误。

      您可以尝试在此处执行操作:

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

      second callIofCompleteRequest会导致I / O管理器恢复调用IRP的完成。在这里io经理和访问Tail.Overlay.Thread向他插入Apc。最后,在系统访问后,您已经<{1}}已经,并且ObfDereferenceObject(Thread);会先致电StopCompletion。看起来像是正确的但是..如果线程已经终止,我将如何解释 3 这将是错误,因为IofCompleteRequest失败。对于扩展测试 - 从单独的线程调用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, KernelRoutine, 0, 0, KernelMode, 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 KernelRoutine (PKAPC Apc,PKNORMAL_ROUTINE *,PVOID *,PVOID *,PVOID *)
      {
          DbgPrint("KernelRoutine(%p)\n", Apc);
          ExFreePool(Apc);
      }
      

      ThreadTerminating !KeInsertQueueApc !!!!!!!!!!! 将不会被调用(例如和KernelRoutine) - 不会打印出来。

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

      IopCompleteRequest

      您可以安全地更改 Irp->Tail.Overlay.Thread = KeGetCurrentThread(); return ContinueCompletion; - 如果您没有任何指针仅在原始流程上下文中有效。对于内核模式请求也是如此 - 所有缓冲区都在内核模式下并且在任何上下文中都有效。当然你不需要破坏Irp破坏但继续它。正确的免费mdl和所有irp资源。最后系统调用Tail.Overlay.Thread给你。

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

      IoFreeIrp

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

      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, KernelMode, (void**)FileObject, 0);
              NtClose(hFile);
          }
      
          return status;
      }
      

      eb 52 90 4e 54 46 53读好了