青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

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.¢% 閱讀(631) 評論(-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

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            久久久亚洲影院你懂的| 亚洲精品在线视频观看| 久久精品国产一区二区三| 欧美黄色成人网| 激情亚洲网站| 免费成人小视频| 久久永久免费| 欧美日韩精选| 午夜视频精品| 午夜精品久久久久久久久久久久久 | 久久爱另类一区二区小说| 欧美国产激情二区三区| 欧美激情a∨在线视频播放| 在线观看日韩av先锋影音电影院| 欧美激情精品久久久久久黑人| 亚洲一级在线观看| 一区久久精品| 久久手机精品视频| 亚洲素人一区二区| 亚洲网址在线| 99精品视频一区| 玖玖在线精品| 久久99在线观看| 欧美一区日韩一区| 亚洲欧美日韩精品在线| 欧美伊人久久久久久午夜久久久久| 激情综合五月天| 在线看视频不卡| 亚洲国产欧美国产综合一区| 99riav1国产精品视频| 欧美一区二区三区视频免费| 99riav久久精品riav| 欧美一级视频| 欧美精品一区二区三区蜜臀| 好吊色欧美一区二区三区四区 | 欧美亚洲免费在线| 夜色激情一区二区| 久久一区二区三区超碰国产精品| 牛牛影视久久网| 亚洲精品乱码久久久久久日本蜜臀| 欧美一区二区三区精品| 9久草视频在线视频精品| 亚洲风情亚aⅴ在线发布| 欧美一级黄色网| 亚洲欧洲99久久| 亚洲少妇一区| 国产亚洲精品久久久久婷婷瑜伽 | 在线不卡中文字幕| 亚洲天堂av电影| 一区二区三区成人精品| 欧美日韩国产综合视频在线| 欧美激情视频在线播放| 日韩天堂在线视频| 欧美午夜视频一区二区| 久久电影一区| 99一区二区| 久久女同精品一区二区| 一本到高清视频免费精品| 国产精品久久久久国产精品日日| 午夜在线一区| 99精品久久久| 欧美激情久久久| 久久久精品国产99久久精品芒果| 亚洲欧洲一区二区天堂久久| 国产精品尤物| 国产精品久久久久一区二区| 欧美成人国产一区二区| 亚洲欧美久久久| 一区二区三区四区五区视频| 欧美不卡一区| 欧美大片免费久久精品三p | 亚洲精品中文字幕有码专区| 亚洲第一视频| 久久成人精品电影| 欧美在线观看你懂的| 一区二区三区久久| 日韩小视频在线观看| 亚洲国产天堂久久综合网| 激情综合在线| 悠悠资源网亚洲青| 亚洲高清自拍| 99在线精品观看| 99综合精品| 亚洲在线播放| 久久久精品一品道一区| 亚洲视频1区| 销魂美女一区二区三区视频在线| 亚洲一区二区少妇| 久久成人精品无人区| 久久综合导航| av成人动漫| 久久国产精品99国产| 男人的天堂成人在线| 欧美裸体一区二区三区| 国产精品天天摸av网| 在线成人黄色| 亚洲免费人成在线视频观看| 久久aⅴ乱码一区二区三区| 欧美激情一区| 久久高清免费观看| 欧美日本高清视频| 一区二区在线观看视频在线观看| 亚洲精品乱码久久久久久日本蜜臀| 一区二区国产精品| 欧美激情一区二区三区在线视频观看 | 久久精品免费播放| 亚洲国产成人不卡| 欧美一区二区三区视频在线观看| 久久亚洲影音av资源网| 亚洲免费视频成人| 欧美中文在线观看国产| 亚洲一区三区在线观看| 久久久久久久综合| 亚洲一区国产一区| 国产精品久久久久久久久婷婷 | 亚洲欧美日韩国产一区二区三区| 欧美v国产在线一区二区三区| 国内一区二区三区在线视频| 午夜综合激情| 亚洲欧美综合v| 好看的av在线不卡观看| 欧美自拍偷拍午夜视频| 篠田优中文在线播放第一区| 国产精品一区二区你懂的| 欧美专区第一页| 久久久91精品| 99精品欧美| 亚洲在线播放| 亚洲国产第一页| 99天天综合性| 国内综合精品午夜久久资源| 久久久亚洲精品一区二区三区| 久久久久久久久蜜桃| 一二美女精品欧洲| 午夜欧美视频| 亚洲乱码国产乱码精品精| 一本色道久久综合亚洲精品高清 | 欧美a一区二区| 欧美性久久久| 亚洲第一天堂无码专区| 国产精品久久99| 欧美不卡视频| 国际精品欧美精品| 99在线热播精品免费99热| 国内精品视频久久| 一区二区三区欧美激情| 日韩亚洲一区二区| 欧美在线一二三区| 欧美高清在线精品一区| 久久精品亚洲精品国产欧美kt∨| 欧美插天视频在线播放| 久久九九热re6这里有精品| 欧美片在线观看| 亚洲国产精品精华液2区45 | 国产一区二区三区不卡在线观看 | 国产视频一区三区| 亚洲一区欧美一区| 亚洲欧美日韩一区二区| 欧美性做爰猛烈叫床潮| 一本色道久久综合亚洲精品婷婷| 亚洲乱码国产乱码精品精98午夜| 久久久中精品2020中文| 美女999久久久精品视频| 国产日韩精品在线播放| 亚洲欧美日韩一区二区| 欧美一区二区精品| 国内精品伊人久久久久av影院| 香蕉乱码成人久久天堂爱免费| 午夜国产精品影院在线观看| 国产欧美韩日| 久热精品视频在线| 日韩视频精品在线| 久久精品99久久香蕉国产色戒| 雨宫琴音一区二区在线| 免费在线日韩av| 亚洲一区二区免费| 老鸭窝毛片一区二区三区| 亚洲高清在线精品| 国产精品一区久久| 欧美大片免费观看在线观看网站推荐| 亚洲国产老妈| 久久精品30| 亚洲一区综合| 亚洲精品欧美日韩| 国产视频一区在线| 欧美日韩在线播| 久久综合亚州| 久久精品国产精品亚洲综合| 91久久国产精品91久久性色| 欧美一区二区在线播放| 亚洲精品一区二区网址| 国产九九精品视频| 欧美日韩精品一二三区| 久久久久久久综合狠狠综合| 一本久久a久久精品亚洲| 亚洲国产高清一区二区三区| 国产亚洲二区| 韩国女主播一区| 国产色综合网| 韩日视频一区|