注入技术--LSP劫持注入
1.原理
簡單來說,LSP就是一個dll程序. 應用程序通過winsock2進行網絡通信時,會調用ws2_32.dll的導出函數,如connect,accept等.
而后端通過LSP實現這些函數的底層. 簡單來說就是調用winsock2提供的函數時會調用對應的LSP提供的SPI(服務提供者接口)函數.
例如,mswsock.dll 提供了所有tcp協議api對應的spi函數的實現. 但是如果有多個符合條件的SPI,系統將會調用在winsock目錄最前面
的那個. 如果我們注冊一個對應的SPI并調到winsock目錄最前面,這樣就可以替換掉系統默認的了.
一些ring3層的防火墻就是通過這個原理實現的.
詳細介紹:https://en.wikipedia.org/wiki/Layered_Service_Provider
?
2.實現
涉及到的頭文件和庫
#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?或者:
int WSCEnumProtocols( LPINT lpiProtocols, LPWSAPROTOCOL_INFOW lpProtocolBuffer, LPDWORD lpdwBufferLength, LPINT lpErrno );
?
typedef struct _WSAPROTOCOL_INFOW {DWORD dwServiceFlags1;DWORD dwServiceFlags2;//0DWORD dwServiceFlags3;//0DWORD dwServiceFlags4;//0DWORD dwProviderFlags;GUID ProviderId; //guidDWORD dwCatalogEntryId; //winsock目錄idWSAPROTOCOLCHAIN 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; //存儲被安裝的提供者的目錄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;}
?
?
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];//獲取真正的協議目錄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;}轉載于:https://www.cnblogs.com/hjbf/p/10195244.html
總結
以上是生活随笔為你收集整理的注入技术--LSP劫持注入的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 详解Objective-C消息传递机制
- 下一篇: Silverlight 4之旅(三)数据