概述
基于开源tfifw的Windows传输层防火墙实现(实现用户态与内核态交互)
目录
- 基于开源tfifw的Windows传输层防火墙实现(实现用户态与内核态交互)
- 实验要求
- 实验参考
- 实验资料
- 实验原理
- 规则匹配流程图
- 具体代码
- 静态添加规则
- 用户态与内核态交互添加
- 实验结果
- 静态添加
- 用户态与内核态交互动态添加
- 总结
实验要求
- 基于开源的tdifw防火墙代码,实现一个基本的、能够在Windows XP环境下运行的传输层过滤防火墙功能,能够在DbgView等调试软件下看到调试效果。
- 基本要求:tdifw能够正确编译、安装、运行,基本理解tdifw的功能框架,能够使用DbgView等调试软件正确定位tdifw代码中的TCP数据传输。
- 功能要求:在满足基本要求的前提下,能够改写tdifw中关于TCP connect部分的代码,能够读取不同的目标/源IP地址(如:网站)、端口(不同的服务、应用),能够在DbgView中显示这些信息;更进一步,试着根据目标IP地址的不同,对特定IP地址、端口进行拦截;
- 测试网络连接时,可采用自主配置服务器(如web,ssh,ftp等)。
- 拔高:设计并编写一个用户态的交互配置程序。
实验参考
tdifw实验所需的环境、工具,及注意事项
- 参考资料《寒江独钓》,以下简称《寒》;
- 可进阶阅读《寒》第二章内容,了解内核程序的基本特点;
- 学习《寒》第十章内容,了解TDI驱动程序的基本构建,主要学习tdifw的编译方法和程序结构(可重点关注10.4,10.5);
- 基于《寒》第十章的tdifw代码(在tdi-noChinese目录下),编译tdifw驱动;注意,按照《寒》中的简化方法,绝大部分tdifw已经被封装到一个lib库里了(代码位于tdi_fw子目录),另一个子目录tdifw_smpl用来使用这个lib,生成最终的防火墙内核;
- 实验中所需的绝大部分功能位于disp_conn.c文件中,其中函数tdi_connect值得重点关注,这是实现TCP连接的功能函数,本实验所需的各类要求均可在该函数中完成。
- 实验时,也可以自行下载tdifw公开的源代码,而不采用《寒》中的lib封装代码,其原理、方法是一样的;
- 对于代码的阅读、改写、编辑,软件可根据自己的习惯自选用,WDK编译器一律采用命令行编译模式;
- 实验需要的工具:
必需工具:虚拟机工具(含XP镜像)、驱动开发包WDK、调试工具DbgView等(目录中均已包);
可选工具:内核服务开启工具、驱动加载工具等(目录中均已包含) - 需要自行学习WDK的编译方式(在本机上安装WDK开发包并进行编译、生成sys防火墙程序;在虚拟机上测试所生成的sys防火墙程序)、DbgView的调试方式(对于初学者,这是一个相对简单的工具)。
注意:WDK命令行环境下编译驱动程序,全路径名中不允许出现中文字符和空格!
实验资料
https://download.csdn.net/download/PlanetRT/20234142.
实验原理
基于开源的tdifw防火墙代码,实现一个基本的、在windowsXP环境下运行的传输层过滤防火墙功能。主要通过对tdifw防火墙代码中的disp_conn.c、filter.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_npn"));
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(("YESn"));
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 Failn");
}
else
{
printf("CreateFile Successn");
}
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 attributen");
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 attributen");
scanf("%s",in_buffer);
in_buffer_len=strlen(in_buffer);
//printf("Get from driver %d %sn",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 %sn",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 %sn",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: %dn",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 %sn",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查看通信结果:
加入规则后:
总结
本实验为本人课内实验,如有错误,欢迎指正!
最后
以上就是热心发带为你收集整理的基于开源tdifw的Windows传输层防火墙实现(实现用户态与内核态交互)基于开源tfifw的Windows传输层防火墙实现(实现用户态与内核态交互)的全部内容,希望文章能够帮你解决基于开源tdifw的Windows传输层防火墙实现(实现用户态与内核态交互)基于开源tfifw的Windows传输层防火墙实现(实现用户态与内核态交互)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复