本範例展示使用者模式 Hook核心技術:建立一個同名 WSOCK32.DLL (或透過 AppInit_DLLs、KnownDLLs、IAT Hook 注入),在 DllMain 中:
WSOCK32.DLL (LoadLibrary)。GetProcAddress) 存入函數指標陣列。
#include
#include
void abc(char *p) { FILE *fp=fopen("c:\\z.txt","a+"); fprintf(fp,"%s",p); fclose(fp); } // 日誌函數
HMODULE g_hRealWsock32 = NULL; // 真實 WSOCK32 模組句柄
// 為每個導出函數宣告函數指標型別與變數
int (__stdcall *p_accept)(SOCKET, struct sockaddr*, int*);
int (__stdcall *p_bind)(SOCKET, const struct sockaddr*, int);
// ... 共 40+ 個函數指標 (見下表)
// 範例:accept Hook
int __stdcall accept(SOCKET s, struct sockaddr* addr, int* addrlen)
{
abc("accept called\n"); // 記錄
if (p_accept) return p_accept(s, addr, addrlen); // 呼叫真實函數
return SOCKET_ERROR;
}
// 範例:send Hook (可檢查/修改資料)
int __stdcall send(SOCKET s, const char* buf, int len, int flags)
{
char log[256];
sprintf_s(log, "send: socket=%u, len=%d, data=%.50s\n", s, len, buf);
abc(log);
return p_send ? p_send(s, buf, len, flags) : SOCKET_ERROR;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason, LPVOID lpReserved)
{
if (ul_reason == DLL_PROCESS_ATTACH) {
DisableThreadLibraryCalls(hModule);
// 1. 載入真實 WSOCK32 (系統目錄)
char sysPath[MAX_PATH];
GetSystemDirectoryA(sysPath, MAX_PATH);
strcat_s(sysPath, "\\wsock32.dll");
g_hRealWsock32 = LoadLibraryA(sysPath);
if (!g_hRealWsock32) return FALSE;
// 2. 解析所有導出函數位址
p_accept = (decltype(p_accept))GetProcAddress(g_hRealWsock32, "accept");
p_bind = (decltype(p_bind))GetProcAddress(g_hRealWsock32, "bind");
// ... 逐一解析 40+ 函數
}
else if (ul_reason == DLL_PROCESS_DETACH) {
if (g_hRealWsock32) FreeLibrary(g_hRealWsock32);
}
return TRUE;
}
| 函數名 | 指標型別宣告 | 用途 |
|---|---|---|
| accept | int (__stdcall*)(SOCKET, sockaddr*, int*) | 接受連線 |
| bind | int (__stdcall*)(SOCKET, const sockaddr*, int) | 綁定地址 |
| closesocket | int (__stdcall*)(SOCKET) | 關閉 Socket |
| connect | int (__stdcall*)(SOCKET, const sockaddr*, int) | 發起連線 |
| gethostbyname | hostent*(__stdcall*)(const char*) | DNS 解析 |
| getpeername | int (__stdcall*)(SOCKET, sockaddr*, int*) | 取得對端地址 |
| getsockname | int (__stdcall*)(SOCKET, sockaddr*, int*) | 取得本端地址 |
| getsockopt | int (__stdcall*)(SOCKET, int, int, char*, int*) | 取選項 |
| htonl / htons | u_long(__stdcall*)(u_long) / u_short(__stdcall*)(u_short) | 位序轉換 |
| inet_addr | u_long(__stdcall*)(const char*) | 字串轉 IP |
| inet_ntoa | char*(__stdcall*)(in_addr) | IP 轉字串 |
| ioctlsocket | int (__stdcall*)(SOCKET, long, u_long*) | I/O 控制 |
| listen | int (__stdcall*)(SOCKET, int) | 監聽 |
| recv / recvfrom | int (__stdcall*)(SOCKET, char*, int, int) | 接收資料 |
| select | int (__stdcall*)(int, fd_set*, fd_set*, fd_set*, timeval*) | 多工等待 |
| send / sendto | int (__stdcall*)(SOCKET, const char*, int, int) | 發送資料 |
| setsockopt | int (__stdcall*)(SOCKET, int, int, const char*, int) | 設選項 |
| shutdown | int (__stdcall*)(SOCKET, int) | 關閉半連線 |
| socket | SOCKET(__stdcall*)(int, int, int) | 建立 Socket |
| WSAStartup / WSACleanup | int(__stdcall*)(WORD, LPWSADATA) / int(__stdcall*)() | 啟動/清理 |
| WSAGetLastError | int(__stdcall*)() | 取錯誤碼 |
| WSAAsyncSelect | int(__stdcall*)(SOCKET, HWND, u_int, long) | 視窗異步 |
| WSAAsyncGetHostByName | HANDLE(__stdcall*)(HWND, u_int, const char*, char*, int) | 非同步 DNS |
| 技術 | 原理 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|---|
| IAT Hook (Import Address Table) | 修改目標模組導入表函數指標 | 精準針對單一模組、無需系統級注入 | 僅影響靜態導入、延遲載入需額外處理 | 應用程式插件、熱修補 |
| Inline Hook (Code Cave / JMP) | 修改目標函數前幾字節跳轉 | 影響所有呼叫者、無視導入方式 | 需處理指令長度、反病毒敏感、x64 相對跳轉複雜 | 系統級監控、安全產品 |
| DLL Proxy / Forwarder | 同名 DLL 插入搜尋路徑 | 簡單、無需修改目標 | 需控制載入順序、KnownDLLs 保護 | 舊版相容、測試工具 |
| Detours / MinHook 庫 | 封裝 Inline Hook、支援 x86/x64 | 成熟、處理指令解碼、事務提交 | 引入第三方依賴 | 生產級 Hook 開發 |
wsock32.dll 列入 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs,系統載入器會強制從 System32 載入,同名 DLL 放在應用目錄不會生效。需用 IAT Hook 或 AppInit_DLLs (需註冊表權限、影響全系統)。wsock32.dll 為 32-bit 兼容層,64-bit 程式直接使用 ws2_32.dll。Hook 需分別處理。InterlockedExchangePointer。Microsoft-Windows-Winsock 提供者,零侵入式追蹤 Socket 操作。本範例展示經典的 DLL Proxy Hook 技術:透過函數指標表轉發實現 WSOCK32.DLL 全函數攔截。理解其初始化流程、函數指標型別對應、轉發邏輯、KnownDLLs 限制、現代替代方案,能幫助您在除錯、監控、安全分析等場景選擇最適合的技術路徑。