驱动开发:内核封装TDI网络通信接口

驱动,开发,内核,封装,tdi,网络,通信接口 · 浏览次数 : 438

小编点评

**内容生成时需要带简单的排版** **排版格式示例:** ``` 姓名:约翰 年龄:30 ``` **代码示例:** ```C++ #include // 初始化LySocket对象 LySocket lysocket; // 连接服务器 LySocket::connect(lysocket, "localhost", 8888, LySocket::SOCK_STREAM); // 发送数据 LySocket::send(lysocket, "Hello lyshark.com", 10, LySocket::SOCK_STREAM); // 接收数据 LySocket::receive(lysocket, 10, LySocket::SOCK_STREAM); // 关闭连接 lysocket.close(); ``` **结果:** ``` Hello lyshark.com ``` **注意:** * 代码示例中的排版格式仅供示例,实际应用中可能需要根据具体需求进行调整。 * 排版格式示例可以根据实际应用中需求进行调整。 * 请确保代码示例中所有变量和函数名都符合实际应用的命名习惯。

正文

在上一篇文章《驱动开发:内核封装WSK网络通信接口》中,LyShark已经带大家看过了如何通过WSK接口实现套接字通信,但WSK实现的通信是内核与内核模块之间的,而如果需要内核与应用层之间通信则使用TDK会更好一些因为它更接近应用层,本章将使用TDK实现,TDI全称传输驱动接口,其主要负责连接Socket和协议驱动,用于实现访问传输层的功能,该接口比NDIS更接近于应用层,在早期Win系统中常用于实现过滤防火墙,同样经过封装后也可实现通信功能,本章将运用TDI接口实现驱动与应用层之间传输字符串,结构体,多线程收发等技术。

  • TDI传输字符串
  • TDI多线程收发
  • TDI传数结构实现认证

SDK库提取,将其命名为MyTDI.hpp放入到代码同级目录下。

拷贝SDK代码
#include <ntifs.h>
#include <tdikrnl.h>
#include <ntstatus.h>

// TCP驱动设备名称
#define COMM_TCP_DEV_NAME L"\\Device\\Tcp"

// 地址转换的宏
#define INETADDR(a, b, c, d) (a + (b<<8) + (c<<16) + (d<<24))
#define HTONL(a) (((a & 0xFF)<<24) + ((a & 0xFF00)<<8) + ((a & 0xFF0000)>>8) + (a&0xFF000000)>>24)
#define HTONS(a) (((a & 0xFF)<<8) + ((a & 0xFF00)>>8))

// 完成回调函数
NTSTATUS TdiCompletionRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
{
  if (NULL != pContext)
  {
    KeSetEvent((PKEVENT)pContext, IO_NO_INCREMENT, FALSE);
  }
  return STATUS_MORE_PROCESSING_REQUIRED;
}

// TDI初始化设置
NTSTATUS TdiOpen(PDEVICE_OBJECT *ppTdiAddressDevObj, PFILE_OBJECT *ppTdiEndPointFileObject, HANDLE *phTdiAddress, HANDLE *phTdiEndPoint)
{
  NTSTATUS status = STATUS_UNSUCCESSFUL;
  PFILE_FULL_EA_INFORMATION pAddressEaBuffer = NULL;
  ULONG ulAddressEaBufferLength = 0;
  PTA_IP_ADDRESS pTaIpAddr = NULL;
  UNICODE_STRING ustrTDIDevName;
  OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
  IO_STATUS_BLOCK iosb = { 0 };
  HANDLE hTdiAddress = NULL;
  PFILE_OBJECT pTdiAddressFileObject = NULL;
  PDEVICE_OBJECT pTdiAddressDevObj = NULL;
  PFILE_FULL_EA_INFORMATION pContextEaBuffer = NULL;
  ULONG ulContextEaBufferLength = 0;
  HANDLE hTdiEndPoint = NULL;
  PFILE_OBJECT pTdiEndPointFileObject = NULL;
  KEVENT irpCompleteEvent = { 0 };
  PIRP pIrp = NULL;

  do
  {
    // 为本地地址拓展属性结构申请内存及初始化
    ulAddressEaBufferLength = sizeof(FILE_FULL_EA_INFORMATION) + TDI_TRANSPORT_ADDRESS_LENGTH + sizeof(TA_IP_ADDRESS);
    pAddressEaBuffer = (PFILE_FULL_EA_INFORMATION)ExAllocatePool(NonPagedPool, ulAddressEaBufferLength);
    if (NULL == pAddressEaBuffer)
    {
      break;
    }
    RtlZeroMemory(pAddressEaBuffer, ulAddressEaBufferLength);
    RtlCopyMemory(pAddressEaBuffer->EaName, TdiTransportAddress, (1 + TDI_TRANSPORT_ADDRESS_LENGTH));
    pAddressEaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
    pAddressEaBuffer->EaValueLength = sizeof(TA_IP_ADDRESS);

    // 初始化本机IP地址与端口
    pTaIpAddr = (PTA_IP_ADDRESS)((PUCHAR)pAddressEaBuffer->EaName + pAddressEaBuffer->EaNameLength + 1);
    pTaIpAddr->TAAddressCount = 1;
    pTaIpAddr->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
    pTaIpAddr->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
    pTaIpAddr->Address[0].Address[0].sin_port = 0;     // 0表示本机任意随机端口
    pTaIpAddr->Address[0].Address[0].in_addr = 0;      // 0表示本机本地IP地址
    RtlZeroMemory(pTaIpAddr->Address[0].Address[0].sin_zero, sizeof(pTaIpAddr->Address[0].Address[0].sin_zero));

    // 创建TDI驱动设备字符串与初始化设备对象
    RtlInitUnicodeString(&ustrTDIDevName, COMM_TCP_DEV_NAME);
    InitializeObjectAttributes(&ObjectAttributes, &ustrTDIDevName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

    // 根据本地地址拓展属性结构创建本地地址对象
    status = ZwCreateFile(&hTdiAddress, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
      &ObjectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL,
      FILE_SHARE_READ, FILE_OPEN, 0, pAddressEaBuffer, ulAddressEaBufferLength);
    if (!NT_SUCCESS(status))
    {
      break;
    }

    // 根据本地地址对象句柄获取对应的本地地址文件对象
    status = ObReferenceObjectByHandle(hTdiAddress,
      FILE_ANY_ACCESS, 0, KernelMode, &pTdiAddressFileObject, NULL);
    if (!NT_SUCCESS(status))
    {
      break;
    }

    // 获取本地地址文件对象对应的驱动设备
    pTdiAddressDevObj = IoGetRelatedDeviceObject(pTdiAddressFileObject);
    if (NULL == pTdiAddressDevObj)
    {
      break;
    }

    // 为上下文拓展属性申请内存并初始化
    ulContextEaBufferLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + TDI_CONNECTION_CONTEXT_LENGTH + 1 + sizeof(CONNECTION_CONTEXT);
    pContextEaBuffer = (PFILE_FULL_EA_INFORMATION)ExAllocatePool(NonPagedPool, ulContextEaBufferLength);
    if (NULL == pContextEaBuffer)
    {
      break;
    }
    RtlZeroMemory(pContextEaBuffer, ulContextEaBufferLength);
    RtlCopyMemory(pContextEaBuffer->EaName, TdiConnectionContext, (1 + TDI_CONNECTION_CONTEXT_LENGTH));
    pContextEaBuffer->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH;
    pContextEaBuffer->EaValueLength = sizeof(CONNECTION_CONTEXT);

    // 根据上下文创建TDI端点对象
    status = ZwCreateFile(&hTdiEndPoint, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
      &ObjectAttributes, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
      FILE_OPEN, 0, pContextEaBuffer, ulContextEaBufferLength);
    if (!NT_SUCCESS(status))
    {
      break;
    }
    // 根据TDI端点对象句柄获取对应的端点文件对象
    status = ObReferenceObjectByHandle(hTdiEndPoint,
      FILE_ANY_ACCESS, 0, KernelMode, &pTdiEndPointFileObject, NULL);
    if (!NT_SUCCESS(status))
    {
      break;
    }

    // 设置事件
    KeInitializeEvent(&irpCompleteEvent, NotificationEvent, FALSE);

    // 将TDI端点与本地地址对象关联, 创建TDI的I/O请求包:TDI_ASSOCIATE_ADDRESS
    pIrp = TdiBuildInternalDeviceControlIrp(TDI_ASSOCIATE_ADDRESS,
      pTdiAddressDevObj, pTdiEndPointFileObject, &irpCompleteEvent, &iosb);
    if (NULL == pIrp)
    {
      break;
    }

    // 拓展I/O请求包
    TdiBuildAssociateAddress(pIrp, pTdiAddressDevObj, pTdiEndPointFileObject, NULL, NULL, hTdiAddress);

    // 设置完成实例的回调函数
    IoSetCompletionRoutine(pIrp, TdiCompletionRoutine, &irpCompleteEvent, TRUE, TRUE, TRUE);

    // 发送I/O请求包并等待执行
    status = IoCallDriver(pTdiAddressDevObj, pIrp);
    if (STATUS_PENDING == status)
    {
      KeWaitForSingleObject(&irpCompleteEvent, Executive, KernelMode, FALSE, NULL);
    }

    // 返回数据
    *ppTdiAddressDevObj = pTdiAddressDevObj;
    *ppTdiEndPointFileObject = pTdiEndPointFileObject;
    *phTdiAddress = hTdiAddress;
    *phTdiEndPoint = hTdiEndPoint;

  } while (FALSE);

  // 释放内存
  if (pTdiAddressFileObject)
  {
    ObDereferenceObject(pTdiAddressFileObject);
  }
  if (pContextEaBuffer)
  {
    ExFreePool(pContextEaBuffer);
  }
  if (pAddressEaBuffer)
  {
    ExFreePool(pAddressEaBuffer);
  }
  return status;
}

// TDI TCP连接服务器
NTSTATUS TdiConnection(PDEVICE_OBJECT pTdiAddressDevObj, PFILE_OBJECT pTdiEndPointFileObject, LONG *pServerIp, LONG lServerPort)
{
  NTSTATUS status = STATUS_SUCCESS;
  IO_STATUS_BLOCK iosb = { 0 };
  PIRP pIrp = NULL;
  KEVENT connEvent = { 0 };
  TA_IP_ADDRESS serverTaIpAddr = { 0 };
  ULONG serverIpAddr = 0;
  USHORT serverPort = 0;
  TDI_CONNECTION_INFORMATION serverConnection = { 0 };

  // 创建连接事件
  KeInitializeEvent(&connEvent, NotificationEvent, FALSE);

  // 创建TDI连接I/O请求包:TDI_CONNECT
  pIrp = TdiBuildInternalDeviceControlIrp(TDI_CONNECT, pTdiAddressDevObj, pTdiEndPointFileObject, &connEvent, &iosb);
  if (NULL == pIrp)
  {
    return STATUS_INSUFFICIENT_RESOURCES;
  }

  // 初始化服务器IP地址与端口
  serverIpAddr = INETADDR(pServerIp[0], pServerIp[1], pServerIp[2], pServerIp[3]);
  serverPort = HTONS(lServerPort);
  serverTaIpAddr.TAAddressCount = 1;
  serverTaIpAddr.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
  serverTaIpAddr.Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
  serverTaIpAddr.Address[0].Address[0].sin_port = serverPort;
  serverTaIpAddr.Address[0].Address[0].in_addr = serverIpAddr;
  serverConnection.UserDataLength = 0;
  serverConnection.UserData = 0;
  serverConnection.OptionsLength = 0;
  serverConnection.Options = 0;
  serverConnection.RemoteAddressLength = sizeof(TA_IP_ADDRESS);
  serverConnection.RemoteAddress = &serverTaIpAddr;

  // 把上述的地址与端口信息增加到I/O请求包中,增加连接信息
  TdiBuildConnect(pIrp, pTdiAddressDevObj, pTdiEndPointFileObject, NULL, NULL, NULL, &serverConnection, 0);

  // 设置完成实例回调函数
  IoSetCompletionRoutine(pIrp, TdiCompletionRoutine, &connEvent, TRUE, TRUE, TRUE);

  // 发送I/O请求包并等待执行
  status = IoCallDriver(pTdiAddressDevObj, pIrp);
  if (STATUS_PENDING == status)
  {
    KeWaitForSingleObject(&connEvent, Executive, KernelMode, FALSE, NULL);
  }
  return status;
}

// TDI TCP发送信息
NTSTATUS TdiSend(PDEVICE_OBJECT pTdiAddressDevObj, PFILE_OBJECT pTdiEndPointFileObject, PUCHAR pSendData, ULONG ulSendDataLength)
{
  NTSTATUS status = STATUS_SUCCESS;
  KEVENT sendEvent;
  PIRP pIrp = NULL;
  IO_STATUS_BLOCK iosb = { 0 };
  PMDL pSendMdl = NULL;

  // 初始化事件
  KeInitializeEvent(&sendEvent, NotificationEvent, FALSE);

  // 创建I/O请求包:TDI_SEND
  pIrp = TdiBuildInternalDeviceControlIrp(TDI_SEND, pTdiAddressDevObj, pTdiEndPointFileObject, &sendEvent, &iosb);
  if (NULL == pIrp)
  {
    return STATUS_INSUFFICIENT_RESOURCES;
  }

  // 创建MDL
  pSendMdl = IoAllocateMdl(pSendData, ulSendDataLength, FALSE, FALSE, pIrp);
  if (NULL == pSendMdl)
  {
    return STATUS_INSUFFICIENT_RESOURCES;
  }
  MmProbeAndLockPages(pSendMdl, KernelMode, IoModifyAccess);

  // 拓展I/O请求包,添加发送信息
  TdiBuildSend(pIrp, pTdiAddressDevObj, pTdiEndPointFileObject, NULL, NULL, pSendMdl, 0, ulSendDataLength);

  // 设置完成实例回调函数
  IoSetCompletionRoutine(pIrp, TdiCompletionRoutine, &sendEvent, TRUE, TRUE, TRUE);

  // 发送I/O请求包并等待执行
  status = IoCallDriver(pTdiAddressDevObj, pIrp);
  if (STATUS_PENDING == status)
  {
    KeWaitForSingleObject(&sendEvent, Executive, KernelMode, FALSE, NULL);
  }

  // 释放MDL
  if (pSendMdl)
  {
    IoFreeMdl(pSendMdl);
  }
  return status;
}

// TDI TCP接收信息
ULONG_PTR TdiRecv(PDEVICE_OBJECT pTdiAddressDevObj, PFILE_OBJECT pTdiEndPointFileObject, PUCHAR pRecvData, ULONG ulRecvDataLength)
{
  NTSTATUS status = STATUS_SUCCESS;
  KEVENT recvEvent;
  PIRP pIrp = NULL;
  IO_STATUS_BLOCK iosb = { 0 };
  PMDL pRecvMdl = NULL;
  ULONG_PTR ulRecvSize = 0;

  // 初始化事件
  KeInitializeEvent(&recvEvent, NotificationEvent, FALSE);

  // 创建I/O请求包:TDI_SEND
  pIrp = TdiBuildInternalDeviceControlIrp(TDI_RECV, pTdiAddressDevObj, pTdiEndPointFileObject, &recvEvent, &iosb);
  if (NULL == pIrp)
  {
    return STATUS_INSUFFICIENT_RESOURCES;
  }

  // 创建MDL
  pRecvMdl = IoAllocateMdl(pRecvData, ulRecvDataLength, FALSE, FALSE, pIrp);
  if (NULL == pRecvMdl)
  {
    return STATUS_INSUFFICIENT_RESOURCES;
  }
  MmProbeAndLockPages(pRecvMdl, KernelMode, IoModifyAccess);

  // 拓展I/O请求包,添加发送信息
  TdiBuildReceive(pIrp, pTdiAddressDevObj, pTdiEndPointFileObject, NULL, NULL, pRecvMdl, TDI_RECEIVE_NORMAL, ulRecvDataLength);

  // 设置完成实例回调函数
  IoSetCompletionRoutine(pIrp, TdiCompletionRoutine, &recvEvent, TRUE, TRUE, TRUE);

  // 发送I/O请求包并等待执行
  status = IoCallDriver(pTdiAddressDevObj, pIrp);
  if (STATUS_PENDING == status)
  {
    KeWaitForSingleObject(&recvEvent, Executive, KernelMode, FALSE, NULL);
  }

  // 获取实际接收的数据大小
  ulRecvSize = pIrp->IoStatus.Information;

  // 释放MDL
  if (pRecvMdl)
  {
    IoFreeMdl(pRecvMdl);
  }
  return status;
}

// TDI关闭释放
VOID TdiClose(PFILE_OBJECT pTdiEndPointFileObject, HANDLE hTdiAddress, HANDLE hTdiEndPoint)
{
  if (pTdiEndPointFileObject)
  {
    ObDereferenceObject(pTdiEndPointFileObject);
  }
  if (hTdiEndPoint)
  {
    ZwClose(hTdiEndPoint);
  }
  if (hTdiAddress)
  {
    ZwClose(hTdiAddress);
  }
}

TDI 传输字符串: 服务端在应用层侦听,客户端是驱动程序,驱动程序加载后自动连接应用层并发送消息。

首先来看应用层(服务端)代码,具体我就不说了,来看教程的都是有基础的。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>  
#include <winsock2.h>  

#pragma comment(lib,"ws2_32.lib")
#define PORT 8888 

int main(int argc, char *argv[])
{
  printf("hello lyshark.com \n");
  WSADATA WSAData;
  SOCKET sock, msgsock;
  struct sockaddr_in ServerAddr;

  if (WSAStartup(MAKEWORD(2, 0), &WSAData) != SOCKET_ERROR)
  {
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_port = htons(PORT);
    ServerAddr.sin_addr.s_addr = INADDR_ANY;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    int BindRet = bind(sock, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));
    int LinsRet = listen(sock, 10);
  }

  while (1)
  {
    char buf[1024] = { 0 };
    msgsock = accept(sock, (LPSOCKADDR)0, (int *)0);
    memset(buf, 0, sizeof(buf));

    recv(msgsock, buf, 1024, 0);
    printf("内核返回: %s \n", buf);

    char send_buffer[1024] = { 0 };
    memset(send_buffer, 0, 1024);
    strcpy(send_buffer, "Hi,R0 !");
    send(msgsock, send_buffer, strlen(send_buffer), 0);
    closesocket(msgsock);
  }
  closesocket(sock);
  WSACleanup();
  return 0;
}

再来是驱动层代码,如下所示;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include "MyTDI.hpp"

// 发送接收数据
NTSTATUS SendOnRecv()
{
  NTSTATUS status = STATUS_SUCCESS;
  HANDLE hTdiAddress = NULL;
  HANDLE hTdiEndPoint = NULL;
  PDEVICE_OBJECT pTdiAddressDevObj = NULL;
  PFILE_OBJECT pTdiEndPointFileObject = NULL;
  LONG pServerIp[4] = { 127, 0, 0, 1 };
  LONG lServerPort = 8888;
  UCHAR szSendData[] = "hello lyshark";
  ULONG ulSendDataLength = 1 + strlen(szSendData);
  HANDLE hThread = NULL;

  // TDI初始化
  status = TdiOpen(&pTdiAddressDevObj, &pTdiEndPointFileObject, &hTdiAddress, &hTdiEndPoint);
  if (!NT_SUCCESS(status))
  {
    return STATUS_SUCCESS;
  }

  // TDI TCP连接服务器
  status = TdiConnection(pTdiAddressDevObj, pTdiEndPointFileObject, pServerIp, lServerPort);
  if (!NT_SUCCESS(status))
  {
    return STATUS_SUCCESS;
  }

  // TDI TCP发送信息
  status = TdiSend(pTdiAddressDevObj, pTdiEndPointFileObject, szSendData, ulSendDataLength);
  if (!NT_SUCCESS(status))
  {
    return STATUS_SUCCESS;
  }
  DbgPrint("发送: %s\n", szSendData);

  // 创建接收信息多线程, 循环接收信息

  char szRecvData[1024] = { 0 };
  ULONG ulRecvDataLenngth = 1024;
  RtlZeroMemory(szRecvData, ulRecvDataLenngth);

  // TDI TCP接收信息
  do
  {
    ulRecvDataLenngth = TdiRecv(pTdiAddressDevObj, pTdiEndPointFileObject, szRecvData, ulRecvDataLenngth);
    if (0 < ulRecvDataLenngth)
    {
      DbgPrint("接收数据: %s\n", szRecvData);
      break;;
    }

  } while (TRUE);

  // 释放
  TdiClose(pTdiEndPointFileObject, hTdiAddress, hTdiEndPoint);
  return STATUS_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
  DbgPrint("驱动卸载成功 \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
  for (int x = 0; x < 10; x++)
  {
    SendOnRecv();
  }

  DbgPrint("驱动加载成功 \n");
  Driver->DriverUnload = UnDriver;
  return STATUS_SUCCESS;
}

首先运行应用层开启服务端侦听,然后运行驱动程序,会输出如下信息;

TDI 多线程收发包: 实现驱动内部发送数据包后开启一个线程用于等待应用层返回并输出结果,多线程收发在发送数据包后需要创建新的线程等待接收。

首先是服务端代码。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>  
#include <winsock2.h>  

#pragma comment(lib,"ws2_32.lib")
#define PORT 8888 

int main(int argc, char *argv[])
{
	printf("hello lyshark.com \n");
	WSADATA WSAData;
	SOCKET sock, msgsock;
	struct sockaddr_in ServerAddr;

	if (WSAStartup(MAKEWORD(2, 0), &WSAData) != SOCKET_ERROR)
	{
		ServerAddr.sin_family = AF_INET;
		ServerAddr.sin_port = htons(PORT);
		ServerAddr.sin_addr.s_addr = INADDR_ANY;

		sock = socket(AF_INET, SOCK_STREAM, 0);
		int BindRet = bind(sock, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));
		int LinsRet = listen(sock, 10);
	}

	while (1)
	{
		char buf[1024] = { 0 };
		msgsock = accept(sock, (LPSOCKADDR)0, (int *)0);
		memset(buf, 0, sizeof(buf));

		recv(msgsock, buf, 1024, 0);
		printf("内核返回: %s \n", buf);

		char send_buffer[1024] = { 0 };
		memset(send_buffer, 0, 1024);
		strcpy(send_buffer, "Hi,R0 !");
		send(msgsock, send_buffer, strlen(send_buffer), 0);
		closesocket(msgsock);
	}
	closesocket(sock);
	WSACleanup();
	return 0;
}

驱动程序代码如下,RecvThreadProc主要负责数据接收,SendThreadData负责数据发送。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include "LySocket.hpp"

typedef struct _MY_DATA
{
	PDEVICE_OBJECT pTdiAddressDevObj;
	PFILE_OBJECT pTdiEndPointFileObject;
	HANDLE hTdiAddress;
	HANDLE hTdiEndPoint;
}MY_DATA, *PMY_DATA;

// 接收信息多线程
VOID RecvThreadProc(_In_ PVOID StartContext)
{
	PMY_DATA pMyData = (PMY_DATA)StartContext;
	NTSTATUS status = STATUS_SUCCESS;
	char szRecvData[1024] = { 0 };
	ULONG ulRecvDataLenngth = 1024;
	RtlZeroMemory(szRecvData, ulRecvDataLenngth);

	// TDI TCP接收信息
	do
	{
		ulRecvDataLenngth = TdiRecv(pMyData->pTdiAddressDevObj, pMyData->pTdiEndPointFileObject, szRecvData, ulRecvDataLenngth);
		if (0 < ulRecvDataLenngth)
		{
			DbgPrint("线程句柄:%x --> 接收数据包: %s\n", pMyData->hTdiEndPoint, szRecvData);
			break;;
		}

	} while (TRUE);

	// 释放
	TdiClose(pMyData->pTdiEndPointFileObject, pMyData->hTdiAddress, pMyData->hTdiEndPoint);
	ExFreePool(pMyData);
}

// 多线程发送
NTSTATUS SendThreadData()
{
	NTSTATUS status = STATUS_SUCCESS;
	HANDLE hTdiAddress = NULL;
	HANDLE hTdiEndPoint = NULL;
	PDEVICE_OBJECT pTdiAddressDevObj = NULL;
	PFILE_OBJECT pTdiEndPointFileObject = NULL;
	LONG pServerIp[4] = { 127, 0, 0, 1 };
	LONG lServerPort = 8888;
	UCHAR szSendData[] = "hello lyshark";
	ULONG ulSendDataLength = 1 + strlen(szSendData);
	HANDLE hThread = NULL;

	// TDI初始化
	status = TdiOpen(&pTdiAddressDevObj, &pTdiEndPointFileObject, &hTdiAddress, &hTdiEndPoint);
	if (!NT_SUCCESS(status))
	{
		return STATUS_SUCCESS;
	}

	// TDI TCP连接服务器
	status = TdiConnection(pTdiAddressDevObj, pTdiEndPointFileObject, pServerIp, lServerPort);
	if (!NT_SUCCESS(status))
	{
		return STATUS_SUCCESS;
	}

	// TDI TCP发送信息
	status = TdiSend(pTdiAddressDevObj, pTdiEndPointFileObject, szSendData, ulSendDataLength);
	if (!NT_SUCCESS(status))
	{
		return STATUS_SUCCESS;
	}
	DbgPrint("发送 %s\n", szSendData);

	// 创建接收信息多线程, 循环接收信息
	PMY_DATA pMyData = ExAllocatePool(NonPagedPool, sizeof(MY_DATA));
	pMyData->pTdiAddressDevObj = pTdiAddressDevObj;
	pMyData->pTdiEndPointFileObject = pTdiEndPointFileObject;
	pMyData->hTdiAddress = hTdiAddress;
	pMyData->hTdiEndPoint = hTdiEndPoint;

	PsCreateSystemThread(&hThread, 0, NULL, NtCurrentProcess(), NULL, RecvThreadProc, pMyData);
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("驱动卸载成功 \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("hello lyshark.com \n");

	for (int x = 0; x < 10; x++)
	{
		SendThreadData();
	}
	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

运行应用层服务端等待侦听,运行驱动程序输出如下效果;

TDI 传数结构实现认证: 驱动内部发送结构体给应用层,应用层验证结构体成员,此功能可实现对驱动程序的控制机制,例如是否允许驱动加载卸载等,通常用于驱动辅助认证。

应用层代码

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>  
#include <winsock2.h>  

#pragma comment(lib,"ws2_32.lib")
#define PORT 8888

// 传输结构体
typedef struct
{
	int uuid;
	char username[256];
	char password[256];
}SocketData;

int main(int argc, char *argv[])
{
	printf("hello lyshark.com \n");

	WSADATA WSAData;
	SOCKET sock, msgsock;
	struct sockaddr_in ServerAddr;

	if (WSAStartup(MAKEWORD(2, 0), &WSAData) != SOCKET_ERROR)
	{
		ServerAddr.sin_family = AF_INET;
		ServerAddr.sin_port = htons(PORT);
		ServerAddr.sin_addr.s_addr = INADDR_ANY;

		sock = socket(AF_INET, SOCK_STREAM, 0);
		int BindRet = bind(sock, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));
		int LinsRet = listen(sock, 10);
	}

	while (1)
	{
		char buf[8192] = { 0 };
		msgsock = accept(sock, (LPSOCKADDR)0, (int *)0);
		memset(buf, 0, sizeof(buf));

		// 接收返回数据
		recv(msgsock, buf, sizeof(SocketData), 0);

		// 强转结构体
		SocketData* msg = (SocketData*)buf;

		printf("UUID = %d \n", msg->uuid);
		printf("名字 = %s \n", msg->username);
		printf("密码 = %s \n", msg->password);

		// 验证通过则继续使用
		if ((strcmp(msg->username, "lyshark") == 0) && (strcmp(msg->password, "123") == 0))
		{
			char send_buffer[8192] = { 0 };
			memset(send_buffer, 0, 8192);
			strcpy(send_buffer, "success");
			send(msgsock, send_buffer, strlen(send_buffer), 0);
			closesocket(msgsock);
		}
		// 不通过则禁止驱动加载
		else
		{
			char send_buffer[8192] = { 0 };
			memset(send_buffer, 0, 8192);
			strcpy(send_buffer, "error");
			send(msgsock, send_buffer, strlen(send_buffer), 0);
			closesocket(msgsock);
		}
	}
	closesocket(sock);
	WSACleanup();
	return 0;
}

驱动层代码

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
#include "LySocket.hpp"

// 传输结构体
typedef struct
{
	int uuid;
	char username[256];
	char password[256];
}SocketData;

// 验证账号密码是否正确
BOOLEAN CheckDriver()
{
	NTSTATUS status = STATUS_SUCCESS;
	HANDLE hTdiAddress = NULL;
	HANDLE hTdiEndPoint = NULL;
	PDEVICE_OBJECT pTdiAddressDevObj = NULL;
	PFILE_OBJECT pTdiEndPointFileObject = NULL;
	LONG pServerIp[4] = { 127, 0, 0, 1 };
	LONG lServerPort = 8888;

	// TDI初始化
	status = TdiOpen(&pTdiAddressDevObj, &pTdiEndPointFileObject, &hTdiAddress, &hTdiEndPoint);
	if (!NT_SUCCESS(status))
	{
		return STATUS_SUCCESS;
	}

	// TDI TCP连接服务器
	status = TdiConnection(pTdiAddressDevObj, pTdiEndPointFileObject, pServerIp, lServerPort);
	if (!NT_SUCCESS(status))
	{
		return STATUS_SUCCESS;
	}

	SocketData ptr;

	RtlZeroMemory(&ptr, sizeof(SocketData));

	// 填充结构
	ptr.uuid = 1001;
	RtlCopyMemory(ptr.username, "lyshark", strlen("xxxxxxx"));
	RtlCopyMemory(ptr.password, "123123", strlen("xxxxxx"));

	// TDI TCP发送信息
	status = TdiSend(pTdiAddressDevObj, pTdiEndPointFileObject, &ptr, sizeof(SocketData));
	if (!NT_SUCCESS(status))
	{
		return STATUS_SUCCESS;
	}

	// 创建接收信息多线程, 循环接收信息
	char szRecvData[8192] = { 0 };
	ULONG ulRecvDataLenngth = 8192;
	RtlZeroMemory(szRecvData, ulRecvDataLenngth);

	// TDI TCP接收信息
	do
	{
		ulRecvDataLenngth = TdiRecv(pTdiAddressDevObj, pTdiEndPointFileObject, szRecvData, ulRecvDataLenngth);
		if (0 < ulRecvDataLenngth)
		{
			DbgPrint("接收数据: %s\n", szRecvData);

			if (strncmp(szRecvData, "success", 7) == 0)
			{
				// 释放
				TdiClose(pTdiEndPointFileObject, hTdiAddress, hTdiEndPoint);
				return TRUE;
			}
			else if (strncmp(szRecvData, "error", 5) == 0)
			{
				// 释放
				TdiClose(pTdiEndPointFileObject, hTdiAddress, hTdiEndPoint);
				return FALSE;
			}
			break;;
		}
	} while (TRUE);

	// 释放
	TdiClose(pTdiEndPointFileObject, hTdiAddress, hTdiEndPoint);
	return STATUS_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("驱动卸载成功 \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("hello lyshark.com \n");

	BOOLEAN ref = CheckDriver();

	if (ref == FALSE)
	{
		DbgPrint("[LyShark.com] 驱动已过期,无法加载 \n");
		Driver->DriverUnload = UnDriver;
		return STATUS_SUCCESS;
	}

	DbgPrint("[*] 驱动正常使用 \n");
	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

运行应用层服务端,并运行驱动程序,则会验证该驱动是否合法,如果合法则加载不合法则拒绝;

参考文献

《WINDOWS黑客编程技术详解》甘迪文

与驱动开发:内核封装TDI网络通信接口相似的内容:

驱动开发:内核封装TDI网络通信接口

在上一篇文章`《驱动开发:内核封装WSK网络通信接口》`中,`LyShark`已经带大家看过了如何通过WSK接口实现套接字通信,但WSK实现的通信是内核与内核模块之间的,而如果需要内核与应用层之间通信则使用TDK会更好一些因为它更接近应用层,本章将使用TDK实现,TDI全称传输驱动接口,其主要负责连接`Socket`和协议驱动,用于实现访问传输层的功能,该接口比`NDIS`更接近于应用层,在早期W

驱动开发:内核封装WSK网络通信接口

本章`LyShark`将带大家学习如何在内核中使用标准的`Socket`套接字通信接口,我们都知道`Windows`应用层下可直接调用`WinSocket`来实现网络通信,但在内核模式下应用层API接口无法使用,内核模式下有一套专有的`WSK`通信接口,我们对WSK进行封装,让其与应用层调用规范保持一致,并实现内核与内核直接通过`Socket`通信的案例。

驱动开发:内核封装WFP防火墙入门

WFP框架是微软推出来替代TDIHOOK传输层驱动接口网络通信的方案,其默认被设计为分层结构,该框架分别提供了用户态与内核态相同的AIP函数,在两种模式下均可以开发防火墙产品,以下代码我实现了一个简单的驱动过滤防火墙。WFP 框架分为两大层次模块,用户态基础过滤引擎`BFE (BaseFilteringEngine)` ,以及内核态过滤引擎 `KMFE (KMFilteringEngine)`,基

驱动开发:内核枚举LoadImage映像回调

在笔者之前的文章`《驱动开发:内核特征码搜索函数封装》`中我们封装实现了特征码定位功能,本章将继续使用该功能,本次我们需要枚举内核`LoadImage`映像回调,在Win64环境下我们可以设置一个`LoadImage`映像加载通告回调,当有新驱动或者DLL被加载时,回调函数就会被调用从而执行我们自己的回调例程,映像回调也存储在数组里,枚举时从数组中读取值之后,需要进行位运算解密得到地址。

驱动开发:内核实现SSDT挂钩与摘钩

在前面的文章`《驱动开发:内核解析PE结构导出表》`中我们封装了两个函数`KernelMapFile()`函数可用来读取内核文件,`GetAddressFromFunction()`函数可用来在导出表中寻找指定函数的导出地址,本章将以此为基础实现对特定`SSDT`函数的`Hook`挂钩操作,与`《驱动开发:内核层InlineHook挂钩函数》`所使用的挂钩技术基本一致,不同点是前者使用了`CR3`

驱动开发:内核解析PE结构导出表

在笔者的上一篇文章`《驱动开发:内核特征码扫描PE代码段》`中`LyShark`带大家通过封装好的`LySharkToolsUtilKernelBase`函数实现了动态获取内核模块基址,并通过`ntimage.h`头文件中提供的系列函数解析了指定内核模块的`PE节表`参数,本章将继续延申这个话题,实现对PE文件导出表的解析任务,导出表无法动态获取,解析导出表则必须读入内核模块到内存才可继续解析,所

驱动开发:内核读写内存浮点数

如前所述,在前几章内容中笔者简单介绍了`内存读写`的基本实现方式,这其中包括了`CR3切换`读写,`MDL映射`读写,`内存拷贝`读写,本章将在如前所述的读写函数进一步封装,并以此来实现驱动读写内存浮点数的目的。内存`浮点数`的读写依赖于`读写内存字节`的实现,因为浮点数本质上也可以看作是一个字节集,对于`单精度浮点数`来说这个字节集列表是4字节,而对于`双精度浮点数`,此列表长度则为8字节。

驱动开发:内核解析PE结构节表

在笔者上一篇文章`《驱动开发:内核解析PE结构导出表》`介绍了如何解析内存导出表结构,本章将继续延申实现解析PE结构的PE头,PE节表等数据,总体而言内核中解析PE结构与应用层没什么不同,在上一篇文章中`LyShark`封装实现了`KernelMapFile()`内存映射函数,在之后的章节中这个函数会被多次用到,为了减少代码冗余,后期文章只列出重要部分,读者可以自行去前面的文章中寻找特定的片段。

驱动开发:内核注册表增删改查

注册表是Windows中的一个重要的数据库,用于存储系统和应用程序的设置信息,注册表是一个巨大的树形结构,无论在应用层还是内核层操作注册表都有独立的API函数可以使用,而在内核中读写注册表则需要使用内核装用API函数,如下将依次介绍并封装一些案例,实现对注册表的创建,删除,更新,查询等操作。

驱动开发:内核PE结构VA与FOA转换

本章将继续探索内核中解析PE文件的相关内容,PE文件中FOA与VA,RVA之间的转换也是很重要的,所谓的FOA是文件中的地址,VA则是内存装入后的虚拟地址,RVA是内存基址与当前地址的相对偏移,本章还是需要用到`《驱动开发:内核解析PE结构导出表》`中所封装的`KernelMapFile()`映射函数,在映射后对其PE格式进行相应的解析,并实现转换函数。