Data Transfer Manipulation (WinSock2 / NtDeviceIoControlFile)

310 Views Asked by At

I'm trying to write a library that will intercept a data transfer in the current process (not mine) for further manipulation. My task is to make sure that the library that I embed in the process can decide whether to skip data further or not, moreover, do it in such a way that the client does not suspect anything.

Empirically, I found out that all data transfer functions (send / recv / WSASend / WSARecv etc.) ultimately lead to the NtDeviceIoControlFile function from the ntdll.dll library. In IoControlCode you can see what exactly is happening.

#define IOCTL_AFD_RECV   0x12017
#define IOCTL_AFD_SEND   0x1201F
#define IOCTL_AFD_SELECT 0x12024

On the Internet, I managed to find these two structures.

struct AFD_WSABUF
{
    UINT len;
    PCHAR buf;
};

struct AFD_INFO
{
    AFD_WSABUF* BufferArray;
    ULONG BufferCount;
    ULONG AfdFlags;
    ULONG TdiFlags;
};

With the imitation of a successful send, I seem to have coped.

// Intercepted function NtDeviceIOControlFile
int WINAPI myNtDeviceIoControlFile(
    HANDLE FileHandle,
    HANDLE Event,
    PIO_APC_ROUTINE ApcRoutine,
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    ULONG IoControlCode,
    PVOID InputBuffer,
    ULONG InputBufferLength,
    PVOID OutputBuffer,
    ULONG OutputBufferLength)
{
    if (IoControlCode == IOCTL_AFD_SEND)
    {
        const AFD_INFO* info = reinterpret_cast<AFD_INFO*>(InputBuffer);

        if (GetAsyncKeyState(VK_XBUTTON1) < 0)
        {
            IoStatusBlock->Status = STATUS_SUCCESS;
            IoStatusBlock->Pointer = nullptr;

            // I'm not entirely sure that I correctly calculate the length of all data, since there may be several buffers,
            // I tried to do it differently, but the process began to crash for me :/
            IoStatusBlock->Information = info->BufferArray->len;

            return STATUS_SUCCESS;
        }
    }

    // Calling the original function
    return pNtDeviceIoControlFile(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, IoControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength);
}

The client considers that the data was successfully sent.

However, I do not understand how to make sure that the client does not see that some data has arrived? I tried to do something similar with IOCTL_AFD_RECV, but I was either disconnected from the server with an error, or the data was still read after the original function was called.

One of my old projects has a similar implementation, it uses intercepting the select function and setting readfds->fd_count to 0. However, after I resume receiving data, the data that I "missed" will still be received. And I need to drop them. If this is not possible, then, at least, how to do the same but using NtDeviceIoControlFile? I heard there is IOCTL_AFD_SELECT, but I couldn't find a structure to go to readfds->fd_count to 0.

upd: I found these two structures on the internet, but I still do not understand how to correctly call FD_ZERO

struct AFD_POLL_HANDLE_INFO
{
    HANDLE handle;
    ULONG events;
    NTSTATUS status;
};

struct AFD_POLL_INFO
{
    LARGE_INTEGER timeout;
    ULONG numberOfHandles;
    ULONG exclusive;
    AFD_POLL_HANDLE_INFO* handles;
};
0

There are 0 best solutions below