如何通過HOOK改變windows的API函數(找到函數的相對偏移)


我們知道,系統函數都是以DLL封裝起來的,應用程序應用到系統函數時,應首先把該DLL加載到當前的進程空間中,調用的系統函數的入口地址,可以通過GetProcAddress函數進行獲取。當系統函數進行調用的時候,首先把所必要的信息保存下來(包括參數和返回地址,等一些別的信息),然后就跳轉到函數的入口地址,繼續執行。其實函數地址,就是系統函數“可執行代碼”的開始地址。那么怎么才能讓函數首先執行我們的函數呢?呵呵,應該明白了吧,把開始的那段可執行代碼替換為我們自己定制的一小段可執行代碼,這樣系統函數調用時,不就按我們的意圖乖乖行事了嗎?其實,就這么簡單。Very   very簡單。   :P.實際的說,就可以修改系統函數入口的地方,讓他調轉到我們的函數的入口點就行了。采用匯編代碼就能簡單的實現Jmp   XXXX,   其中XXXX就是要跳轉的相對地址。   
    
  我們的做法是:把系統函數的入口地方的內容替換為一條Jmp指令,目的就是跳到我們的函數進行執行。而Jmp后面要求的是相對偏移,也就是我們的函數入口地址到系統函數入口地址之間的差異,再減去我們這條指令的大小。用公式表達如下:   
    
  int   nDelta   =   UserFunAddr   –   SysFunAddr   -   (我們定制的這條指令的大小);   
    
  Jmp   nDleta;   
    
  為了保持原程序的健壯性,我們的函數里做完必要的處理后,要回調原來的系統函數,然后返回。所以調用原來系統函數之前必須先把原來修改的系統函數入口地方給恢復,否則,系統函數地方被我們改成了Jmp   XXXX就會又跳到我們的函數里,死循環了。   
    
  那么說一下程序執行的過程。   
    
  我們的dll“注射”入被hook的進程   ->   保存系統函數入口處的代碼   ->   替換掉進程中的系統函數入口指向我們的函數   ->   當系統函數被調用,立即跳轉到我們的函數   ->   我們函數進行處理   ->   恢復系統函數入口的代碼   ->   調用原來的系統函數   ->   再修改系統函數入口指向我們的函數(為了下次hook)->   返回。   
    
  於是,一次完整的Hook就完成了。   
    
    
    
  好,這個問題明白以后,講一下下個問題,就是如何進行dll“注射”?即將我們的dll注射到要Hook的進程中去呢?   
    
  很簡單哦,這里我們采用調用Windows提供給我們的一些現成的Hook來進行注射。舉個例子,鼠標鈎子,鍵盤鈎子,大家都知道吧?我們可以給系統裝一個鼠標鈎子,然后所有響應到鼠標事件的進程,就會“自動”(其實是系統處理了)載入我們的dll然后設置相應的鈎子函數。其實我們的目的只是需要讓被注射進程載入我們的dll就可以了,我們可以再dll實例化的時候進行函數注射的,我們的這個鼠標鈎子什么都不干的。   
    
    
    
    
    
  4簡單的例子OneAddOne   
    
  講了上面的原理,現在我們應該實戰一下了。先不要考慮windows系統那些繁雜的函數,我們自己編寫一個API函數來進行Hook與被Hook的練習吧,哈哈。   
    
    
    
  ////////////////////   
    
  第一步,首先編寫一個add.dll,很簡單,這個dll只輸出一個API函數,就是add啦。   
    
  新建一個win32   dll工程,   
    
  add.cpp的內容:   
    
    
    
  #i   nclude   "stdafx.h"   
    
    
    
  int   WINAPI   add(int   a,int   b){     file://千萬別忘記聲明WINAPI   否則調用的時候回產生聲明錯誤哦!   
    
    return   a+b;   
    
  }   
    
    
    
  BOOL   APIENTRY   DllMain(   HANDLE   hModule,   
    
                                                DWORD     ul_reason_for_call,   
    
                                                LPVOID   lpReserved   
    
              )   
    
  {   
    
          return   TRUE;   
    
  }   
    
    
    
    
    
  然后別忘了在add.def里面輸出函數:   
    
    
    
  LIBRARY     Add   
    
  DESCRIPTION   "ADD   LA"   
    
  EXPORTS   
    
    add     @1;   
    
    
    
  編譯,ok,我們獲得了add.dll   
    
    
    
  ////////////////////   
    
  第二步,編寫1+1主程序   
    
  新建一個基於對話框的工程One,   
    
  在   OnOK()里面調用add函數:   
    
  ConeDlg.h里面加入一些變量的聲明:   
    
  ….   
    
  Public:   
    
    HINSTANCE   hAddDll;   
    
    typedef   int   (WINAPI*AddProc)(int   a,int   b);   
    
    AddProc   add;   
    
  …   
    
    
    
  ConeDlg.cpp里進行調用:   
    
    
    
  void   COneDlg::OnOK()   
    
  {   
    
    //   TODO:   Add   extra   validation   here   
    
    if   (hAddDll==NULL)   
    
      hAddDll=::LoadLibrary("add.dll");   
    
    
    
    add=(AddProc)::GetProcAddress(hAddDll,"add");   
    
    
    
    int   a=1;   
    
    int   b=2;   
    
    int   c=add(a,b);   
    
    CString   temp;   
    
    temp.Format("%d+%d=%d",a,b,c);   
    
    AfxMessageBox(temp);   
    
  }   
    
    
    
  OK,編譯運行,正確的話就會顯示1+2=3咯   
    
  ////////////////////   
    
  第3步,要動手Hook咯,爽阿   
    
  新建一個MFC的dll工程,Hook   
    
  在Hook.dll工程里:   
    
    
    
  添加一個鼠標Hook   MouseProc,鼠標hook什么也不做   
    
  LRESULT   CALLBACK   MouseProc(int   nCode,WPARAM   wParam,LPARAM   lParam)   
    
  {   
    
    LRESULT   RetVal=   CallNextHookEx(hhk,nCode,wParam,lParam);   
    
    return   RetVal;   
    
  }   
    
    
    
  添加鼠標鈎子的安裝和卸載函數:   
    
  BOOL   InstallHook()   
    
  {   
    
    
    
    hhk=::SetWindowsHookEx(WH_MOUSE,MouseProc,hinst,0);   
    
    
    
    ….   
    
    return   true;   
    
  }   
    
    
    
  void   UninstallHook()   
    
  {   
    
    ::UnhookWindowsHookEx(hhk);   
    
  }   
    
    
    
  再實例化中獲得一些參數   
    
  BOOL   CHookApp::InitInstance()   
    
  {   
    
    獲得dll   實例,進程句柄   
    
  hinst=::AfxGetInstanceHandle();   
    
    DWORD   dwPid=::GetCurrentProcessId();   
    
    hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);   
    
  調用注射函數   
    
    Inject();   
    
    return   CWinApp::InitInstance();   
    
  }   
    
    
    
  好,最重要的注射函數:   
    
  void   Inject()   
    
  {   
    
    
    
    if   (m_bInjected==false)   
    
    {   保證只調用1次   
    
      m_bInjected=true;   
    
    
    
      獲取add.dll中的add()函數   
    
      HMODULE   hmod=::LoadLibrary("add.dll");   
    
      add=(AddProc)::GetProcAddress(hmod,"add");   
    
      pfadd=(FARPROC)add;   
    
    
    
      if   (pfadd==NULL)   
    
      {   
    
        AfxMessageBox("cannot   locate   add()");   
    
      }   
    
    
    
      //   將add()中的入口代碼保存入OldCode[]   
    
      _asm   
    
      {   
    
        lea   edi,OldCode   
    
        mov   esi,pfadd   
    
        cld   
    
        movsd   
    
        movsb   
    
      }   
    
    
    
      NewCode[0]=0xe9;//實際上0xe9就相當於jmp指令   
    
      //獲取Myadd()的相對地址   
    
      _asm   
    
      {   
    
        lea   eax,Myadd   
    
        mov   ebx,pfadd   
    
        sub   eax,ebx   
    
        sub   eax,5   
    
        mov   dword   ptr   [NewCode+1],eax   
    
      }   
    
      //填充完畢,現在NewCode[]里的指令相當於Jmp   Myadd   
    
      HookOn();   //可以開啟鈎子了   
    
    }   
    
  }   
    
    
    
  開啟鈎子的函數   
    
  void   HookOn()   
    
  {   
    
    ASSERT(hProcess!=NULL);   
    
    
    
    DWORD   dwTemp=0;   
    
    DWORD   dwOldProtect;   
    
    
    
  //將內存保護模式改為可寫,老模式保存入dwOldProtect   
    
    VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);   
    
            //將所屬進程中add()的前5個字節改為Jmp   Myadd   
    
    WriteProcessMemory(hProcess,pfadd,NewCode,5,0);   
    
    //將內存保護模式改回為dwOldProtect   
    
    VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp);   
    
    
    
    bHook=true;   
    
  }   
    
    
    
  關閉鈎子的函數   
    
  void   HookOff()//將所屬進程中add()的入口代碼恢復   
    
  {   
    
    ASSERT(hProcess!=NULL);   
    
    
    
    DWORD   dwTemp=0;   
    
    DWORD   dwOldProtect;   
    
    
    
    VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);   
    
    WriteProcessMemory(hProcess,pfadd,OldCode,5,0);   
    
    VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp);   
    
    bHook=false;   
    
  }   
    
    
    
  然后,寫我們自己的Myadd()函數   
    
  int   WINAPI   Myadd(int   a,int   b)   
    
  {   
    
    //截獲了對add()的調用,我們給a,b都加1   
    
    a=a+1;   
    
    b=b+1;   
    
    
    
    HookOff();//關掉Myadd()鈎子防止死循環   
    
    
    
    int   ret;   
    
    ret=add(a,b);   
    
    
    
    HookOn();//開啟Myadd()鈎子   
    
    
    
    return   ret;   
    
  }   
    
    
    
  然后別忘記在hook.def里面輸出   
    
  InstallHook   
    
    MouseProc   
    
    Myadd   
    
    UninstallHook   
    
  四個函數。   
    
    
    
    
  好到這里基本上大功告成咯   
    
    
    
  ////////////////////   
    
    
    
  第4步,我們就可以修改前面的One的測試程序了   
    
    
    
  增加一個安裝鈎子的函數/按鈕   
    
  void   COneDlg::doHook()   
    
  {   
    
    hinst=LoadLibrary("hook.dll");   
    
    if(hinst==NULL)   
    
    {   
    
      AfxMessageBox("no   hook.dll!");   
    
      return;   
    
    }   
    
    typedef   BOOL   (CALLBACK   *inshook)();   
    
    inshook   insthook;   
    
    
    
    insthook=::GetProcAddress(hinst,"InstallHook");   
    
    if(insthook==NULL)   
    
    {   
    
      AfxMessageBox("func   not   found!");   
    
      return;   
    
    }   
    
    
    
    DWORD   pid=::GetCurrentProcessId();   
    
    BOOL   ret=insthook();   
    
  }   
    
    
    
  別忘了退出時卸掉鈎子   
    
  void   COneDlg::OnCancel()   
    
  {   
    
    //   TODO:   Add   extra   cleanup   here   
    
    typedef   BOOL   (CALLBACK   *UnhookProc)();   
    
    UnhookProc   UninstallHook;   
    
    
    
    UninstallHook=::GetProcAddress(hinst,"UninstallHook");   
    
    if(UninstallHook==NULL)   UninstallHook();   
    
    if   (hinst!=NULL)   
    
    {   
    
      ::FreeLibrary(hinst);   
    
    }   
    
    if   (hAddDll!=NULL)   
    
    {   
    
      ::FreeLibrary(hAddDll);   
    
    }   
    
    CDialog::OnCancel();   
    
  }   

 

http://blog.csdn.net/jiangxinyu/article/details/5385821


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM