Windows下Hook API 技術(HOOK SEND)

來源:互聯網
上載者:User
Windows下Hook API技術
    什麼叫Hook API?所謂Hook就是鉤子的意思,而API是指Windows開放給程式員的編程介面,使得在使用者層級下可以對作業系統進行控制,也就是一般的應用程式都需要調用API來完成某些功能,Hook API的意思就是在這些應用程式調用真正的系統API前可以先被截獲,從而進行一些處理再調用真正的API來完成功能。在講Hook API之前先來看一下如何Hook訊息,例如Hook全域鍵盤訊息,從而可以知道使用者按了哪些鍵,這種Hook訊息的功能可以由以下函數來完成,該函數將一個新的Hook加入到原來的Hook鏈中,當某一訊息到達後會依次經過它的Hook鏈再交給應用程式。 HHOOK SetWindowsHookEx(
    int idHook,                     //Hook類型,例如WH_KEYBOARD,WH_MOUSE
    HOOKPROC lpfn,             //Hook處理過程函數的地址
    HINSTANCE hMod,          //包含Hook處理過程函數的dll控制代碼(若在本進程可以為NULL)
    DWORD dwThreadId     //要Hook的線程ID,若為0,表示全域Hook所有
);     這裡需要提一下的就是如果是Hook全域的而不是某個特定的進程則需要將Hook過程編寫為一個DLL,以便讓任何程式都可以載入它來擷取Hook過程函數。    而對於Hook API微軟並沒有提供直接的介面函數,也許它並不想讓我們這樣做,不過有2種方法可以完成該功能。第一種,修改可執行檔的IAT表(即輸入表),因為在該表中記錄了所有調用API的函數地址,則只需將這些地址改為自己函數的地址即可,但是這樣有一個局限,因為有的程式會加殼,這樣會隱藏真實的IAT表,從而使該方法失效。第二種方法是直接跳轉,改變API函數的頭幾個位元組,使程式跳轉到自己的函數,然後恢複API開頭的幾個位元組,在調用AP完成功能後再改回來又能繼續Hook了,但是這種方法也有一個問題就是同步的問題,當然這是可以克服的,並且該方法不受程式加殼的限制。    下面將以一個Hook指定程式send函數的例子來詳細描述如何Hook API,以達到監視程式發送的每個封包的目的。採用的是第二種方法,編寫為一個dll。首先是一些全域聲明, //本dll的handle
HANDLE g_hInstance = NULL;
//修改API入口為 mov eax, 00400000;jmp eax是程式能跳轉到自己的函數
BYTE g_btNewBytes[8] = { 0xB8, 0x0, 0x0, 0x40, 0x0, 0xFF, 0xE0, 0x0 };
//儲存原API入口的8個位元組
DWORD g_dwOldBytes[2][2] = { 0x0, 0x0, 0x0, 0x0 };
//鉤子控制代碼
HHOOK   g_hOldHook = NULL;
//API中send函數的地址
DWORD g_pSend = 0;
//事務,解決同步問題
HANDLE g_hSendEvent = NULL;//自己的send函數地址,參數必須與API的send函數地址相同int _stdcall hook_send( SOCKET s, const char *buf, int len, int flags );//要Hook的進程和主線程ID號DWORD g_dwProcessID = 0;
DWORD g_dwThreadID = 0;     從聲明可以看出,我們會把API函數的首8個位元組改為 mov eax, 00400000;jmp eax ,使程式能夠跳轉,只需擷取我們自己的函數地址填充掉00400000即可實現跳轉。而g_dwOldBytes是用來儲存API開頭原始的8個位元組,在真正執行API函數是需要寫回。還有一點,在聲明新的函數時,該例中為hook_send,除了保正參數與API的一致外,還需要聲明為__stdcall類型,表示函數在退出前自己來清理堆棧,因為這裡是直接跳轉到新函數處,所以必須自己清理堆棧。下面看主函數, BOOL APIENTRY DllMain( HANDLE hModule, 
                                   DWORD  ul_reason_for_call, 
                                   LPVOID lpReserved
                                 )
{
    if(ul_reason_for_call == DLL_PROCESS_ATTACH)
   {      //擷取本dll控制代碼
      g_hInstance = hModule;            //建立事務
      g_hSendEvent = CreateEvent( NULL, FALSE, TRUE, NULL );
      
      //重寫API開頭的8位元組
      HMODULE hWsock = LoadLibrary( "wsock32.dll" );
      g_pSend = ( DWORD )GetProcAddress( hWsock, "send" );       //儲存原始位元組      ReadProcessMemory( INVALID_HANDLE_VALUE, ( void * )g_pSend, 
          ( void * )g_dwOldBytes[0], sizeof( DWORD )*2, NULL );      //將00400000改寫為我們函數的地址
      *( DWORD* )( g_btNewBytes + 1 ) = ( DWORD )hook_send;
      WriteProcessMemory( INVALID_HANDLE_VALUE, ( void * )g_pSend, 
          ( void * )g_btNewBytes, sizeof( DWORD )*2, NULL ); 
    }
    return TRUE;
}     以上是dll的main函數,在被指定的程式載入的時候會自動運行dll的main函數來完成初始化,這裡就是改寫API的首地址來完成跳轉。當然本程式是對於指定程式進行Hook,如果要進行全域Hook,可以在main函數中用GetModuleFileName函數來擷取exe檔案完整路徑,判斷當前進程是否是想要Hook的進程。寫函數中使用INVALID_HANDLE_VALUE,表示寫本進程。 int _stdcall hook_send( SOCKET s, const char *buf, int len, int flags )
{
   int nRet;   WaitForSingleObject( g_hSendEvent, INFINITE );     //恢複API頭8個位元組
   WriteProcessMemory( INVALID_HANDLE_VALUE, ( void* )g_pSend, 
      ( void* )g_dwOldBytes[0], sizeof( DWORD )*2, NULL );    /*   這裡可以添加想要進行的處理過程   */    //真正執行API函數
   nRet = send( s, buf, len, flags );    //寫入跳躍陳述式,繼續Hook
   WriteProcessMemory( INVALID_HANDLE_VALUE, ( void* )g_pSend, 
      ( void* )g_btNewBytes, sizeof( DWORD )*2, NULL );    SetEvent( g_hSendEvent );    return nRet;
} HOOK_API BOOL StartHook(HWND hWnd)
{    //通過傳入的視窗控制代碼擷取線程控制代碼
    g_dwThreadID = GetWindowThreadProcessId( hWnd, &g_dwProcessID );     //WH_CALLWNDPROC類型的Hook
    g_hOldHook = SetWindowsHookEx( WH_CALLWNDPROC,  HookProc,          ( HINSTANCE ) g_hInstance, g_dwThreadID );    if( g_hOldHook == NULL )
        return FALSE;    return TRUE;
} static LRESULT WINAPI HookProc( int nCode, WPARAM wParam, LPARAM lParam ) 
{ return CallNextHookEx( g_hOldHook, nCode, wParam, lParam ); 
} HOOK_API void StopHook(void)
{
   if(g_hOldHook != NULL)
   {
       WaitForSingleObject( g_hSendEvent, INFINITE );       HANDLE hProcess = NULL;
       hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_dwProcessID);       DWORD dwOldProc;
       DWORD dwNewProc;        //改變頁面屬性為讀寫
       VirtualProtectEx( hProcess, ( void* )g_pSend, 8, PAGE_READWRITE, &dwOldProc );        //恢複API的首8個位元組
       WriteProcessMemory( hProcess, ( void* )g_pSend, 
            ( void* )g_dwOldBytes[0], sizeof( DWORD )*2, NULL );        //恢複分頁檔的屬性
       VirtualProtectEx( hProcess, ( void* )g_pSend, 8, dwOldProc, &dwNewProc );
  
       CloseHandle(g_hSendEvent);
  
       UnhookWindowsHookEx( g_hOldHook );
    }
}    可以看出,我們建立的Hook類型是WH_CALLWNDPROC類型,該類型的Hook在進程與系整合通訊時就會被載入到進程空間,從而調用dll的main函數完成真正的Hook,而在SetWindowsHookEx函數中指定的HookProc函數將不作任何處理,只是調用CallNextHookEx將訊息交給Hook鏈中下一個環節處理,因為這裡SetWindowsHookEx的唯一作用就是讓進程載入我們的dll。    以上就是一個最簡單的Hook API的例子,該種技術可以完成許多功能。例如網遊外掛製作過程中截取發送的與收到的封包即可使用該方法,或者也可以在Hook到API後加入木馬功能,反向串連指定的主機或者監聽某一連接埠,還有許多加殼也是用該原理來隱藏IAT表,填入自己的函數地址。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.