需求:允許特定時段(如週六 08:00-20:00)上網,其餘時間封鎖。用戶態實現主要有三種路徑:Hook 瀏覽器網址列(不準)、Winsock SPI 封包過濾(推薦)、WSAIoctl 原始 Socket 抓包(易漏本機發送封包)。本文聚焦 SPI 方案,並解決代理上網繞過直連過濾的問題。
封包目的 IP 為公網位址。在 WSPSendTo 中攔截,判斷目標 IP 是否屬於區域網段(如 192.168.x.x),非區網即丟棄:
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) {
sockaddr_in sin = *(sockaddr_in*)lpTo;
if (sin.sin_addr.S_un.S_un_b.s_b1 != 192) { // 非 192.168.x.x
OutputDebugString(_T("WSPSendTo Filtered"));
return 0; // 丟棄封包
}
return NextProcTable.lpWSPSendTo(s, lpBuffers, dwBufferCount, ...); // 放行
}
所有對外封包目的 IP 均為代理伺服器區網 IP,直連過濾失效。解法:
InternetQueryOption(NULL, INTERNET_OPTION_PROXY, ...) 取得 IE 設定的代理 IP 與埠號
INTERNET_PROXY_INFO* pInfo = NULL;
DWORD dwSize = 0;
InternetQueryOption(NULL, INTERNET_OPTION_PROXY, pInfo, &dwSize); // 取得緩衝區大小
pInfo = (INTERNET_PROXY_INFO*)new char[dwSize];
InternetQueryOption(NULL, INTERNET_OPTION_PROXY, pInfo, &dwSize); // 實際取值
// pInfo->lpszProxy 格式如 "http=192.168.1.100:8080;ftp=..."
// 解析出 IP 與 Port 存入全域變數 g_ProxyIP, g_ProxyPort
過濾判斷:
if (sin.sin_addr.S_un.S_addr == g_ProxyIP && ntohs(sin.sin_port) == g_ProxyPort)
return 0; // 丟棄經代理的對外請求
WSPStartup、WSPCleanup、WSPSocket、WSPSendTo、WSPRecvFrom 等必要入口WSCInstallProvider 將自訂 SPI 註冊至 Winsock 目錄,並設定鏈路順序置於基礎 TCP/IP 提供者之前Layered.zip 範例結合直連 IP 判斷與 IE Proxy 偵測雙重過濾,可有效限制 PC 對外網路存取,同時保留區域網通訊。完整原始碼可作為網路存取控制模組的參考實現。