SPI学习

Winsock2服务提供者接口。Winsock2不仅提供了一个供应用程序访问网络服务的windows socket应用程序编程接口(API),还包含了由传输服务提供者和名字解析服务提供者实现的winsock服务提供者接口SPI和ws2_32.dll。他们的层次关系如图:

一个应用程序 在调用 Winsock 2 函数时,Ws2_32.dll会调用相应的Winsock SPI函数,利用特定的服务提供者执行所请求的服务。

eg:    select  ->  WSPSelect    connect 对应 WSAConnect API -> WSPConnect SPI


传输服务提供者 是以DLL的形式存于系统之中的。  实际上WINDOWS 系统中有多个传输服务提供者,被顺序注册在系统中形成一个有序的WINSOCK目录

IceSword 可以查看系统中注册的 SPI动态库

SPI(Service Provider Interface)


下面叙述 TCP协议安装我们的传输服务提供者 从而实现过滤拦截功能的步骤:

1)首先在WINSOCK 目录安装一个基于 TCP协议的传输服务提供者的分层协议

2)安装一个基于TCP协议的传输服务提供者的协议链

3)协议链中将TCP协议所需的基础协议和我们先前安装的分层协议连接起来

4)最后调整协议链在WINSOCK目录中的顺序使协议链位于最顶端

下面是函数介绍:

The UuidCreate function creates a new UUID.

RPC_STATUS RPC_ENTRY UuidCreate(
  UUID *Uuid
);
The WSCInstallProvider function installs the specified transport provider into the system configuration database.

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

The WSCEnumProtocols function retrieves information about available transport protocols.

int WSCEnumProtocols (
  LPINT lpiProtocols,
  LPWSAPROTOCOL_INFOW lpProtocolBuffer,
  LPDWORD lpdwBufferLength,
  LPINT lpErrno 
);
typedef struct _WSAPROTOCOL_INFO {
  DWORD  dwServiceFlags1;
  DWORD  dwServiceFlags2;
  DWORD  dwServiceFlags3;
  DWORD  dwServiceFlags4;
  DWORD  dwProviderFlags;
  GUID  ProviderId;
  DWORD  dwCatalogEntryId;
  WSAPROTOCOLCHAIN  ProtocolChain;
  int  iVersion;
  int  iAddressFamily;
  int  iMaxSockAddr;
  int  iMinSockAddr;
  int  iSocketType;
  int  iProtocol;
  int  iProtocolMaxOffset;
  int  iNetworkByteOrder;
  int  iSecurityScheme;
  DWORD  dwMessageSize;
  DWORD  dwProviderReserved;
  TCHAR  szProtocol[WSAPROTOCOL_LEN+1];
} WSAPROTOCOL_INFO, *LPWSAPROTOCOL_INFO;
The WSCDeinstallProvider function removes the specified transport provider from the system configuration database.

int WSCDeinstallProvider (
  LPGUID lpProviderId,
  LPINT lpErrno 
);
The WSCWriteProviderOrder function is used to reorder the available transport providers. The order of the protocols determines the priority of a protocol when being enumerated or selected for us.

int WSCWriteProviderOrder (
  LPDWORD lpwdCatalogEntryId,
  DWORD dwNumberOfEntries 
);


下面是修改一下就可用的   安装SPI  和  删除指定  GUID SPI  的代码:

// InstallProvider.cpp : Defines the entry point for the console application.
//

#define UNICODE
#define _UNICODE

#include "stdafx.h"
#include <RPC.H>
#include <Rpcdce.h>
#include <Ws2spi.h>
#include <Sporder.h>		// 定义了WSCWriteProviderOrder函数
#include <windows.h>
#include <stdio.h>

#pragma comment(lib, "Sporder.lib")	// 实现了UuidCreate函数
#pragma comment(lib, "Rpcrt4.lib")	// 实现了UuidCreate函数
#pragma comment(lib, "WS2_32")	// 链接到WS2_32.lib

BOOL UnInstall(GUID guidFilter);
int GetProvider(LPWSAPROTOCOL_INFOW &pProtoInfo)
{	
	//  首次调用,pProtoInfo传入NULL,取得需要的缓冲区长度
	DWORD dwSize = 0;
	int nError = 0;
	if(WSCEnumProtocols(NULL, NULL, &dwSize, &nError) == SOCKET_ERROR)
	{
		if(nError != WSAENOBUFS)
		{
			return 0;
		}
	}
	// 申请足够缓冲区内存。
	pProtoInfo = (LPWSAPROTOCOL_INFOW)GlobalAlloc(GPTR, dwSize);
	if (pProtoInfo == NULL)
	{
		return 0;
	}
	//再次调用WSCEnumProtocols函数
	return WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError);
}
#define Safe_Delete(pPointer) if(pPointer != NULL) {delete []pPointer; pPointer=NULL;}
#define Safe_New(pPointer, Type, Size)  pPointer = new Type[Size]; if (pPointer == NULL) goto Exit;

BOOL InstallProvider(WCHAR * pProviderName, WCHAR * pwszPathName, int *pProtocols, int iNum)
{
	if (pwszPathName == NULL || pProviderName == NULL)
	{
		return FALSE;
	}
	LPWSAPROTOCOL_INFOW pProtoInfo = NULL;
	//保存要安装的协议类型
	int nIndex = 0, nProtocols, nError, i=0, nArrayCount = 0, *pTemp = NULL;
	DWORD dwSize = 0, *pdwIds = NULL,*pdwOrigCatalogId = NULL;
	WSAPROTOCOL_INFOW *pOriginalProtocolInfo = NULL;
	BOOL bRet = FALSE;
	Safe_New(pTemp, int, iNum)
	//备份一份要安装的协议于pTemp指向的内存中
	memcpy(pTemp, pProtocols, iNum*sizeof(int));
	//为每个协议链准备WSAPROTOCOL_INFOW结构体
	Safe_New(pOriginalProtocolInfo, WSAPROTOCOL_INFOW, iNum)
	//pdwOrigCatalogId指向的数组成员用于保存分层协议下层的基础协议目录ID
	Safe_New(pdwOrigCatalogId, DWORD, iNum)
	DWORD dwLayeredCatalogId;// 我们分层协议的目录ID号
	// 枚举所有服务程序提供者
	nProtocols = GetProvider(pProtoInfo);
	//将所有要安装新提供者的协议在Winsock目录中已存在的位于前端的提供者的WSAPROTOCOL_INFOW保存起来
	for(; i<nProtocols; i++) 	//遍历Winsock目录中所有提供者
	{   

		if (i==16 || i==17)
		{
			UnInstall(pProtoInfo[i].ProviderId);
		}
// 		for (int j=0; j<iNum; j++)
// 		{	//有iNum个协议要安装新提供者。
// 			//判断当前提供者对应的协议是否是要安装新提供者的协议
// 			if(pProtoInfo[i].iAddressFamily == AF_INET && pProtoInfo[i].iProtocol == pTemp[j])
// 			{
// 				pTemp[j] = -1; //清除此类型,确保不要查找到重复协议的提供者,只要最前端的。
// 				//保存该协议正在使用的提供者的信息
// 				memcpy(&pOriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW));
// 				//分层协议中应该去除XP1_IFS_HANDLES标志。
// 				pOriginalProtocolInfo[nArrayCount].dwServiceFlags1 = 
// 					pOriginalProtocolInfo[nArrayCount].dwServiceFlags1 & (~XP1_IFS_HANDLES); 
// 				//将该协议正在使用的提供者的目录ID保存起来。
// 				pdwOrigCatalogId[nArrayCount] = pProtoInfo[i].dwCatalogEntryId;
// 				nArrayCount++;
// 				break; //找到后不必再循环查找其他的
// 			}
// 		}
	}  
	// 安装我们的分层协议,获取一个目录ID。
	// 随便找一个下层协议的结构复制过来即可
	WSAPROTOCOL_INFOW LayeredProtocolInfo;
	memcpy(&LayeredProtocolInfo, &pOriginalProtocolInfo[0], sizeof(WSAPROTOCOL_INFOW));
	// 修改协议名称,类型,设置PFL_HIDDEN标志
	wcscpy(LayeredProtocolInfo.szProtocol, pProviderName);
	LayeredProtocolInfo.ProtocolChain.ChainLen = LAYERED_PROTOCOL; // 设置分层协议标志;
	LayeredProtocolInfo.dwProviderFlags |= PFL_HIDDEN;
	
	// 获取一个Guid,安装分层协议
	GUID ProviderLayeredGuid;
	if(UuidCreate(&ProviderLayeredGuid) == RPC_S_OK)
	{
		if(WSCInstallProvider(&ProviderLayeredGuid,
			pwszPathName, &LayeredProtocolInfo, 1, &nError) == SOCKET_ERROR)
		{
			goto Exit;
		}
	}
	// 重新枚举协议,获取分层协议的目录ID号
	GlobalFree(pProtoInfo);
	nProtocols = GetProvider(pProtoInfo);
	for(i=0; i<nProtocols; i++)
	{	//查找新安装的分层协议提供者的ID并保存。
		if(memcmp(&pProtoInfo[i].ProviderId, &ProviderLayeredGuid, 
			sizeof(ProviderLayeredGuid)) == 0)
		{
			dwLayeredCatalogId = pProtoInfo[i].dwCatalogEntryId;
			break;
		}
	}
	// 安装协议链,多个协议链共用一个分层协议。有多少个协议要安装新提供者就要安装多少个协议链
	// 修改协议名称,类型
	WCHAR wszChainName[WSAPROTOCOL_LEN + 1];
	for(i=0; i<iNum; i++)
	{
		swprintf(wszChainName, L"%ws over %ws", pProviderName, pOriginalProtocolInfo[i].szProtocol);
		wcscpy(pOriginalProtocolInfo[i].szProtocol, wszChainName);
		if(pOriginalProtocolInfo[i].ProtocolChain.ChainLen == 1)
		{	//当前协议正在使用的提供者是基础提供者,令该提供者的ID位于新安装的协议链中第二个位置
			pOriginalProtocolInfo[i].ProtocolChain.ChainEntries[1] = pdwOrigCatalogId[i];
		}
		else
		{
			//当前协议正在使用的提供者是协议链提供者,则令该协议链整体后移,协议链首位置放分层协议
			for(int j = pOriginalProtocolInfo[i].ProtocolChain.ChainLen; j>0; j--)
			{
				pOriginalProtocolInfo[i].ProtocolChain.ChainEntries[j] 
				= pOriginalProtocolInfo[i].ProtocolChain.ChainEntries[j-1];
			}
		}
		//将刚安装的分层协议放置到协议链的链首.
		pOriginalProtocolInfo[i].ProtocolChain.ChainEntries[0] = dwLayeredCatalogId;
		//协议链中新安装了一个分层协议,所以使协议链中所链的协议数量加1。
		pOriginalProtocolInfo[i].ProtocolChain.ChainLen++;	
	}
	// 获取一个Guid,安装协议链
	GUID ProviderChainGuid;
	if(UuidCreate(&ProviderChainGuid) == RPC_S_OK)
	{
		if(WSCInstallProvider(&ProviderChainGuid,
			pwszPathName, pOriginalProtocolInfo, iNum, &nError) == SOCKET_ERROR)
		{
			goto Exit;	
		}
	}
	else
	{
		goto Exit;
	}
	// 重新排序Winsock目录,将我们的协议链提前
	// 重新枚举安装的协议
	GlobalFree(pProtoInfo);
	nProtocols = GetProvider(pProtoInfo);
	Safe_New(pdwIds, DWORD,nProtocols)
	// 将我们新安装的协议链提供者ID放置到ID数组前端
	for(i=0; i<nProtocols; i++)
	{
		if((pProtoInfo[i].ProtocolChain.ChainLen > 1) &&
			(pProtoInfo[i].ProtocolChain.ChainEntries[0] == dwLayeredCatalogId))
		{	//我们新安装的所有协议链的链首都是分层协议,据此查找新安装的协议链
			pdwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId;
		}
	}
	// 添加其它协议提供者ID到ID数组中
	for(i=0; i<nProtocols; i++)
	{
		if((pProtoInfo[i].ProtocolChain.ChainLen <= 1) ||
			(pProtoInfo[i].ProtocolChain.ChainEntries[0] != dwLayeredCatalogId))
			pdwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId;
	}
	// 根据ID数组中顺序重新排序Winsock目录
	if((nError = ::WSCWriteProviderOrder(pdwIds, nIndex)) != ERROR_SUCCESS)
	{
		goto Exit;
	}
	bRet = TRUE;   //到这里安装成功
Exit:
	Safe_Delete(pdwIds)
	Safe_Delete(pOriginalProtocolInfo)
	Safe_Delete(pTemp)
	Safe_Delete(pdwOrigCatalogId)
	if (pProtoInfo != NULL)
	{
		GlobalFree(pProtoInfo);
		pProtoInfo = NULL;
	}
	return bRet;
}

BOOL UnInstall(GUID guidFilter)
{
	BOOL bRet = FALSE;
	LPWSAPROTOCOL_INFOW pProtoInfo = NULL;
	DWORD dwLayeredCatalogId;
	// 根据Guid取得分层协议的目录ID号
	int nProtocols = GetProvider(pProtoInfo);
	int nError, i=0;
	for(; i<nProtocols; i++)
	{
		if(memcmp(&guidFilter, &pProtoInfo[i].ProviderId, sizeof(guidFilter)) == 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))
			{
				bRet = WSCDeinstallProvider(&pProtoInfo[i].ProviderId, &nError);
			}
		}
		// 移除分层协议
		bRet = WSCDeinstallProvider(&guidFilter, &nError);
	}
	return bRet;
}

int main(int argc, char* argv[])
{
	int iArray[3] = {IPPROTO_UDP, IPPROTO_TCP, IPPROTO_IP};
	InstallProvider(L"SpiDll2", L"SpiDll2.dll", iArray, 3);
	return 0;
}


例外这里:

WSAStartup API 被调用时不会去调用对应的WSPStartup SPI 而是

在应用程序 调用 socket/WSASocket 时 在WINsock目录中查找匹配的提供者

找到后加载对应的DLL库,然后调用它的WSPStartup函数

而WSPStartup功能是获得其他SPI函数的地址

并且将各个函数地址保存到WSPStartup函数的第五个参数所指向的内存中

可以称这块内存为SPI函数地址表

其他的WINsock API在调用相应的Winsock SPI时就可以利用这个SPI 函数地址表查找相应的SPI函数地址,然后调用


源码中这个DLL中含有过滤函数。

处理方案:

1)将Winsock目录下的病毒安装的传输服务提供者删除掉,上面有源码

2)使用函数将系统中所有DLL模块的线程结束掉,当然我们不知道是什么DLL   使用 UnLoadModuleInSystem函数卸载系统中所有的指定的DLL:



用这个软件业可以去卸载掉 handle

卸载DLL用  CProcess中的源码







原文地址:https://www.cnblogs.com/zcc1414/p/3982486.html