phoenix Fire Wall (凤凰防火墙)原理分析

本文深入解析Phoenix防火墙的工作原理,包括应用层通过Winsock2 SPI拦截网络请求,以及驱动层如何利用NDIS过滤网络数据包。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

phoenix防火墙原理

 

 

应用层原理

Winsock 2 服务提供者接口(SPI)为应用程序提供服务。
SPI的体系结构为:
(协议层次)
Winsock 2 API -> WS2_32.DLL -> SPI -> Transport ,Layered Protocol, SPI -> BaseProtocol

 

(Winsock 2 结构)
Winsock 2 Application -> Winsock 2 API -> Winsock 2 DLL -> Winsock 2 Transport Function SPI -> Transport Service Provider

分层服务提供者将自己安装到Winsock目录中的基础提供者上面,截取来自应用程序的 Winsock API 调用。
用户创建套接字时,套接字创建函数会在Winsock目录中寻找合适的协议,然后调用此协议的提供者导出的函数完成各种功能。

 

安装LSP 的过程是:
1、先安装一个分层协议,然后安装协议链; WSCInstallProvider
2、重新为目录排序 WSCWriteProviderOrder

移除LSP 使用 WSCDeinstallProvider

 

具体编写 LSP 的过程是,导出 WSPStartup函数,在这个函数里面修改 WSPPROC_TABLE 指向的 Winsock 2 API 函数的地址,指向自己的函数.

 

 

 

驱动层原理

NDIS 网络驱动程序为网络驱动抽象了网络硬件,为上层驱动抽象了管理硬件的下层驱动。

Transport Driver Interface (TDI)
|
NDIS Interface
|
NetCard

中间层驱动位于微端口驱动和传输驱动之间,是基于链路层和网络层之间的驱动。
中间层驱动的主要用途之一是过滤网络封包,它能够截获所有的网络数据包。

 

PassThru 是 DDK 自带的中间层例子,
在 PassThru中,有 4 处封包是可以截获的。
MPSend
MPSendPackets
PTReceive
PTReceivePacket

 

获取网络数据包的函数是 NdisQueryBufferSafe ,网络数据包在系统内部使用 NDIS_BUFFER 类型的链表存储,取出数据包后根据过滤规则检查,允许的就推进到下一层,不允许的直接丢弃。


-----------------------------------------------

代码分析:

phoenixFW 一共有 3 个模块 :
phoenixFW.exe phoenixLSP.dll passthru.sys

phoenixLSP.dll导出WSPStartup,WSPStartup保存下层提供者的函数表,修改Winsock API 函数的地址

g_NextProcTable = *lpProcTable;
 lpProcTable->lpWSPSocket = WSPSocket;
 lpProcTable->lpWSPCloseSocket = WSPCloseSocket;
 lpProcTable->lpWSPBind = WSPBind;
 lpProcTable->lpWSPAccept = WSPAccept;
 lpProcTable->lpWSPConnect = WSPConnect;
 lpProcTable->lpWSPSendTo = WSPSendTo; 
 lpProcTable->lpWSPRecvFrom = WSPRecvFrom;

 

TCP向外发起连接使用 connect函数,connect使用WSPConnect,
WSPConnect
{
 //根据规则检查是否允许连接到远程主机
 //如果允许通过,直接调用原来的下层函数,否则直接返回SOCKET_ERROR
 return g_NextProcTable.lpWSPConnect
}
接受连接使用 accept,accept调用WSPAccept;

 

UDP 向外发包使用 sendto,sendto调用WSPSendTo
接收UDP封包的函数 recvfrom调用WSPRecvFrom

 

phoenixLSP.dll使用 #pragma指令设置共享内存,用来和主模块 phoenixFW.exe进行通信

#pragma data_seg(".initdata")
HWND   g_hPhoenixWnd = NULL;   // 主窗口句柄
int   g_nWorkMode = PF_PASS_ALL;  // 工作模式
#pragma data_seg()

#pragma bss_seg(".uinitdata")
RULE_ITEM  g_Rule[MAX_RULE_COUNT];      // 应用层规则
ULONG   g_RuleCount;
QUERY_SESSION g_QuerySession[MAX_QUERY_SESSION];   // 向主程序发送会话询问时使用
SESSION   g_SessionBuffer[MAX_SESSION_BUFFER];  // 向主程序发送会话信息时使用
TCHAR   g_szPhoenixFW[MAX_PATH];     // 记录主程序路径
#pragma bss_seg()


extern TCHAR g_szCurrentApp[MAX_PATH];

每当一个应用程序调用 Winsock 2 API,就会自动载入phoenixLSP.dll,此时记录下这个进程的路径
 case DLL_PROCESS_ATTACH:
  {
   // 取得主模块的名称
   ::GetModuleFileName(NULL, g_szCurrentApp, MAX_PATH);
  }

data_seg 和 bss_seg 段,无论多少进程加载这个DLL ,都只有1份,也就是说只要1个模块修改这些内容,其他模块都会改变。


phoenixLSP.dll 导出一个函数,用来修改 data_seg 和 bss_seg 段
__declspec(dllexport) int __stdcall PLSPIoControl(struct LSP_IO_CONTROL *pIoControl, int nType);
int __stdcall PLSPIoControl(struct LSP_IO_CONTROL *pIoControl, int nType)
{
 switch(nType)
 {
 case IO_CONTROL_SET_RULE_FILE:   // 设置应用层规则
  break;
 case IO_CONTROL_SET_WORK_MODE:   // 设置工作模式
  break;
 case IO_CONTROL_GET_WORK_MODE:   // 获取工作模式
  break;
 case IO_CONTROL_SET_PHOENIX_INSTANCE: // 设置主模块信息
  break;
 case IO_CONTROL_GET_SESSION:   // 获取一个会话
  break;
 case IO_CONTROL_SET_QUERY_SESSION:  // 返回DLL询问的结果
  break;
 case IO_CONTROL_GET_QUERY_SESSION:  // 获取发出询问的会话
  break;
 }
 return 0;
}
phoenixLSP.dll检查应用程序的权限后,根据规则,如果找到规则,直接按规则处理,否则 PostMessage 到phoenixFW.exe 询问该如何操作;

phoenixFW.exe 使用 class CPIOControl 来和phoenixLSP.dll通信,用来改变其中的data_seg 和 bss_seg 段。

 

passthru.sys 使用链表来保存规则内容

// 过滤规则
typedef struct _PassthruFilter
{
 USHORT protocol;  // 使用的协议

 //驱动程序中不方便操作 TCHAR 类型的字符串,所以改为 ULONG 类型
 ULONG SourceIpFrom;
 ULONG SourceIpTo;
 ULONG DestIpFrom;
 ULONG DestIpTo;


 USHORT sourcePort;  // 源端口号
 USHORT destinationPort; // 目的端口号
 
 BOOLEAN bDrop;   // 是否丢弃此封包

}PassthruFilter, *PPassthruFilter;

// 过滤规则列表
typedef struct _PassthruFilterList
{
 PassthruFilter filter;
 struct _PassthruFilterList *pNext;

} PassthruFilterList, *PPassthruFilterList;

 

phoenixFW.exe 使用CreateFile打开到驱动程序所控制设备的句柄 ,枚举所有的适配器, 打开每个控制设备对象句柄,使用DeviceIoControl函数添加过滤规则
passthru 添加我们自己的派遣函数入口
DispatchTable[IRP_MJ_DEVICE_CONTROL] = DevIoControl;

DevIoControl
{
 switch(uIoControlCode)
 {
  case IOCTL_PTUSERIO_ENUMERATE: //枚举适配器
   break;
  case IOCTL_PTUSERIO_OPEN_ADAPTER: //打开适配器
   break;
  case IOCTL_PTUSERIO_QUERY_OID: //获取OID
  case IOCTL_PTUSERIO_SET_OID:  //设置OID
   break;
  default:
   return FltDevIoControl(pDeviceObject, pIrp); //处理其他控制命令
 }
}

FltDevIoControl
{
 switch(uIoControlCode)
 {
 case IOCTL_PTUSERIO_QUERY_STATISTICS:  // 获取网络活动状态
  break;
 case IOCTL_PTUSERIO_RESET_STATISTICS:  // 重设网络活动状态
  break;
 case IOCTL_PTUSERIO_ADD_FILTER:    // 添加一个过滤规则
  break;
 case IOCTL_PTUSERIO_CLEAR_FILTER:   // 清除过滤规则
  break;
 }
}

对于发送单个封包的函数
MPSend
{
     if(!FltFilterSendPacket(pAdapt,Packet,TRUE))
   {
    //
    // 如果拒绝的话,就欺骗上层,说已经发送成功了(虽然并没有真正地发送)
    //
    return NDIS_STATUS_SUCCESS;
   }
}

FltFilterSendPacket
{
  FltReadPacketData
  bPass =FltCheckFilterRules
  return bPass;
}

FltReadPacketData
{
  //使用NdisQueryBufferSafe 获取网络封包链表中的数据 Ethheader + IPheader + TCP/UDP header +净额
}

FltCheckFilterRules
{
  //根据过滤规则检查这个网络封包的源IP,目的IP,源端口,目的端口
  //因为这里得到的IP都是网络字节序,所以要转换成本机字节序,来判断是否在我们指定的IP范围内,如192.168.1.2-192.168.1.254
  #define NTOHL(val,x,y,m,n)  (((val) & (x)) << 24)|  /
 ((((val) & (y))>> 8) << 16)|  /
 ((((val) & (m))>> 16) << 8)|  /
 (((val) & (n)) >> 24)

 int x,y,m,n;
 x=0xff;
 y=0xff << 8;
 m=0xff << 16;
 n=0xff << 24;
  ipSource=pIpHdr->ipSource;
  ipSource=NTOHL(ipSource,x,y,m,n);
  //因为在phoenixFW.exe 中设置的源IP 目的IP 默认是发送封包时的顺序,所以当接收封包时,要把规则中的源IP 和封包中的目的IP 进行比较
 
  return !pFilterList->filter.bDrop;
}
MPSendPackets
PtReceive
{
  bPass = FltFilterReceive
 if(!bPass)
 {
 // 拒绝这个封包
 Status = NDIS_STATUS_SUCCESS;
  break;
 }
}

FltFilterReceive
{
  bPass = FltCheckFilterRules
  return bPass;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值