基于开源tdifw的Windows传输层防火墙实现(实现用户态与内核态交互)

基于开源tfifw的Windows传输层防火墙实现(实现用户态与内核态交互)

实验要求

  1. 基于开源的tdifw防火墙代码,实现一个基本的、能够在Windows XP环境下运行的传输层过滤防火墙功能,能够在DbgView等调试软件下看到调试效果。
  2. 基本要求:tdifw能够正确编译、安装、运行,基本理解tdifw的功能框架,能够使用DbgView等调试软件正确定位tdifw代码中的TCP数据传输。
  3. 功能要求:在满足基本要求的前提下,能够改写tdifw中关于TCP connect部分的代码,能够读取不同的目标/源IP地址(如:网站)、端口(不同的服务、应用),能够在DbgView中显示这些信息;更进一步,试着根据目标IP地址的不同,对特定IP地址、端口进行拦截;
  4. 测试网络连接时,可采用自主配置服务器(如web,ssh,ftp等)。
  5. 拔高:设计并编写一个用户态交互配置程序

实验参考

tdifw实验所需的环境工具,及注意事项

  1. 参考资料《寒江独钓》,以下简称《寒》;
  2. 可进阶阅读《寒》第二章内容,了解内核程序的基本特点;
  3. 学习《寒》第十章内容,了解TDI驱动程序的基本构建,主要学习tdifw的编译方法和程序结构(可重点关注10.4,10.5);
  4. 基于《寒》第十章的tdifw代码(在tdi-noChinese目录下),编译tdifw驱动;注意,按照《寒》中的简化方法,绝大部分tdifw已经被封装到一个lib库里了(代码位于tdi_fw子目录),另一个子目录tdifw_smpl用来使用这个lib,生成最终的防火墙内核;
  5. 实验中所需的绝大部分功能位于disp_conn.c文件中,其中函数tdi_connect值得重点关注,这是实现TCP连接的功能函数,本实验所需的各类要求均可在该函数中完成。
  6. 实验时,也可以自行下载tdifw公开的源代码,而不采用《寒》中的lib封装代码,其原理、方法是一样的;
  7. 对于代码的阅读、改写、编辑,软件可根据自己的习惯自选用,WDK编译器一律采用命令行编译模式;
  8. 实验需要的工具:
    必需工具:虚拟机工具(含XP镜像)、驱动开发包WDK、调试工具DbgView等(目录中均已包);
    可选工具:内核服务开启工具、驱动加载工具等(目录中均已包含)
  9. 需要自行学习WDK的编译方式(在本机上安装WDK开发包并进行编译、生成sys防火墙程序;在虚拟机上测试所生成的sys防火墙程序)、DbgView的调试方式(对于初学者,这是一个相对简单的工具)。
    注意:WDK命令行环境下编译驱动程序,全路径名中不允许出现中文字符和空格

实验资料

https://download.csdn.net/download/PlanetRT/20234142.

实验原理

基于开源的tdifw防火墙代码,实现一个基本的、在windowsXP环境下运行的传输层过滤防火墙功能。主要通过对tdifw防火墙代码中的disp_conn.cfilter.c等文件进行修改。
Tdifw过滤的主要原理是通过获取连接的请求(request),并将连接的各个属性加入到请求(request)中。通过调用过滤器过滤(quick_filter(&request, &rule)),将请求的各个属性放到过滤器(filter)的规则链(rule chain) 中进行匹配,当匹配成功时,返回匹配成功的规则的结果(result)。根据返回的结果(result)对该连接请求做出相应的操作(允许连接:FILTER_ALLOW,拒绝连接:FILTER_DENY)。
添加过滤规则的主要原理是通过向过滤器中添加规则来实现过滤。首先在filter.c文件中创建新的规则(声明一个规则结构体:struct flt_rule rlues),并将规则中的属性(例如:proto(协议)、result(结果)、direction(方向)、addr_from(源IP地址)、mask_from(源地址掩码)、port_from(源端口号)、port2_from(源端口范围)等)设定为想要过滤的连接的对应属性。然后将新的规则(rules)加入到过滤器(filter)的规则链中(add_flt_rule(chain,& flt_rule)),并将该规则链进行激活(activate_flt_chain(chain)),即可对目标连接进行过滤。当不需要过滤某一连接时,可将过滤器中对应的规则删除(clear_flt_chain(chain))。
过滤规则匹配的主要原理是首先判断请求(request)的IP地址是否合法,再判断过滤器(filter)的规则链(rule chain)是否激活。然后依次循环规则链(rule chain)中的规则。

内核驱动程序的编译安装此处不做演示。

规则匹配流程图

如下:
在这里插入图片描述

具体代码

静态添加规则

部分可能用到的宏定义:

//方向
#define DIRECTION_IN	0
#define DIRECTION_OUT	1
#define DIRECTION_ANY	-1

#define RULE_LOG_NOLOG			0
#define RULE_LOG_LOG			1
#define RULE_LOG_COUNT			2

//协议
#define IPPROTO_ANY		-1
#define IPPROTO_IP              0               /* dummy for IP */
#define IPPROTO_ICMP            1               /* control message protocol */
#define IPPROTO_TCP             6               /* tcp */
#define IPPROTO_UDP             17              /* user datagram protocol */

/* filter result */
enum {
	FILTER_ALLOW = 1,
	FILTER_DENY,
	FILTER_PACKET_LOG,
	FILTER_PACKET_BAD,
	FILTER_DISCONNECT
};

//端口检查
#define CHECK_ADDR_PORT(r_addr_from, r_mask_from, r_port_from, r_port2_from,										\
						r_addr_to, r_mask_to, r_port_to, r_port2_to)												\
			((r_addr_from & r_mask_from) == (from->sin_addr.s_addr & r_mask_from) &&								\
			(r_addr_to & r_mask_to) == (to->sin_addr.s_addr & r_mask_to) &&											\
			(r_port_from == 0 || ((r_port2_from == 0) ? (r_port_from == from->sin_port) :							\
				(ntohs(from->sin_port) >= ntohs(r_port_from) && ntohs(from->sin_port) <= ntohs(r_port2_from)))) &&	\
			(r_port_to == 0 || ((r_port2_to == 0) ? (r_port_to == to->sin_port) :									\
				(ntohs(to->sin_port) >= ntohs(r_port_to) && ntohs(to->sin_port) <= ntohs(r_port2_to)))))			\

规则结构体(ipc.c):

//规则结构体
struct flt_rule {
	union {
		struct	flt_rule *next;		// for internal use
		int		chain;				// useful for IOCTL_CMD_APPENDRULE
	};
	int		result;			//匹配结果
	int		proto;			//协议
	int		direction;		//方向 
	ULONG	addr_from;		//源地址		注意:均使用网络字节顺序,而不是主机字节顺序!!!转换后使用十进制即可。 
	ULONG	mask_from;		//源掩码
	USHORT	port_from;		//源端口
	USHORT	port2_from;		//源端口范围
	ULONG	addr_to;		//目的地址
	ULONG	mask_to;		//目的掩码
	USHORT	port_to;		//目的端口
	USHORT	port2_to;		//目的端口范围额 
	int		log;			// see RULE_LOG_xxx 

	UCHAR	sid_mask[MAX_SIDS_COUNT / 8];	// SIDs bitmask 

	char	rule_id[RULE_ID_SIZE];
};

//一个Web的例子
struct flt_rule rlues1 = {
		{0},
		FILTER_DENY,
		IPPROTO_TCP,
		DIRECTION_OUT,
		0,
		65535,
		0,
		0,
		18095020,
		65535,
		20480,
		0,
		RULE_LOG_LOG,
		"1111111111111111",	// setup mask before using it!
		"startup"		// rule for startup only*
	};

规则匹配(disp_conn.c):

//quick_filter就是将request里的地址和端口和rule规则进行匹配 返回结果
	result = quick_filter(&request, &rule);
			if(result == FILTER_ALLOW){
					KdPrint(("fuck  %x  \n",((const struct sockaddr_in *)&request.addr.from)->sin_addr.s_addr));
			}

添加规则(filter.c):

// 添加规则函数 将rule添加到chain链中
NTSTATUS
add_flt_rule(int chain, const struct flt_rule *rule)
{
	NTSTATUS status;
	struct flt_rule *new_rule;
	KIRQL irql;

	// sanity check
	if (chain < 0 || chain >= MAX_CHAINS_COUNT)
		return STATUS_INVALID_PARAMETER_1;
	
	KeAcquireSpinLock(&g_rules.guard, &irql);

	new_rule = (struct flt_rule *)malloc_np(sizeof(struct flt_rule));
	if (new_rule == NULL) {
		KdPrint(("[tdi_fw] add_flt_rule: malloc_np\n"));
		status = STATUS_INSUFFICIENT_RESOURCES;
		goto done;
	}

	memcpy(new_rule, rule, sizeof(*new_rule));

	// append
	new_rule->next = NULL;

	if (g_rules.chain[chain].tail == NULL) {
		g_rules.chain[chain].head = new_rule;
		g_rules.chain[chain].tail = new_rule;
	} else {
		g_rules.chain[chain].tail->next = new_rule;
		g_rules.chain[chain].tail = new_rule;
	}

	status = STATUS_SUCCESS;
	KdPrint(("YES\n"));

done:
	KeReleaseSpinLock(&g_rules.guard, irql);
	return status;
}
	
	//几个实例
	//添加 web	
	add_flt_rule(0,&rlues1);
	//添加 ftp
	add_flt_rule(0,&rlues2);
	//激活函数 添加规则后均需要激活规则链,但一般0链是默认激活的,其他链默认关闭
	activate_flt_chain(0);

如果不需要通过用户态与内核态通信添加,则只需在filter.c中的过滤器初始化函数filter_init中添加即可。
但这种方法比较固定,如果需要更新规则,则只能在代码中添新规则后重新编译安装。通过用户态与内核态的交互会在下边实现。

用户态与内核态交互添加

上边已经实现了静态添加规则的方法,这部分主要实现用户态与内核态交互的动态添加方式。
首先需要实现的是用户态与内核态的通信,具体原理及实现网上教程很多,此处不一一赘述。主要是通信后,内核态对用户态传入数据的处理和规则的添加。
本实验没有实现用户态的可视化界面,如果有需要可自行添加。
进程通信:

//用户调程序
include <windows.h>
#include <stdio.h>
#include <winioctl.h>

#define MY_DVC_IN_CODE (ULONG)CTL_CODE(FILE_DEVICE_UNKNOWN,0xa01,METHOD_BUFFERED,FILE_READ_DATA|FILE_WRITE_DATA)//设备的宏定义
#define MY_DVC_OUT_CODE (ULONG)CTL_CODE(FILE_DEVICE_UNKNOWN,0xa00,METHOD_BUFFERED,FILE_READ_DATA|FILE_WRITE_DATA)//设备的宏定义

int main()
{
    HANDLE device=CreateFile("\\\\.\\tdifw",
                             GENERIC_READ|GENERIC_WRITE,0,0,
                             OPEN_EXISTING,
                             FILE_ATTRIBUTE_SYSTEM,0);
    if(device==INVALID_HANDLE_VALUE)
    {
        printf("CreateFile Fail\n");
    }
    else
    {
        printf("CreateFile Success\n");
    }
    char in_buffer[1000];
    int in_buffer_len=strlen(in_buffer);
    char out_buffer[1000];
    int out_buffer_len=1000;
    DWORD length=0;
	
	//输出提示信息
    printf("***********< Welcome to use XR's TDI_FW >***********\n");
    //输入的规则 一定要按这个顺序输入,避免内核态读取错误
	printf("Tips:Input rules in the roder of whether allowed,protocol,\n");
	printf("direction,source IP,source mask,source port,source port range,\n");
	printf("destination IP,destination mask,destination port,destination port range.\n");
		
	printf("Please input '*' after input an attribute\n");
	scanf("%s",in_buffer);		//输入你需要添加的规则
		
    in_buffer_len=strlen(in_buffer);
    while(DeviceIoControl(device,
                          MY_DVC_IN_CODE,
                          in_buffer,
                          in_buffer_len,
                          out_buffer,
                          out_buffer_len,
                          &length,
                          NULL))
    {

		printf("Please input '*' after input an attribute\n");
		scanf("%s",in_buffer);
		
		in_buffer_len=strlen(in_buffer);
		
        //printf("Get from driver %d %s\n",out_buffer_len,out_buffer);
        //printf("to driver:");
    }
    CloseHandle(device);
	    while(1) {}
    return 0;
}

内核态处理(tdi_fw.c):

//内核态通讯及处理函数
NTSTATUS MyDeviceIoControl(PDEVICE_OBJECT dev,
						   PIRP irp
						   )
{	
	INT ret;
	PIO_STACK_LOCATION irpsp=IoGetCurrentIrpStackLocation(irp);
	ULONG code=irpsp->Parameters.DeviceIoControl.IoControlCode;
	ULONG  in_len=irpsp->Parameters.DeviceIoControl.InputBufferLength;
	ULONG out_len=irpsp->Parameters.DeviceIoControl.OutputBufferLength;
	PVOID  in_buffer=irp->AssociatedIrp.SystemBuffer;
	PVOID out_buffer=irp->AssociatedIrp.SystemBuffer;
	//KdPrint(("in_buffer %s\n",in_buffer));
	char *inbuffder=(char *)in_buffer;

	if(irp->MdlAddress)
	{
		out_buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
	}
	if(code==MY_DVC_IN_CODE)
	{	
		temp=0;
		num=0;
		KdPrint(("in_buffer %s\n",in_buffer));
		while(*inbuffder){
			//传入信息的处理 按*划分
			if(*inbuffder!='*'){
					str[num] = *inbuffder;
					nnum=(str[num]-'0')+nnum*10;
					num++;	
			}
			else if(*inbuffder=='*'){
				
				temp++;				
				sum=nnum;
				nnum=0;

				KdPrint(("sum: %d\n",sum));
				//添加规则属性
				switch(temp){
					case 1:rlues.result=sum;break;
					case 2:rlues.proto=sum;break;
					case 3:rlues.direction=sum;break;
					case 4:rlues.addr_from=sum;break;
					case 5:rlues.mask_from=sum;break;
					case 6:rlues.port_from=sum;break;
					case 7:rlues.port2_from=sum;break;
					case 8:rlues.addr_to=sum;break;
					case 9:rlues.mask_to=sum;break;
					case 10:rlues.port_to=sum;break;
					case 11:rlues.port2_to=sum;break;
				}
				num=0;
			}
			inbuffder++;
		}
		
		//添加规则
		add_flt_rule(0,&rlues);
		KdPrint(("add rlue successful! by XR \n"));

		
		if(in_buffer&&out_buffer)
		{
			RtlCopyMemory(out_buffer, in_buffer, out_len);
		}	
		KdPrint(("out_buffer %s\n",out_buffer));
		irp->IoStatus.Information=out_len;
		irp->IoStatus.Status=STATUS_SUCCESS;
		
	}
	else 
	{
		
		irp->IoStatus.Information=0;
		irp->IoStatus.Status=STATUS_SUCCESS;
		//irp->IoStatus.Status=STATUS_INVALID_PARAMETER;
	}
	IoCompleteRequest(irp,IO_NO_INCREMENT);
	return irp->IoStatus.Status;
}

至此,所有有关代码均已贴出,如有兴趣,可查看实验资料中上传的全部代码(部分主要文件我加了详细注释)。

实验结果

静态添加

未启动驱动程序时,XP虚拟机(IP:10.0.2.15)对web服务器和ftp服务器的访问情况:
在这里插入图片描述
在这里插入图片描述

驱动程序启动后,XP虚拟机(IP:10.0.2.15)对web服务器和ftp服务器的访问情况:
在这里插入图片描述
在这里插入图片描述

用户态与内核态交互动态添加

加入规则前:
在这里插入图片描述

添加规则:
在这里插入图片描述

DbgView查看通信结果:

在这里插入图片描述

加入规则后:
在这里插入图片描述

总结

本实验为本人课内实验,如有错误,欢迎指正!

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PlanetRT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值