1. 程式人生 > 實用技巧 >第34章:高階全域性API鉤取 : IE連線控制

第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攔截的

ssdt 全稱為 System Services Descriptor Table,中文為系統服務描述符表,ssdt 表就是把 ring3 的 Win32 API 和 ring0 的核心 API 聯絡起來。SSDT 並不僅僅只包含一個龐大的地址索引表,它還包含著一些其它有用的資訊,諸如地址索引的基地址、服務函式個數等.

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 時完成程式後會退出.