C/C++病毒木马LSP劫持应用

应用程序通过 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];//获取真正的协议目录id
int 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;
}

// 加载真正dll
HMODULE 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; //存储被安装的提供者的目录id
if (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; //分层协议的guid
UuidCreate(&pguid);
memcpy(&layerGuid,&pguid,sizeof(GUID));
if (WSCInstallProvider(&pguid, dllpath, &Lpi, 1, 0) == SOCKET_ERROR) //安装该分层协议
{
free(info);
return 0;
}

//重新枚举协议以获取分层协议的目录id
free(info); //因为添加了一个分层协议,所以需要重新分配内存
DWORD layerId; //保存分层协议目录id
WSCEnumProtocols(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); //改名字1
if (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;
}
//遍历获取我们的协议链的目录id
DWORD* 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;

}

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