网络编程之编写LSP进行Winsock API监控拦截或LSP注入

xiaoxiao2021-02-28  33

zz from: http://blog.csdn.net/Aaron133/article/details/78028942?locationNum=8&fps=1

【1】工具介绍:

用到的工具:VS2015

语言:C/C++

需要系统提供的动态链接库:1、 sporder.dll    //很多系统不自带着个dll,导致编译时缺少dll无法编译. 

                                                          本人只提供:   WIN7 64位的sporder.dll :http://pan.baidu.com/s/1nuRku9r

                                                          其他系统自行网上下载.

安装、移除LSP、编写分层提供者DLL、测试程序的源码:

http://pan.baidu.com/s/1boP2Y3X  (除了文章中的源码之外,包含了测试程序的源码)

【2】编写LSP第三方系统组件须知的概念:

1、先看我写的SPI接口的概念:http://blog.csdn.net/aaron133/article/details/78005779

2、本章就是介绍安装SPI的分层协议提供者(LSP),即第三方系统网络组件.

3、当Windows程序想要使用网络时,必须加载SPI的基础提供者(TCP、UDP、原始)才能进行网络通讯.

4、安装LSP分层服务提供者就是想让一个自己写的DLL,让网络程序先加载进去调用,然后再我们的DLL内,再调用基础服务提供者,进行网络通讯,

     所以在这过程中,我们可以对系统上所有使用特定协议的网络程序,在用户模式下进行Winsock API调用监控,HOOK拦截,甚至利用LSP注入DLL.

5、LSP一般是对网络进行更高级的通讯服务管理、过滤,黑客常用它来进行浏览器劫持、监控用户信息等等.

6、360所谓的修复LSP或CMD的netsh winsock reset命令,就是清除第三方的LSP提供者,并清除它的DLL,留下系统的基础服务提供者.

【3】不能拦截的Winsock API函数:

1、htonl,htons仅在ws2_32.dll中实现. 2、inet_addr,inet_ntoa,gethostname,WSACreateEvent,WSACloseEvent等等都不在SPI中.

3、如果程序直接使用传输驱动接口(TDI)进行TCP/IP发送数据包,那么拦截不了.

4、所以在用户模式下,使用LSP过滤网络封包是一个很好的选择.

【4】LSP分层协议提供者的编写:(DLL)

【须知】

1、编写LSP提供者就是写一个DLL.

2、WSPStartup是LSP必须导出的函数.

3、加载下层提供者的DLL,并调用它的初始化WSPStartup是LSP必须做的事情.

4、拦截API函数就是将下层提供者(基础协议)的函数地址记录下来,将我们自定义的函数地址替换上去,执行到如send时

      就会先调用我们的自定义函数,再由我们的自定义函数,考虑要不要调用真正的send.

【开始编写】

1、步骤 :创建Win32程序     -->     DLL开发

2、新建一个.def文件:

EXPORTS WSPStartup      @2

【代码步骤分析】

1、网络程序加载LSP分层服务提供者DLL,并调用了DLL里的WSPStartup初始化函数.

2、在LSP的WSPStartup函数中,加载它的下层服务提供者的DLL,并调用它的WSPStartup初始化函数.

3、对下层提供者的函数表地址进行修改,修改感兴趣的网络函数指向我们的自定义函数,进行拦截、监视Winsock API.

4、下面的例子中拦截了connect函数、sendto函数.

头文件:        //讲解SPI篇的时候,用到的函数,用于遍历系统所有协议,包括分层协议

#include <WinSock2.h> #include <WS2spi.h> #include <tchar.h>

LPWSAPROTOCOL_INFOW GetProvider(LPINT lpnTotalProtocols) {//遍历所有协议 int nError = 0; DWORD dwSize = 0; LPWSAPROTOCOL_INFOW pProtoInfo = NULL; if (WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError) == SOCKET_ERROR) { if (nError != WSAENOBUFS) return NULL; } pProtoInfo = (LPWSAPROTOCOL_INFOW)new WSAPROTOCOL_INFOW[dwSize / sizeof(WSAPROTOCOL_INFOW)]; if (!pProtoInfo) return NULL; ZeroMemory(pProtoInfo, dwSize); *lpnTotalProtocols = WSAEnumProtocols(NULL, pProtoInfo, &dwSize); return pProtoInfo; } void FreeProvider(LPWSAPROTOCOL_INFOW pProtoInfo) { delete[] pProtoInfo; }

源文件:

WSPPROC_TABLE g_NextProcTable;  //下层提供者的函数表        全局

//LSP的初始化函数(唯一的导出函数)

int WSPAPI WSPStartup( WORD wVersionRequested,                          //用户程序加载套接字库的版本号(in) LPWSPDATA lpWSPData,                               //用于取得Winsock服务的详细信息 LPWSAPROTOCOL_INFO lpProtocolInfo,   //指定想得到的协议的特征 WSPUPCALLTABLE UpcallTable,                 //Ws2_32.dll向上调用转发的函数表 LPWSPPROC_TABLE lpProTable                 //下层提供者的函数表(一般为基础协议,共30个服务函数) ) {   //如果协议位分层协议或基础协议,那么返回错误 if (lpProtocolInfo->ProtocolChain.ChainLen <= 1) {   //无法加载或初始化请求的服务提供程序 return WSAEPROVIDERFAILEDINIT; } //找到下层协议的WSAPROTOCOL_INFOW结构体 WSAPROTOCOL_INFOW NextProtocolInfo; int nTotalProtols; LPWSAPROTOCOL_INFOW pProtoInfo = GetProvider(&nTotalProtols); //下层提供者的入口ID DWORD dwBaseEntryId = lpProtocolInfo->ProtocolChain.ChainEntries[1]; //遍历所有协议 int i = 0; for (; i < nTotalProtols; i++) {//找到下层提供者协议 if (pProtoInfo[i].dwCatalogEntryId == dwBaseEntryId) { memcpy(&NextProtocolInfo, &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW)); break; } } //如果没找到 if (i >= nTotalProtols) return WSAEPROVIDERFAILEDINIT; //加载下层协议的Dll int nError = 0; TCHAR szBaseProviderDll[MAX_PATH]; int nLen = MAX_PATH; //取得下层提供者的DLL路径(可能包含坏境变量) if(WSCGetProviderPath(&NextProtocolInfo.ProviderId, szBaseProviderDll, &nLen, &nError) == SOCKET_ERROR) return WSAEPROVIDERFAILEDINIT; //坏境变量转换字符串 if(!ExpandEnvironmentStrings(szBaseProviderDll, szBaseProviderDll, MAX_PATH)) return WSAEPROVIDERFAILEDINIT; //加载dll HMODULE hModdule = LoadLibrary(szBaseProviderDll); if(hModdule == NULL) return WSAEPROVIDERFAILEDINIT; //取出下层提供者的WSPStartup函数 LPWSPSTARTUP pfnWSPStartup = (LPWSPSTARTUP)GetProcAddress(hModdule, "WSPStartup"); if(NULL == pfnWSPStartup ) return WSAEPROVIDERFAILEDINIT; LPWSAPROTOCOL_INFOW pInfo = lpProtocolInfo; if (NextProtocolInfo.ProtocolChain.ChainLen == BASE_PROTOCOL)//如果下层提供者是基础协议 pInfo = &NextProtocolInfo;                               //赋给pInfo指针 //调用下层提供者的初始化函数 int nRet = pfnWSPStartup(wVersionRequested, lpWSPData, lpProtocolInfo, UpcallTable, lpProTable); //初始化失败 if (nRet != ERROR_SUCCESS) return nRet; //初始化完成后,复制下层提供者(基础协议)的整个函数表 g_NextProcTable = *lpProTable; //将基础协议的SendTo函数指针,指向我们的WSPSendTo函数,在我们的函数内,再确定要不要调用回基础协议的Sendto函数 lpProTable->lpWSPSendTo = WSPSendTo;  lpProTable->lpWSPConnect = WSPConnect; FreeProvider(pProtoInfo, nTotalProtols); return nRet; }

//下面对sendto、connect函数的8888端口进行拦截:

int WSPAPI WSPConnect(       //自定义的WSPConnect函数 SOCKET s, const struct sockaddr FAR * name, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, LPINT lpErrno ) { sockaddr_in* info = (sockaddr_in*)name; USHORT port = ntohs(info->sin_port); if (port == 8888)   //如果是8888端口,那么拦截 { int nError = 0;

               //因为整个dll已经加载进程序里,这里对我的控制台程序进行测试 SetConsoleTitle(_T("sorry,we shutdown you tcp protocol port<8888>!"));  g_NextProcTable.lpWSPShutdown(s, SD_BOTH, &nError); //设置错误信息 *lpErrno = WSAECONNABORTED;    return SOCKET_ERROR;  }

       //如果不是,调用下层提供者的函数表中的WSPConnect函数 return g_NextProcTable.lpWSPConnect(s, name, namelen, lpCallerData, lpCalleeData, lpSQOS, lpGQOS, lpErrno); } int WSPAPI WSPSendTo         //自定义的WSPSendTo函数 ( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, const struct sockaddr FAR * lpTo, int iTolen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, LPWSATHREADID lpThreadId, LPINT lpErrno ) { sockaddr_in* info = (sockaddr_in*)lpTo; USHORT port = ntohs(info->sin_port); if (port == 8888)    //如果是8888端口,那么拦截 { int nError = 0; SetConsoleTitle(_T("sorry,we shutdown you udp protocol port<8888>!")); g_NextProcTable.lpWSPShutdown(s, SD_BOTH, &nError); //设置错误信息 *lpErrno = WSAECONNABORTED; return SOCKET_ERROR; }

        //如果不是,调用下层提供者的函数表中的WSPSendTo函数 return g_NextProcTable.lpWSPSendTo(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpTo, iTolen, lpOverlapped, lpCompletionRoutine, lpThreadId, lpErrno); }

【5】LSP的DLL编写完成后,编写安装与卸载LSP的程序: 【须知】 1、安装、卸载LSP必须是管理员用户组的权限才能使用. 2、下面的例子中,我一共安装了1个分层协议(DLL),3个协议链(用于抢在TCP、UDP、原始套接字提供者前执行) 3、在http://blog.csdn.net/aaron133/article/details/78005779篇中的      “网络程序是如何调用Winsock2 服务提供者进行网络通讯     调用网络通讯的机制,所以要将新安装的协议链,排在遍历函数的最前面,网络程序先找到适合的协议,就会用那个协议.     如果排在后面,就可能载入别的相同类型的协议的提供者,而不使用我们的分层提供者. 【代码步骤分析】 一、遍历所有协议,将UDP、TCP、原始的Winsock目录入口(结构体)各复制一份出来. 二、随便找一个下层协议(基础服务提供者)的Winsock目录入口结构体作为模板,用于安装LSP时作为它的Winsock目录入口(结构体).             1)必须修改协议名称(szProtocol成员).             2) dwServiceFlags1成员必须将XP1_IFS_HANDLES标志去掉.             3)提供者的类型:ProtocolChain成员ChainLen变量 = LAYERED_PROTOCOL(0) //0暗示为分层协议提供者    不懂这个概念的先看我上一篇讲解SPI文章.                                                                                                                                                              //地址:http://blog.csdn.net/aaron133/article/details/78005779             4)表示方式:dwProviderFlags成员 = PFL_HIDDEN; //由提供者自己设置的Winsock目录入口.             5)安装分层协议提供者. 三、安装3个协议链(协议链,排在第一位的就是我们新安装的分层提供者)              1)为什么有3个协议链,因为它们各对应一个基础协议提供者,分别是TCP、UDP、原始,当网络程序使用TCP、UDP、原始,会先加载我们的                  分层服务提供者LSP的DLL. 四、重新排序Winsock目录               1)因为新安装的提供者,都会排在最后,这样如果前面有网络程序适合的提供者时,就会直接载入它的DLL,而不载入我们LSP的DLL. 头文件: #include <WS2spi.h> #include <winsock2.h> #include <process.h> #include <ws2tcpip.h> #include <mstcpip.h> #include <Windows.h> #include <iostream> #include <tchar.h> using namespace std; #pragma warning(disable:4996) #pragma comment(lib,"Sporder.lib") #pragma comment(lib, "Ws2_32.lib") #include <sporder.h> //安装LSP class installLSP { public: installLSP() { WSADATA wsa; WSAStartup(MAKEWORD(2, 2), &wsa); CoCreateGuid(&this->Layered_guid); CoCreateGuid(&this->AgreementChain_guid); } ~installLSP() { WSACleanup(); } public: //安装LSP,并安装3个协议链 BOOL InstallProvider(WCHAR* wszDllPath)  //参数:LSP的DLL的地址 { WCHAR wszLSPName[] = _T("AaronLSP"); LPWSAPROTOCOL_INFOW pProtoInfo = NULL; int nProtocols = 0; //分层协议     取出来的模板 WSAPROTOCOL_INFOW OriginalProtocolInfo[3]; //数组成员为TCP、UDP、原始的目录入口信息 DWORD dwOrigCatalogId[3]; //记录入口ID号 int nArrayCount = 0;      //数组个数索引 DWORD dwLayeredCatalogId; //分层协议的入口ID号 int nError; pProtoInfo = GetProvider(&nProtocols); if (nProtocols < 1 || pProtoInfo == NULL) return FALSE; BOOL bFindUdp = FALSE; BOOL bFindTcp = FALSE; BOOL bFindRaw = FALSE; for (int i = 0; i < nProtocols; i++) {   //查找地址族为AF_INET的协议 if (pProtoInfo[i].iAddressFamily == AF_INET) { if (!bFindUdp && pProtoInfo[i].iProtocol == IPPROTO_UDP) { memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW)); //去除XP1_IFS_HANDLES标志,防止提供者返回的句柄是真正的操作系统句柄 OriginalProtocolInfo[nArrayCount].dwServiceFlags1 &= (~XP1_IFS_HANDLES); //记录目录入口ID dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId;  bFindUdp = TRUE; } if (!bFindTcp && pProtoInfo[i].iProtocol == IPPROTO_TCP) { memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW)); //去除XP1_IFS_HANDLES标志,防止提供者返回的句柄是真正的操作系统句柄 OriginalProtocolInfo[nArrayCount].dwServiceFlags1 &= (~XP1_IFS_HANDLES); //记录目录入口ID dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId; bFindTcp = TRUE; } if (!bFindRaw && pProtoInfo[i].iProtocol == IPPROTO_IP) { memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW)); //去除XP1_IFS_HANDLES标志,防止提供者返回的句柄是真正的操作系统句柄 OriginalProtocolInfo[nArrayCount].dwServiceFlags1 &= (~XP1_IFS_HANDLES); //记录目录入口ID dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId; bFindRaw = TRUE; } } } if (nArrayCount == 0) { FreeProvider(pProtoInfo); return FALSE; } //安装LSP分层协议 WSAPROTOCOL_INFOW LayeredProtocolInfo; memcpy(&LayeredProtocolInfo, &OriginalProtocolInfo[0], sizeof(WSAPROTOCOL_INFOW)); //修改协议名称的字符串 wcscpy(LayeredProtocolInfo.szProtocol, wszLSPName); //表示分层协议 LayeredProtocolInfo.ProtocolChain.ChainLen = LAYERED_PROTOCOL;//0 //表示方式为由提供者自己设置 LayeredProtocolInfo.dwProviderFlags = PFL_HIDDEN; //安装分层协议 if (SOCKET_ERROR == WSCInstallProvider(&Layered_guid, wszDllPath, &LayeredProtocolInfo, 1, &nError)) { FreeProvider(pProtoInfo); return FALSE; } FreeProvider(pProtoInfo); //重新遍历协议,获取分层协议的目录ID号 pProtoInfo = GetProvider(&nProtocols); if (nProtocols < 1 || pProtoInfo == NULL) return FALSE; for (int i = 0; i < nProtocols; i++)//一般安装新入口后,会排在最低部 { if (memcmp(&pProtoInfo[i].ProviderId, &Layered_guid, sizeof(GUID)) == 0) { //取出分层协议的目录入口ID dwLayeredCatalogId = pProtoInfo[i].dwCatalogEntryId; break; } } //安装协议链                 256 WCHAR wszChainName[WSAPROTOCOL_LEN + 1];//新分层协议的名称  over   取出来的入口模板的名称 for (int i = 0; i < nArrayCount; i++) { swprintf(wszChainName, _T("%s over %s"), wszLSPName, OriginalProtocolInfo[i].szProtocol); wcscpy(OriginalProtocolInfo[i].szProtocol, wszChainName);  //将这个模板的名称改成新名称↑ if (OriginalProtocolInfo[i].ProtocolChain.ChainLen == 1)//这是基础协议的模板 {   //修改基础协议模板的协议链, 在协议链[1]写入真正UDP[基础协议]的入口ID OriginalProtocolInfo[i].ProtocolChain.ChainEntries[1] = dwOrigCatalogId[i]; } else {//如果大于1,相当于是个协议链,表示:将协议链中的入口ID,全部向后退一格,留出[0] for (int j = OriginalProtocolInfo[i].ProtocolChain.ChainLen; j > 0; j--) OriginalProtocolInfo[i].ProtocolChain.ChainEntries[j] = OriginalProtocolInfo[i].ProtocolChain.ChainEntries[j - 1]; } //让新分层协议排在基础协议的前面(如果为协议链排就排在开头了) OriginalProtocolInfo[i].ProtocolChain.ChainLen++; OriginalProtocolInfo[i].ProtocolChain.ChainEntries[0] = dwLayeredCatalogId; } //一次安装3个协议链 if (SOCKET_ERROR == WSCInstallProvider(&AgreementChain_guid, wszDllPath, OriginalProtocolInfo, nArrayCount, &nError)) { FreeProvider(pProtoInfo); return FALSE; } //第三步:将所有3种协议进行重新排序,以让系统先调用我们的协议(让协议链排第一,协议链中[0]是新分层协议,[1]基础UDP协议) //重新遍历所有协议 FreeProvider(pProtoInfo); pProtoInfo = GetProvider(&nProtocols); if (nProtocols < 1 || pProtoInfo == NULL) return FALSE; DWORD dwIds[20]; int nIndex = 0; //添加我们的协议链 for (int i = 0; i < nProtocols; i++) {//如果是我们新创建的协议链 if (pProtoInfo[i].ProtocolChain.ChainLen > 1 && pProtoInfo[i].ProtocolChain.ChainEntries[0] == dwLayeredCatalogId) dwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId;//将3个协议链排在前3 } //添加其他协议 for (int i = 0; i < nProtocols; i++) {//如果是基础协议,分层协议(不包括我们的协议链,但包括我们的分层协议) if (pProtoInfo[i].ProtocolChain.ChainLen <= 1 || pProtoInfo[i].ProtocolChain.ChainEntries[0] != dwLayeredCatalogId) dwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId; } //重新排序Winsock目录 if (WSCWriteProviderOrder(dwIds, nIndex) != ERROR_SUCCESS) return FALSE; FreeProvider(pProtoInfo); return TRUE; }         //卸载LSP void RemoveProvider() { LPWSAPROTOCOL_INFOW pProtoInfo = NULL; int nProtocols = 0; DWORD dwLayeredCatalogId = 0; //分层协议提供者的入口ID号  //遍历出所有协议 pProtoInfo = GetProvider(&nProtocols); if (nProtocols < 1 || pProtoInfo == NULL) return; int nError = 0; int i = 0; for (i = 0; i < nProtocols; i++) { //查找分层协议提供者 if (memcmp(&Layered_guid, &pProtoInfo[i].ProviderId, sizeof(GUID)) == 0) { dwLayeredCatalogId = pProtoInfo[i].dwCatalogEntryId; break; } } if (i < nProtocols) { for (i = 0; i < nProtocols; i++) {//查找协议链(这个协议链的[0]为分层协议提供者) if (pProtoInfo[i].ProtocolChain.ChainLen > 1 && pProtoInfo[i].ProtocolChain.ChainEntries[0] == dwLayeredCatalogId) {//先卸载协议链 WSCDeinstallProvider(&pProtoInfo[i].ProviderId, &nError); break; } } WSCDeinstallProvider(&Layered_guid, &nError); } } private:        //这两个函数是遍历所有协议函数,在编写DLL时,已经把源代码放出来了,这里就不放出来了.        LPWSAPROTOCOL_INFOW GetProvider(LPINT lpnTotalProtocols);        void FreeProvider(LPWSAPROTOCOL_INFOW pProtoInfo);  private: GUID Layered_guid;        //分层协议GUID GUID AgreementChain_guid; //协议链GUID }; 源文件: #include "Hello.h" #define PATH _T("C:\\Users\\Administrator\\Desktop\\实例\\网络实验\\安装LSP服务提供程序\\LSPDll\\Debug\\LSPDll.dll") SOCKET server_tcp = NULL; SOCKET server_udp = NULL; SOCKET client_tcp = NULL; SOCKET client_udp = NULL; int main(int argc,char** argv) { system("color 4e"); SetConsoleTitle(_T("安装LSP提供者程序实验")); ProtocolTraversestheExperiment2 s; printf("安装LSP前的所有协议:\r\n"); s.ShowAllProtocol(); installLSP LSP; LSP.InstallProvider(PATH); printf("安装LSP后的所有协议:\r\n"); s.ShowAllProtocol(); getchar(); LSP.RemoveProvider(); printf("清除LSP完成\r\n"); getchar(); return 0; } 【测试】 1、安装了一个LSP分层服务提供者(相当于HOOK了TCP、UDP、原始套接字),三个协议链 2、当有程序使用8888端口进行TCP连接或8888端口使用UDP发送数据时,就会拦截禁止:
转载请注明原文地址: https://www.6miu.com/read-1000161.html

最新回复(0)