• <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>

            S.l.e!ep.¢%

            像打了激速一樣,以四倍的速度運轉,開心的工作
            簡單、開放、平等的公司文化;尊重個性、自由與個人價值;
            posts - 1098, comments - 335, trackbacks - 0, articles - 1
              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

            用 Thunk 實現 COM 的掛鉤

            Posted on 2010-02-06 17:05 S.l.e!ep.¢% 閱讀(619) 評論(-1)  編輯 收藏 引用 所屬分類: COM

            用 Thunk 實現 COM 的掛鉤

            您可以任意轉載這篇文章,但請在轉載時注明原始鏈接和作者,謝謝。

            本文鏈接: http://blog.titilima.com/show-557-1.html
            相關分享: 收藏到QQ書簽 ?添加到百度搜藏 ?新浪微博分享 ?提交天天網摘

            COM 的掛鉤其實已經是一個很古老的話題了,其核心技術就是替換 COM 對象虛表中相應位置的函數指針,從而達到掛鉤的效果。順便說一句,這個方法和內核的 SSDT 掛鉤是十分類似的。其相應的實現代碼也十分簡單,如下所示:

            C++代碼
            1. typedef ? struct ?_tagHookHelper?{ ??
            2. ???? PVOID *?vptr; ??
            3. }?HOOKHELPER,?*PHOOKHELPER; ??
            4. ??
            5. PVOID ?WINAPI?LSetComHook( ??
            6. ????IUnknown*?unk, ??
            7. ???? int ?index, ??
            8. ???? PVOID ?pfnHook) ??
            9. { ??
            10. ????PHOOKHELPER?p?=?(PHOOKHELPER)unk; ??
            11. ???? PVOID ?ret?=?p->vptr[index]; ??
            12. ??
            13. ???? DWORD ?dwOldProtect; ??
            14. ????VirtualProtect(&p->vptr[index],? sizeof ( PVOID ),?PAGE_READWRITE, ??
            15. ????????&dwOldProtect); ??
            16. ????p->vptr[index]?=?pfnHook; ??
            17. ????VirtualProtect(&p->vptr[index],? sizeof ( PVOID ),?dwOldProtect,?NULL); ??
            18. ???? return ?ret; ??
            19. }??

            需要指出的是,這里要使用 VirtualProtect 改變虛表的頁面屬性,就像掛鉤 SSDT 時要改變 cr0 的保護屬性一樣。
            整個的掛鉤過程及使用類似于這個樣子:

            C++代碼
            1. typedef ? HRESULT ?(STDCALL?*?QIPtr)(IUnknown*?This,?REFIID?riid,? PVOID *?ppv); ??
            2. ??
            3. QIPtr?g_pfnQueryInterface?=?NULL; ??
            4. ??
            5. HREUSLT?STDCALL?HookQueryInterface(IUnknown*?This,?REFIID?riid,? PVOID *?ppv) ??
            6. { ??
            7. ???? HRESULT ?hr?=?g_pfnQueryInterface(This,?riid,?ppv); ??
            8. ????OutputDebugString(_T( "HookQueryInterface.\n" )); ??
            9. ???? return ?hr; ??
            10. } ??
            11. ??
            12. IUnknown*?punk?=?NULL; ??
            13. //?CoCreateInstance.... ??
            14. g_pfnQueryInterface?=?(QIPtr)LSetComHook(punk,?0,?HookQueryInterface); ??
            15. punk->QueryInterface(...);??

            這種掛鉤的方式有一個局限性,就是掛鉤函數 HookQueryInterface 不能作為一個非 static 的類成員函數來實現。與之類似,Win32 的 WNDPROC 也無法使用非 static 的類成員函數來封裝,實乃一大憾事。

            當然,我們可以通過非常規的方法來解決這個問題,比如 thunk。
            在開始實現我的 thunk 之前,先來看看一個 COM 方法調用的過程,考慮如下代碼:

            C++代碼
            1. class ?A ??
            2. { ??
            3. public : ??
            4. ???? virtual ? void ?WINAPI?foo( int ?i); ??
            5. ???? int ?m_n; ??
            6. }; ??
            7. ??
            8. void ?WINAPI?A::foo( int ?i) ??
            9. { ??
            10. ????printf( "m_n?=?%d,?i?=?%d\n" ,?m_n,?i); ??
            11. } ??
            12. ??
            13. A?a; ??
            14. A*?pa?=?&a; ??
            15. pa->m_n?=?1; ??
            16. pa->foo(2);??

            這個調用過程所對應的匯編代碼為:

            反匯編代碼
            1. push????????2 ??
            2. mov?????????eax,dword?ptr?[pa] ??
            3. ;?vptr ??
            4. mov?????????ecx,dword?ptr?[eax] ??
            5. ;?this ??
            6. mov?????????edx,dword?ptr?[pa] ??
            7. push????????edx ??
            8. mov?????????eax,dword?ptr?[ecx] ??
            9. call????????eax??

            也就是說,一個 COM 方法調用的壓棧順序為:

            1. 由右至左的各個參數,也就是 STDCALL 調用約定的壓棧順序;
            2. this 指針;
            3. 當然,還有 call 的返回地址,這個壓棧是在 call 指令內部完成的。

            從上面可以看出來,為了把一個 COM 調用重定向到我們自己的類成員函數中,需要做以下工作:

            1. 保留原 COM 方法的各個參數;
            2. 保留原 COM 對象的 this 指針;
            3. 加入我們自己類對象的 this 指針;
            4. 保留 call 原有的返回地址。

            簡單說來,這個重定向的過程是將堆棧中插入另外一個 this 指針,僅此而已。
            明確了這個操作的步驟,我們可以寫出如下的 thunk 代碼,這段代碼將被放到目標 COM 對象的虛表中。

            匯編代碼
            1. ;?彈出?call?的返回地址 ??
            2. pop?eax ??
            3. ;?加入自己的?this?指針 ??
            4. push?this ??
            5. ;?重新壓入?call?的返回地址 ??
            6. push?eax ??
            7. ;?跳至掛鉤函數之中 ??
            8. jmp?addr??

            相應地,我們為這個 thunk 定義一個結構:

            C++代碼
            1. #pragma?pack(push,?1) ??
            2. typedef ? struct ?_tagHookThunk?{ ??
            3. ???? BYTE ?PopEax;?? //?0x58 ??
            4. ???? BYTE ?Push;???? //?0x68 ??
            5. ???? PVOID ?This; ??
            6. ???? BYTE ?PushEax;? //?0x50 ??
            7. ???? BYTE ?Jmp;????? //?0xe9 ??
            8. ???? PBYTE ?Addr; ??
            9. }?HOOKTHUNK,?*PHOOKTHUNK; ??
            10. #pragma?pack(pop) ??

            以及一個用于保存掛鉤信息的結構:

            C++代碼
            1. typedef ? struct ?_tagComHook?{ ??
            2. ????HOOKTHUNK?Thunk; ??
            3. ???? PVOID *?vptr; ??
            4. ???? int ?index; ??
            5. ???? PVOID ?pfnOriginal; ??
            6. }?COMHOOK;??

            最后,就可以實現這個升級版的掛鉤函數了,如下:

            C++代碼
            1. HCOMHOOK?WINAPI?LSetComHook( ??
            2. ????IUnknown*?unk, ??
            3. ???? int ?index, ??
            4. ???? PVOID ?This, ??
            5. ???? PVOID ?pfnHook, ??
            6. ???? PVOID *?pfnOriginal) ??
            7. { ??
            8. ????PHOOKHELPER?p?=?(PHOOKHELPER)unk; ??
            9. ??
            10. ????HCOMHOOK?h?=? new ?COMHOOK; ??
            11. ???? //?pop?eax ??
            12. ????h->Thunk.PopEax?=?0x58; ??
            13. ???? //?push?this ??
            14. ????h->Thunk.Push?=?0x68; ??
            15. ????h->Thunk.This?=?This; ??
            16. ???? //?push?eax ??
            17. ????h->Thunk.PushEax?=?0x50; ??
            18. ???? //?jmp?addr ??
            19. ????h->Thunk.Jmp?=?0xe9; ??
            20. ????h->Thunk.Addr?=?( PBYTE )(( int )pfnHook?-?( int )h?-? sizeof (HOOKTHUNK)); ??
            21. ????::FlushInstructionCache(::GetCurrentProcess(),?&h->Thunk, ??
            22. ???????? sizeof (HOOKTHUNK)); ??
            23. ??
            24. ????h->vptr?=?p->vptr; ??
            25. ????h->index?=?index; ??
            26. ????h->pfnOriginal?=?LSetComHook(unk,?index,?&h->Thunk); ??
            27. ??
            28. ????*pfnOriginal?=?h->pfnOriginal; ??
            29. ???? return ?h; ??
            30. }??

            測試代碼如下,使用 B 類中的 hook_foo 掛鉤了上文中的 A::foo。

            C++代碼
            1. typedef ? void ?(WINAPI?*?ptr)(A*?This,? int ?i); ??
            2. ??
            3. class ?B ??
            4. { ??
            5. public : ??
            6. ???? void ?WINAPI?hook_foo(A*?This,? int ?i); ??
            7. ????ptr?pfn; ??
            8. }; ??
            9. ??
            10. void ?WINAPI?B::hook_foo(A*?This,? int ?i) ??
            11. { ??
            12. ????puts( "hooked?by?B" ); ??
            13. ????pfn(This,?i); ??
            14. } ??
            15. ??
            16. B?b; ??
            17. HCOMHOOK?h?=?LSetComHook((IUnknown*)pa,?0,?&b, ??
            18. ????member_cast< PVOID >(&B::hook_foo),?( PVOID *)&b.pfn); ??
            19. pa->foo(2);??

            其中 member_cast 用于非 static 成員的類型轉換,可以參考《獲取成員函數的指針》一文,再次感謝 likunkun 所提供的優雅解決方案。
            全部示例代碼見附件。

            附件: comhook.zip (3.52 K, 下載次數:210)

            Tags: c/c++, win32, com

            久久精品国产一区二区三区| 狠狠色丁香婷婷久久综合不卡| 久久天天躁狠狠躁夜夜躁2O2O| 久久久久久久免费视频| 亚洲精品视频久久久| 中文字幕精品久久| 亚洲一级Av无码毛片久久精品| 无码人妻久久一区二区三区蜜桃| 国产激情久久久久影院| 国内精品久久久久影院网站| 久久久久99精品成人片| 看全色黄大色大片免费久久久 | 亚洲精品成人网久久久久久| 久久人人爽人人爽人人片AV麻豆| 一级做a爰片久久毛片16| 久久精品国产亚洲av瑜伽| 久久综合色区| 久久久久亚洲AV无码专区体验| 99久久国产综合精品麻豆| 国产精品永久久久久久久久久| 久久久国产精品| 亚洲色婷婷综合久久| 97精品伊人久久大香线蕉app| 天天综合久久久网| 性高朝久久久久久久久久| 少妇精品久久久一区二区三区 | 日韩亚洲国产综合久久久| 久久精品国产亚洲AV忘忧草18| 久久精品www人人爽人人| 久久久久人妻一区精品| 色8久久人人97超碰香蕉987| 精品久久久久久久久久久久久久久| 色悠久久久久久久综合网| 久久精品人人做人人爽电影蜜月 | 国产99久久九九精品无码| 午夜精品久久久久9999高清| 久久丫精品国产亚洲av| 免费精品久久久久久中文字幕| 久久综合给久久狠狠97色| 久久噜噜久久久精品66| 久久99毛片免费观看不卡|