驅動程式 - Windows NT Driver (Legacy) - 使用範例 - C/C++ (DDK) - Winsock Kernel(TCP) - Send Data



參考資訊:
https://github.com/wbenny/KSOCKET
https://learn.microsoft.com/en-us/windows-hardware/drivers/network/establishing-a-connection-with-a-destination
https://learn.microsoft.com/en-us/windows-hardware/drivers/network/roadmap-for-developing-network-drivers-with-winsock-kernel
https://community.osr.com/t/wsksocketconnect-how-to-safely-use-the-associated-irp-to-get-the-resulting-wsk-socket-object/53185

main.c

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

WSK_REGISTRATION wreg;

static PIRP irp = NULL;
static KEVENT event = { 0 };
static PVOID pthread = NULL;
static WSK_CLIENT_NPI w = { 0 };
static WSK_PROVIDER_NPI npi = { 0 };
static PWSK_SOCKET socket = NULL;
static PWSK_PROVIDER_CONNECTION_DISPATCH sd = NULL;
static const WSK_CLIENT_DISPATCH wdispatch = {
    MAKE_WSK_VERSION(1, 0),
    0,
    NULL
};

#define SERVER_IP "192.168.229.128"
#define SERVER_PORT 9999

static NTSTATUS complete_routine(PDEVICE_OBJECT	DeviceObject, PIRP Irp, PKEVENT evt)
{
	KeSetEvent(evt, IO_NO_INCREMENT, FALSE);

	return STATUS_MORE_PROCESSING_REQUIRED;
}

static void reuse_irp(void)
{
	KeResetEvent(&event);
	IoReuseIrp(irp, STATUS_UNSUCCESSFUL);
	IoSetCompletionRoutine(irp, &complete_routine, &event, TRUE, TRUE, TRUE);
}

static void connect_thread(PVOID Context)
{
	NTSTATUS status = 0;

	do {
		w.ClientContext = NULL;
		w.Dispatch = &wdispatch;
		status = WskRegister(&w, &wreg);
		if (!NT_SUCCESS(status)) {
			break;
		}
		
		status = WskCaptureProviderNPI(&wreg, WSK_INFINITE_WAIT, &npi);
		if (!NT_SUCCESS(status)) {
			break;
		}
		KeInitializeEvent(&event, SynchronizationEvent, FALSE);

		irp = IoAllocateIrp(1, FALSE);
		if (irp == NULL) {
			status = STATUS_INSUFFICIENT_RESOURCES;
			break;
		}
		IoSetCompletionRoutine(irp, &complete_routine, &event, TRUE, TRUE, TRUE);

		SOCKADDR_IN local = { 0 };
		local.sin_family = AF_INET;
		local.sin_addr.s_addr = INADDR_ANY;
		local.sin_port = 0;

		PCWSTR term = NULL;
		SOCKADDR_IN remote = { 0 };
		remote.sin_family = AF_INET;
		remote.sin_port = RtlUshortByteSwap(SERVER_PORT);
		status = RtlIpv4StringToAddress(SERVER_IP, TRUE, &term, &remote.sin_addr);
		if (!NT_SUCCESS(status)) {
			break;
		}

		status = npi.Dispatch->WskSocketConnect(
			npi.Client,
			SOCK_STREAM,
			IPPROTO_TCP,
			(PSOCKADDR)&local,
			(PSOCKADDR)&remote,
			WSK_FLAG_CONNECTION_SOCKET,
			NULL,
			NULL,
			NULL,
			NULL,
			NULL,
			irp
		);

		if (status == STATUS_PENDING){
			KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
			status = irp->IoStatus.Status;
			socket = (PWSK_SOCKET)irp->IoStatus.Information;
			sd = socket->Dispatch;
		}

		if (!NT_SUCCESS(status)) {
			break;
		}
		DbgPrint("Connected to server !");

		char buffer[] = { "Hello, world!" };
		WSK_BUF WskBuffer;
		WskBuffer.Offset = 0;
		WskBuffer.Length = strlen(buffer);
		WskBuffer.Mdl = IoAllocateMdl(buffer, (ULONG)WskBuffer.Length, FALSE, FALSE, NULL);

		__try {
			MmProbeAndLockPages(WskBuffer.Mdl, KernelMode, IoWriteAccess);
		}
		__except (EXCEPTION_EXECUTE_HANDLER) {
			status = STATUS_ACCESS_VIOLATION;
			break;
		}

		reuse_irp();
		status = sd->WskSend(socket, &WskBuffer, 0, irp);
		if (status == STATUS_PENDING) {
			KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
			status = irp->IoStatus.Status;
		}

		if (!NT_SUCCESS(status)) {
			break;
		}
		MmUnlockPages(WskBuffer.Mdl);
		DbgPrint("Sent data !");

		reuse_irp(); 
		status = sd->WskCloseSocket(socket, irp);
		if (status == STATUS_PENDING) {
			KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
			status = irp->IoStatus.Status;
		}

		if (!NT_SUCCESS(status)) {
			break;
		}
		DbgPrint("Closed socket !");

	} while(0);
	
	if (irp) {
		IoFreeIrp(irp);
	}

	WskReleaseProviderNPI(&wreg);
	WskDeregister(&wreg);
	PsTerminateSystemThread(STATUS_SUCCESS);
}

void Unload(PDRIVER_OBJECT pMyDriver)
{
	if (pthread != NULL) {
		KeWaitForSingleObject(pthread, Executive, KernelMode, FALSE, NULL);
		ObDereferenceObject(pthread);
	}
}
  
NTSTATUS DriverEntry(PDRIVER_OBJECT pMyDriver, PUNICODE_STRING pMyRegistry)
{
    HANDLE thread = 0;
	NTSTATUS status = 0;

	status = PsCreateSystemThread(&thread,
		THREAD_ALL_ACCESS,
		NULL,
		NULL,
		NULL,
		connect_thread,
		NULL
	);

    if (!NT_SUCCESS(status)) {
		DbgPrint("failed to create thread\n");
        return status;
    }
	
	status = ObReferenceObjectByHandle(thread, THREAD_ALL_ACCESS, NULL, KernelMode, &pthread, NULL);
    ZwClose(thread);
    if (!NT_SUCCESS(status)) {
		DbgPrint("failed to reference thread object\n");
		return status;
    }

    pMyDriver->DriverUnload = Unload;
	
    return STATUS_SUCCESS;
}

server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
  
int main(int argc, char const* argv[])
{
    ssize_t r = 0;
    int opt = 1;
    int fd = -1;
    int new_socket = 0;
    char buf[255] = { 0 };
    struct sockaddr_in addr = { 0 };
    const char *hello = "hello from server !";
  
    if (argc != 3) {
        printf("Usage: %s IP Port\n", argv[0]);
        return 0;
    }
  
    socklen_t addrlen = sizeof(addr);
    fd = socket(AF_INET, SOCK_STREAM, 0);
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
  
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    addr.sin_port = htons(atoi(argv[2]));
  
    bind(fd, (struct sockaddr *)&addr, sizeof(addr));
    listen(fd, 3);
    new_socket = accept(fd, (struct sockaddr *)&addr, &addrlen);
    printf("new_socket %d\n", new_socket);
  
    r = read(new_socket, buf, sizeof(buf));
    printf("%s (r=%d)\n", buf, r);
    send(new_socket, hello, strlen(hello), 0);
    close(new_socket);
    close(fd);
 
    return 0;
}