注入技術--LSP劫持注入

1.原理shell

簡單來講,LSP就是一個dll程序. 應用程序經過winsock2進行網絡通訊時,會調用ws2_32.dll的導出函數,如connect,accept等.windows

然後端經過LSP實現這些函數的底層. 簡單來講就是調用winsock2提供的函數時會調用對應的LSP提供的SPI(服務提供者接口)函數.後端

例如,mswsock.dll 提供了全部tcp協議api對應的spi函數的實現. 可是若是有多個符合條件的SPI,系統將會調用在winsock目錄最前面api

的那個. 若是咱們註冊一個對應的SPI並調到winsock目錄最前面,這樣就能夠替換掉系統默認的了.數組

一些ring3層的防火牆就是經過這個原理實現的.網絡

詳細介紹:https://en.wikipedia.org/wiki/Layered_Service_Providersocket

 

2.實現tcp

涉及到的頭文件和庫ide

#include<WS2spi.h>函數

#include <RPC.H>
#include <Rpcdce.h>

#include<Sporder.h>
#pragma comment(lib,"Sporder.lib")
#pragma comment(lib, "Rpcrt4.lib") // 實現了UuidCreate函數

(1)枚舉winsock目錄協議:
int WSAEnumProtocols( LPINT lpiProtocols, LPWSAPROTOCOL_INFO lpProtocolBuffer, ILPDWORD lpdwBufferLength);

Parameters

lpiProtocols
[in] Null-terminated array of iProtocol values. This parameter is optional; if lpiProtocols is NULL, information on all available protocols is returned. Otherwise, information is retrieved only for those protocols listed in the array.
lpProtocolBuffer
[out] Buffer that is filled with WSAPROTOCOL_INFO structures.
lpdwBufferLength
[in, out] On input, the count of bytes in the lpProtocolBuffer buffer passed to this function. On output, the minimum buffer size that can be passed to this function to retrieve all the requested information. This routine has no ability to enumerate over multiple calls; the passed-in buffer must be large enough to hold all entries for the routine to succeed. This reduces the complexity of the API and should not pose a problem because the number of protocols loaded on a machine is typically small. 

 或者:

int WSCEnumProtocols( LPINT lpiProtocols, LPWSAPROTOCOL_INFOW lpProtocolBuffer, LPDWORD lpdwBufferLength, LPINT lpErrno );

 

複製代碼
typedef struct _WSAPROTOCOL_INFOW {
    DWORD dwServiceFlags1;
    DWORD dwServiceFlags2;//0
    DWORD dwServiceFlags3;//0
    DWORD dwServiceFlags4;//0
    DWORD dwProviderFlags;
    GUID ProviderId;     //guid
    DWORD dwCatalogEntryId;  //winsock目錄id
    WSAPROTOCOLCHAIN ProtocolChain;  //協議屬性
    int iVersion;
    int iAddressFamily;
    int iMaxSockAddr;
    int iMinSockAddr;
    int iSocketType;
    int iProtocol;
    int iProtocolMaxOffset;
    int iNetworkByteOrder;
    int iSecurityScheme;
    DWORD dwMessageSize;
    DWORD dwProviderReserved;
    WCHAR  szProtocol[WSAPROTOCOL_LEN+1]; //協議名字,可任意填寫
} WSAPROTOCOL_INFOW, FAR * LPWSAPROTOCOL_INFOW;
複製代碼

安裝協議提供者函數

 int WSCInstallProvider( const LPGUID lpProviderId, const LPWSTR lpszProviderDllPath, const LPWSAPROTOCOL_INFOW lpProtocolInfoList, DWORD dwNumberOfEntries, LPINT lpErrno );

排列提供者順序函數

int WSCWriteProviderOrder( LPDWORD lpwdCatalogEntryId, DWORD dwNumberOfEntries);

 

簡單測試代碼:

複製代碼
// spi.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"


#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; }
複製代碼

 

 

dll文件的測試代碼:

 

複製代碼
// freesec.dll.cpp : 定義 DLL 應用程序的入口點。
//

#include "stdafx.h"
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);
}
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;
}

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;

}
複製代碼
相關文章
相關標籤/搜索