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 放在udp协议上

1.找到UDP的WSAPROTOCOL_INFOW 

WSAPROTOCOL_INFOW, UDPChainInfo; // 我们要安装的UDP分层协议和协议链

2.复用1可找到的这个UDP信息,修改其中几个自定义的字段,然后安装即可,此步骤可以获取一个系统分配的一个catalog id,同时这步只是安装了一个 chainLen=0的协议,只是告诉系统我们有一个这样的分层协议。

// 首先安装分层协议,获取一个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

3。因为我们是layer,还要修改协议链。

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; //上面都是把原来的CatalogId往后挪,然后把我们的放在元素0,为什么要放在元素0呢,是因为系统在加载service provider的时候,是取这个链的元素0信息加载的
    // 获取一个Guid,安装之
    GUID ProviderChainGuid;
    if(::UuidCreate(&ProviderChainGuid) == RPC_S_OK)
    {
        if(::WSCInstallProvider(&ProviderChainGuid, 
                    wszDllPath, &UDPChainInfo, 1, &nError) == SOCKET_ERROR)
                    return nError;
    }

  即安装协议链,安装协议链也是使用WSCInstallProvider函数,上2中的不同是这回我们的ChainLen一定大于1.,用上面的代码安装完后,和我们LSP相关的信息在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WinSock2\Parameters\Protocol_Catalog9\Catalog_Entries 下就会有2条(判断方法可以说调用了WSCInstallProvider几次就有几条)

第一条是Chainlen=0,第二次是ChainLen>1,

 

4。上面执行完后,还没完。这时候我们的LSP还不会被加载。因为系统socket操作发生的时候,是从HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WinSock2\Parameters\Protocol_Catalog9\Catalog_Entries 下的第一项开始搜索,

根据WSCInstallProvider()注册的信息对比socket的三要素(address family, sockettype, protocol)来匹配使用正确的provider的。

lsposed仓库加载 加载lsp文件_ide

 

而我们注册后, 会发现我们第二次调用WSCInstallProvider注册的信息是在注册表项的最后,如上图的000000000018 这项。当系统到Catalog_Entries 这里搜索的时候,如果00000000001就能满足的话,就直接使用00000000001对应的provider了。

而通常系统的00000000001~00000000003这3项就是对应常见的tcp,udp和raw 的provider,对应的动态库是mswsock.dll,通常这个是一个Base Services Provider(ChainLen=1)。

因为要想我们的lsp能被正常加载,还需要调用WSCWriteProviderOrder,把我们的Catalog对应的协议链项移到注册表的前面。代码如下:

//获取目前所有的provider协议(链)信息
    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);