第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 时完成程序后会退出.

另外,在 VS 中编译以前的代码时,需要在 工程 - 属性 - C/C++ - 常规中,把 SDL 检查关闭,

并且,如果运行的系统中没有相应的 DLL ,可以在 VS 中选择 代码生成 ->  运行库,将模式改为多线程调试.即设置为静态库. VS 默认是动态库.

posted @ 2020-08-10 19:54  Rev_omi  阅读(380)  评论(0编辑  收藏  举报