分层服务提供者(LSP)(1)

开发过滤数据包的LSP程序可以定义过滤规则,恩,先看看
LSP本身是DLL,可以将它安装至Winsock目录,创建套接字的应用程序不必知道此LSP的任何信息就能调用它

1. 运行原理

用户创建套接字
1) 套接字创建函数(如socket)在Winsock目录寻找合适的协议
2) 此协议的提供者导出的函数完成各种功能

我们的目的:
1) 将自己编写的提供者安装到Winsock目录中,让用户调用我们的服务提供者
2) 由我们的提供者调用下层提供者
就可以截获所有的Winsock调用

服务提供者本身是DLL,导出与Windows API相对应的函数,如WSPStartup,WSPSocket,WSPSendTo等.
这里的LSP向上导出所有的SPI函数供Ws2_32.dll调用,在内部又通过基础服务提供者实现这些SPI

2. 安装LSP

实现LSP之前,将分层提供者安装到Winsock目录,安装一个LSP包括安装一个WSAPROTOCOL_INFOW结构(协议的入口),让创建套接字的应用程序可以枚举到它.
WSAPROTOCOL_INFOW结构:定义了分层提供者的特性和LSP是如何填写"链"的.

(1) 协议链

LSP和基础提供者连在一起形成了协议链,协议链描述了分层提供者加入Winsock目录的顺序.
SPI提供三中协议:分层协议,基础协议,协议链
协议类型由WSAPROTOCOL_INFOW结构内的WSAPROTOCOLCHAIN结构中数据指定,如下:

typedef struct _WSAPROTOCOLCHAIN {

int ChainLen;                                                    // 链的大小,也就是下面数组的大小
DWORD ChainEntries[MAX_PROTOCOL_CHAIN];     // 协议链入口数组,数组成员为链中协议的目录ID号

} WSAPROTOCOLCHAIN, *LPWSAPROTOCOLCHAIN;

ChainLen暗示了入口的提供者类型

  • 0, 分层协议
  • 1, 基础协议(基础提供者,如TCP和UDP提供者,有与之关联的内核模式协议驱动TCPIP.SYS)
  • 2, 协议链(目录ID-WSAPROTOCOL_INFOW结构中的dwCatalogEntryId域)

LSP在协议链中位置的影响:
顶层:被Ws2_32.dll加载
非顶层:被链中位于它上层的LSP加载

LSP被加载后的动作:
1) 首先调用LSP导出的函数WSPStartup
2) 将包含协议链的WSAPROTOCOL_INFOW结构传递给这个函数
3) LSP再找到协议链中位于自己下方的提供者,进而加载它

安装LSP
1) 安装一个分层协议,用系统分配给此协议的目录ID和下层提供者的目录ID构建一个ChainEntries数组,进而构建一个WSAPROTOCOL_INFOW结构
2) 安装协议链

(2)安装函数

该函数只需提供LSP的GUID,DLL位置,描述它支持的协议的一个或多个WSAPROTOCOL_INFOW结构即可

int WSCInstallProvider(
  const LPGUID lpProviderId,                                    // 要安装的提供者的GUID
  const LPWSTR lpszProviderDllPath,                           // 指向提供者DLL的路径
  const LPWSAPROTOCOL_INFO lpProtocolInfoList,        // 指向一个WSAPROTOCOL_INFOW结构数组
  DWORD dwNumberOfEntries,                                    // lpProtocolInfoList数组中入口的数量,即数组大小
  LPINT lpErrno                                                        // 返回可能的失败代码
);                                                                          // 只有UNICODE版本,失败则返回SOCKET_ERROR

lpProviderId: GUID可以通过命令行工具UUIDGEN或者编程中是用UuidCreate函数生成.
lpszProviderDllPath: UNICODE字符串,包含环境变量.如 %SYSTEMROOT%
lpProtocolInfoList: WSAPROTOCOL_INFOW结构的数组,每个数组成员是一个要安装的单独的入口,即可一次安装多个服务提供者
通常从它要分层的下层提供者拷贝,两种情况例外:
    第一,szProtocol域要修改,以包含新提供者的名称
    第二,如有XP1_IFS_HANDLES标志,从dwServiceFlags1域中移除
            XP1_IFS_HANDLES标志表示此提供者返回的句柄是真正地操作系统句柄,可能会被传递到内部API.此提供者必须关联内核模式组件,创建TCPIP.SYS一样的句柄.

(3) 重新为目录排序

int WSCWriteProviderOrder(
  LPDWORD lpwdCatalogEntryId,    // WSAPROTOCOL_INFOW结构中的CatalogEntryId元素数组
  DWORD dwNumberOfEntries        // 上面数组的大小
);

lpwdCatalogEntryId: DWORD类型数组,以新的顺序包含了目录中每个提供者的目录入口.

这个函数定义在SPORDER.H和SPORDER.LIB库中,新的SDK中,定义转移到WS2_32.LIB中

总结:
1) 安装分层协议入口,获取系统分配的目录ID号
2) 安装一个或多个协议链,安装数量根据要分层的下层协议数量决定,如要把LSP安装在TCP,UDP和Raw之上,就要安装3个协议链.

(4) 示例代码

// 函数名 InstallProvider
// 参数:WCHAR* wszDllPath,为安装的LSP DLL路径地址
// 功能:将LSP安装到UDP协议之上
// 返回值: 错误值
// 步骤
// 1. 枚举所有的服务程序提供者,当发现其iAddressFamily == AF_INET和iProtocol == IPPROTO_UDP停止
// 2. 得到该协议信息,为WSAPROTOCOL_INFOW结构
// 3. 安装分层协议,根据上面得到的结构修改szProtocol,ChainLen,dwProviderFlags
// 5. WSCInstallProvider安装
// 6. 获取分层协议的目录ID号,根据ProviderId成员的判断得到该ID号
// 7. 安装协议链,原UDP的协议放在分层协议下层,协议链取新名,注意协议链与分层协议是两者不相关的
// 8. 重新排序Winsock目录,将我们的协议链提前
//    首先先查找协议链,找到ChainEntries[0]=该分层协议的协议链,放在第一个位置
//    而后添加其他基本协议(包括分层协议)或ChainEntries[0]不等于该分层协议的协议链

3. 移除LSP

传递要移除的提供者的GUID即可
int WSCDeinstallProvider(
  LPGUID lpProviderId,
  LPINT lpErrno
);
首先根据分层协议的GUID号找到其目录ID号,然后逐个移除各协议链,最后再移除分层协议的提供者.

// 函数名: InstallProvider
// 功能: 移除该LSP
// 找到协议链和协议,移除
// 1. 根据协议的GUID找到dwCatalogEntryId,然后根据这个找到ChainEntries[0]等于该ID的协议链
// 2. 移除协议和协议链

4. 编写LSP

Winsock2 LSP实现在标准的Windows DLL中, 每个LSP必须实现和导出WSPStartup函数,函数原型如下:

int WSPStartup(
  WORD wVersionRequested,                    // 调用者可以使用的Winsock SPI的最高版本号, 高字节是小版本号
  LPWSPDATA lpWSPData,                        // 指向一个WSPDATA结构, 用于取得Winsock服务提供者的详细信息
  LPWSAPROTOCOL_INFO lpProtocolInfo,    // 指向一个WSAPROTOCOL_INFO结构,用来指定想得到的协议特征
  WSPUPCALLTABLE UpcallTable,                // Ws2_32.dll提供的向上调用转发的函数表结构
  LPWSPPROC_TABLE lpProcTable                // 指向SPI函数表结构的指针, 用来返回30个SPI服务函数
);

其他的SPI函数都经由LSP的分派表----pProcTable参数导出,描述分派表的WSPPROC_TABLE结构定义了必须在LSP实现的函数,原型如下

typedef struct _WSPPROC_TABLE {
  LPWSPACCEPT  lpWSPAccept;
  LPWSPADDRESSTOSTRING  lpWSPAddressToString;
  LPWSPASYNCSELECT  lpWSPAsyncSelect;
  LPWSPBIND  lpWSPBind;
  LPWSPCANCELBLOCKINGCALL  lpWSPCancelBlockingCall;
  LPWSPCLEANUP  lpWSPCleanup;
  LPWSPCLOSESOCKET  lpWSPCloseSocket;
  LPWSPCONNECT  lpWSPConnect;
  LPWSPDUPLICATESOCKET  lpWSPDuplicateSocket;
  LPWSPENUMNETWORKEVENTS  lpWSPEnumNetworkEvents;
  LPWSPEVENTSELECT  lpWSPEventSelect;
  LPWSPGETOVERLAPPEDRESULT  lpWSPGetOverlappedResult;
  LPWSPGETPEERNAME  lpWSPGetPeerName;
  LPWSPGETSOCKNAME  lpWSPGetSockName;
  LPWSPGETSOCKOPT  lpWSPGetSockOpt;
  LPWSPGETQOSBYNAME  lpWSPGetQOSByName;
  LPWSPIOCTL  lpWSPIoctl;
  LPWSPJOINLEAF  lpWSPJoinLeaf;
  LPWSPLISTEN  lpWSPListen;
  LPWSPRECV  lpWSPRecv;
  LPWSPRECVDISCONNECT  lpWSPRecvDisconnect;
  LPWSPRECVFROM  lpWSPRecvFrom;
  LPWSPSELECT  lpWSPSelect;
  LPWSPSEND  lpWSPSend;
  LPWSPSENDDISCONNECT  lpWSPSendDisconnect;
  LPWSPSENDTO  lpWSPSendTo;
  LPWSPSETSOCKOPT  lpWSPSetSockOpt;
  LPWSPSHUTDOWN  lpWSPShutdown;
  LPWSPSOCKET  lpWSPSocket;
  LPWSPSTRINGTOADDRESS  lpWSPStringToAddress;
} WSPPROC_TABLE, FAR * LPWSPPROC_TABLE;

在DLL中实现函数表中的函数很简单,大多数情况下,只需要调用下层提供者导出的对应函数即可.
当应用程序调用Winsock API时,Ws2_32.dll最终会调用特定服务提供者中对应的Winsock2 SPI函数来执行指定的功能.
并非所有的函数都用SPI,如inet_addr,inet_ntoa等等.

系统如何调用WSPStartup函数:
1) 应用程序调用WSAStartup时,什么都不做
2) 应用程序创建套接字时,准备调用提供者的WSPStartup.
3) 系统在Winsock目录查找匹配的入口,找到后,加载提供者DLL,调用该WSAStartup函数

WSPStartup函数的作用:
1) 根据协议链找到下层提供者,调用它的WSPStartup函数初始化下层提供者,这是一个不断向下递归的过程
2) 并取得SPI服务函数的指针,向上返回这些指针之前,可以用自定义的函数指针覆盖它.实现截获Winsock调用

加载下层提供者:
1) 根据lpProtocolInfo参数找到下层提供者的目录ID,再枚举所有提供者,找到下层提供者入口的WSAPROTOCOL_INFOW结构
2) 加载下层提供者
3) 使用函数WSCGetProviderPath函数得到提供者的DLL路径

int WSCGetProviderPath(
  LPGUID lpProviderId,
  LPWSTR lpszProviderDllPath,
  LPINT lpProviderDllPathLen,
  LPINT lpErrno
);

4) 由于DLL路径可能包含环境变量,用ExpandEnvironmentStrings函数将它展开

DWORD ExpandEnvironmentStrings(
  LPCTSTR lpSrc,
  LPTSTR lpDst,
  DWORD nSize
);

5) 用LoadLibrary加载该DLL,调用GetProcAddress取得WSPStartup函数指针
6) 调用该WSPStartup函数初始化下层提供者

之后可以根据自己的兴趣截获自己感兴趣的Winsock调用

5. LSP实例

主要是建立DLL文件,只导出函数WSPStartup
该函数主要部分为:
1. 找到下层协议的WSAPROTOCOL_INFOW结构
2. 下层入口ID
3. 取得下层提供程序DLL路径
4. 加载该DLL
5. 导入下层提供程序的WSPStartup函数
6. 调用下层提供者的WSPStartup函数
7. 保存下层提供者的函数表
8. 修改传递给上层的函数表

实现自己的WSPSendTo函数,然后在第八步用下句实现
lpProcTable->lpWSPSendTo = WSPSendTo;

分层服务提供者(LSP)的更多相关文章

  1. 基于SPI的数据报过滤原理与实现

    一.个人防火墙技术概述 随着网络安全问题日益严重,广大用户对网络安全产品也越来越关注.防火墙作为一种网络安全工具,早已受到大家的青睐.在PC机上使用的个人防火墙,很大程度上成为广大网民的安全保护者.W ...

  2. LSP(分层服务提供程序)

    一.简介 LSP即分层服务提供商,Winsock 作为应用程序的 Windows 的网络套接字工具,可以由称为"分层服务提供商"的机制进行扩展.Winsock LSP 可用于非常广 ...

  3. 注入技术--LSP劫持注入

    1.原理 简单来说,LSP就是一个dll程序. 应用程序通过winsock2进行网络通信时,会调用ws2_32.dll的导出函数,如connect,accept等. 而后端通过LSP实现这些函数的底层 ...

  4. [翻译]LSP程序的分类

    翻译的太垃圾,不建议其它人阅读本文. Note:LSP现在已经不推荐使用.自windows8和windows Server2012开始,使用Windows Filtering Platform. Wi ...

  5. LSP“浏览器劫持概念

    关于Winsock LSP“浏览器劫持”,中招者一直高居不下,由于其特殊性,直接删除而不恢复LSP的正常状态很可能会导致无法上网所以对其修复需慎重.   先说说什么是Winsock LSP“浏览器劫持 ...

  6. RSF 分布式 RPC 服务框架的分层设计

    RSF 是个什么东西? 一个高可用.高性能.轻量级的分布式服务框架.支持容灾.负载均衡.集群.一个典型的应用场景是,将同一个服务部署在多个Server上提供 request.response 消息通知 ...

  7. 协议栈处理中的conntrack HASH查找/Bloom过滤/CACHE查找/大包与小包/分层处理风格

    1.路由CACHE的优势与劣势 分级存储体系已经存在好多年了.其精髓在于"将最快的存储器最小化.将最慢的存储器最大化",这样的结果就使资源利用率的最大化.既提高了訪问效率,又节省了 ...

  8. 修复LSP 解决不能上网问题

    电脑突然不能上网,ping路由提示"传输失败,常见故障" 1, 打开CMD 2, 输入"netsh winsock reset" 回车 3, 重启电脑 LSP ...

  9. 源码解读Dubbo分层设计思想

    一.Dubbo分层整体设计概述 我们先从下图开始简单介绍Dubbo分层设计概念: (引用自Duboo开发指南-框架设计文档) 如图描述Dubbo实现的RPC整体分10层:service.config. ...

随机推荐

  1. c/c++多线程编程中最好不要加volatile

    来自https://www.zhihu.com/question/31459750 答主解释说:不能指望volatile能解决多线程竞争问题,除非所用的环境系统不可靠才会为了保险加上volatile, ...

  2. CSS3字体图标

    网址:http://icomoon.io/http://iconfont.cn/  阿里巴巴字体库 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1 ...

  3. codeforces 285 D. Permutation Sum 状压 dfs打表

    题意: 如果有2个排列a,b,定义序列c为: c[i] = (a[i] + b[i] - 2) % n + 1 但是,明显c不一定是一个排列 现在,给出排列的长度n (1 <= n <= ...

  4. 基于HTML5的多张图片上传

    图片上传之前也有写过demo,不过是单张上传的,最近有个业务需求是需要多张上传的,于是乎从新改写了一下 HTML结构: 1 2 3 4 <div class="container&qu ...

  5. CentOS配置网卡,重启网络显示:Device does not seem to be present(转载)

    From:http://www.cnblogs.com/fbwfbi/archive/2013/04/29/3050907.html 一.故障现象: [root@c1node01 ~]# servic ...

  6. compass项目监控文件报 /usr/bin/env 找不到文件

    1 找到ruby执行文件目录 $ wherris ruby ruby: /usr/lib/ruby /home/rudy/.rbenv/shims/ruby 2 设置软链接 sudo ln -s /h ...

  7. plsql如果表和函数等显示不出来

    就把用户设为所有用户,所有的东西就会都显示出来了,然后再把用户切换为当前用户和My objects,你想看的东西就全部显示出来了.

  8. [ASP.NET]动态绑定树控件:

    public void BindTree(TreeView tview, TreeNode tn_main, string parentId,string sql) { TreeNode tn=nul ...

  9. [Excel]C#操作Excel(导入导出)

    /// <summary> /// 读取Excel文档 /// </summary> /// <param name="Path">文件名称&l ...

  10. (easy)LeetCode 263.Ugly Number

    Write a program to check whether a given number is an ugly number. Ugly numbers are positive numbers ...