【问题标题】:WskSocketConnect -- possible memory corruption issue?WskSocketConnect - 可能的内存损坏问题?
【发布时间】:2013-01-21 21:17:03
【问题描述】:

我正在尝试使用驱动程序中的 WinSock API 连接到另一台计算机。问题是WskSocketConnect(和WskConnect,如果我走那条路)失败STATUS_INVALID_ADDRESS。我的代码是:

#pragma warning(push)
#pragma warning(disable:4510)
#pragma warning(disable:4512)
#pragma warning(disable:4610)

#include <ntddk.h>
#include <wsk.h>

#pragma warning(pop)

const WSK_CLIENT_DISPATCH WskClientDispatch = {
    MAKE_WSK_VERSION(1, 0),
    0,
    NULL
};

WSK_REGISTRATION WskRegistration;

typedef struct _ASYNCHRONOUS_OPERATION_CONTEXT {
    KEVENT Event;
    PIRP Irp;
} ASYNCHRONOUS_OPERATION_CONTEXT, *PASYNCHRONOUS_OPERATION_CONTEXT;

extern "C" DRIVER_INITIALIZE DriverEntry;
extern "C" DRIVER_UNLOAD DriverUnload;
extern "C" KSTART_ROUTINE WorkerThread;
extern "C" IO_COMPLETION_ROUTINE SyncIrpCompletionRoutine;
extern "C" NTSTATUS WorkerThreadImpl();
extern "C" NTSTATUS InitializeAsynchronousOperationContext(PASYNCHRONOUS_OPERATION_CONTEXT Context);
extern "C" void FreeAsynchronousOperationContext(PASYNCHRONOUS_OPERATION_CONTEXT Context);
extern "C" void ReuseAsynchronousOperationContext(PASYNCHRONOUS_OPERATION_CONTEXT Context);
extern "C" NTSTATUS WaitForOperation(PASYNCHRONOUS_OPERATION_CONTEXT Context, NTSTATUS DispatchStatus);

NTSTATUS
    DriverEntry(
    __in PDRIVER_OBJECT DriverObject,
    __in PUNICODE_STRING RegistryPath
    );

VOID
    WorkerThread(
    __in PVOID Context
    );

VOID
    DriverUnload(
    __in PDRIVER_OBJECT DriverObject
    );

#ifdef ALLOC_PRAGMA

#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, DriverUnload)
#pragma alloc_text(PAGE, WorkerThread)
#pragma alloc_text(PAGE, WorkerThreadImpl)
#pragma alloc_text(PAGE, InitializeAsynchronousOperationContext)
#pragma alloc_text(PAGE, FreeAsynchronousOperationContext)
#pragma alloc_text(PAGE, ReuseAsynchronousOperationContext)
#pragma alloc_text(PAGE, WaitForOperation)

#endif

#define DATA_BUFFER_POOL_TAG 'wskt'

NTSTATUS
    DriverEntry(
    __in PDRIVER_OBJECT DriverObject,
    __in PUNICODE_STRING RegistryPath
    )
{
    NTSTATUS status;
    HANDLE threadHandle;
    WSK_CLIENT_NPI wskClientNpi;

    UNREFERENCED_PARAMETER(RegistryPath);

    PAGED_CODE();

    DbgPrint("Loading");

    wskClientNpi.ClientContext = NULL;
    wskClientNpi.Dispatch = &WskClientDispatch;
    status = WskRegister(&wskClientNpi, &WskRegistration);

    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = PsCreateSystemThread(
        &threadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL,
        WorkerThread, NULL);

    if (!NT_SUCCESS(status)) {
        WskDeregister(&WskRegistration);
        return status;
    }

    ZwClose(threadHandle);

    DriverObject->DriverUnload = DriverUnload;

    DbgPrint("Loaded");

    return STATUS_SUCCESS;
}

VOID
    DriverUnload(
    __in PDRIVER_OBJECT DriverObject
    )
{  
    UNREFERENCED_PARAMETER(DriverObject);

    PAGED_CODE();

    DbgPrint("Unloading");
    WskDeregister(&WskRegistration);
    DbgPrint("Unloaded");
}

WSK_PROVIDER_NPI wskProviderNpi;
//CHAR Foo[10]; // padding
PCHAR DataBuffer;

USHORT htons(USHORT value)
{
    return value >> 8 | ((value & 0xff) << 8);
}

NTSTATUS InitializeAsynchronousOperationContext(PASYNCHRONOUS_OPERATION_CONTEXT Context)
{
    PAGED_CODE();

    Context->Irp = IoAllocateIrp(1, FALSE);
    if (Context->Irp == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    KeInitializeEvent(&Context->Event, SynchronizationEvent, FALSE);

    IoSetCompletionRoutine(Context->Irp,
        SyncIrpCompletionRoutine,
        &Context->Event, TRUE, TRUE, TRUE);

    return STATUS_SUCCESS;
}

void FreeAsynchronousOperationContext(PASYNCHRONOUS_OPERATION_CONTEXT Context)
{
    PAGED_CODE();

    IoFreeIrp(Context->Irp);
}

void ReuseAsynchronousOperationContext(PASYNCHRONOUS_OPERATION_CONTEXT Context)
{
    PAGED_CODE();

    IoReuseIrp(Context->Irp, STATUS_UNSUCCESSFUL);
    IoSetCompletionRoutine(Context->Irp,
        SyncIrpCompletionRoutine,
        &Context->Event, TRUE, TRUE, TRUE);
}

NTSTATUS WaitForOperation(PASYNCHRONOUS_OPERATION_CONTEXT Context, NTSTATUS DispatchStatus)
{
    PAGED_CODE();

    if (DispatchStatus == STATUS_PENDING) {
        KeWaitForSingleObject(&Context->Event, Executive, KernelMode, FALSE, NULL);
    }

    return Context->Irp->IoStatus.Status;
}

NTSTATUS WorkerThreadImpl()
{
    PAGED_CODE();

    NTSTATUS status;

    status = WskCaptureProviderNPI(
        &WskRegistration, 
        WSK_INFINITE_WAIT,
        &wskProviderNpi);

    if (!NT_SUCCESS(status)) {
        DbgPrint("WskCaptureProviderNPI FAIL");
        return status;
    }

    DataBuffer = (PCHAR) ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, DATA_BUFFER_POOL_TAG);
    if (DataBuffer == NULL) {
        WskReleaseProviderNPI(&WskRegistration);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    ASYNCHRONOUS_OPERATION_CONTEXT OperationContext;
    status = InitializeAsynchronousOperationContext(&OperationContext);
    if (!NT_SUCCESS(status)) {
        WskReleaseProviderNPI(&WskRegistration);
        ExFreePoolWithTag(DataBuffer, DATA_BUFFER_POOL_TAG);
        return status;
    }

    SOCKADDR_IN localAddr  = { AF_INET, 0, IN4ADDR_ANY_INIT };
    SOCKADDR_IN remoteAddr = { AF_INET, htons(7777), IN4ADDR_LOOPBACK_INIT };
    DbgPrint("%p", DataBuffer);

    status = wskProviderNpi.Dispatch->WskSocketConnect(&wskProviderNpi.Client,
        SOCK_STREAM, IPPROTO_TCP,
        (PSOCKADDR) &localAddr, (PSOCKADDR) &remoteAddr, 0,
        NULL, NULL, NULL, NULL, NULL,
        OperationContext.Irp);
    // BUGBUG here, DataBuffer has changed since the previous DbgPrint()
    DbgPrint("%p", DataBuffer);
    status = WaitForOperation(&OperationContext, status);

    if (NT_SUCCESS(status)) {
        PWSK_SOCKET socket = (PWSK_SOCKET) OperationContext.Irp->IoStatus.Information;
        PWSK_PROVIDER_CONNECTION_DISPATCH connectionDispatch = (PWSK_PROVIDER_CONNECTION_DISPATCH) socket->Dispatch;
        ReuseAsynchronousOperationContext(&OperationContext);
        status = connectionDispatch->Basic.WskCloseSocket(socket, OperationContext.Irp);
        status = WaitForOperation(&OperationContext, status);
    }

    FreeAsynchronousOperationContext(&OperationContext);

    WskReleaseProviderNPI(&WskRegistration);
    ExFreePoolWithTag(DataBuffer, DATA_BUFFER_POOL_TAG);
    return STATUS_SUCCESS;
}

VOID
    WorkerThread(
    __in PVOID Context
    )
{
    UNREFERENCED_PARAMETER(Context);

    PAGED_CODE();

    PsTerminateSystemThread(WorkerThreadImpl());
}

NTSTATUS
    SyncIrpCompletionRoutine(
    __in PDEVICE_OBJECT Reserved,
    __in PIRP Irp,
    _In_reads_opt_(_Inexpressible_("varies")) PVOID Context
    )
{    
    UNREFERENCED_PARAMETER(Reserved);
    UNREFERENCED_PARAMETER(Irp);

    ASSERT(Context != NULL);

    if (Irp->PendingReturned) {
        KeSetEvent((PKEVENT) Context, 2, FALSE);
    }

    return STATUS_MORE_PROCESSING_REQUIRED;
}

调用与0xc0000141 (STATUS_INVALID_ADDRESS) 同步失败。我确实有一个应用程序在 localhost 上的该端口上侦听,并且它与远程计算机的行为方式相同。

任何提示将不胜感激!

编辑现在事情变得越来越奇怪了。我用我的完整文件更新了源代码。

我发现WskSocketConnect 的问题:我正在重构一些东西,我试图将WskCaptureProviderNPIWskReleaseProviderNPI 移动到不同的函数,但我开始遇到另一个错误。这很奇怪,所以我将我的 WSK_PROVIDER_NPI 设为全局——它起作用了。

稍后,我尝试在该套接字上发送一条消息,但我在它之前收到了两个字节的垃圾。经过几个小时和一些调试消息后,我注意到WskSocketConnect 调用后缓冲区地址发生了变化。现在,缓冲区地址也是一个全局变量,在我的 WSK_PROVIDER_NPI 结构之后声明。添加一些填充(搜索“Foo”和“WorkerThread()”)修复它。

现在呢?我对此完全陌生,但看起来内存损坏是由Wsk 函数引起的,特别是因为我的代码在DriverEntryWskSocketConnect 调用之间运行的并不多。有什么想法吗?

这是在 Windows 7 x64 上。

更新 2 我在代码开头禁用的编译警告如下:

warning C4510: '_WSK_TDI_MAP_INFO' : default constructor could not be generated
warning C4512: '_WSK_TDI_MAP_INFO' : assignment operator could not be generated
warning C4610: struct '_WSK_TDI_MAP_INFO' can never be instantiated - user defined constructor required

我想将文件编译为 C 可以修复它们。

关于我的WSK_CLIENT_NPIWskRegister 的文档没有提到它应该保持有效(就像它对另一个参数所做的那样),而 DDK 中的echosrv 示例也做同样的事情。可以肯定的是,我尝试将其设为全局,但我得到了相同的行为。

【问题讨论】:

  • 请为我们提供一个最小的可编译测试用例。最小意味着“只使用足够的代码来显示问题的症状”,而可编译意味着“可以在我们的系统上编译和运行,而无需填写空白和做出假设”。
  • 我添加了有关我的问题的更多信息。我不知道是否包含构建文件以及有关我如何运行它的详细信息(安装和启动驱动程序的控制台应用程序)。如果可以节省某人的时间,我可以提供给他们。
  • 这既不是最小的也不是可编译的。从我之前的评论中你有什么不明白的地方吗?
  • 对,第一个版本最后剩下两行代码;我删除了它们,还删除了一些在问题发生后正在运行的代码。我把清理代码留在里面了。

标签: c windows driver wdk


【解决方案1】:

我不建议忽略或禁用警告,因为警告可以很好地表明代码可能无法执行您想要的操作。自豪地炫耀您的警告,我们将帮助您以标准方式删除它们。确保向我们展示警告所在的行以及警告消息。在我看来,您可能会调用未定义的行为,而警告可能会突出显示。

带有前导下划线的标识符也可能很麻烦,因为这种格式是为操作系统 API、编译器和标准库保留的。请重命名 _ASYNCHRONOUS_OPERATION_CONTEXT。

%p 告诉 DbgPrint(它使用 printf 格式说明符)期望哪种指针?空白 *。我建议进行一些强制转换,以处理 printf 引起问题的不太可能的情况。

似乎 &wskProviderNpi.Client 旨在引用与 &wskClientNpi 相同的对象。这里的问题是,当 DriverEntry 返回时 wskClientNpi 将被破坏,从而导致您提到的“STATUS_INVALID_ADDRESS”错误(以一种活泼的方式)。您是否尝试将 wskClientNpi 声明直接放在 wskRegistration 声明下方?

【讨论】:

  • 我更新了问题;似乎延长该对象的生命周期没有什么区别,就像删除 DbgPrint 调用一样。我同意我的结构命名错误。
  • 很好。您是否尝试过编写单线程版本,并通过将多线程暴露于最有益的部分来扩展它?
  • 我在这里使用的唯一线程从 DriverEntry 的末尾开始,运行并退出。我这样做是为了避免在 DriverEntry 函数中调用WskCaptureProviderNPI——它可以无限期地阻塞。
  • 无论如何我都试过了,唯一的区别是驱动程序验证程序(我已启用)做得更好,并在ExFreePoolWithTag(DataBuffer, DATA_BUFFER_POOL_TAG) 行崩溃,因为指针已损坏。
猜你喜欢
  • 2021-03-05
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
  • 2012-05-27
  • 2013-06-20
  • 2010-09-15
  • 2015-06-11
  • 1970-01-01
相关资源
最近更新 更多