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