如何使用lsp的hook技术解决TCP链接超时测试场景的模拟

问题:在测试网络层链接建立工具的时候,遇到这样的测试用例:需要模拟不同链接按预定的链接先后次序建立,如TCP链接先于UDP链接或者UDP链接先于TCP链接。查用了很多防火墙,都没有提供类似的功能。后来我们使用了Winsock LSP 开发了小工具解决了这个问题!

关于WinSock LSP网上有详细的例子和讲解如下:

LSP :分层服务提供者,顾名思义是某种服务的提供者且具有分层的特性,它是 WinSock2 的特性吧,与以往的 WinSock1.0 不同,WinSock1.0 仅围绕着TCP/IP协议运行的,但是在Winsock2中却增加了对更多传输协议的支持。“Winsock2不仅提供了一个供应用程序访问网络服务的Windowssocket应用程序编程接口(API),还包含了由传输服务提供者和名字解析服务提供者实现的Winsock服务提供者接口(SPI)和ws2_32.dll。”。就现在 NT 以后的网络而言(不知道Vista及后的了),应用层EXE创建套接字时,会先在 WinSock 目录中寻找全适的协议(如 TCP、UDP ......),找到之后再调用此协议的 提供者 来完成各种功能,其中 提供者 又是通过 SPI函数 来完成任务的。所以,对于这种类型的网络调用而言, LSP 是主题而其中的 SPI 则是灵魂,没有 SPI 的话 LSP 就只是一个框架而已,LSP大概的层次如图 :

---------------------------------
│ Windows socket 2 EXE│
---------------------------------Windows socket 2 API
│ WS2_32.DLL            │
---------------------------------Windows socket 2 传输SPI
│ LSP                       DLL │
-----------------------------------
│ 基础服务提供程序       │
----------------------------------

        现在可以知道几乎所有的 WinSock API 调用都会先告诉 WS2_32.DLL ,再由 WS2_32.DLL 传达给 LSP (类型转换的那些不是,不知道还有没有......-_-!),最后到 基础服务提供程序 全权代理实现。所以,大多数的 WinSock API 都是可以 HOOK 的,前提是要安装了 LSP ,由 SPI 进行 HOOK。

        上图中已示意了 LSP 其实是 DLL 的形式,它是不可以直接执行的,得由 EXE 程序来安装,所以在想采用 LSP 来过滤封包,还得先编译个可执行程序来安装这,至于这个安装 LSP 的程序很多时候都是一个固定的框架:枚举 LSP、安装 LSP、卸载 LSP ,在实现的时候,我用了一个头文件来实现这个框架(本想用类来封装的~~~):

LSPIn-Unstall.h

#define UNICODE
#define _UNICODE

#include <Ws2spi.h>
// 定义了WSCWriteProviderOrder函数
#include <Sporder.h> 
#include <windows.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")
// 实现了UuidCreate函数
#pragma comment(lib, "Rpcrt4.lib")

// 要安装的LSP的硬编码,在移除的时候还要使用它
GUID ProviderGuid = {0x8a, 0x88b, 0x888c,
        {0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a}};

//获得所有已经安装的传输服务提供者
LPWSAPROTOCOL_INFOW GetProvider(LPINT lpnTotalProtocols)
{
DWORD dwSize = 0;
int nError;
LPWSAPROTOCOL_INFOW pProtoInfo = NULL;

// 取得需要的长度
if(::WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError) == SOCKET_ERROR)
{
   if(nError != WSAENOBUFS)
    return NULL;
}

pProtoInfo = (LPWSAPROTOCOL_INFOW)::GlobalAlloc(GPTR, dwSize);
*lpnTotalProtocols = ::WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError);
return pProtoInfo;
}
//释放存储空间
void FreeProvider(LPWSAPROTOCOL_INFOW pProtoInfo)
{
::GlobalFree(pProtoInfo);
}
//安装分层协议,协议链及排序
//这里将指定LSP提供者安装到TCP UDP 和原始套节字之上
BOOL InstallProvider(WCHAR *pwszPathName)
{
//LSP的名字
WCHAR wszLSPName[] = L"MyFirstLSP";
LPWSAPROTOCOL_INFOW pProtoInfo;
int nProtocols;
WSAPROTOCOL_INFOW OriginalProtocolInfo[3];
DWORD    dwOrigCatalogId[3];
int nArrayCount = 0;

// 我们分层协议的目录ID号
DWORD dwLayeredCatalogId; 

int nError;

// 找到我们的下层协议,将信息放入数组中
// 枚举所有服务程序提供者
pProtoInfo = GetProvider(&nProtocols);
BOOL bFindUdp = FALSE;
BOOL bFindTcp = FALSE;
BOOL bFindRaw = FALSE;
for(int i=0; i<nProtocols; i++)
{
   if(pProtoInfo[i].iAddressFamily == AF_INET)
   {
   if(!bFindUdp && pProtoInfo[i].iProtocol == IPPROTO_UDP)
    {
     memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW));
     OriginalProtocolInfo[nArrayCount].dwServiceFlags1 =
      OriginalProtocolInfo[nArrayCount].dwServiceFlags1 & (~XP1_IFS_HANDLES);
   
     dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId;

     bFindUdp = TRUE;
    }

   if(!bFindTcp && pProtoInfo[i].iProtocol == IPPROTO_TCP)
    {
     memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW));
     OriginalProtocolInfo[nArrayCount].dwServiceFlags1 =
      OriginalProtocolInfo[nArrayCount].dwServiceFlags1 & (~XP1_IFS_HANDLES);
   
     dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId;

     bFindTcp = TRUE;
    }
   if(!bFindRaw && pProtoInfo[i].iProtocol == IPPROTO_IP)
    {
     memcpy(&OriginalProtocolInfo[nArrayCount], &pProtoInfo[i], sizeof(WSAPROTOCOL_INFOW));
     OriginalProtocolInfo[nArrayCount].dwServiceFlags1 =
      OriginalProtocolInfo[nArrayCount].dwServiceFlags1 & (~XP1_IFS_HANDLES);
   
     dwOrigCatalogId[nArrayCount++] = pProtoInfo[i].dwCatalogEntryId;

     bFindRaw = TRUE;
    }
   }
}

// 安装我们的分层协议,获取一个dwLayeredCatalogId
// 随便找一个下层协议的结构复制过来即可
WSAPROTOCOL_INFOW LayeredProtocolInfo;
memcpy(&LayeredProtocolInfo, &OriginalProtocolInfo[0], sizeof(WSAPROTOCOL_INFOW));
// 修改协议名称,类型,设置PFL_HIDDEN标志
wcscpy(LayeredProtocolInfo.szProtocol, wszLSPName);
LayeredProtocolInfo.ProtocolChain.ChainLen = LAYERED_PROTOCOL; // 0;
LayeredProtocolInfo.dwProviderFlags |= PFL_HIDDEN;
// 安装
if(::WSCInstallProvider(&ProviderGuid,
      pwszPathName, &LayeredProtocolInfo, 1, &nError) == SOCKET_ERROR)
{
   return FALSE;
}
// 重新枚举协议,获取分层协议的目录ID号
FreeProvider(pProtoInfo);
pProtoInfo = GetProvider(&nProtocols);
for(i=0; i<nProtocols; i++)
{
   if(memcmp(&pProtoInfo[i].ProviderId, &ProviderGuid, sizeof(ProviderGuid)) == 0)
   {
    dwLayeredCatalogId = pProtoInfo[i].dwCatalogEntryId;
    break;
   }
}

// 安装协议链
// 修改协议名称,类型
WCHAR wszChainName[WSAPROTOCOL_LEN + 1];
for(i=0; i<nArrayCount; i++)
{
   swprintf(wszChainName, L"%ws over %ws", wszLSPName, OriginalProtocolInfo[i].szProtocol);
   wcscpy(OriginalProtocolInfo[i].szProtocol, wszChainName);
   if(OriginalProtocolInfo[i].ProtocolChain.ChainLen == 1)
   {
    OriginalProtocolInfo[i].ProtocolChain.ChainEntries[1] = dwOrigCatalogId[i];
   }
   else
   {
    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;
}
// 获取一个Guid,安装之
GUID ProviderChainGuid;
if(::UuidCreate(&ProviderChainGuid) == RPC_S_OK)
{
   if(::WSCInstallProvider(&ProviderChainGuid,
      pwszPathName, OriginalProtocolInfo, nArrayCount, &nError) == SOCKET_ERROR)
   {
    return FALSE;
   }
}
else
   return FALSE;

// 重新排序Winsock目录,将我们的协议链提前
// 重新枚举安装的协议
FreeProvider(pProtoInfo);
pProtoInfo = GetProvider(&nProtocols);

DWORD dwIds[20];
int nIndex = 0;
// 添加我们的协议链
for(i=0; i<nProtocols; i++)
{
   if((pProtoInfo[i].ProtocolChain.ChainLen > 1) &&
      (pProtoInfo[i].ProtocolChain.ChainEntries[0] == dwLayeredCatalogId))
    dwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId;
}
// 添加其它协议
for(i=0; i<nProtocols; i++)
{
   if((pProtoInfo[i].ProtocolChain.ChainLen <= 1) ||
     (pProtoInfo[i].ProtocolChain.ChainEntries[0] != dwLayeredCatalogId))
    dwIds[nIndex++] = pProtoInfo[i].dwCatalogEntryId;
}
// 重新排序Winsock目录
if((nError = ::WSCWriteProviderOrder(dwIds, nIndex)) != ERROR_SUCCESS)
{
   return FALSE;
}
FreeProvider(pProtoInfo);
return TRUE;
}
//卸载分层协议和协议链
BOOL RemoveProvider()
{
LPWSAPROTOCOL_INFOW pProtoInfo;
int nProtocols;
DWORD dwLayeredCatalogId;

// 根据Guid取得分层协议的目录ID号
pProtoInfo = GetProvider(&nProtocols);
int nError;
for(int i=0; i<nProtocols; i++)
{
   if(memcmp(&ProviderGuid, &pProtoInfo[i].ProviderId, sizeof(ProviderGuid)) == 0)
   {
    dwLayeredCatalogId = pProtoInfo[i].dwCatalogEntryId;
    break;
   }
}
if(i < nProtocols)
{
   // 移除协议链
   for(i=0; i<nProtocols; i++)
   {
    if((pProtoInfo[i].ProtocolChain.ChainLen > 1) &&
      (pProtoInfo[i].ProtocolChain.ChainEntries[0] == dwLayeredCatalogId))
    {
     ::WSCDeinstallProvider(&pProtoInfo[i].ProviderId, &nError);
    }
   }
   // 移除分层协议
   ::WSCDeinstallProvider(&ProviderGuid, &nError);
}
return TRUE;
}

        接着就是主函数的调用了:

LSPControl.cpp

#include <iostream.h>
#include "LSPIn-Unstall.h"

//安装LSP
void install();
//卸载LSP
void remove();
//查看已安装了的LSP
void view();

void main()
{
//命令参数
char order[128];

while(1)
{
   cin.getline(order,128);
   if(strcmp(order,"exit")==0)
    break;
   else if(strcmp(order,"install")==0)
    install();
   else if(strcmp(order,"remove")==0)
    remove();
   else if(strcmp(order,"view")==0)
    view();
}
}
//安装LSP
void install()
{
TCHAR szPathName[256];
TCHAR* p;

//LSP所在的路径
if(::GetFullPathName(L"MyFirstLSP.dll", 256, szPathName, &p) != 0)
{
   if(InstallProvider(szPathName))
   {
    printf(" ^-^ 安装成功!/n");
    return;
   }
}
printf(" o_o! 安装失败!/n");
}
//卸载LSP
void remove()
{
if(RemoveProvider())
   printf(" ^-^ 卸载成功!/n");
else
   printf(" o_o! 卸载失败!/n");
}
//查看已安装了的LSP
void view()
{
LPWSAPROTOCOL_INFOW pProtoInfo;
int nProtocols;
pProtoInfo = GetProvider(&nProtocols);

printf("/n   ---------------------------   已安装的LSP   ---------------------------   /n/n");
for(int i=0; i<nProtocols; i++)
{
   printf(" Protocol: %ws /n", pProtoInfo[i].szProtocol);
   printf(" CatalogEntryId: %d   ChainLen: %d /n/n", pProtoInfo[i].dwCatalogEntryId, pProtoInfo[i].ProtocolChain.ChainLen);
}
printf("/n   ---------------------------   已安装的LSP   ---------------------------   /n/n");
}

        看起来是很繁琐,实际上也就是一些框架,已经实现了查看(view)、安装(install)、卸载(remove)。

        有了可以操控 LSP 程序,接下来就是生成 LSP 了,LSP 以DLL的形式来实现的,这里用的是 VC++6.0 的动态链接工程,在建立好该工程后,还要在工程里头导入一个 .def 格式的文件,其中有导出 LSP WSPStarUP 函数的 EXPORTS 语句,之后就是编写代码了。LSP 只是提供了一个接口,有些代码跟上面的一样看似繁,实则是一个框架,了解就OK了(汗,说到繁,NDIS 更让人头晕```),下面的代码中,最后的 WSPSendTo()、WSPBind() 函数的定义就是 SPI 了,累了那么久,就是用到这些 SPI 来进行包过滤(甚至是实现防火墙等):

Debug.h

#ifndef __DEBUG_H__
#define __DEBUG_H__
#ifdef _DEBUG

#define ODS(szOut)      /
{          /
   OutputDebugString(szOut);   /
}

#define ODS1(szOut, var)    /
{          /
   TCHAR sz[1024];        /
   _stprintf(sz, szOut, var);   /
   OutputDebugString(sz);    /
}

#else

#define ODS(szOut)
#define ODS1(szOut, var)

#endif

#endif

LSP.cpp

// --- 声明要使用UNICODE字符串---(到了驱动的话,大都是用 UNICODE 的)
#define UNICODE
#define _UNICODE

#include <Winsock2.h>
#include <Ws2spi.h>
#include <Windows.h>
#include <tchar.h>
#include "Debug.h"
#include <fstream.h>

#pragma comment(lib, "Ws2_32.lib")

WSPUPCALLTABLE g_pUpCallTable;   // 上层函数列表。如果LSP创建了自己的伪句柄,才使用这个函数列表
WSPPROC_TABLE g_NextProcTable;   // 下层函数列表
TCHAR g_szCurrentApp[MAX_PATH]; // 当前调用本DLL的程序的名称

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD ul_reason_for_call,
                       LPVOID lpReserved
      )
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
   {
    // 取得主模块的名称
    ::GetModuleFileName(NULL, g_szCurrentApp, MAX_PATH);
   }
   break;
}
return TRUE;
}
//得到当前所有已安装的LSP
LPWSAPROTOCOL_INFOW GetProvider(LPINT lpnTotalProtocols)
{
DWORD dwSize = 0;
int nError;
LPWSAPROTOCOL_INFOW pProtoInfo = NULL;

// 取得需要的长度
if(::WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError) == SOCKET_ERROR)
{
   if(nError != WSAENOBUFS)
    return NULL;
}
pProtoInfo = (LPWSAPROTOCOL_INFOW)::GlobalAlloc(GPTR, dwSize);
*lpnTotalProtocols = ::WSCEnumProtocols(NULL, pProtoInfo, &dwSize, &nError);
return pProtoInfo;
}
//释放空间
void FreeProvider(LPWSAPROTOCOL_INFOW pProtoInfo)
{
::GlobalFree(pProtoInfo);
}


int WSPAPI 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
)
{
ODS1(L" query send to... %s", g_szCurrentApp);

SOCKADDR_IN sa = *(SOCKADDR_IN*)lpTo;
if(sa.sin_port == htons(要过滤的端口<为 int 类型>) )   // 若符合则过滤掉了

{
   int iError;
   g_NextProcTable.lpWSPShutdown(s, SD_BOTH, &iError);
   *lpErrno = WSAECONNABORTED;

   ODS(L" deny a sendto ");
   return SOCKET_ERROR;
}


return g_NextProcTable.lpWSPSendTo(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpTo
    , iTolen, lpOverlapped, lpCompletionRoutine, lpThreadId, lpErrno);
}

int WSPAPI WSPBind(SOCKET s, const struct sockaddr* name, int namelen, LPINT lpErrno)
{
//这里再进行处理(一般情况之下,Bind 都是作为服务程序用的函数:绑定端口后再进而监听)
return g_NextProcTable.lpWSPBind(s, name, namelen, lpErrno);
}

//这里为入口函数(必备)
int WSPAPI WSPStartup(
WORD wVersionRequested,
LPWSPDATA lpWSPData,
LPWSAPROTOCOL_INFO lpProtocolInfo,
WSPUPCALLTABLE UpcallTable,
LPWSPPROC_TABLE lpProcTable
)
{
ODS1(L" WSPStartup... %s /n", g_szCurrentApp);

if(lpProtocolInfo->ProtocolChain.ChainLen <= 1)
{
   return WSAEPROVIDERFAILEDINIT;
}

// 保存向上调用的函数表指针(在这里没有使用它)
g_pUpCallTable = UpcallTable;

// 枚举协议,找到下层协议的WSAPROTOCOL_INFOW结构
WSAPROTOCOL_INFOW NextProtocolInfo;
int nTotalProtos;
LPWSAPROTOCOL_INFOW pProtoInfo = GetProvider(&nTotalProtos);
// 下层入口ID
DWORD dwBaseEntryId = lpProtocolInfo->ProtocolChain.ChainEntries[1];
for(int i=0; i<nTotalProtos; i++)
{
   if(pProtoInfo[i].dwCatalogEntryId == dwBaseEntryId)
   {
    memcpy(&NextProtocolInfo, &pProtoInfo[i], sizeof(NextProtocolInfo));
    break;
   }
}
if(i >= nTotalProtos)
{
   ODS(L" WSPStartup: Can not find underlying protocol /n");
   return WSAEPROVIDERFAILEDINIT;
}

// 加载下层协议的DLL
int nError;
TCHAR szBaseProviderDll[MAX_PATH];
int nLen = MAX_PATH;
// 取得下层提供程序DLL路径
if(::WSCGetProviderPath(&NextProtocolInfo.ProviderId, szBaseProviderDll, &nLen, &nError) == SOCKET_ERROR)
{
   ODS1(L" WSPStartup: WSCGetProviderPath() failed %d /n", nError);
   return WSAEPROVIDERFAILEDINIT;
}
if(!::ExpandEnvironmentStrings(szBaseProviderDll, szBaseProviderDll, MAX_PATH))
{
   ODS1(L" WSPStartup: ExpandEnvironmentStrings() failed %d /n", ::GetLastError());
   return WSAEPROVIDERFAILEDINIT;
}
// 加载下层提供程序
HMODULE hModule = ::LoadLibrary(szBaseProviderDll);
if(hModule == NULL)
{
   ODS1(L" WSPStartup: LoadLibrary() failed %d /n", ::GetLastError());
   return WSAEPROVIDERFAILEDINIT;
}

// 导入下层提供程序的WSPStartup函数
LPWSPSTARTUP pfnWSPStartup = NULL;
pfnWSPStartup = (LPWSPSTARTUP)::GetProcAddress(hModule, "WSPStartup");
if(pfnWSPStartup == NULL)
{
   ODS1(L" WSPStartup: GetProcAddress() failed %d /n", ::GetLastError());
   return WSAEPROVIDERFAILEDINIT;
}

// 调用下层提供程序的WSPStartup函数
LPWSAPROTOCOL_INFOW pInfo = lpProtocolInfo;
if(NextProtocolInfo.ProtocolChain.ChainLen == BASE_PROTOCOL)
   pInfo = &NextProtocolInfo;

int nRet = pfnWSPStartup(wVersionRequested, lpWSPData, pInfo, UpcallTable, lpProcTable);
if(nRet != ERROR_SUCCESS)
{
   ODS1(L" WSPStartup: underlying provider's WSPStartup() failed %d /n", nRet);
   return nRet;
}
// 保存下层提供者的函数表
g_NextProcTable = *lpProcTable;
// 修改传递给上层的函数表,Hook感兴趣的函数
// 好像可以 Hook 30个函数,书里头写着列表有 30 个ISP函数
lpProcTable->lpWSPSendTo = WSPSendTo;
lpProcTable->lpWSPBind = WSPBind;

FreeProvider(pProtoInfo);
return nRet;
}

         一个防火墙的模型就出来了,不过在用户模式下它虽然不错,但是并非能够过滤所有的封包,除非编写内核模式的 NDIS 驱动(强捍的NDIS)~~~

        编译成 DLL 后,将上面生成的操作 LSP 的程序与 生成的 DLL 放在同一目录之下,DLL 的由字要是 MyFirstLSP (因为上面:GetFullPathName(L"MyFirstLSP.dll", 256, szPathName, &p) 中的DLL文件就是它,
        以前调试的时候忘记看了,试了N次都没找出失误的原因 @_@ )
       

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值