第34章:高階全域性API鉤取 : IE連線控制
Win 7 中 Process explorer 中 IE 瀏覽器所載入的 DLL 檔案:
如何檢視庫檔案的匯出函式:
下載 VS 後直接在搜尋欄輸入 VS (第一個和第三個都可以),開啟命令視窗,輸入 命令 "dumpbin /exports 庫檔案絕對路徑 " 就可以看到函式. 若後面再加上" > C:\Users\123456\Desktop\1.txt " ,
就可以將匯出函式儲存到檔案中.
wininet.dll 中有一個匯出函式 InternetConnect() ,可能與網路連線有關.
在Win 7 64位作業系統上,對 InternetConnectW() 下斷點.
修改字串後,禁止斷點後繼續執行,成功跳轉到修改後的網站.
目前的瀏覽器將每個選項卡都對應為一個程序,因此要鉤取整個 IE 需要採用全域性鉤取.
但是,同樣需要注意的是,此處注入 DLL 成功後, 已載入的程序不受影響,未來載入的程式才會受影響.
執行配套的程序,確實如書上所說,執行完 ZwCreateUserProcess() ,程式就被掛起( Suspended ) , EP 程式碼未執行:
而 NtResumeThread() 從名字可知就是恢復執行緒執行的.
在使用者模式下, Zw 和 Nt 系列的函式呼叫並無不同.在核心模式下,有不同:
Nt 系列 API 將直接呼叫對應的函式程式碼,而 Zw 系列 API 則通過 KiSystemService,最終跳轉到對應的函式程式碼.
ZW開頭的函式是通過eax中系統服務號去SSDT中查詢相應的系統服務,然後呼叫之,若在驅動中直接呼叫NT開頭的函式是不會經過SSDT的 也不會被SSDT HOOK攔截的
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {char szCurProc[MAX_PATH] = {0,}; char *p = NULL; switch( fdwReason ) { case DLL_PROCESS_ATTACH : DebugLog("DllMain() : DLL_PROCESS_ATTACH\n"); GetModuleFileNameA(NULL, szCurProc, MAX_PATH); p = strrchr(szCurProc, '\\'); if( (p != NULL) && !_stricmp(p+1, "iexplore.exe") ) { DebugLog("DllMain() : current process is [iexplore.exe]\n"); // 鉤取 wininet!InternetConnectW() API 之前 // 預先載入 wininet.dll if( NULL == LoadLibrary(L"wininet.dll") ) { DebugLog("DllMain() : LoadLibrary() failed!!! [%d]\n", GetLastError()); } } // hook hook_by_code("ntdll.dll", "ZwResumeThread", (PROC)NewZwResumeThread, g_pZWRT); // NewZwResumeThread、NewInternetConnectW 是本 DLL 中的函式 hook_by_code("wininet.dll", "InternetConnectW", // 因此會直接表示為地址傳入函式 (PROC)NewInternetConnectW, g_pICW); break; case DLL_PROCESS_DETACH : DebugLog("DllMain() : DLL_PROCESS_DETACH\n"); // unhook unhook_by_code("ntdll.dll", "ZwResumeThread", g_pZWRT); unhook_by_code("wininet.dll", "InternetConnectW", g_pICW); break; } return TRUE; }
BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes) { FARPROC pFunc = NULL; DWORD dwOldProtect = 0, dwAddress = 0; BYTE pBuf[5] = {0xE9, 0, }; PBYTE pByte = NULL; HMODULE hMod = NULL; hMod = GetModuleHandleA(szDllName); if( hMod == NULL ) { DebugLog("hook_by_code() : GetModuleHandle(\"%s\") failed!!! [%d]\n", szDllName, GetLastError()); return FALSE; } pFunc = (FARPROC)GetProcAddress(hMod, szFuncName); if( pFunc == NULL ) { DebugLog("hook_by_code() : GetProcAddress(\"%s\") failed!!! [%d]\n", szFuncName, GetLastError()); return FALSE; } pByte = (PBYTE)pFunc; if( pByte[0] == 0xE9 ) { DebugLog("hook_by_code() : The API is hooked already!!!\n"); return FALSE; } if( !VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect) ) { DebugLog("hook_by_code() : VirtualProtect(#1) failed!!! [%d]\n", GetLastError()); return FALSE; } memcpy(pOrgBytes, pFunc, 5); //修改5位元組的屬性 dwAddress = (DWORD)pfnNew - (DWORD)pFunc - 5; // 計算相對地址 memcpy(&pBuf[1], &dwAddress, 4); // 儲存原地址 memcpy(pFunc, pBuf, 5); // 覆寫函式首地址 if( !VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect) ) { DebugLog("hook_by_code() : VirtualProtect(#2) failed!!! [%d]\n", GetLastError()); return FALSE; } return TRUE; }
#define STR_MODULE_NAME (L"redirect.dll")
#define STATUS_SUCCESS (0x00000000L)
NTSTATUS WINAPI NewZwResumeThread(HANDLE ThreadHandle, PULONG SuspendCount) // 對 ZwResumeThread API 進行全域性鉤取 { // 每次建立新程序,都會呼叫CreateProcess 裡面的 ZwResumeThread 函式來喚醒程序 NTSTATUS status, statusThread; // 鉤取此函式,可以在每次喚醒執行緒前,將 DLL 注入子程序中. FARPROC pFunc = NULL, pFuncThread = NULL; DWORD dwPID = 0; static DWORD dwPrevPID = 0; THREAD_BASIC_INFORMATION tbi; HMODULE hMod = NULL; TCHAR szModPath[MAX_PATH] = {0,}; DebugLog("NewZwResumeThread() : start!!!\n"); hMod = GetModuleHandle(L"ntdll.dll"); if( hMod == NULL ) { DebugLog("NewZwResumeThread() : GetModuleHandle() failed!!! [%d]\n", GetLastError()); return NULL; } // call ntdll!ZwQueryInformationThread() pFuncThread = GetProcAddress(hMod, "ZwQueryInformationThread"); if( pFuncThread == NULL ) { DebugLog("NewZwResumeThread() : GetProcAddress() failed!!! [%d]\n", GetLastError()); return NULL; } statusThread = ((PFZWQUERYINFORMATIONTHREAD)pFuncThread) // 呼叫 ZwQueryInformationThread API, 獲取當前執行緒的 PID 值 (ThreadHandle, 0, &tbi, sizeof(tbi), NULL); if( statusThread != STATUS_SUCCESS ) { DebugLog("NewZwResumeThread() : pFuncThread() failed!!! [%d]\n", GetLastError()); return NULL; } dwPID = (DWORD)tbi.ClientId.UniqueProcess; // 獲取到 PID 值 if ( (dwPID != GetCurrentProcessId()) && (dwPID != dwPrevPID) ) // 第一次呼叫時 PID 值不能為 0,後面再次呼叫時不能注入上次注入過的程序. { DebugLog("NewZwResumeThread() => call InjectDll()\n"); dwPrevPID = dwPID; // change privilege if( !SetPrivilege(SE_DEBUG_NAME, TRUE) ) DebugLog("NewZwResumeThread() : SetPrivilege() failed!!!\n"); // get injection dll path GetModuleFileName(GetModuleHandle(STR_MODULE_NAME), // 獲取要注入的 DLL 的路徑 szModPath, MAX_PATH); if( !InjectDll(dwPID, szModPath) ) DebugLog("NewZwResumeThread() : InjectDll(%d) failed!!!\n", dwPID); //注入程序 } // call ntdll!ZwResumeThread() if( !unhook_by_code("ntdll.dll", "ZwResumeThread", g_pZWRT) ) //脫鉤 { DebugLog("NewZwResumeThread() : unhook_by_code() failed!!!\n"); return NULL; } pFunc = GetProcAddress(hMod, "ZwResumeThread"); //獲取原 API 地址 if( pFunc == NULL ) { DebugLog("NewZwResumeThread() : GetProcAddress() failed!!! [%d]\n", GetLastError()); goto __NTRESUMETHREAD_END; } status = ((PFZWRESUMETHREAD)pFunc)(ThreadHandle, SuspendCount); // 呼叫原 API ,執行掛起的執行緒 if( status != STATUS_SUCCESS ) { DebugLog("NewZwResumeThread() : pFunc() failed!!! [%d]\n", GetLastError()); goto __NTRESUMETHREAD_END; } __NTRESUMETHREAD_END: if( !hook_by_code("ntdll.dll", "ZwResumeThread", // 執行完函式之後,繼續鉤取函式 (PROC)NewZwResumeThread, g_pZWRT) ) { DebugLog("NewZwResumeThread() : hook_by_code() failed!!!\n"); } DebugLog("NewZwResumeThread() : end!!!\n"); return status; }
HINTERNET WINAPI NewInternetConnectW ( HINTERNET hInternet, LPCWSTR lpszServerName, INTERNET_PORT nServerPort, LPCTSTR lpszUsername, LPCTSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext ) { HINTERNET hInt = NULL; FARPROC pFunc = NULL; HMODULE hMod = NULL; // 先 unhook if( !unhook_by_code("wininet.dll", "InternetConnectW", g_pICW) ) // 脫鉤 { DebugLog("NewInternetConnectW() : unhook_by_code() failed!!!\n"); return NULL; } // call original API hMod = GetModuleHandle(L"wininet.dll"); if( hMod == NULL ) { DebugLog("NewInternetConnectW() : GetModuleHandle() failed!!! [%d]\n", GetLastError()); goto __INTERNETCONNECT_EXIT; } pFunc = GetProcAddress(hMod, "InternetConnectW"); // 獲取原 API 地址 if( pFunc == NULL ) { DebugLog("NewInternetConnectW() : GetProcAddress() failed!!! [%d]\n", GetLastError()); goto __INTERNETCONNECT_EXIT; } if( !_tcsicmp(lpszServerName, L"www.naver.com") || // 對指定網址進行替換 !_tcsicmp(lpszServerName, L"www.daum.net") || !_tcsicmp(lpszServerName, L"www.nate.com") || !_tcsicmp(lpszServerName, L"www.yahoo.com") ) { DebugLog("[redirect] naver, daum, nate, yahoo => reversecore\n"); hInt = ((PFINTERNETCONNECTW)pFunc)(hInternet, L"www.reversecore.com", nServerPort, lpszUsername, lpszPassword, dwService, dwFlags, dwContext); } else { DebugLog("[no redirect]\n"); hInt = ((PFINTERNETCONNECTW)pFunc)(hInternet, lpszServerName, nServerPort, lpszUsername, lpszPassword, dwService, dwFlags, dwContext); } __INTERNETCONNECT_EXIT: //函式執行完之後重新鉤取原 API. // hook if( !hook_by_code("wininet.dll", "InternetConnectW", (PROC)NewInternetConnectW, g_pICW) ) { DebugLog("NewInternetConnectW() : hook_by_code() failed!!!\n"); } return hInt; }
CreateProcess 裡面封裝了 CreateProcessInternal , 實際測試中,鉤取 CreateProcess ,並在其中呼叫CreateProcessInternal 是沒有問題的.
在 XP 系統中能正常 hide 程序,只是在 show 時完成程式後會退出.