基于tdi实现http的get和post请求

*本文对以下源代码进行学习总结:https://github.com/alexkrnl/NetDriver

最近在看一个样本里面涉及到驱动层的http请求,分析起来有点吃力,细节比较繁杂。于是到github上找找,就发现上面的源码,真香,还逆个啥。

tdi(transport driver interface)对应的传输层,在内核中实现。如下图:

 (图片来自网上:https://docs.microsoft.com/zh-CN/windows-hardware/drivers/network/images/101osi.png

该工程将接口已经进行封装,与应用层网络通信接口使用非常相似。大体分为以下五步:

1,TDIKernelCreateTCPSocket,创建套接字

2,TDIKernelConnect,连接服务器

3,TDISend,发送数据

4,TDIRecv,接收数据

5,TDICloseSocket,关闭套接字

各接口内部通过构造不同的irp,直接与\device\tcp通信。

注意:由于是在传输层实现的通信,所有会被wireshark(在网络层实现抓包,ndis)捕捉到。

以下是完整的代码:

#include <ntddk.h>
#include <tdi.h>
#include <tdikrnl.h>
#include <ntstrsafe.h>

/*/
//
//	Description: Super basic TDI kernel-mode client
//		which can send and receive http requests
//
//	Contains basic routines & implementations for
//	such actions.
*//



#define TCPDRIVER L"\\Device\\Tcp"
#define UDPDRIVER L"\\Device\\Udp"


UNICODE_STRING tcpip = RTL_CONSTANT_STRING(TCPDRIVER);
UNICODE_STRING udpdriver = RTL_CONSTANT_STRING(UDPDRIVER);
#define HTONS(a) (((0xFF&a)<<8) + ((0xFF00&a)>>8))
#define INETADDR(a, b, c, d) (a + (b<<8) + (c<<16) + (d<<24))

#define _malloc(_s) ExAllocatePoolWithTag(NonPagedPool,_s,'AlO');
#define _free(_s) ExFreePoolWithTag(_s, 'AlO');

/*///
//
//	Function: TdiCompletionRoutine
//
//	Purpose: Callback for the IRP
//
//
*

NTSTATUS TdiCompletionRoutine(IN PDEVICE_OBJECT Object, IN PIRP Irp, IN PVOID Context) 
{
	if (Context != NULL) 
	{
		KeSetEvent((PKEVENT)Context, 0, FALSE);
	}

	return STATUS_MORE_PROCESSING_REQUIRED;
}


VOID Unload(PDRIVER_OBJECT pdriverobject) 
{
	DbgPrint("NetDrv Unloaded");
}

/*///
//
//	Function: TDIKernelConnection
//
//	Purpose: Create Connection Object
//  to create socket in Association
//  with Address Object
//
//	Environment: Kernel Mode only
//
*

NTSTATUS TDIKernelConnection(IN PHANDLE Handle, IN PFILE_OBJECT **ConnectionObject) 
{
	NTSTATUS st = STATUS_UNSUCCESSFUL;
	OBJECT_ATTRIBUTES oa;
	ULONG ubuffer;
	IO_STATUS_BLOCK io;
	CHAR EA_Buffer[sizeof(FILE_FULL_EA_INFORMATION) + sizeof(TdiTransportAddress)-1 + sizeof(TA_IP_ADDRESS)];
	PFILE_FULL_EA_INFORMATION	pEA_Buffer = (PFILE_FULL_EA_INFORMATION)EA_Buffer;
	CONNECTION_CONTEXT			contextplaceholder = NULL;
	
	ubuffer = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + sizeof(TdiConnectionContext)-1 + 1 + sizeof(CONNECTION_CONTEXT);
	pEA_Buffer = (PFILE_FULL_EA_INFORMATION)ExAllocatePool(NonPagedPool, ubuffer);
	if (pEA_Buffer == NULL) 
	{
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	RtlSecureZeroMemory(pEA_Buffer, ubuffer);
	pEA_Buffer->NextEntryOffset = 0;
	pEA_Buffer->Flags = 0;
	pEA_Buffer->EaNameLength = sizeof(TdiConnectionContext) - 1;
	RtlCopyMemory(pEA_Buffer->EaName, TdiConnectionContext, pEA_Buffer->EaNameLength + 1);

	pEA_Buffer->EaValueLength = sizeof(CONNECTION_CONTEXT);
	*(CONNECTION_CONTEXT*)(pEA_Buffer->EaName + (pEA_Buffer->EaNameLength + 1)) = (CONNECTION_CONTEXT)contextplaceholder;

	InitializeObjectAttributes(&oa, &tcpip, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

	st = ZwCreateFile(Handle,FILE_GENERIC_READ |FILE_GENERIC_WRITE |SYNCHRONIZE,&oa,&io,0,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,FILE_OPEN,0,pEA_Buffer,sizeof(EA_Buffer));
	if (NT_SUCCESS(st))
	{
		st = ObReferenceObjectByHandle(*Handle,FILE_GENERIC_READ,NULL,KernelMode,(PVOID*)*ConnectionObject,NULL);
		if (NT_SUCCESS(st))
		{
			st = STATUS_SUCCESS;
		}
	}

	return st;
}

/*///
//
//	Function: TDIKernelOpenAddress
//
//	Purpose: Create Address Object
//  to create socket in Association
//  with Connection Object
//
//	Environment:
//			Kernel mode only
//			
*/

NTSTATUS TDIKernelOpenAddress(IN PHANDLE *Handle, IN PFILE_OBJECT *ConnectionAddress, IN ULONG Ip, IN USHORT Port) 
{
	NTSTATUS st = STATUS_UNSUCCESSFUL;
	OBJECT_ATTRIBUTES TDIObjectAttributes = { 0 };
	IO_STATUS_BLOCK io;
	PTA_IP_ADDRESS ipaddress;
	CHAR EABuffer[sizeof(FILE_FULL_EA_INFORMATION) + sizeof(TdiTransportAddress)-1 + sizeof(TA_IP_ADDRESS)];	// Fill the Extended Attributes Buffer
	PFILE_FULL_EA_INFORMATION pEABuffer = (PFILE_FULL_EA_INFORMATION)EABuffer;								// Define the pointer


	InitializeObjectAttributes(&TDIObjectAttributes, &tcpip, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

	pEABuffer->NextEntryOffset = 0;
	pEABuffer->Flags = 0;
	pEABuffer->EaNameLength = sizeof(TdiTransportAddress) - 1;
	RtlCopyMemory(pEABuffer->EaName, TdiTransportAddress, pEABuffer->EaNameLength + 1);

	pEABuffer->EaValueLength = sizeof(TA_IP_ADDRESS);

	ipaddress = (PTA_IP_ADDRESS)(pEABuffer->EaName + pEABuffer->EaNameLength + 1);
	ipaddress->TAAddressCount = 1;																// Number of Addresses, only one
	ipaddress->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;								// length
	ipaddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;									// type of address
	ipaddress->Address[0].Address[0].sin_port = Port;											// define 0 for both port and Ip address
	ipaddress->Address[0].Address[0].in_addr = Ip;

	RtlSecureZeroMemory(ipaddress->Address[0].Address[0].sin_zero, sizeof(ipaddress->Address[0].Address[0].sin_zero));

	st = ZwCreateFile(*Handle, FILE_GENERIC_READ | FILE_GENERIC_WRITE | SYNCHRONIZE, &TDIObjectAttributes, &io, 0, FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ,FILE_OPEN,0, pEABuffer, sizeof(EABuffer));
	if (NT_SUCCESS(st))
	{

		st = ObReferenceObjectByHandle(**Handle, FILE_ANY_ACCESS, 0,KernelMode,(PVOID*)ConnectionAddress,NULL);
		if (NT_SUCCESS(st))
		{
			st = STATUS_SUCCESS;

		}

	}

	return st;
}

/*/
//
//	Function: TDIKernelCreateTCPSocket
//
//	Purpose: Create socket by associating
//    both the address object and the
//	  connection object.
//
//	Environment: Kernel mode only
//
*/

NTSTATUS TDIKernelCreateTCPSocket(IN PHANDLE AddressHandle, IN PFILE_OBJECT *ConnectionObject, IN PDEVICE_OBJECT *SocketObject) 
{
	NTSTATUS st = STATUS_UNSUCCESSFUL;
	HANDLE objecthandle;
	KEVENT Event;
	PIRP Irp;
	PFILE_OBJECT AddressObject;
	IO_STATUS_BLOCK io;

	st = TDIKernelOpenAddress(&AddressHandle, &AddressObject, 0, 0);
	if (NT_SUCCESS(st))
	{

		st = TDIKernelConnection(&objecthandle, &ConnectionObject);
		if (NT_SUCCESS(st))
		{

			*SocketObject = IoGetRelatedDeviceObject(AddressObject);
			if (*SocketObject && MmIsAddressValid(*SocketObject))
			{
				KeInitializeEvent(&Event, NotificationEvent, FALSE);
				Irp = TdiBuildInternalDeviceControlIrp(TDI_ASSOCIATE_ADDRESS, *SocketObject, *ConnectionObject, &Event, &io);
				if (Irp)
				{
					
					TdiBuildAssociateAddress(Irp, *SocketObject, *ConnectionObject, NULL, NULL, *AddressHandle);
					IoSetCompletionRoutine(Irp, TdiCompletionRoutine, &Event, TRUE, TRUE, TRUE);

					st = IofCallDriver(*SocketObject, Irp);
					if (st == STATUS_PENDING)
					{
						KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
					}
					else
					{
						st = STATUS_SUCCESS;
					}
				}
				else
				{
					return STATUS_INSUFFICIENT_RESOURCES;
				}
			}
		}
	}

	return st;
}

/*/
//
//	Function: TDIKernelConnect
//
//	Purpose: Connects to existing
//    socket.
//	 Emulates connect function from winsock
//
//	Environment: Kernel mode only
//
*/

NTSTATUS TDIKernelConnect(IN PFILE_OBJECT *ConnectionObject, IN PDEVICE_OBJECT *DeviceObject, IN USHORT PortNumber, IN ULONG Firstoctal, IN ULONG Secondoctal, IN ULONG Thirdoctal, IN ULONG Lastoctal) 
{
	NTSTATUS st;
	PIRP Irp;
	TA_IP_ADDRESS ipaddress;
	USHORT Port;
	ULONG Ip;
	KEVENT Event;
	IO_STATUS_BLOCK io;
	TDI_CONNECTION_INFORMATION connectinfo;

	KeInitializeEvent(&Event, NotificationEvent, FALSE);

	Irp = TdiBuildInternalDeviceControlIrp(TDI_CONNECT, *DeviceObject, *ConnectionObject, &Event, &io);
	if (Irp == NULL) 
	{
		
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	Port = HTONS(PortNumber);
	Ip = INETADDR(Firstoctal, Secondoctal, Thirdoctal, Lastoctal);

	ipaddress.TAAddressCount = 1;
	ipaddress.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
	ipaddress.Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
	ipaddress.Address[0].Address[0].sin_port = Port;
	ipaddress.Address[0].Address[0].in_addr = Ip;

	connectinfo.UserDataLength = 0;
	connectinfo.UserData = 0;
	connectinfo.OptionsLength = 0;
	connectinfo.Options = 0;
	connectinfo.RemoteAddressLength = sizeof(ipaddress);
	connectinfo.RemoteAddress = &ipaddress;

	TdiBuildConnect(Irp, *DeviceObject, *ConnectionObject, NULL, NULL, NULL, &connectinfo, 0);

	IoSetCompletionRoutine(Irp, TdiCompletionRoutine, &Event, TRUE, TRUE, TRUE);

	st = IofCallDriver(*DeviceObject, Irp);
	if (st == STATUS_PENDING)
	{
		KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, 0);
	}

	return st;

}

/*/
//
//	Function: TDISend
//
//	Purpose: Sends data to remote server
//    
//	 Emulates send function from winsock
//
//	Environment: Kernel mode only
//
*/

NTSTATUS TDISend(IN PFILE_OBJECT ConnectionObject, IN PDEVICE_OBJECT DeviceObject, IN PCHAR Data, IN ULONG Length)
{
	NTSTATUS st = STATUS_UNSUCCESSFUL;
	KEVENT Event;
	PIRP Irp;
	PMDL Mdl;
	PCHAR Buffer = NULL;
	IO_STATUS_BLOCK io;

	Buffer = ExAllocatePool(NonPagedPool, Length);
	RtlCopyMemory(Buffer, Data, Length);

	KeInitializeEvent(&Event, NotificationEvent, FALSE);
	
	Irp = TdiBuildInternalDeviceControlIrp(TDI_SEND, DeviceObject, ConnectionObject, &Event, &io);
	if (Irp)
	{
		Mdl = IoAllocateMdl(Buffer, Length, FALSE, FALSE, Irp);
		if (Mdl)
		{
			__try
			{
				MmProbeAndLockPages(Mdl, KernelMode, IoModifyAccess);
			}
			__except (EXCEPTION_EXECUTE_HANDLER)
			{
				return STATUS_UNSUCCESSFUL;
			}

			TdiBuildSend(Irp, DeviceObject, ConnectionObject, NULL, NULL, Mdl, 0, Length);

			IoSetCompletionRoutine(Irp, TdiCompletionRoutine, &Event, TRUE, TRUE, TRUE);

			st = IofCallDriver(DeviceObject, Irp);
			if (st == STATUS_PENDING)
			{
				KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
			}
			else
			{
				st = STATUS_SUCCESS;
			}

		}
		else
		{
			return STATUS_INSUFFICIENT_RESOURCES;
		}
	}
	else
	{
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	return st;
}

/*/
//
//	Function: TDIRecv
//
//	Purpose: Receives data from remote server
//    
//	 Emulates recv function from winsock
//
//	Environment: Kernel mode only
//
*/

NTSTATUS TDIRecv(IN PFILE_OBJECT ConnectionObject, IN PDEVICE_OBJECT DeviceObject, PCHAR Data, ULONG Length)
{
	NTSTATUS st = STATUS_UNSUCCESSFUL;
	PIRP Irp;
	KEVENT Event;
	IO_STATUS_BLOCK io;
	PMDL Mdl;

	KeInitializeEvent(&Event, NotificationEvent, FALSE);

	Irp = TdiBuildInternalDeviceControlIrp(TDI_RECEIVE, DeviceObject, ConnectionObject,&Event, &io);
	if (Irp)
	{
		Mdl = IoAllocateMdl(Data, Length, FALSE, FALSE, Irp);
		if (Mdl)
		{
			__try
			{
				MmProbeAndLockPages(Mdl, KernelMode, IoModifyAccess);
			}
			__except (EXCEPTION_EXECUTE_HANDLER)
			{
				return GetExceptionCode();
				st = STATUS_UNSUCCESSFUL;
			}

			TdiBuildReceive(Irp, DeviceObject, ConnectionObject, NULL, NULL, Mdl, TDI_RECEIVE_NORMAL, Length);

			IoSetCompletionRoutine(Irp, TdiCompletionRoutine, &Event, TRUE, TRUE, TRUE);

			st = IofCallDriver(DeviceObject, Irp);
			if (st == STATUS_PENDING)
			{
				KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
			}
			else
			{
				st = STATUS_SUCCESS;
			}

		}
		else
		{
			st = STATUS_INSUFFICIENT_POWER;
		}
	}
	else
	{
		st = STATUS_INSUFFICIENT_RESOURCES;
	}

	return st;

}

/*/
//
//	Function: TDICloseSocket
//
//	Purpose: Closes existing ConnectionObject
//    
//	 Emulates closesocket function from winsock
//
//	Environment: Kernel mode only
//
*/

NTSTATUS TDICloseSocket(IN PFILE_OBJECT ConnectionObject, IN PDEVICE_OBJECT DeviceObject)
{
	NTSTATUS st = STATUS_UNSUCCESSFUL;
	PIRP Irp;
	IO_STATUS_BLOCK io;
	KEVENT Event;

	KeInitializeEvent(&Event, NotificationEvent, FALSE);

	Irp = TdiBuildInternalDeviceControlIrp(TDI_DISCONNECT, DeviceObject, ConnectionObject, &Event, &io);
	if (Irp)
	{
		TdiBuildDisconnect(Irp, DeviceObject, ConnectionObject, NULL, NULL, NULL, TDI_DISCONNECT_RELEASE, 0, 0);

		IoSetCompletionRoutine(Irp, TdiCompletionRoutine, &Event, TRUE, TRUE, TRUE);

		st = IofCallDriver(DeviceObject, Irp);
		if (st == STATUS_PENDING)
		{
			KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
		}
		else
		{
			st = STATUS_SUCCESS;
		}
	}
	else
	{
		return STATUS_INSUFFICIENT_RESOURCES;
	}

	return st;
}



NTSTATUS DriverEntry(PDRIVER_OBJECT pdriverobject, PUNICODE_STRING RegisterPath) 
{
	
	DbgPrint("NetDrv Loaded");

	char recvrequest[1024] = { 0 };
	int size = 0;
	HANDLE AddressHandle = 0;
	PDEVICE_OBJECT DeviceObject = NULL;
	PFILE_OBJECT FileObject = NULL;
	NTSTATUS st;

	CHAR szHeader[] =
		"POST /panel/client.php HTTP/1.0\r\n"
		"Host: 192.168.1.33\r\n"
		"Content-Type: application/octet-stream\r\n"
		"Content-Encoding: binary\r\n"
		"Content-Length: 27\r\n"
		"Connection: close\r\n"
		"\r\n";

	char postreq[] = "name=kernelmode&password=tdi";
	int datalen = strlen(postreq);
	char header[sizeof(szHeader) + 100];

	RtlStringCchCopyA(header,sizeof(header), szHeader);
	int headerlen = strlen(header);

	// TDIKernelCreateTCPSocket acts like WSAStartup from winsock

	st = TDIKernelCreateTCPSocket(&AddressHandle, &FileObject, &DeviceObject);
	if (!NT_SUCCESS(st))
	{
		return STATUS_UNSUCCESSFUL;
	}

	// Connect to the remote server given 4 octal numbers and port number

	st = TDIKernelConnect(&FileObject, &DeviceObject, 80, 192, 168, 1, 33);
	if (!NT_SUCCESS(st))
	{
		return STATUS_UNSUCCESSFUL;
	}

	// send POST Request to the php panel

	st = TDISend(FileObject, DeviceObject, header, headerlen);
	if (!NT_SUCCESS(st))
	{
		return STATUS_UNSUCCESSFUL;
	}

	// send data

	st = TDISend(FileObject, DeviceObject, postreq, datalen);
	if (!NT_SUCCESS(st))
	{
		return STATUS_UNSUCCESSFUL;
	}

	// Receive HTTP request, response from the server

	st = TDIRecv(FileObject, DeviceObject, recvrequest, sizeof(recvrequest));
	if (!NT_SUCCESS(st))
	{
		return STATUS_UNSUCCESSFUL;
	}

	// null terminate the string

	recvrequest[strlen(recvrequest)] = '\0';
	DbgPrint("%s", recvrequest);

	// close socket
	// close AddressHandle
	// Decrement Object, we dont need it anymore

	TDICloseSocket(FileObject, DeviceObject);
	ZwClose(AddressHandle);
	ObfDereferenceObject(FileObject);


	pdriverobject->DriverUnload = Unload;

	
	return STATUS_SUCCESS;

}

基于TDI 的 TCP数据传输 1.上位机 上位机包括tcp和tcp.cpp 1.1 对外函数说明 HANDLE TdiTcpOpen(); TdiTcpOpen用于打开设备,成功返回有效的句柄,失败返回INVALID_HANDLE_VALUE. BOOL TdiTcpClose(HANDLE hDevice); TdiTcpClose用于关闭设备,成功返回TRUE,失败返回FALSE; hDevice为TdiTcpOpen返回的句柄 BOOL TdiTcpConnect(HANDLE hDevice,const PCHAR pIpAddres,USHORT uPort); TdiTcpConnect用于与服务器建链,pIpAddres为服务器IP地址,uPort为服务器端口地址。 hDevice为TdiTcpOpen返回的句柄 pIpAddres为IP地址,如”10.0.0.20” uPort为端口地址 BOOL TdiTcpSend(HANDLE hDevice,PVOID pBuff,ULONG nLen,PULONG pRtn); TdiTcpSend用于给服务器发送数据. hDevice为TdiTcpOpen返回的句柄 pBuff接向发送数据的指针 nLen发送数据长度 pRtn发送成功长度 BOOL TdiTcpRcv(HANDLE hDevice,PVOID pBuff,ULONG nLen,PULONG pRtn); TdiTcpRcv用于从服务器接收数据 hDevice为TdiTcpOpen返回的句柄 pBuff接收数据缓冲区 nLen接收数据缓冲区长度 pRtn实际接收数据长度指针 BOOL TdiTcpSetRcvTimeOut(HANDLE hDevice,ULONG ulSecond); TdiTcpSetRcvTimeOut用于设置接收数据超时时间,默认为3秒。 hDevice为TdiTcpOpen返回的句柄 ulSecond为超时时间 2.下位机 下位机包括D1603.h D1603.cpp和Tdifun.cpp TdiFun.h 2.1 关键数据结构 驱动与应用连接服务器结构体 typedef struct _CONNECT_STRUCT { ULONG ip; //服务器IP地址 USHORT port; //服务器端口 }CONNECT_STRUCT,*PCONNECT_STRUCT; //设备展结构体 typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT pDeviceObject; //设备指针 UNICODE_STRING wstrDeviceName ; //设备名 UNICODE_STRING wstrSymbolicLinkName;//设备链接名 }DEVICE_EXTENSION,*PDEVICE_EXTENSION; 读数据链接 typedef struct _RCV_IPR_LIST { PIRP pIrp; //指向读IPR LIST_ENTRY ListEntry; //链表 }RCV_IPR_LIST,*PRCV_IPR_LIST; 当前链接上下文 typedef struct _SOCKET_CONTEXT { HANDLE TransportAddressHandle; //传输地址句柄 FILE_OBJECT* pTrasnportAddressFile;//传输地址指针 HANDLE ConnectionHandle;//连接地址句柄 FILE_OBJECT* pConnectionFile;//连接地址指针 LIST_ENTRY RcvHead; //接收IRP链表头 KEVENT event; //接收数据同步事件 ULONG uTimeOut; // 接收数据超时 }SOCKET_CONTEXT,*PSOCKET_CONTEXT; 2.2 外函数说明 驱动装载主入口函数 NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath); 驱动卸载函数 VOID D1603Unload(PDRIVER_OBJECT DriverObject); 默认IRP回调函数 NTSTATUS D1603Dispatch(PDEVICE_OBJECT DeviceObject,PIRP Irp); TdiTcpOpen对应的IPR函数 NTSTATUS D1603Create(PDEVICE_OBJECT
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值