• <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>
            不在浮沙筑高臺(tái)-demons
            C++/OS/disassembly/Anti-virus
            posts - 5,  comments - 5,  trackbacks - 0
             
              風(fēng)格:為了讓國(guó)外朋友也能看懂,代碼中的注釋全部用英文,而分析部分我用中文做詳細(xì)解釋, 如果您不習(xí)慣這種表達(dá)風(fēng)格,或者有什么意見,歡迎向我反饋.


                前言:本文主要討論現(xiàn)在網(wǎng)傳很廣的單線程函數(shù)hook 類CULHook 掛鉤代碼的致命漏洞以及設(shè)計(jì)缺陷,并給出我的解決方案.


               知識(shí)前提:您應(yīng)該有windows 程序設(shè)計(jì)和C++的基本知識(shí),以及對(duì)操作系統(tǒng)原理稍微了解.

                 -----------人因相互學(xué)習(xí)而進(jìn)步,若您發(fā)現(xiàn)文中有錯(cuò)誤,請(qǐng)不吝賜教,能夠告訴我,感恩您.

                once, I saw a cpp class that realized the API hook by inline assembly  in user mode. Maybe some of you have known this class that named "CULHook" too.
            However, there are some problems existed by now. One problem is that  hook method doesn't work well on multiple threads condition. And another  problem
            seems not to be clear but fatal !

              This is the original code of the class CULHook:             代碼來(lái)自 <windows 程序設(shè)計(jì)> -王艷平         注: 注解見下面,可參考我的CSTAPIHook類的代碼


            #include <windows.h>

            class CULHook
            {
            public:
                CULHook(LPSTR pszModName, LPSTR pszFuncName, PROC pfnHook);
                ~CULHook();
                void Unhook();
                void Rehook();
            protected:
                PROC m_pfnOrig;        
                BYTE m_btNewBytes[8];
                BYTE m_btOldBytes[8];   
                HMODULE m_hModule;
            };

            CULHook::CULHook(LPSTR pszModName, LPSTR pszFuncName, PROC pfnHook)
            {
                // jmp eax == 0xFF, 0xE0
                BYTE btNewBytes[8] = { 0xB8, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xE0, 0x00 };
                memcpy(m_btNewBytes, btNewBytes, 8);
                *(DWORD *)(m_btNewBytes + 1) = (DWORD)pfnHook;
                m_hModule = ::LoadLibrary(pszModName);

                if(m_hModule == NULL)
                {
                    m_pfnOrig = NULL;
                    return;
                }
                m_pfnOrig = ::GetProcAddress(m_hModule, pszFuncName);
                if(m_pfnOrig != NULL)
                {
                    DWORD dwOldProtect;
                    MEMORY_BASIC_INFORMATION    mbi;                                         //1
                    ::VirtualQuery( m_pfnOrig, &mbi, sizeof(mbi) );
                    ::VirtualProtect(m_pfnOrig, 8, PAGE_READWRITE, &dwOldProtect);   //2
                    memcpy(m_btOldBytes, m_pfnOrig, 8);
                    ::WriteProcessMemory(::GetCurrentProcess(), (void *)m_pfnOrig, m_btNewBytes, sizeof(DWORD)*2, NULL);
                    ::VirtualProtect(m_pfnOrig, 8, mbi.Protect, 0);
                }
            }

            CULHook::~CULHook()
            {
                Unhook();
                if(m_hModule != NULL)
                    ::FreeLibrary(m_hModule);
            }

            void CULHook::Unhook()
            {
                if(m_pfnOrig != NULL)
                {
                    DWORD dwOldProtect;
                    MEMORY_BASIC_INFORMATION    mbi;
                    ::VirtualQuery(m_pfnOrig, &mbi, sizeof(mbi));                               //1
                    ::VirtualProtect(m_pfnOrig, 8, PAGE_READWRITE, &dwOldProtect);  //2

                    ::WriteProcessMemory(::GetCurrentProcess(), (void *)m_pfnOrig,
                        m_btOldBytes, sizeof(DWORD)*2, NULL);

                    ::VirtualProtect(m_pfnOrig, 8, mbi.Protect, 0);
                }
            }

            void CULHook::Rehook()
            {
                if(m_pfnOrig != NULL)
                {
                    DWORD dwOldProtect;                                        
                    MEMORY_BASIC_INFORMATION    mbi;                                         //1                       
                    ::VirtualQuery( m_pfnOrig, &mbi, sizeof(mbi) );                               
                    ::VirtualProtect(m_pfnOrig, 8, PAGE_READWRITE, &dwOldProtect);    //2
                    ::WriteProcessMemory(::GetCurrentProcess(), (void *)m_pfnOrig, m_btNewBytes, sizeof(DWORD)*2, NULL);
                    ::VirtualProtect(m_pfnOrig, 8, mbi.Protect, 0);
                }
            }

             
            一個(gè)事實(shí):我用這個(gè)類掛鉤TerminateProcess和Process32NextW函數(shù)時(shí),目標(biāo)進(jìn)程崩潰(下面你將會(huì)看到在什么情況下會(huì)失敗)
               凡是我標(biāo)1的地方都有設(shè)計(jì)問(wèn)題,凡是我標(biāo)2的地方都有嚴(yán)重安全隱患。


               1, 首先我們先看一下這個(gè)類,有著很明顯的設(shè)計(jì)問(wèn)題,我們可以看看構(gòu)造函數(shù)和ReHook成員函數(shù),有著明顯的代碼重復(fù),一種解決方案是,把掛鉤功能放在一個(gè)成員函數(shù)中,在構(gòu)造函數(shù)中調(diào)用即可。
               2, 凡是我標(biāo)1 的地方完全沒(méi)有必要,我們知道VirtualProtect 的最后一個(gè)參數(shù)可以返回原來(lái)的權(quán)限,寫完內(nèi)存后直接把 dwOldProtect再傳給 VirtualProtect 以恢復(fù)頁(yè)面權(quán)限屬性,而無(wú)需
                  再大費(fèi)周章的得到 mbi 結(jié)構(gòu).
              3, 最不容易被發(fā)現(xiàn)的一點(diǎn),也就是我標(biāo)2的地方,也就是我們應(yīng)該獲取何種訪問(wèn)權(quán)限,代碼中是PAGE_READWRITE, 這有問(wèn)題嗎?我們不能給教科書挑錯(cuò)吧,畢竟原書示例程序的卻是用這個(gè)類   攔截了Ws2_32.dll中的winsock2的 一些函數(shù). 但是,這句有問(wèn)題,要弄清楚這個(gè)問(wèn)題就必須清楚這個(gè)類的原理.


              原理: 通過(guò)修把要掛鉤函數(shù)的前8個(gè)字節(jié)數(shù)據(jù)改為跳轉(zhuǎn)到我們函數(shù)的機(jī)器指令(原來(lái)的8字節(jié)保存起來(lái)),然后在我們的鉤子函數(shù)中執(zhí)行我們的操作,我們要干預(yù)的動(dòng)作全部結(jié)束后,再把原來(lái)       的8字節(jié)數(shù)據(jù)寫回去,再調(diào)用原函數(shù),為了再次攔截目標(biāo)函數(shù),原函數(shù)調(diào)用完后, 要再次把原函數(shù)前8個(gè)字節(jié)寫成跳轉(zhuǎn)到我們函數(shù)的機(jī)器指令,至于為什么是8字節(jié)? 我們來(lái)看看實(shí)現(xiàn)這個(gè)跳轉(zhuǎn)的匯   編指令吧:


                            mov eax,our_Hookfunction         //把我們函數(shù)地址裝入到eax寄存器的指令占1字節(jié) ,我們的函數(shù)地址占4字節(jié),  即:0xB8, 0x00, 0x00, 0x00, 0x00 
                            jmp eax                                  //jmp 指令碼1字節(jié), eax 寄存器號(hào)1字節(jié) 即: 0xFF, 0xE0, 


            好了 1+4+2=7 , 按照對(duì)齊原則,最后一字節(jié)我們以0填充.
            問(wèn)題在哪里,我說(shuō)過(guò)這個(gè)類是掛鉤單線程函數(shù)的,怎么理解, 試想這種情況:當(dāng)我們修改目標(biāo)函數(shù)前8個(gè)字節(jié)時(shí),在32位系統(tǒng)中,,要寫完8字節(jié)(64位)得用兩個(gè)存儲(chǔ)周期,假如第一次寫了32位數(shù)據(jù)后,系統(tǒng)發(fā)生線程換,如果新線程也要調(diào)用我們要掛鉤的函數(shù),因?yàn)檫@個(gè)函數(shù)我們只寫了前4個(gè)字節(jié),后4字節(jié)還沒(méi)寫,天知道會(huì)發(fā)生什么事。假如只有一個(gè)線程會(huì)調(diào)用我們的目標(biāo)函數(shù),則萬(wàn)事大吉。當(dāng)然這個(gè)類本身就是掛鉤單線程函數(shù)的,我在這里說(shuō)明是因?yàn)橛腥巳藛?wèn)過(guò)我這個(gè)問(wèn)題。

            好了既然搞清楚了原理,我們就言歸正傳,分析下面這種情況:假設(shè)我們要掛鉤的目標(biāo)進(jìn)程中有D和W兩個(gè)線程分別只會(huì)調(diào)用fun1,fun2兩個(gè)函數(shù),而fun1和fun2又都是dw.dll的導(dǎo)出函數(shù),且在同一個(gè)頁(yè)面中,現(xiàn)在我們用上面的類掛鉤函數(shù)fun1,顯然fun1是單線程(D)調(diào)用的,符合單線程安全,按理說(shuō)應(yīng)該會(huì)成功. 難道真相就是能成功掛鉤嗎,如果是,我這篇博客就沒(méi)必要寫了.根據(jù)上面類的代碼我們來(lái)分析運(yùn)行過(guò)程:首先我們把跳轉(zhuǎn)到我們函數(shù)的機(jī)器指令寫到fun1的前8個(gè)字節(jié),首先代碼中獲取了PAGE_READWRITE權(quán)限,然后有了權(quán)限后開始寫,若只寫完前4個(gè)字節(jié)后,系統(tǒng)切換到線程W,假設(shè)W線程在這個(gè)時(shí)間片里沒(méi)有調(diào)用fun2,而線程W不會(huì)調(diào)用fun1,等線程切回來(lái)之后我們寫完剩下的4字節(jié),如果是這樣則掛鉤成功,但是如果在寫了前4字節(jié)切換到W時(shí),W調(diào)用了fun2,會(huì)發(fā)生什么,這就是我本篇博文主要的焦點(diǎn)了,因?yàn)閒un1和fun2是在同一個(gè)頁(yè)面中,而VirtualProtect給該頁(yè)面獲取了PAGE_READWRITE權(quán)限,也許有人會(huì)認(rèn)為只給fun1獲取了PAGE_READWRITE 權(quán)限,而fun2權(quán)限沒(méi)變,這是錯(cuò)誤的,因?yàn)橄到y(tǒng)權(quán)限管理是以頁(yè)為單位的,VirtualProtect 會(huì)給fun1所在的所有頁(yè)都會(huì)獲取PAGE_READWRITE 權(quán)限(如果fun1的代碼橫跨兩個(gè)頁(yè)面,則這兩個(gè)頁(yè)面都會(huì)被改為PAGE_READWRITE 權(quán)限),也就是說(shuō)此時(shí)fun2的權(quán)限是PAGE_READWRITE 即只能讀或?qū)?而不能執(zhí)行,那么fun2調(diào)用將會(huì)失敗,產(chǎn)生非法操作,這時(shí)您的目標(biāo)進(jìn)程就被您成功的搞崩潰了.這也就解釋了我們用這個(gè)類掛鉤其他某些函數(shù)時(shí),一掛鉤,目標(biāo)進(jìn)程就崩潰. 還有一個(gè)問(wèn)題就是如果目標(biāo)進(jìn)程是單線程的,那么掛鉤還有沒(méi)有可能會(huì)失敗, 請(qǐng)讀者思考,我把答案直接告訴你,有可能!如果您實(shí)在想不到為什么可以向我反饋,我在這里說(shuō)出原因主要是想讓您能夠獨(dú)立思考,授人以魚不如授人以漁.


            解決方案:在VirtualProtect指定要獲取的操作權(quán)限時(shí)把PAGE_EXECUTE_WRITECOPY 或PAGE_EXECUTE_READWRITE,也就是加上EXECUTE可執(zhí)行權(quán)限,就行了,到此我們的主要問(wèn)題就解決了,但是別忘了你現(xiàn)在瀏覽的是Duwen的C++博客,所以我設(shè)計(jì)了CSTAPIHook類 意思就是 CPP Single Thread API Hook 的意思:


            /************************************************************************

              Description :   SINGLE THREAD API Hook Class( CSTAPIHook)
              Notices      :   Copyright (c) Duwen
              TIME         :   4/27/2012
            ************************************************************************/

            #ifndef _STAPIHOOK_
            #define _STAPIHOOK_
            #include<windows.h>

            /*************************************************************
              NOTE: This C++ class is not thread safe. it's only allowed to hook SINGLE thread
              API.  once you using this class to hook multiple threads accessible API, it maybe
              lead to your whole system run abnormally.
            ************************************************************
            */

            // CSTAPIHook (Single Thread API Hook)
            class CSTAPIHook
            {
            public:
                CSTAPIHook(LPSTR pszModName, LPSTR pszFuncName, PROC pfnHook);
                ~CSTAPIHook();
                void Hook();// hook   
                void Unhook();// Cancel hook
            private:
                PROC m_pfnOrig;              // Destination API address.
                BYTE m_btNewBytes[8];    // the new start 8B data of the original API .
                BYTE m_btOldBytes[8];        // the old start 8B data of the original API .
                HMODULE m_hModule;      // DLL module handle.
            };
            #endif 
            #include "stdafx.h"
            #include "STAPIHOOK.h"

            CSTAPIHook::CSTAPIHook(LPSTR pszModName, LPSTR pszFuncName, PROC pfnHook)
            {
                // jmp eax == 0xFF, 0xE0
                BYTE btNewBytes[8] = { 0xB8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00 };
                memcpy(m_btNewBytes, btNewBytes, 8);
                //save our hook function address to btNewBtytes[1]~ btNewBtytes[4]
                *((DWORD *)(m_btNewBytes + 1)) = reinterpret_cast<DWORD>(pfnHook);

                // Loading dll module.
                m_hModule = ::LoadLibraryA(pszModName);
                if(m_hModule == NULL)
                {
                    m_pfnOrig = NULL;
                    return;
                }
                m_pfnOrig =(PROC)(::GetProcAddress(m_hModule, pszFuncName));

                // Revising the original  8B data of the hook_function start entry.
                if(m_pfnOrig != NULL)
                    Hook();
            }

            void CSTAPIHook:: Hook()
            {
                DWORD dwOldProtect=0;
                ::VirtualProtect((void *)m_pfnOrig, 8, PAGE_EXECUTE_WRITECOPY, &dwOldProtect);
                 memcpy(m_btOldBytes,(void *) m_pfnOrig, 8);// Save the original 8B data.
               
            // Write the new 8B data to the hook_function entry .
                ::WriteProcessMemory(::GetCurrentProcess(), (void *)m_pfnOrig, m_btNewBytes, 8, NULL);
                //Recover the page protection property.
                ::VirtualProtect((void*)m_pfnOrig,8, dwOldProtect, 0);

            }


            CSTAPIHook::~CSTAPIHook()
            {
                Unhook();
                if(m_hModule != NULL)
                    ::FreeLibrary(m_hModule);
            }

            void CSTAPIHook::Unhook()
            {
                if(m_pfnOrig != NULL)
                {
                    DWORD dwOldProtect;
                    ::VirtualProtect(m_pfnOrig, 8, PAGE_EXECUTE_WRITECOPY, &dwOldProtect);
                    // write the original 8B data
                    ::WriteProcessMemory(::GetCurrentProcess(), (void *)m_pfnOrig, m_btOldBytes, 8, NULL);
                    ::VirtualProtect(m_pfnOrig, 8, dwOldProtect , 0);
                }
            }

            /*********************END FILE******************************/

            上面的代碼我給您使用以及傳播的權(quán)利,但我不希望以后有人說(shuō)我是在那里抄的xx的,所以轉(zhuǎn)載或使用請(qǐng)說(shuō)明Copyright (c) Duwen,請(qǐng)您尊重原創(chuàng),支持原創(chuàng).也希望廣大網(wǎng)友支持.
            posted on 2012-05-08 20:41 demons 閱讀(1757) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Program in Windows

            <2025年8月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            31123456

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            文章分類

            文章檔案

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久久久人妻精品一区三寸蜜桃| 久久精品无码免费不卡| 蜜臀久久99精品久久久久久小说| 国产欧美久久久精品影院| 久久香蕉超碰97国产精品| 91久久九九无码成人网站| 久久久久久久波多野结衣高潮 | 久久99精品久久久久婷婷| 久久综合综合久久狠狠狠97色88| 久久综合给合综合久久| 无码人妻久久一区二区三区免费 | 亚洲国产精品无码久久久不卡| 精品久久久久香蕉网| 久久WWW免费人成—看片| 亚洲成色WWW久久网站| 亚洲国产成人久久精品动漫| 久久精品国产亚洲av麻豆图片| 久久精品无码午夜福利理论片| 久久久精品久久久久特色影视| 久久亚洲中文字幕精品有坂深雪| 久久久久综合中文字幕| 日本精品久久久久中文字幕| 久久AV高潮AV无码AV| 青青热久久国产久精品| 久久精品国产一区| 久久午夜伦鲁片免费无码| 狠狠色综合网站久久久久久久高清| 国产成人精品久久综合| 91久久精品国产成人久久| 久久亚洲精品视频| 嫩草影院久久国产精品| 99久久国产综合精品麻豆| 久久久久人妻一区精品色 | 久久91这里精品国产2020| 久久青青草原国产精品免费 | 一本色道久久88—综合亚洲精品| 欧美国产成人久久精品| 欧美激情精品久久久久久久| 国产一区二区久久久| 亚洲成色WWW久久网站| 成人久久精品一区二区三区|