网络编程之编写LSP进行Winsock API监控拦截或LSP注入
扫描二维码
随时随地手机看文章
【1】工具介绍:
用到的工具:VS2015 语言:C/C++ 需要系统提供的动态链接库:1、 sporder.dll //很多系统不自带着个dll,导致编译时缺少dll无法编译. (发布时必须将此dll放到程序目录) 本人只提供: WIN7 64位的sporder.dll :http://download.csdn.net/download/aaron133/10153240 其他系统自行网上下载. 安装、移除LSP、编写分层提供者DLL、测试程序的源码:(申明:本人只在Win7 32/64位 和 Win10 64测试过) http://download.csdn.net/download/aaron133/10152873 (除了文章中的源码之外,包含了测试程序的源码)
【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.
二、开始编写LSP分层服务提供者的DLL:
【开始编写】
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#include#includeLPWSAPROTOCOL_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 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!")); 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!")); 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 服务提供者进行网络通讯” 调用网络通讯的机制,所以要将新安装的协议链,排在遍历函数的最前面,网络程序先找到适合的协议,就会用那个协议,如果排在后面,就可能载入别的相同类型的协议的提供者,而不使用我们的分层提供者.
二、开始编写安装LSP程序:
【编写步骤分析】一、遍历所有协议,将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#include#include#include#include#include#include#includeusing namespace std; #pragma warning(disable:4996) #pragma comment(lib,"Sporder.lib") #pragma comment(lib, "Ws2_32.lib") #include//安装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") int main(int argc,char** argv) { system("color 4e"); SetConsoleTitle(_T("安装LSP提供者程序实验")); ProtocolTraversestheExperiment2 s; printf("安装LSP前的所有协议:rn"); s.ShowAllProtocol(); installLSP LSP; LSP.InstallProvider(PATH); printf("安装LSP后的所有协议:rn"); s.ShowAllProtocol(); getchar(); LSP.RemoveProvider(); printf("清除LSP完成rn"); getchar(); return 0; }
【测试】1、安装了一个LSP分层服务提供者(相当于HOOK了TCP、UDP、原始套接字),三个协议链
2、当有程序使用8888端口进行TCP连接或8888端口使用UDP发送数据时,就会拦截禁止:
附加说明:1、Sporder.dll在编写LSP程序时,32位放在WOWSys64文件夹、64位放在system32文件夹.2、发布时必须携带着Sporder.dll在程序目录存放.3、编写64位LSP程序的程序时,不要包含#pragma comment(lib,"Sporder.lib")否则程序出错!