对IoGetDeviceObjectPointer()返回的设备对象使用IRP进行I/O [英] Using IRPs for I/O on device object returned by IoGetDeviceObjectPointer()

查看:113
本文介绍了对IoGetDeviceObjectPointer()返回的设备对象使用IRP进行I/O的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在IoGetDeviceObjectPointer()返回的设备对象上,可以将IoCallDriver()与IoBuildAsynchronousFsdRequest()创建的IRP一起使用吗?我当前遇到的故障是蓝屏(BSOD)0x7E(未处理的异常),当捕获时显示访问冲突(0xc0000005).堆叠设备时使用相同的代码(使用IoAttachDeviceToDeviceStack()返回的设备对象).

Can one use IoCallDriver() with an IRP created by IoBuildAsynchronousFsdRequest() on a device object returned by IoGetDeviceObjectPointer()? What I have currently fails with blue screen (BSOD) 0x7E (unhandled exception), which when caught shows an Access Violation (0xc0000005). Same code worked when the device was stacked (using the device object returned by 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是\ Device \ HarddiskVolume7,根据WinObj.exe存在.

device_name is \Device\HarddiskVolume7 which exists according to WinObj.exe .

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

buffer has enough space and is read/writable. offset and io_stat are on stack (also tried with heap, didn't help). When catching the exception (SEH exception) it doesn't blue screen but shows an access violation as reason for the exception. io is IRP_MJ_READ.

我想念一些明显的东西吗?通常,使用IRP比使用ZwCreateFile/ZwReadFile/ZwWriteFile API更好(可以选择,但是不慢吗?)?我还尝试了ZwCreateFile来获得额外的引用,但这也无济于事.

Do I miss something obvious? Is it in general better to use IRPs than the ZwCreateFile / ZwReadFile / ZwWriteFile API (which would be an option, but isn't that slower?)? I also tried a ZwCreateFile to have an extra reference, but this also didn't help.

感谢您的见解.

推荐答案

您在此代码中至少产生了2个严重错误.

you make in this code how minimum 2 critical errors.

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

  1. can I ask - from which file you try read (or write) data ? from FileObject you say ? but how file system driver, which will handle this request know this ? you not pass any file object to newIrp. look for IoBuildAsynchronousFsdRequest - it have no file object parameter (and impossible get file object from device object - only visa versa - because on device can be multiple files open). so it and can not be filled by this api in newIrp. you must setup it yourself:

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

我想这个错误正是文件系统尝试访问FileObject时的错误 从您的情况下为0的irp中获取.还阅读了有关的文档 IRP_MJ_READ - IrpSp-> FileObject - 指向与DeviceObject关联的文件对象的指针

I guess bug was exactly when file system try access FileObject from irp which is 0 in your case. also read docs for IRP_MJ_READ - IrpSp->FileObject - Pointer to the file object that is associated with DeviceObject

您还能解释下一段代码行吗?

also can you explain (not for me - for self) next code line ?:

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

谁和何处取消引用线程以及在什么意义上如此?

who and where dereference thread and what sense in this ?

可以使用...

Can one use ...

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

we can use all, but with condition - we understand what we doing and deep understand system internally.

使用IRP是否比使用ZwCreateFile/ZwReadFile更好? /ZwWriteFile API

Is it in general better to use IRPs than the ZwCreateFile / ZwReadFile / ZwWriteFile API

性能-是的,更好.但这需要更多代码和更复杂的代码比较api调用.并需要更多知识.另外,如果您知道以前的模式是内核模式-您可以使用NtCreateFile,NtWriteFile,NtReadFile-这当然会有点慢(每次每次按句柄引用文件时都需要),但是比Zw版本更快

for performance - yes, better. but this require more code and more complex code compare api calls. and require more knowledge. also if you know that previous mode is kernel mode - you can use NtCreateFile, NtWriteFile, NtReadFile - this of course will be bit slow (need every time reference file object by handle) but more faster compare Zw version

只需添加需要ObReferenceObjectByPointer 因为 IRP 引用了当前线程,该线程可能在 请求已完成.在完成中已取消引用 常规.还提示完成例程必须返回 STATUS_MORE_PROCESSING_REQUIRED如果它释放了 IRP (请了我几个 天找出来).

Just wanted to add that the ObReferenceObjectByPointer is needed because the IRP references the current thread which may exit before the request is completed. It is dereferenced in the Completion Routine. Also as a hint the completion routine must return STATUS_MORE_PROCESSING_REQUIRED if it frees the IRP (took me several days to figure that out).

在这里您会再次犯几个错误.在完成例行程序中我如何理解您,下一步:

here you make again several mistakes. how i understand you in completion routine do next:

IoFreeIrp(Irp);
return StopCompletion;

,但仅调用IoFreeIrp就可以了,这是错误-资源泄漏.我建议您此时检查(DbgPrint)Irp->MdlAddress.如果您从文件系统对象中读取数据并请求完成异步操作-文件系统始终在任意上下文中为访问用户缓冲区分配 Mdl .现在的问题-谁释放了这个 Mdl ? IoFreeIrp-仅释放 Irp 内存-仅此而已.你自己做这个吗?怀疑.但是 Irp 是复杂的对象,内部包含许多资源.结果不仅需要释放它的内存,还需要为其调用析构函数".此析构函数"为IofCompleteRequest.当您返回StopCompletion(=STATUS_MORE_PROCESSING_REQUIRED)时,您一开始就破坏了该析构函数.但您必须稍后再次呼叫IofCompleteRequest,以继续 Irp (及其资源)正确销毁.

but call simply call IoFreeIrp here is error - resource leak. i advice you check (DbgPrint) Irp->MdlAddress at this point. if you read data from file system object and request completed asynchronous - file system always allocate Mdl for access user buffer in arbitrary context. now question - who free this Mdl ? IoFreeIrp - simply free Irp memory - nothing more. you do this yourself ? doubt. but Irp is complex object, which internally hold many resources. as result need not only free it memory but call "destructor" for it. this "destructor" is IofCompleteRequest. when you return StopCompletion (=STATUS_MORE_PROCESSING_REQUIRED) you break this destructor at very begin. but you must latter again call IofCompleteRequest for continue Irp (and it resources) correct destroy.

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

about referencing Tail.Overlay.Thread - what you doing - have no sense:

在完成例程中已取消引用.

It is dereferenced in the Completion Routine.

  1. 但是IofCompleteRequest访问Tail.Overlay.Thread 之后 调用完成例程(如果不返回,则调用 StopCompletion).结果您的引用/取消引用线程丢失 意识-因为您在之前系统之前就过早了 实际访问它.
  2. 也可以,如果您返回StopCompletion并且没有更多通话 IofCompleteRequest为此 Irp -系统无法访问 完全Tail.Overlay.Thread.并且您不需要在此引用它 情况.
  3. 还有另外一个原因,为什么引用线程是毫无意义的.系统 访问Tail.Overlay.Thread仅用于将Apc插入他-进行通话 原始Irp销毁的最后部分(IopCompleteRequest) 线程上下文.实际上,这仅需要用户模式Irp的请求, 缓冲区和 iosb 位于用户模式下,仅在 进程的上下文(原始线程).但是如果线程终止了- KeInsertQueueApc 失败的调用-系统不允许将apc插入 死线程.结果将不会调用IopCompleteRequest,并且 资源未释放.
  1. but IofCompleteRequest access Tail.Overlay.Thread after it call your completion routine (and if you not return StopCompletion). as result your reference/dereference thread lost sense - because you deference it too early, before system actually access it.
  2. also if you return StopCompletion and not more call IofCompleteRequest for this Irp - system not access Tail.Overlay.Thread at all. and you not need reference it in this case.
  3. and exist else one reason, why reference thread is senseless. system access Tail.Overlay.Thread only for insert Apc to him - for call final part (IopCompleteRequest) of Irp destruction in original thread context. really this need only for user mode Irp's requests, where buffers and iosb located in user mode and valid only in context of process (original thread ). but if thread is terminated - call of KeInsertQueueApc fail - system not let insert apc to died thread. as result IopCompleteRequest will be not called and resources not freed.

因此您或提早取消引用Tail.Overlay.Thread或根本不需要执行此操作.和死线程的参考仍然没有帮助.在所有情况下,您所做的都是错误.

so you or dereference Tail.Overlay.Thread too early or you not need do this at all. and reference for died thread anyway not help. in all case what you doing is error.

您可以尝试在此处进行下一步:

you can try do next here:

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 manager并访问Tail.Overlay.Thread向他插入Apc.最后,在系统访问后已经调用ObfDereferenceObject(Thread);并返回StopCompletion中断第一次调用IofCompleteRequest.看起来是正确的,但是..如果线程已经终止,那么我在 3 中的解释将是错误的,因为KeInsertQueueApc失败.对于扩展测试-从单独的线程中调用IofCallDriver并从中退出.完成后,运行下一个代码:

A second call to IofCompleteRequest causes the I/O manager to resume calling the IRP's completion. here io manager and access Tail.Overlay.Thread insert Apc to him. and finally you call ObfDereferenceObject(Thread); already after system access it and return StopCompletion for break first call to IofCompleteRequest. look like correct but.. if thread already terminated, how i explain in 3 this will be error, because KeInsertQueueApc fail. for extended test - call IofCallDriver from separate thread and just exit from it. and in completion run next code:

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);
}

,您必须获得下一个调试输出:

and you must got next debug output:

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

KernelRoutine将不会被调用(如和IopCompleteRequest)-无法打印.

and KernelRoutine will be not called (like and IopCompleteRequest) - no print from it.

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

so what is correct solution ? this of course not documented anywhere, but based on deep internal understand. you not need reference original thread. you need do next:

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

您可以安全地更改Tail.Overlay.Thread-如果您没有仅在原始过程上下文中有效的任何指针.这对于内核模式请求是正确的-您的所有缓冲区都处于内核模式,并且在任何上下文中均有效.当然,您不需要破坏Irp破坏,而是继续进行.以获得正确的免费MDL和所有IRP资源.最后为您系统调用IoFreeIrp.

you can safe change Tail.Overlay.Thread - if you have no any pointers valid only in original process context. this is true for kernel mode requests - all your buffers in kernel mode and valid in any context. and of course you not need break Irp destruction but continue it. for correct free mdl and all irp resources. and finally system call IoFreeIrp for you.

,然后再次选择iosb指针.我怎么说过传递局部变量地址,如果您在IRP完成(并访问此iosb)之前退出函数是错误的.如果您破坏了Irp破坏,当然不会访问iosb,但是在这种情况下,最好将0指针作为iosb传递. (如果您后来进行了某些更改,并且将访问iosb指针-这将是最严重的错误-任意内存损坏-效果无法预料.对此的研究崩溃将非常困难).但是,如果您执行完例行程序-根本不需要单独的iosb-您已经完成了irp并可以直接访问内部iosb-您还需要其他什么吗?所以最好的解决方案是下一步:

and again for iosb pointer. how i say pass local variable address, if you exit from function before irp completed (and this iosb accessed) is error. if you break Irp destruction, iosb will be not accessed of course, but in this case much better pass 0 pointer as iosb. (if you latter something change and iosb pointer will be accessed - will be the worst error - arbitrary memory corrupted - with unpredictable effect. and research crash of this will be very-very hard). but if you completion routine - you not need separate iosb at all - you have irp in completion and can direct access it internal iosb - for what you need else one ? so the best solution will be do next:

Irp->UserIosb = &Irp->IoStatus;

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

full correct example how read file asynchronous:

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;
}

并输出:

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 阅读确定

这篇关于对IoGetDeviceObjectPointer()返回的设备对象使用IRP进行I/O的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆