LSP(分层服务提供者)

LSP本身是DLL,可以将它安装到winsock目录,以便创建套接字的应用程序不必知道此LSP的任何信息就能调用它。

运行原理:

套接字创建函数会在winsock目录中寻找合适的协议

  调用此协议,的提供者导出的函数 完成各种功能。

编写目的:

让用户调用自定义的服务提供者,有自定义的服务提供者调用下层提供者。这样便截获所有的winsock调用了。

服务提供者本身是DLL,导出一些与winsock API相对应的SPI函数。winsock库加载服务提供者时,便依靠这些函数来实现winsockAPI。

LSP也是如此,它向上导出所有的SPI函数供 Ws2_32.dll调用,在内部通过调用基础提供者实现这些SPI。

安装LSP:

实现LSP之前,要先将分层提供者安装到winsock目录,安装包括一个WSAPPROTOCOL_INFOW结构,定义了分层提供者的特性和LSP填写链的方式。(也叫做协议入口)

协议链:

  协议链描述了 分层提供者 加入winsock的目录的顺序。

typedef struct _WSAPROTOCOLCHAIN{
  int ChainLen;
  DWORD ChainEntries[Max_PROTOCOL_CHAN];
}WSAPROTOCOLCHAIN,*LPWSAPROTOCOLCHAIN;

ChainLen为0:分层协议  为1 基础协议   大于1 协议链

当ChainLen为0或者1时,ChainEntries数组无意义

大于1时,各个服务提供者的目录ID就包含在数组中。

实现LSP的DLL要么被另一个LSP加载,要么直接被WS2_32.DLL加载。取决于它的位置。

如果LSP没有在协议链的顶端,就会被链中位于它上层的LSP加载,否则的话,将被WS2_32.DLL加载。

安装LSP时,必须在winsock目录中安装两种协议:一个分层协议,一个协议链

安装分层协议视为了获取winsock库分配的目录ID号,一边在协议链中标识自己的位置。

协议链才是winsock目录中LSP的真正入口,连中包含了自己分层协议的目录ID号和下层提供者的目录ID号。

在安装时,要先安装一个分层协议,用系统分配给此分层协议的目录ID和下层提供者的目录ID构建一个 ChainEntries数组,进而构建一个WSAPROTOCOL_INFOW结构,然后再安装这个协议链。

安装函数:

提供LSP GUID DLL WSAPROTOCOL_INFOW结构便可。

int WSCInstallProvider(
const LPGUID lpProviderId,
const LPWSTR lpszProviderDllPath,
const LPWSAPROTOCOL_INFOW lpProtocolInfoList,
DWORD dwNumberOfEntries,
LPINT lpErrno
);

每一个安装提供者需要一个GUID来标识它的入口,GUID可以通过命令行工具UUIDGEN或者在编程使用UuidCreate函数来生成。

LSP的WSAPROTOCOL_INFOW结构通常从它要分层的下层提供者拷贝

1 szProtocol域要修改,以包含新提供者的名称

2 如果包含XP1_IFS_HANDLES标识,要从dwServiceFlags1域移除。

重新目录排序  WSCWriteProviderOrder

新安装的LSP会默认安装到winsock目录的结尾,这样系统调用的时候,还是会调用原先调用的LSP,因此只有进行重新的排序才能让系统调用到新安装的LSP。

总结:

  1 安装分层协议入口,以便获取系统分配的目录ID号。

  2 安装一个或者多个协议链,安装的数量取决于要分层的下层协议的数量。

  3 在结尾进行目录排序。

示例代码:

////////////////////////////////////////////////////////
// InstDemo.cpp

#include <Ws2spi.h>
#include <Sporder.h>                // 定义了WSCWriteProviderOrder函数

#include <windows.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Rpcrt4.lib")    // 实现了UuidCreate函数


// 要安装的LSP的硬编码,在移除的时候还要使用它
GUID  ProviderGuid = {0xd3c21122, 0x85e1, 0x48f3, {0x9a,0xb6,0x23,0xd9,0x0c,0x73,0x07,0xef}};


LPWSAPROTOCOL_INFOW GetProvider(LPINT lpnTotalProtocols)
{
    DWORD dwSize = 0;
    int nError;
    LPWSAPROTOCOL_INFOW pProtoInfo = NULL;
    
    // 取得需要的长度
    if(::WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError) == SOCKET_ERROR)
    {
        if(nError != WSAENOBUFS)
            return NULL;
    }
    
    pProtoInfo = (LPWSAPROTOCOL_INFOW)::GlobalAlloc(GPTR, dwSize);
    *lpnTotalProtocols = ::WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError);
    return pProtoInfo;
}

void FreeProvider(LPWSAPROTOCOL_INFOW pProtoInfo)
{
    ::GlobalFree(pProtoInfo);
}


// 将LSP安装到UDP协议提供者之上
int InstallProvider(WCHAR *wszDllPath)
{
    WCHAR wszLSPName[] = L"TinyLSP";    // 我们的LSP的名称
    int nError = NO_ERROR;

    LPWSAPROTOCOL_INFOW pProtoInfo;
    int nProtocols;
    WSAPROTOCOL_INFOW UDPLayeredInfo, UDPChainInfo; // 我们要安装的UDP分层协议和协议链
    DWORD dwUdpOrigCatalogId, dwLayeredCatalogId;

        // 在Winsock目录中找到原来的UDP协议服务提供者,我们的LSP要安装在它之上
    // 枚举所有服务程序提供者
    pProtoInfo = GetProvider(&nProtocols);
    for(int i=0; i<nProtocols; i++)
    {
        if(pProtoInfo[i].iAddressFamily == AF_INET && pProtoInfo[i].iProtocol == IPPROTO_UDP)
        {
            memcpy(&UDPChainInfo, &pProtoInfo[i], sizeof(UDPLayeredInfo));
            // 
            UDPChainInfo.dwServiceFlags1 = UDPChainInfo.dwServiceFlags1 & ~XP1_IFS_HANDLES;  
            // 保存原来的入口ID
            dwUdpOrigCatalogId = pProtoInfo[i].dwCatalogEntryId;
            break;
        }
    }  

        // 首先安装分层协议,获取一个Winsock库安排的目录ID号,即dwLayeredCatalogId
    // 直接使用下层协议的WSAPROTOCOL_INFOW结构即可
    memcpy(&UDPLayeredInfo, &UDPChainInfo, sizeof(UDPLayeredInfo));
    // 修改协议名称,类型,设置PFL_HIDDEN标志
    wcscpy(UDPLayeredInfo.szProtocol, wszLSPName);
    UDPLayeredInfo.ProtocolChain.ChainLen = LAYERED_PROTOCOL;        // LAYERED_PROTOCOL即0
    UDPLayeredInfo.dwProviderFlags |= PFL_HIDDEN;
    // 安装
    if(::WSCInstallProvider(&ProviderGuid, 
                    wszDllPath, &UDPLayeredInfo, 1, &nError) == SOCKET_ERROR)
        return nError;
    // 重新枚举协议,获取分层协议的目录ID号
    FreeProvider(pProtoInfo);
    pProtoInfo = GetProvider(&nProtocols);
    for(i=0; i<nProtocols; i++)
    {
        if(memcmp(&pProtoInfo[i].ProviderId, &ProviderGuid, sizeof(ProviderGuid)) == 0)
        {
            dwLayeredCatalogId = pProtoInfo[i].dwCatalogEntryId;
            break;
        }
    }

        // 安装协议链
    // 修改协议名称,类型
    WCHAR wszChainName[WSAPROTOCOL_LEN + 1];
    swprintf(wszChainName, L"%ws over %ws", wszLSPName, UDPChainInfo.szProtocol);
    wcscpy(UDPChainInfo.szProtocol, wszChainName);
    if(UDPChainInfo.ProtocolChain.ChainLen == 1)
    {
        UDPChainInfo.ProtocolChain.ChainEntries[1] = dwUdpOrigCatalogId;
    }
    else
    {
        for(i=UDPChainInfo.ProtocolChain.ChainLen; i>0 ; i--)
        {
            UDPChainInfo.ProtocolChain.ChainEntries[i] = UDPChainInfo.ProtocolChain.ChainEntries[i-1];
        }
    }
    UDPChainInfo.ProtocolChain.ChainLen ++;
    // 将我们的分层协议置于此协议链的顶层
    UDPChainInfo.ProtocolChain.ChainEntries[0] = dwLayeredCatalogId; 
    // 获取一个Guid,安装之
    GUID ProviderChainGuid;
    if(::UuidCreate(&ProviderChainGuid) == RPC_S_OK)
    {
        if(::WSCInstallProvider(&ProviderChainGuid, 
                    wszDllPath, &UDPChainInfo, 1, &nError) == SOCKET_ERROR)
                    return nError;
    }
    else
        return GetLastError();



        // 重新排序Winsock目录,将我们的协议链提前
    // 重新枚举安装的协议
    FreeProvider(pProtoInfo);
    pProtoInfo = GetProvider(&nProtocols);

    DWORD dwIds[20];
    int nIndex = 0;
    // 添加我们的协议链
    for(i=0; i<nProtocols; i++)
    {
        if((pProtoInfo[i].ProtocolChain.ChainLen > 1) &&
                    (pProtoInfo[i].ProtocolChain.ChainEntries[0] == dwLayeredCatalogId))
            dwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId;
    }
    // 添加其它协议
    for(i=0; i<nProtocols; i++)
    {
        if((pProtoInfo[i].ProtocolChain.ChainLen <= 1) ||
                (pProtoInfo[i].ProtocolChain.ChainEntries[0] != dwLayeredCatalogId))
            dwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId;
    }
    // 重新排序Winsock目录
    nError = ::WSCWriteProviderOrder(dwIds, nIndex);

    FreeProvider(pProtoInfo);
    return nError;
}

void RemoveProvider()
{    
    LPWSAPROTOCOL_INFOW pProtoInfo;
    int nProtocols;
    DWORD dwLayeredCatalogId;

    // 根据Guid取得分层协议的目录ID号
    pProtoInfo = GetProvider(&nProtocols);
    int nError;
    for(int i=0; i<nProtocols; i++)
    {
        if(memcmp(&ProviderGuid, &pProtoInfo[i].ProviderId, sizeof(ProviderGuid)) == 0)
        {
            dwLayeredCatalogId = pProtoInfo[i].dwCatalogEntryId;
            break;
        }
    }

    if(i < nProtocols)
    {
        // 移除协议链
        for(i=0; i<nProtocols; i++)
        {
            if((pProtoInfo[i].ProtocolChain.ChainLen > 1) &&
                    (pProtoInfo[i].ProtocolChain.ChainEntries[0] == dwLayeredCatalogId))
            {
                ::WSCDeinstallProvider(&pProtoInfo[i].ProviderId, &nError);
            }
        }
        // 移除分层协议
        ::WSCDeinstallProvider(&ProviderGuid, &nError);
    }
}



////////////////////////////////////////////////////

int binstall = 0;
void main()
{
    if(binstall)
    {
        if(InstallProvider(L"lsp.dll") == ERROR_SUCCESS)
        {
            printf(" Install successully \n");
        }
        else
        {
            printf(" Install failed \n");
        }
    }
    else
        RemoveProvider();
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Oozie 快速入门

    设想一下,当你的系统引入了spark或者hadoop以后,基于Spark和Hadoop已经做了一些任务,比如一连串的Map Reduce任务,但是他们之间彼此...

    用户1154259
  • Java直接内存与非直接内存性能测试

    什么是直接内存与非直接内存 根据官方文档的描述: A byte buffer is either direct or non-direct. Given a d...

    用户1154259
  • Sqoop切分数据的思想概况

    Sqoop通过--split-by指定切分的字段,--m设置mapper的数量。通过这两个参数分解生成m个where子句,进行分段查询。因此sqoop的spl...

    用户1154259
  • 线性分类器-中篇

    导读: 神经网络 反向传播算法 线性分类器-上篇 1 损失函数 在上一节定义了从图像像素值到所属类别的评分函数(score function),该函数的参数是权...

    昱良
  • 终端循环执行命令

    用户2398817
  • 如何实现云计算项目目标利润的问题

    在所有的云计算项目中,几乎有一半都在项目启动之后内部或外部的审计而失败,或者至少是部分的失败。在三分之二的案例中,云计算迁移所承诺的成本节省好处都被认为是真实的...

    静一
  • responder初体验

    responder 是 @kennethreitz 新开发的一个项目, 是一个基于 Python 的 HTTP 服务框架. 底层用了 Starlette 的框架...

    用户1416054
  • APT预警:CVE-2017-11882的两种变式攻击

      近段时间APT预警平台(威胁分析平台)捕获了一些CVE-2017-11882的变化利用样本,这些变化利用能一定程度上绕过杀毒软件的检测。 这里列举两种变式...

    安恒信息
  • 页面里的父类——BaseUI源代码下载(2009.10.15更新)

    一、介绍和下载 名称: 页面里的基类(BaseUI) 版本: 1.0.2 上传时间: 2009.10.15 主要功能: 权限验证,URL参数获取、...

    用户1174620
  • 使用vue实现行列转换的一种方法。

      行列转换是一个老生常谈的问题,这几天逛知乎有遇到了这个问题。一个前端说,拿到的数据是单列的需要做转换才能够绑定,折腾了好久才搞定,还说这个应该后端直接出数据...

    用户1174620

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动