• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            c++初學(xué)者

            專注技術(shù)開發(fā)

            【轉(zhuǎn)】阻止全局鉤子的加載

              網(wǎng)上有一篇關(guān)于這個(gè)問題的文章,題目叫《防止全局鉤子的侵入》,作者不祥。文中簡單分析了一下鉤子的原理,然后使用了微軟的Detours庫進(jìn)行API攔截。如果只是為了攔截一個(gè)函數(shù),使用Detours好像有點(diǎn)兒浪費(fèi)。本文不使用Detours庫,直接對(duì)LoadLibraryExW函數(shù)進(jìn)行攔截。
              先說一下全局鉤子是怎么進(jìn)入到我們的程序里來的。假如有個(gè)程序A安裝了WH_GETMESSAGE的全局鉤子,鉤子函數(shù)在B.dll中,那么當(dāng)其它程序在調(diào)用GetMessage函數(shù)從自己的消息隊(duì)列中取消息的時(shí)候,系統(tǒng)發(fā)現(xiàn)程序A安裝了WH_GETMESSAGE的全局鉤子,就會(huì)檢查調(diào)用GetMessage的進(jìn)程是否加載了B.dll,如果沒有,就調(diào)用LoadLibrary進(jìn)行加載,然后調(diào)用B.dll中的鉤子過程。這樣,鉤子dll就會(huì)在所有調(diào)用GetMessage的進(jìn)程中加載。
            我們要做的工作,就是在系統(tǒng)調(diào)用LoadLibrary的時(shí)候讓它失敗。這樣做有兩個(gè)問題:
            1. LoadLibrary函數(shù)這一次失敗了,下一次系統(tǒng)還是會(huì)去嘗試加載它。看起來可能會(huì)影響效率,但是即使你不讓它失敗,每次有消息的時(shí)候,系統(tǒng)也是會(huì)去調(diào)用那個(gè)鉤子過程的,哪種方法更影響效率呢?這就不知道了,呵呵;
            2. 怎么知道什么時(shí)候讓LoadLibrary失敗呢?不能都讓它失敗吧,這樣會(huì)死的很慘的:(.經(jīng)過研究發(fā)現(xiàn),正常的加載dll函數(shù)調(diào)用都是從kernel32.dll中來的,而只有加載鉤子過程是在user32.dll中進(jìn)行的(winxp系統(tǒng)下,以后的不知道是否也是這樣)。我們可以判斷一下LoadLibrary函數(shù)的返回地址,如果是在user32.dll的地址空間,就認(rèn)為是鉤子dll的加載,直接返回0就可以了。

              然后就來談?wù)勎覀兊腁PI攔截。因?yàn)閡ser32.dll中是用的LoadLibraryExW來加載鉤子dll的,所以我們只需要攔截這么一個(gè)函數(shù)就可以了。分成三個(gè)步驟:

            1. 提供一個(gè)替代LoadLibraryExW的函數(shù),假設(shè)名字叫newLoadLibraryExW,注意,函數(shù)原型要和LoadLibraryExW一模一樣,本進(jìn)程內(nèi)所有對(duì)LoadLibraryExW的調(diào)用都會(huì)轉(zhuǎn)到這兒來;
              HMODULE WINAPI newLoadLibraryExW(LPCWSTR lpLibFileName,HANDLE hFile,DWORD dwFlags)
                  {
                  //獲取函數(shù)的返回地址參考文章最后的注1
                  DWORD dwCaller;
                  __asm push dword ptr [ebp+4]
                  __asm pop  dword ptr [dwCaller]
                  //判斷是否是從User32.dll調(diào)用的
                  //m_dwUser32Low和m_dwUser32Hi保存user32.dll的加載地址的上下限
                  if(dwCaller > m_dwUser32Low && dwCaller < m_dwUser32Hi)
                  {
                  //TRACE something hint infomation
                  return 0;
                  }
                  return rawLoadLibraryExW(lpLibFileName,hFile,dwFlags);
                  }		
            2. 提供一塊空間,假設(shè)這塊空間的起始地址是fakeLoadLibraryExW,把LoadLibraryExW函數(shù)前N個(gè)字節(jié)保存下來,然后再用一個(gè)jmp指令跳回(LoadLibraryExW+N)地址處繼續(xù)執(zhí)行,在這里,N取7,具體原因在下邊講;
            3. 修改LoadLibraryExW函數(shù)的前5個(gè)字節(jié),用一個(gè)jmp 指令跳到我們的newLoadLibraryExW函數(shù)起始處。雖然這里只用了5個(gè)字節(jié),但是我們先看一下LoadLibraryExW函數(shù)的前兩條指令:
            //你機(jī)器上的版本具體的數(shù)字可能和我的不一樣
            push 34h?//6A 34
            push 7C80E288h?//68 88 E2 80 7C

              一共有7個(gè)字節(jié),我們不能只修改前5個(gè)字節(jié),然后從fakeLoadLibraryExW函數(shù)跳到第6個(gè)字節(jié)處開始執(zhí)行,而要跳到第三條指令即第8個(gè)字節(jié)開始處,這就是上一步N為什么取7的原因。畫個(gè)圖示意一下,修改前:

            修改后:

             

            以下是封裝的一個(gè)類,使用時(shí)定義一個(gè)該類的全局變量,調(diào)用一下PatchLoadLibrary函數(shù)即可。

             

            //***********************************************************************************//
            //  FileName	: GBlockHookDll.h
            //  Author	:耿海增
            //  Date	: 2006.10.07
            //***********************************************************************************//
            #pragma once
            #include
            
            #pragma comment(lib,"psapi.lib")
            class GBlockHookDll
            {
            public:
             GBlockHookDll()
             {
              MODULEINFO user32ModInfo = {0};
             
              //獲取user32.dll的加載基址和映象大小  
              GetModuleInformation(GetCurrentProcess(),GetModuleHandle("user32.dll"),&user32ModInfo,sizeof(user32ModInfo));
              m_dwUser32Low = (DWORD)user32ModInfo.lpBaseOfDll;
              m_dwUser32Hi = (DWORD)user32ModInfo.lpBaseOfDll+user32ModInfo.SizeOfImage;
             }
             void PatchLoadLibrary()
             {
              //LoadLibraryExW
              //7C801AF1 6A 34                push        34h
              //7C801AF3 68 88 E2 80 7C       push        7C80E288h
              LPVOID* pfnRaw = (LPVOID*)&rawLoadLibraryExW;
              LPVOID fnNew = (LPVOID)newLoadLibraryExW;
              BYTE* fnRaw = (BYTE*)*pfnRaw;
              //1 save the first 7 bytes
              const int nFirstBytes = 7;
              BYTE* fnFake = (BYTE*)fakeLoadLibraryExW;
              memcpy(fnFake,*pfnRaw,nFirstBytes);
              fnFake[nFirstBytes] = 0xE9;  //jmp to rawAddr+nFirstBytes
              *(UINT32*)(fnFake + nFirstBytes+1) = (UINT32)fnRaw+nFirstBytes - (UINT32)(fnFake + nFirstBytes + 5);
              //2 modify the raw to jmp to fnNew
              DWORD dwOldProtect = 0;
              VirtualProtect(fnRaw,nFirstBytes,PAGE_READWRITE,&dwOldProtect); //修改該代碼段的屬性為可寫
              *fnRaw = 0xE9;
              *(UINT32*)(fnRaw+1) = (UINT32)fnNew - (UINT32)(fnRaw + 5);
              VirtualProtect(fnRaw,nFirstBytes,dwOldProtect,0);
              //3 change the rawPointer
              *pfnRaw = fnFake;
             }
            private: 
             static HMODULE WINAPI newLoadLibraryExW(LPCWSTR lpLibFileName,HANDLE hFile,DWORD dwFlags)
             {
              //get the return address
              DWORD dwCaller;
              __asm push dword ptr [ebp+4]
              __asm pop  dword ptr [dwCaller]
              if(dwCaller > m_dwUser32Low && dwCaller < m_dwUser32Hi)
              {
             #ifdef _DEBUG
               UINT uLenWide = lstrlenW(lpLibFileName);
               char* pNewChar = new char[uLenWide + 1];
               memset(pNewChar,0,uLenWide+1);
               WideCharToMultiByte(CP_ACP,0,lpLibFileName,-1,pNewChar,uLenWide,NULL,NULL);
               TRACE2(".......................LoadLibrary:return addr 0x%x,%s ",dwCaller,pNewChar);
               TRACE("Blocked.......................\n");
               delete []pNewChar;
             #endif
               return 0;
              }
              return rawLoadLibraryExW(lpLibFileName,hFile,dwFlags);
             }
            private:
             static DWORD m_dwUser32Low;    //user32.dll 的加載基址
             static DWORD m_dwUser32Hi;    //user32.dll 的加載基址+I(xiàn)mageSize
             static BYTE  fakeLoadLibraryExW[12]; //save first bytes of the raw function,and jmp back to that function
             //保存LoadLibraryExW的指針,然后修改為fakeLoadLibraryExW
             static HMODULE (WINAPI *rawLoadLibraryExW)( LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags );
            };
            DWORD GBlockHookDll::m_dwUser32Low = 0;
            DWORD GBlockHookDll::m_dwUser32Hi  = 0;
            BYTE GBlockHookDll::fakeLoadLibraryExW[12] = {0};
            HMODULE (WINAPI *GBlockHookDll::rawLoadLibraryExW)(LPCWSTR lpLibFileName,HANDLE hFile,DWORD dwFlags) = LoadLibraryExW;
            
            注1:怎么知道函數(shù)的返回地址呢?我們都知道,函數(shù)調(diào)用的時(shí)候,先要把參數(shù)入棧,然后把返回地址入棧,這樣,在我們的函數(shù)里,esp指向的應(yīng)該就是函數(shù)的返回地址了。但是為了返回函數(shù)時(shí)恢復(fù)原來的棧和在函數(shù)中方便引用傳遞的參數(shù),編譯器一般都會(huì)產(chǎn)生兩條指令:
            push ebp
            mov ebp,esp

              先把ebp入棧,把原來的esp保存在ebp寄存器中,這樣,我們的返回地址就是[ebp+4],第一個(gè)參數(shù)是[ebp+8],第二個(gè)是[ebp+0xC]
            注2:如果想寫一個(gè)通用一點(diǎn)兒的API Hook,就不能簡單的patch前5個(gè)或者前7字節(jié)了,需要根據(jù)不同的指令分析需要patch多少字節(jié)。可以參考微軟的 Detours 的實(shí)現(xiàn)。

            posted on 2008-09-28 12:24 大海 閱讀(512) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            久久国产乱子精品免费女| 亚洲国产婷婷香蕉久久久久久| 午夜精品久久影院蜜桃| 久久国语露脸国产精品电影| 亚洲中文字幕无码久久综合网 | 99久久99久久久精品齐齐| 久久精品国产亚洲7777| 久久亚洲国产成人精品性色| 久久人人爽人人爽AV片| 伊人色综合久久天天| 亚洲av成人无码久久精品| 久久夜色撩人精品国产小说| 亚洲精品无码久久久久sm| 国产精品欧美亚洲韩国日本久久| 久久精品国产亚洲AV香蕉| 人妻少妇久久中文字幕一区二区 | 99精品国产免费久久久久久下载| 国产精品美女久久久久AV福利| 欧美亚洲国产精品久久| 国内精品伊人久久久久网站| 国产精品免费久久| 久久99精品久久久久婷婷| 97久久超碰国产精品旧版| 久久久亚洲裙底偷窥综合| 免费一级欧美大片久久网| 99久久成人18免费网站| www.久久热| 色欲久久久天天天综合网精品 | 国产V亚洲V天堂无码久久久| 亚洲国产精品综合久久一线| 久久99精品久久久久久9蜜桃| 777米奇久久最新地址| 久久人人爽人人爽人人AV| 亚洲人成伊人成综合网久久久| 亚洲国产一成久久精品国产成人综合| 国产AV影片久久久久久| 国产精品美女久久久| 久久99精品综合国产首页| 久久国产乱子精品免费女| 青青青青久久精品国产| 伊人伊成久久人综合网777|