原理

应用程序通过 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. 【转】C 从函数返回数组
  2. View的三大流程之View的测量
  3. 作者:孙忠富(1957-),男,中国农业科学院农业环境与可持续发展研究所研究员。...
  4. Lisp的本质(The Nature of Lisp)
  5. 阿里云河源数据中心正式开服 疫情期间曾2小时扩容1万台云服务器
  6. Mysql添加用户错误:ERROR 1364 (HY000): Field ‘ssl_cipher‘ doesn‘t have a default value解决方法
  7. java 分布式同步锁_java编程进阶之路:回归锁的本质,探索分布式锁之源头
  8. python源代码制作星空_用python画星空源代码是什么
  9. 蓝桥杯2021年第十二届省赛-双向排序
  10. Windows 7 Build 7068 下载泄露
  11. HDU 4514 湫湫系列故事——设计风景线 (并查集)
  12. 笔记本没有计算机和网络怎么回事,华为笔记本连wifi无internet怎么办?华为笔记本电脑怎么连不上网?...
  13. Java 判断当前日期是否 是这个月的最后七天且是否为工作日(星期一到星期五)
  14. 亚马逊云科技与格雷西司,助力卡洛哈应对全球运营挑战
  15. R语言中如何进行PCA分析?利用ggplot和prcomp绘制基因表达量分析图
  16. 足球俱乐部介绍——拜仁慕尼黑
  17. 编写程序,输入圆的半径,并选择计算面积或周长
  18. 【起航计划 001】2015 起航计划 踏在起跑线上
  19. 【Multisim仿真】CD4017循环计数器走马灯
  20. virgo server使用指南

热门文章

  1. python办公自动化(入门)
  2. 【3Dtiles】3Dmax模型处理为gltf和3dtiles,包含LOD效果
  3. linux mencoder 安装,Mencoder与X264安装
  4. jquery设为首页,加入收藏代码
  5. 左右手桌面股票盯盘软件(DesktopStockTracking)
  6. 股票软件开发搭建:准备与过程
  7. 2013 前瞻 + 技术牛
  8. VS2012安装步骤
  9. AJAX网页抓取工具 Krabber 0.2.9正式发布
  10. 宅男福利!20行Python代码,一网打尽B站小姐姐的直播信号源!