可以在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以获得额外的参考,但这也没有帮助。
感谢您的任何见解。
答案 0 :(得分:2)
您在此代码中指出了至少2个严重错误。
我可以问 - 您尝试从哪个文件读取(或写入)数据?从
你说FileObject
?但文件系统驱动程序如何处理
这个要求知道吗?您没有将任何文件对象传递给newIrp
。
寻找IoBuildAsynchronousFsdRequest
- 它没有文件对象
参数(并且不可能从设备对象获取文件对象 - 仅
反之亦然 - 因为在设备上可以打开多个文件)。所以
并且不能在newIrp
中被这个api填充。你必须设置它
自己:
PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation( newIrp );
irpSp->FileObject = FileObject;
我猜错误就是文件系统尝试访问时FileObject
来自irp,在你的情况下为0。还阅读了文档
IRP_MJ_READ
- IrpSp-> FileObject -
指向与DeviceObject关联的文件对象的指针
io_stat
(和offset
)
IoBuildAsynchronousFsdRequest
。因此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
你也可以解释(不适合我 - 为自己)下一个代码行吗?:
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
- 你在做什么 - 没有任何意义:
在完成例程中将其取消引用。
IofCompleteRequest
后访问Tail.Overlay.Thread
打电话给你的完成例程(如果你没有回来
StopCompletion
)。结果你的引用/解引用线程丢失了
感觉 - 因为你过早地尊重它,之前系统
实际访问它。StopCompletion
而不是更多电话
此 Irp 的IofCompleteRequest
- 系统无法访问
Tail.Overlay.Thread
一点都没有。你不需要参考它
情况下。Tail.Overlay.Thread
仅用于向他插入Apc - 用于呼叫
原始的Irp破坏的最后部分(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;
second call到IofCompleteRequest
会导致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;
}