原理

应用程序通过 socket 进行网络通信时会调用 ws2_32.dll 的导出函数,比如 send/recv 等,而这些函数时通过更底层的 LSP 提供的 SPI(服务提供者接口)实现的。划重点!!! :如果有多个符合条件的 SPI,系统将会调用在 winsock 目录最前面的那个 。所以注册一个 SPI 并插入到 winsock 目录的最前面就可以劫持 LSP 了!

另外劫持 LSP 需要将代码卸载 DLL 里(毕竟人家也叫劫持嘛 ~)

代码(来自网络)

freesec.dll :

// 全局遍历
WCHAR exepath[MAX_PATH] = { 0 };
WSPPROC_TABLE trueTable = { 0 };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);
}int WSPConnect(SOCKET s, const struct sockaddr FAR* name, int namelen,LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS,LPINT lpErrno)
{SOCKADDR_IN addr = *(SOCKADDR_IN*)name;if (addr.sin_port==htons(80)){MessageBoxW(0, L"有程序访问外网80端口", L"拒绝访问", 0);return SOCKET_ERROR;}return trueTable.lpWSPConnect(s, name, namelen, lpCallerData, lpCalleeData, lpSQOS, lpGQOS, lpErrno);
}int WSPAPI WSPStartup(WORD wVersionRequested,LPWSPDATA lpWSPData,LPWSAPROTOCOL_INFOW lpProtocolInfo,WSPUPCALLTABLE UpcallTable,LPWSPPROC_TABLE lpProcTable
)
/*当应用程序通过SOCKET创建socket时会调用系统根据Winsock目录和程序的需要来将对应的传输服务提供者,即一个dll加载到目标进程中. 然后调用该dll提供的WSPStartup函数来初始化.初始化的目的就是为了通过调用这个函数来获取该这次操作socket的API函数对应的SPI这就是windows上写socket时之前必须通过WSAStartup来进行socket初始化的原因该函数的lpProcTable 参数是个结构体,保存了所有的SPI函数.也就是可以从这个参数来获取SPI所以只需导出这个函数,然后将其他的SPI填写到lpProcTable中,最后返回给程序以上都是正常情况下的调用过程. 如果我们让系统加载我们给它提供的dll就可以导出该函数,并hook掉lpProcTable中的成员进行监控. 但是我们hook该函数后允许的话应该最后要调用正常的SPI,这时参数lpProtocolInfo就能派上用场. 通过该参数可以获取原来的协议的目录id,然后遍历winsock目录找到对应的协议的传输服务提供者即一个dll路径,通过加载该dll并调用其中的WSPStartup即可获取真正的SPI,然后调用它.最终可以实现监控,修改,拦截等功能
*/
{//我们编写的DLL用于协议链中,所以如果是基础协议或分层协议使用则直接返回错误if (lpProtocolInfo->ProtocolChain.ChainLen <= 1){return WSAEPROVIDERFAILEDINIT;}WCHAR exename[100] = { 0 };wsprintf(exename, L"应用程序: %ls 正在联网,是否允许?", exepath);if (MessageBoxW(0,exename,L"温馨提示",MB_YESNO|MB_ICONWARNING)==IDNO){MessageBoxW(0, L"已拦截", L"提示", 0);return WSAEPROVIDERFAILEDINIT;}// 枚举协议,找到下层协议的WSAPROTOCOL_INFOW结构    WSAPROTOCOL_INFOW    trueProtocolInfo;    //保存真正的协议结构LPWSAPROTOCOL_INFOW pProtoInfo = NULL;    int allproto = GetProvider(pProtoInfo);DWORD trueId = lpProtocolInfo->ProtocolChain.ChainEntries[1];//获取真正的协议目录idint i;//遍历查找真正的协议结构for (i = 0; i < allproto; i++){if (pProtoInfo[i].dwCatalogEntryId==trueId){memcpy(&trueProtocolInfo, &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW));break;}}//没找到就返回失败if (i>=allproto){return WSAEPROVIDERFAILEDINIT;}int nError;wchar_t szBaseProviderDll[MAX_PATH];//保存真正dll路径int nLen = MAX_PATH;// 取得下层提供程序DLL路径if (WSCGetProviderPath(&trueProtocolInfo.ProviderId, szBaseProviderDll, &nLen, &nError) == SOCKET_ERROR){return WSAEPROVIDERFAILEDINIT;}//上面的函数执行后路径中会存在环境变量,通过下面展开环境变量if (!ExpandEnvironmentStringsW(szBaseProviderDll, szBaseProviderDll, MAX_PATH)){return WSAEPROVIDERFAILEDINIT;}// 加载真正dllHMODULE hModule = LoadLibraryW(szBaseProviderDll);if (hModule == NULL){return WSAEPROVIDERFAILEDINIT;}// 导入真正dll的WSPStartup函数LPWSPSTARTUP  pfnWSPStartup = NULL;pfnWSPStartup = (LPWSPSTARTUP)GetProcAddress(hModule, "WSPStartup");if (pfnWSPStartup == NULL){return WSAEPROVIDERFAILEDINIT;}// 调用下层提供程序的WSPStartup函数以填充SPI地址表LPWSAPROTOCOL_INFOW pInfo = lpProtocolInfo;//if (trueProtocolInfo.ProtocolChain.ChainLen == BASE_PROTOCOL){pInfo = &trueProtocolInfo;}else{for (int j = 0; j<lpProtocolInfo->ProtocolChain.ChainLen; j++){lpProtocolInfo->ProtocolChain.ChainEntries[j]= lpProtocolInfo->ProtocolChain.ChainEntries[j + 1];}lpProtocolInfo->ProtocolChain.ChainLen--;}//调用真正的WSPStartup, 注意参数,协议结构参数必须是原来我们想劫持的那个协议结构int nRet = pfnWSPStartup(wVersionRequested, lpWSPData, pInfo, UpcallTable, lpProcTable);if (nRet != ERROR_SUCCESS){return nRet;}memcpy(&trueTable, lpProcTable, sizeof(WSPPROC_TABLE)); //保存到trueTable中以便调用//进行api替换lpProcTable->lpWSPConnect = (LPWSPCONNECT)WSPConnect;}BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved
)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:GetModuleFileNameW(0, exepath, MAX_PATH * sizeof(wchar_t));case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;
}

test.cpp:

#include<Windows.h>
#include<locale.h>
#include<stdio.h>
#include<malloc.h>
#pragma comment(lib,"ws2_32.lib")
GUID layerGuid;
#define layerName L"freesec"
DWORD findGuid()
{//枚举winsock目录中的协议LPWSAPROTOCOL_INFOW info;//指向winsock目录中协议DWORD size = 0;            //大小DWORD num;                //数量WSCEnumProtocols(0, 0, &size, 0);info = (LPWSAPROTOCOL_INFOW)malloc(size);num = WSCEnumProtocols(0, info, &size, 0);if (num == SOCKET_ERROR){free(info);return 0;}int i;for ( i= 0; i < num; i++){if (lstrcmpW(info[i].szProtocol,layerName)==0){memcpy(&layerGuid, &info[i].ProviderId, sizeof(GUID));break;}}free(info);if (i==num)//没找到{return 0;}return 1;
}
DWORD lspInject()
{//枚举winsock目录中的协议LPWSAPROTOCOL_INFOW info;//指向winsock目录中协议DWORD size = 0;            //大小DWORD num;                //数量WSCEnumProtocols(0, 0, &size, 0);info = (LPWSAPROTOCOL_INFOW)malloc(size);num = WSCEnumProtocols(0, info, &size, 0);DWORD trueId;            //存储被安装的提供者的目录idif (num == SOCKET_ERROR){free(info);return 0;}WCHAR supplier[] = layerName;WCHAR dllpath[] = L"E:\\0day\\shellcode\\Debug\\freesec.dll";//指定你的dll文件DWORD myId;int proto = IPPROTO_TCP; //目标协议WSAPROTOCOL_INFOW save = { 0 };    //用于存储指定协议的正常的提供者,最后用来作为分层协议和协议链的模板for (int i = 0; i < num; i++){//找符合条件的提供者,但不能是分层协议if (info[i].iAddressFamily == AF_INET&&info[i].iProtocol == proto&&info[i].ProtocolChain.ChainLen!=0){memcpy(&save, &info[i], sizeof(WSAPROTOCOL_INFOW));    //将原来的基础协议信息保存                                                                save.dwServiceFlags1 &= ~XP1_IFS_HANDLES;        //去掉XP1_IFS_HANDLES标志trueId = info[i].dwCatalogEntryId;break;}}//安装分层协议WSAPROTOCOL_INFOW Lpi = { 0 }; //新的分层协议memcpy(&Lpi, &save, sizeof(WSAPROTOCOL_INFOW)); //以这个保存的系统已有协议作为模板lstrcpyW(Lpi.szProtocol, supplier);    //协议名,其实就是一个代号而已,可以随意起名Lpi.ProtocolChain.ChainLen = LAYERED_PROTOCOL;    //设置为分层协议Lpi.dwProviderFlags |= PFL_HIDDEN;        //?GUID pguid;                    //分层协议的guidUuidCreate(&pguid);memcpy(&layerGuid,&pguid,sizeof(GUID));if (WSCInstallProvider(&pguid, dllpath, &Lpi, 1, 0) == SOCKET_ERROR)        //安装该分层协议{free(info);return 0;}//重新枚举协议以获取分层协议的目录idfree(info);            //因为添加了一个分层协议,所以需要重新分配内存DWORD layerId;        //保存分层协议目录idWSCEnumProtocols(0, 0, &size, 0);info = (LPWSAPROTOCOL_INFOW)malloc(size);num = WSCEnumProtocols(0, info, &size, 0);if (num == SOCKET_ERROR){free(info);return 0;}for (int i = 0; i < num; i++)        //遍历协议,直到找到刚才新增的分层协议{if (memcmp(&info[i].ProviderId, &pguid, sizeof(GUID)) == 0){layerId = info[i].dwCatalogEntryId;        //获取分层协议目录id}}//安装协议链WCHAR chainName[WSAPROTOCOL_LEN + 1];            //其实就是一个名字代号,和分层协议的名字一样wsprintf(chainName, L"%ls over %ls", supplier, save.szProtocol);lstrcpyW(save.szProtocol, chainName);        //改名字1if (save.ProtocolChain.ChainLen == 1) //如果目标协议的正常提供者是基础协议则将其目录id放在协议链的第2个位置{save.ProtocolChain.ChainEntries[1] = trueId;        //将id写入到该协议链的ChainEntries数组中,这个数组只有当它是协议链时才有意义}else       //否则就是协议链提供者{for (int i = save.ProtocolChain.ChainLen; i > 0; i--)//如果是协议链则将该协议链中其他协议往后移,//以便将自己的分层协议插入到链首.但是这个数组最大存7个,所以如果原来就占满了,理论上会挤掉最后一个{save.ProtocolChain.ChainEntries[i] = save.ProtocolChain.ChainEntries[i - 1];}}save.ProtocolChain.ChainEntries[0] = layerId;save.ProtocolChain.ChainLen++;//获取guid,安装协议链GUID providerChainGuid;UuidCreate(&providerChainGuid);if (WSCInstallProvider(&providerChainGuid, dllpath, &save, 1, 0) == SOCKET_ERROR){free(info);return 0;}//重新枚举协议free(info);WSCEnumProtocols(0, 0, &size, 0);info = (LPWSAPROTOCOL_INFOW)malloc(size);num = WSCEnumProtocols(0, info, &size, 0);if (num == SOCKET_ERROR){free(info);return 0;}//遍历获取我们的协议链的目录idDWORD* chainId = (DWORD*)malloc(num * sizeof(DWORD)); //这个是协议链的目录id数组,把我们的协议链id//放在最前面,系统原来的按顺序放后面DWORD cindex = 0;for (int i = 0; i < num; i++){if ((info[i].ProtocolChain.ChainLen > 1) && (info[i].ProtocolChain.ChainEntries[0] == layerId)){chainId[cindex] = info[i].dwCatalogEntryId;cindex++;}}for (int i = 0; i < num; i++){if ((info[i].ProtocolChain.ChainLen <= 1) || (info[i].ProtocolChain.ChainEntries[0] != layerId)){chainId[cindex] = info[i].dwCatalogEntryId;cindex++;}}if (WSCWriteProviderOrder(chainId, cindex) != 0){free(info);free(chainId);return 0;}free(info);free(chainId);return 1;}DWORD uninstall()
{if(findGuid()==0){return 0;}//枚举winsock目录中的协议LPWSAPROTOCOL_INFOW info;//指向winsock目录中协议DWORD size = 0;            //大小DWORD num;                //数量DWORD Id;            DWORD result;int cc;  //作为错误码,下面2个函数的错误码地址必须提供,否则会调用失败WSCEnumProtocols(0, 0, &size, 0);info = (LPWSAPROTOCOL_INFOW)malloc(size);num = WSCEnumProtocols(0, info, &size, 0);if (num == SOCKET_ERROR){free(info);return 0;}int i = 0;for (i=0; i < num; i++){if (memcmp(&layerGuid,&info[i].ProviderId,sizeof(GUID))==0){Id = info[i].dwCatalogEntryId;}}if (i<=num){for (i = 0; i < num; i++){if ((info[i].ProtocolChain.ChainLen>1)&&(info[i].ProtocolChain.ChainEntries[0]==Id)){if((result=WSCDeinstallProvider(&info[i].ProviderId, &cc))==SOCKET_ERROR){free(info);return 0;}break;}}free(info); if((result=WSCDeinstallProvider(&layerGuid,  &cc))==SOCKET_ERROR){return 0;}}else{free(info);return 0;    }return 1;
}
int main(int argc, char** argv)
{setlocale(LC_ALL, "chs");int result;if (argc!=2){printf("usage:%s install or uninstall\n", argv[0]);return 0;}if (strcmp(argv[1],"install")==0){if (lspInject()){printf("install success\n");}else{printf("install error code is %d\n", GetLastError());}}else if(strcmp(argv[1], "uninstall") == 0){if (uninstall()){printf("uninstall success\n");}else{printf("uninstall error code is %d\n", GetLastError());}}return 1;}

以上代码未经测试,时间有限用到的时候再改吧。

[病毒木马] LSP劫持相关推荐

  1. 病毒木马防御与分析实战

    <病毒木马防御与分析>系列以真实的病毒木马(或恶意程序)为研究对象,通过现有的技术手段对其分析,总结出它的恶意行为,进而制定出相应的应对方法,对其彻底查杀.当然,因为我个人水平的有限,查杀 ...

  2. 病毒木马查杀实战第009篇:QQ盗号木马之手动查杀

    前言 之前在<病毒木马查杀第002篇:熊猫烧香之手动查杀>中,我在不借助任何工具的情况下,基本实现了对于"熊猫烧香"病毒的查杀.但是毕竟"熊猫烧香" ...

  3. 病毒木马查杀实战第026篇:“白加黑”恶意程序研究(上)

    前言 众所周知,传统的恶意程序都是由单一文件构成的.从而实现某一种或者几种恶意功能. 而这类的恶意程序为了避免被发现以及被查杀,往往会採用五花八门的自我隐藏技术以及免杀技术,病毒程序的作者非常多时候也 ...

  4. 病毒木马查杀实战第010篇:QQ盗号木马之十六进制代码分析

    前言 按照我的个人习惯,在运用诸如IDA Pro与OllyDBG对病毒进行逆向分析之前,我都会利用一些自动化的工具,通过静态或动态的分析方法(参见<病毒木马查杀第008篇:熊猫烧香之病毒查杀总结 ...

  5. Atitit.病毒木马程序的感染 传播扩散 原理

    Atitit.病毒木马程序的感染 传播扩散 原理 1. 从木马的发展史考虑,木马可以分为四代 1 2. 木马有两大类,远程控制  vs  自我复制传播1 3. 自我复制2 3.1. 需要知道当前cpu ...

  6. 可疑文件_鉴定文件是不是病毒木马的可靠方法 | 免费快速精准

    我们经常将计算机病毒和木马放在一起说,这两者是我们无论使用电脑还是手机都最不想碰到的东西. 它们其实是有区别的,病毒得名于它会像自然界的病毒一样破坏系统的功能,而木马的称呼则来源于公元前十二世纪希腊和 ...

  7. 病毒木马入侵招数专题

    病毒木马入侵招数专题 作者:admin 日期:2005-05-10 11:21:21 字体大小: 小 中 大 网络时代可不太平,谁没有遭遇过病毒或木马?从CIH.I Love You到红色代码.Nim ...

  8. 病毒木马入侵招数大曝光

    网络时代可不太平,谁没有遭遇过病毒或木马?从CIH.I Love You到红色代码.Nimda,从BO到冰河,无一不是网友经常懈逅的对象.怎么避免这些"艳遇"是广大用户孜孜以求的目 ...

  9. 记录某大门户网站自动跳转不良网站,团队通宵排查病毒木马全过程

    某周五晚上,自己正舒服地躺在沙发上,手里的王者荣耀,米莱迪正在快乐地偷塔呢,突然一个电话打进来,一看是老板,心里一阵不祥的预感:看来这场游戏是不能善终了,兄弟们对不住了. 果不其然,事情是某机构运行了 ...

  10. 病毒木马查杀实战第013篇:一个基于.NET的“敲竹杠”病毒研究

    前言 恶意程序发展至今,其功能已经从最初的单纯破坏,不断发展为隐私的窥探,信息的盗取,乃至如今非常流行的"敲竹杠"病毒,用于勒索.可见随着时代的发展,病毒的作者们往往也是想利用自己 ...

最新文章

  1. 2021年大数据Spark(十二):Spark Core的RDD详解
  2. Dubbo基础专题——第四章(Dubbo整合Nacos分析细节点)
  3. 华为魔术手机拆机图解_华为P40 Pro上手体验
  4. Dynamips和Vmware完成CCVP试验(4)
  5. DataTable实现分组
  6. 使用双栈实现一个队列
  7. tensorflow版本及其对应环境
  8. 上海——真的可以埋葬一切
  9. Go语言学习Day06
  10. TCP/IP的全部IP协议号
  11. 【大数据部落】R语言RFM模型在电商行业的应用
  12. 组建局域网_组网方案图文教程,双路由器有线搭建网络,公司家庭组建局域网...
  13. 计算机装系统找不到硬盘分区,如果U盘安装系统找不到硬盘分区,该怎么办?...
  14. 内网创建https网站的SSL证书、代码签名证书
  15. 视网膜屏幕和高清视网膜屏幕
  16. datax(二)datax on azkaban架构设计之datax as a service
  17. Unity批量刷草工具及优化
  18. DISTRIB TRAN xxxxx.xxxx.xx.x.xx
  19. 《数学巴士》使用指南
  20. 希腊罗马神话传说和《圣经》中的英语成语典故

热门文章

  1. 全国计算机一级模拟软件安装教程
  2. 最好用的切图工具——firework
  3. 杰·亚伯拉罕的产品营销35种策略完整版
  4. 360se html怎么删除,360安全浏览器服务组件能删除吗 如何删除
  5. 《互联网大厂晋升指南》读书笔记-上
  6. 架构之美:教你如何分析一个接口?
  7. VMware16的下载安装及搭建Linux环境
  8. 数据可视化网页内容自动抓取工具
  9. C# 和欧姆龙 Omron PLC 以太网通信
  10. 绝对值编码器 c语言,绝对式光电编码器