VC实现的简易防火墙

、  一、实验目的(1)  修改代码,完成下述功能:a) 由本机登录ftp服务器成功(通过用户名、密码验证);b) 无法将ftp内容列表或上传、下载数据;(2) 作业要求:a) 明确提出自己的方案设计,即通过何原理实现上述功能;b) 纸版作业只需要粘贴自己撰写的代码部分;c) 实验结果需要截图证明;二、实验过程1, 学习FTP应用的相关知识FTP(File Transfer Protocal),是文件传输协议的简称。用于Internet上的控制文件的双向传输。同时,它也是一个应用程序(Application)。用户可以通过它把自己的PC机与世界各地所有运行FTP协议的服务器相连,访问服务器上的大量程序和信息。TCP/IP协议中,FTP标准命令TCP端口号为21,Port方式数据端口为20。FTP协议的任务是从一台计算机将文件传送到另一台计算机,它与这两台计算机所处的位置、联接的方式、甚至是是否使用相同的操作系统无关。假设两台计算机通过ftp协议对话,并且能访问Internet, 你可以用ftp命令来传输文件。每种操作系统使用上有某一些细微差别,但是每种协议基本的命令结构是相同的。FTP支持两种模式,一种方式叫做Standard (也就是 PORT方式,主动方式),一种是 Passive (也就是PASV,被动方式)。 Standard模式 FTP的客户端发送 PORT 命令到FTP服务器。Passive模式FTP的客户端发送 PASV命令到 FTP Server。   下面介绍一个这两种方式的工作原理:   Port模式FTP 客户端首先和FTP服务器的TCP 21端口建立连接,通过这个通道发送命令,客户端需要接收数据的时候在这个通道上发送PORT命令。 PORT命令包含了客户端用什么端口接收数据。在传送数据的时候,服务器端通过自己的TCP 20端口连接至客户端的指定端口发送数据。 FTP server必须和客户端建立一个新的连接用来传送数据。   Passive模式在建立控制通道的时候和Standard模式类似,但建立连接后发送的不是Port命令,而是Pasv命令。FTP服务器收到Pasv命令后,随机打开一个高端端口(端口号大于1024)并且通知客户端在这个端口上传送数据的请求,客户端连接FTP服务器此端口,然后FTP服务器将通过这个端口进行数据的传送,这个时候FTP server不再需要建立一个新的和客户端之间的连接。2, 熟悉老师给的防火墙代码熟悉fwfilter.h和pfwapi.h了解简单防火墙的工作方式,和使用方法,熟悉掌握通过各种方法过滤端口的方法。在所给的程序中,主要是添加端口,这里所过滤的端口是远程服务器的的端口,凡是用该端口试图与本机连接都会拒绝。其中端口设置有:任意模式,单一模式,列表模式,范围模式。3, 利用套接字抓包,分析包内容利用已经学过的socket抓包方法,检测本机网卡,试图在所有包中找到需要的内容,获取相关信息。 4,  整合代码,完成实验要求 将两个程序整合,完成实验要求。 方案设计1, FTP分析Ftp工作在应用层,控制命令和数据都是通过TCP协议打包发送的,利用两个TCP链接来完成,一个是控制连接,另一个是数据链接。控制链接主要负责协议命令和服务器响应码的传输,通常是21端口。控制连接一旦建立就一直存在,直到一方关闭通信。数据连接只负责数据的传输,包括文件的上传和下载,以及列表的刷新。数据连接不是一直保持的,当数据传输完毕时会自动断开。PASV命令,这个命令请求服务器监听某个数据端口,并等待一个客户的连接,这条命令的相应包括主机地址和服务器正在收听的端口地址。利用flashx的FTP软件在连接时可以看到登录信息: [17:46:15] [右] PASV[17:46:15] [右] 227 Entering Passive Mode (59,64,159,3,12,27)由此可以看出PASV的命令响应号是227,括号是服务器的ip和端口,其中端口由倒数第2位乘以256加上最后一位,上面的例子就是:12×256+27= 3099。2, Socket抓包套接字已经是学过的知识了,所以这里不再赘述,在分析包时,只需要匹配数据块的前三位是不是227就可以得到pasv命令的响应数据。再利用一点简单的算法就可以得到最后的端口号。3, 防火墙实现为了实现实验要求,一方面我们需要封闭20端口,也就是FTP服务端默认使用的端口,另一方面我们将上面获得的端口号也添加进去,这样就可以封掉PASV命令的端口了。三、实验结果  测试软件为DOS和FTPflashxp 1,先用flsahxp登录FTP,其方式默认为PASV    2,运行防火墙 3,再次登录ftp 软件自己在尝试3次PASV无法连接后会自动转到PORT模式,由于之前已经过滤了20端口,所以port模式也是无法连接的。由登录状态可以看到,用户名和密码是通过的。4,刷新列表和下载数据的测试,先登录到ftp打开防火墙 刷新列表 可以看到,已经无法再得到列表了。 DOS下测试正常登录的效果 打开防火墙后登录 四、关键代码重新定义的TCP结构体typedef struct TCP{    u_short SrcPort;    u_short DstPort;    u_long SeqNum;    u_long AckNum;    u_char DataOff; u_char Flag;    u_short Window;    u_short Chksum;    u_short UrgPtr; //Devil----------------用于接收TCP包中数据的字符串-------------Diy// char num[4];//用于接收FTP命令响应号 char a[45];//接收包内容}TCP,*PTCP;防火墙端口设置  Rule.RemotePort.Type = FILTER_PORT_TYPE_LIST;  Rule.RemotePort.PortList.TotalNumber = 2;  Rule.RemotePort.PortList.Ports[0]= htons(20);  Rule.RemotePort.PortList.Ports[1]=htons(port); 解析TCP包void DecodeTCP(char *pData){ TCP *pTCPHdr = (TCP*)pData; int i=44,j=1; int port1=0,port2=0; pTCPHdr->num[3]='/0'; pTCPHdr->a[44]='/0'; if(pTCPHdr->SrcPort==ntohs(21)&&(strcmp(pTCPHdr->num,"227")==0)) {  //printf("Port:%d->%d/n",ntohs(pTCPHdr->SrcPort),ntohs(pTCPHdr->DstPort));    printf("%s/n",pTCPHdr->a);  //获得端口号的算法   while(pTCPHdr->a[i]!=')')//到最后一个括号处    i--;   i--;   while(pTCPHdr->a[i]!=',')   {//获得第一个端口信息    port1 =  (pTCPHdr->a[i]-48)*j+port1;    j = j*10;    i--;   }    i--;   j=1;   while(pTCPHdr->a[i]!=',')   {//获得第二个端口信息    port2 =  (pTCPHdr->a[i]-48)*j+port2;    j = j*10;    i--;   }   //计算端口   port = port2*256+port1;   printf("%d/n",port); }}五、问题及解决途径问题1:解析pasv命令的响应包在实验过程中,利用sniffer抓包工具认真的对整个FTP登录过程进行研究后得到了227号响应包就是PASV的特征,利用这一点和字符串匹配的方法得到了port端口。问题2:现在的大多数FTP软件都使用的PASV模式,只有当它多次尝试PASV无法连接时才使用PORT模式所以,我们还需要封掉20端口来完成整个FTP的过滤。问题3:有时候会出现本机连本机的端口 Pasv传过来的端口号是3579,虽然封掉了这个端口,但是当上述情况出线时,会获得列表。六、实验心得 1,了解了许多相关防火墙的基础知识,对防火墙的基本原理有了更深的理解和认识,学会了利用简单的程序实现防火墙的基本功能。 2,了解了FTP应用层协议的知识,对FTP的工作原理,工作方式有了更深的映像,同时加深了对计算机网络的了解,有一次学习了伟大的TCP/IP协议栈。 3,复习了以前学过的原始套接字的使用方法。 4,由于程序编写主要是用C语言的方法去写,导致程序有点乱,而且许多算法都是凑出来的,仅仅为了实现这次实验,并不能兼容到更好的软件当中,相信如果以后有时间尽量规范的写代码。这样不但可以养成良好的编程习惯,对于别人和自己来阅读代码也有很大方便。 5,一开始想用范围封掉20和1024以后的端口,后来觉得会影响其他软件的正常运行,再说防火墙也不是封端口的软件,而是保护机器的软件。于是就只封的两个端口,其中有一个是动态变化的。源程序:test.cpp // test.cpp : Defines the entry point for the console application.///*Author:DevilDiyDate:2008-05-25School:Information Engineering 信息工程学院Dept:Information Security  信息安全专业Class:05611      05611班Number:052054-19    052054-19Name:DingYu      丁于Summary:      a) 由本机登录ftp服务器成功(通过用户名、密码验证);        b) 无法将ftp内容列表或上传、下载数据; */#include <WinSock2.h>#include <stdio.h>#include "pfwapi.h"//包含了fwfilter中各接口函数的声明#pragma comment(lib,"ws2_32.lib") #pragma comment(lib,"fwfilter.lib") #define SIO_RCVALL            _WSAIOW(IOC_VENDOR,1) typedef struct IP{    u_char  ver_ihl;        // 版本 (4 bits) + 首部长度 (4 bits)    u_char  tos;            // 服务类型(Type of service)     u_short tlen;           // 总长(Total length)     u_short identification; // 标识(Identification)    u_short flags_fo;       // 标志位(Flags) (3 bits) + 段偏移量(Fragment offset) (13 bits)    u_char  ttl;            // 存活时间(Time to live)    u_char  proto;          // 协议(Protocol)    u_short crc;            // 首部校验和(Header checksum) u_long  saddr;      // 源地址(Source address)    u_long  daddr;      // 目的地址(Destination address)    u_int   op_pad;         // 选项与填充(Option + Padding)}IP; typedef struct TCP{    u_short SrcPort;    u_short DstPort;    u_long SeqNum;    u_long AckNum;    u_char DataOff; u_char Flag;    u_short Window;    u_short Chksum;    u_short UrgPtr; //Devil----------------用于接收TCP包中数据的字符串-------------Diy// char num[4];//用于接收FTP命令响应号 char a[45];//接收包内容}TCP,*PTCP; #include "stdafx.h"void DecodeIP(char* pData);//解析IP包void DecodeTCP(char *pData);//解析TCP包 unsigned short port;//全局变量port最终的端口号 VOID Alert(ULONG RuleId, PPACKET_INFO PacketInfo){ if (RuleId == 0)//如果与用户设定规则的ID号相符 {  unsigned long SrcIP =  ntohl(PacketInfo->SrcAddress); //将网络字节形式的IP地址转换为主机形式  unsigned short SrcPort = ntohs(PacketInfo->SrcPort);  unsigned long DstIP =  ntohl(PacketInfo->DestAddress); //将网络字节形式的IP地址转换为主机形式  unsigned short DstPort = ntohs(PacketInfo->DestPort);  //将IP换算为十进制点分形式,在屏幕上打印信息  printf("主机%ld.%ld.%ld.%ld:%d连接本机%d端口!/n",    SrcIP>>24,(SrcIP>>16)%256,(SrcIP>>8)%256,SrcIP%256,SrcPort,DstPort); } }int main(int argc, char* argv[]){ //初始化SOCKET WSADATA wsaData; int iErrorCode = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iErrorCode != NO_ERROR)  printf("Error at WSAStartup()/n");  //创建套接字 SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_IP); if (sRaw == INVALID_SOCKET)  {  printf("Error at socket(): %ld/n", WSAGetLastError());  WSACleanup();  return 0; }  //获取本机IP地址 char szHostName[56]; if(SOCKET_ERROR==gethostname((char*)szHostName,sizeof(szHostName)-1)) {  printf("无法获取主机名!/n");  return 0; }  hostent* pHost; if((pHost=gethostbyname((char*)szHostName))==NULL)  {  printf("无法获取主机信息!/n");  return 0; }  //绑定端口 SOCKADDR_IN addr_in; memcpy(&addr_in.sin_addr.S_un.S_addr,pHost->h_addr_list[0],pHost->h_length); addr_in.sin_family = AF_INET; addr_in.sin_port = htons(0); if(bind(sRaw,(PSOCKADDR)&addr_in,sizeof(addr_in))==SOCKET_ERROR) {  printf("监听端口绑定失败!/n");  return 0; }; printf("Bingding to interface: %s/n",::inet_ntoa(addr_in.sin_addr));  //设置RAW socket; DWORD OutBuf[10]; DWORD InBufSize = 1; DWORD RecvCount = 0; if(SOCKET_ERROR==WSAIoctl(sRaw,SIO_RCVALL,&InBufSize,sizeof(InBufSize),&OutBuf,sizeof(OutBuf),&RecvCount,NULL,NULL)) {  printf("设置socket失败!/n");  return 0; }  BOOL bRet;     FILTER_RULE Rule; //自定义规则        bRet = PFWInitialize(); //初始化fwfilter    if(!bRet)    {        printf("Initialize personal firewall failed./n");        return -1;    }  char buff[1024]; int nRet; while(1) {   nRet = recv(sRaw,buff,1024,0);  if(nRet>0)  //解析包   DecodeIP(buff);   PFWSetAlertCallback(Alert); //设置告警回调函数           // 设置规则:告警流向本机21端口的TCP协议数据流  Rule.RuleId = 0;  Rule.ActionFlag = FILTER_ACTION_DENY|FILTER_ACTION_ALERT;  Rule.RemoteAddress.Type = FILTER_ADDRESS_TYPE_ANY;  Rule.LocalPort.Type = FILTER_PORT_TYPE_ANY;  Rule.Protocol = FILTER_PROTO_TCP;  Rule.RemotePort.Type = FILTER_PORT_TYPE_LIST;  Rule.RemotePort.PortList.TotalNumber = 3;  Rule.RemotePort.PortList.Ports[0]=htons(20);  Rule.RemotePort.PortList.Ports[1]=htons(port);  Rule.Direction = FILTER_DIRECTION_INCOMING;      PFWInsertTailRule(&Rule);//将规则添加到规则链表中  //while(1);//保证程序不间断的运行 }    PFWUnintialize();//关闭fwfilter    return 0;} void DecodeTCP(char *pData){ TCP *pTCPHdr = (TCP*)pData; int i=44,j=1; int port1=0,port2=0; pTCPHdr->num[3]='/0'; pTCPHdr->a[44]='/0'; if(pTCPHdr->SrcPort==ntohs(21)) {  //printf("Port:%d->%d/n",ntohs(pTCPHdr->SrcPort),ntohs(pTCPHdr->DstPort));  if(strcmp(pTCPHdr->num,"227")==0)  {  printf("%s/n",pTCPHdr->a);  //获得端口号的算法   while(pTCPHdr->a[i]!=')')//到最后一个括号处    i--;   i--;   while(pTCPHdr->a[i]!=',')   {//获得第一个端口信息    port1 =  (pTCPHdr->a[i]-48)*j+port1;    j = j*10;    i--;   }    i--;   j=1;   while(pTCPHdr->a[i]!=',')   {//获得第二个端口信息    port2 =  (pTCPHdr->a[i]-48)*j+port2;    j = j*10;    i--;   }   //计算端口   port = port2*256+port1;   printf("%d/n",port);  }   }  } void DecodeIP(char* pData){ IP *pIPHdr = (IP*)pData; in_addr source,dest; char szSourceIp[32],szDestIp[32]; if(pIPHdr->proto==IPPROTO_TCP) {   source.S_un.S_addr = pIPHdr->saddr;  dest.S_un.S_addr = pIPHdr->daddr;  strcpy(szSourceIp,::inet_ntoa(source));  strcpy(szDestIp,::inet_ntoa(dest));  //printf("%s->%s/n",szSourceIp,szDestIp);  int nHeaderLen = (pIPHdr->ver_ihl & 0xf)*sizeof(u_long);  DecodeTCP(pData+nHeaderLen); }}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值