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中的源码