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

隨感而發(fā)

雜七雜八

統(tǒng)計(jì)

留言簿(13)

閱讀排行榜

評論排行榜

API回調(diào)成員函數(shù) THUNK

想來想去還是羅嗦一下,API只能回調(diào)全局函數(shù),而我們有時(shí)候希望他能回調(diào)成員函數(shù)。最常用的就是Timmer和窗口回調(diào)。要實(shí)現(xiàn)這個(gè)需求,就會(huì)用到THUNK技術(shù)。THUNK 我查了一下是:thunk  名詞 n.  ;,鏘 。跟這個(gè)完全沒有關(guān)系嘛(看來英文太爛是壞處還是挺多的)。學(xué)習(xí)了一下之后,我理解的意思就是:貍貓換太子。替換原來意圖,轉(zhuǎn)調(diào)我們需要的地址。

Thunk的原理其實(shí)說起來很簡單:巧妙的將數(shù)據(jù)段的幾個(gè)字節(jié)的數(shù)據(jù)設(shè)為特殊的值,然后告訴系統(tǒng),這幾個(gè)字節(jié)的數(shù)據(jù)是代碼(即將一個(gè)函數(shù)指針指向這幾個(gè)字節(jié)的第一個(gè)字節(jié)),讓系統(tǒng)來執(zhí)行

API回調(diào)成員函數(shù):

直接用成員函數(shù)的地址傳給作為API的回調(diào)函數(shù)顯然會(huì)編譯出錯(cuò)的,原因是他們的調(diào)用規(guī)則不一致,C++編譯器不允許這樣做。具體可以參考:

http://hi.baidu.com/shongbee2/blog/item/7867de9744e3c26155fb9611.html

而剛好THUNK技術(shù)就是讓數(shù)據(jù)段當(dāng)做代碼斷用,如果我把回調(diào)函數(shù)地址用一個(gè)數(shù)據(jù)段給他,然后在數(shù)據(jù)段中再跳轉(zhuǎn)到成員函數(shù)的地址。這樣就可以間接的調(diào)用成員函數(shù)了。不錯(cuò),我就是學(xué)習(xí)的這個(gè)方法。嘻嘻。。

大致方向知道了,還得了解一下細(xì)節(jié),函數(shù)調(diào)用的規(guī)則:

建議看一下http://hi.baidu.com/shongbee2/blog/item/7867de9744e3c26155fb9611.html(也就是上一篇文章啦。)需要注意的:調(diào)用者怎么處理?xiàng)#徽{(diào)用者怎么使用棧和處理?xiàng)!O到y(tǒng)回調(diào)函數(shù)基本上都是_stdcall的調(diào)用方式,成員函數(shù)是__thiscall的調(diào)用方式。他們的區(qū)別為:

關(guān)鍵字

堆棧清除

參數(shù)傳遞

__stdcall

被調(diào)用者

將參數(shù)倒序壓入堆棧(自右向左)

__thiscall

被調(diào)用者

壓入堆棧,this指針保存在 ECX 寄存器中

發(fā)現(xiàn)他們唯一不同的就是__thiscallthis指針保存到了ECX的寄存器中。其他都是一樣的。這種情況我們就方便了,我們只需在他調(diào)用我們的時(shí)候,我們吧this指針保存到ECX,然后跳轉(zhuǎn)到期望的成員函數(shù)地址就可以了。

//我認(rèn)為思路就是這樣了。接下來是實(shí)現(xiàn),貼源代碼:

#include "stdafx.h"
#include "wtypes.h"

#include <iostream>
using namespace std;

typedef void (*FUNC)(DWORD dwThis);
typedef int (_stdcall *FUNC1)(int a, int b);
#pragma pack(push,1)
typedef struct tagTHUNK
{
    BYTE    bMovEcx;   //MOVE ECX 將this指針移動(dòng)到ECX的指令
    DWORD    dwThis;   // this   this指針的地址
    BYTE    bJmp;    //jmp   跳轉(zhuǎn)指令
    DWORD    dwRealProc; //proc offset 跳轉(zhuǎn)偏移

    void Init(DWORD proc,void* pThis)
    {
   bMovEcx = 0xB9;        //注釋見下面說明^_^
        dwThis = (DWORD)pThis;
        bJmp = 0xE9;
        dwRealProc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(THUNK)));
        FlushInstructionCache(GetCurrentProcess(),this,sizeof(THUNK));
    }
}THUNK;
#pragma pack(pop)
/**************************************************************************************
void Init(DWORD proc,void* pThis)里面的說明:
0xB9 為MOVE ECX的指令, 0xE9 跳轉(zhuǎn)的指令,這段初始化表示:
0013FF54 mov         ecx, ptr [this]
0013FF59 jmp         dwRealProc
這個(gè)單步一下便知。
下面那個(gè)API :FlushInstructionCache,查MSDN,表示刷新緩存,
因?yàn)槲覀冃薷牧藬?shù)據(jù),建議他重新載入一下。

我最不能理解的是jmp的偏移是為什么是那樣計(jì)算,所以這里也著重說明一下:
jmp跳轉(zhuǎn)的是當(dāng)前指令地址的偏移,我們參數(shù)中proc是實(shí)際函數(shù)的地址,我們需要
把他轉(zhuǎn)為jmp的偏移: 實(shí)際函數(shù)地址-jmp指令地址。
實(shí)際函數(shù)地址就是proc,jmp地址就是((INT_PTR)this+sizeof(THUNK)),所以就得到
dwRealProc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(THUNK)));這行代碼
還有一點(diǎn),我對匯編不了解,下面是YY:為什么不是:
dwRealProc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(THUNK)) - sizeof(dwRealProc))
直觀上看jmp地址不是:this + sizeof(bMoveEcx) + sizeof(dwThis) + sizeof(bJmp)嗎?
也就是((INT_PTR)this+sizeof(THUNK)) - sizeof(dwRealProc) 啊。可是我看了一下編譯的結(jié)果,
發(fā)現(xiàn)0013FF59 jmp         dwRealProc 是一行的,也就是jmp地址實(shí)際就是:
((INT_PTR)this+sizeof(THUNK)) 這個(gè)地址。經(jīng)過測試也沒有問題,我就認(rèn)為是這樣了,不對的還
忘多指出。嘻嘻。
還有一個(gè)容易混淆的,就是我們會(huì)傳入this指針,在dwRealProc里面和 FlushInstructionCache
里面都用到了this。這里要注意啦:如果你不知道傳入的參數(shù)this指針和使用的這個(gè)this的話,你就該
重新復(fù)習(xí)一下C++基礎(chǔ)了。解釋一下:傳入的this指針變?yōu)閰?shù)pThis,使用的this是THUNK的this。^_^
*****************************************************************************************/

template<typename dst_type,typename src_type>
dst_type pointer_cast(src_type src)
{
return *static_cast<dst_type*>( static_cast<void*>(&src) );
}

class Test
{
public:
int m_nFirst;
    THUNK m_thunk;
    int      m_nTest;

    //構(gòu)造函數(shù)中初始化為3,僅為測試,以便查看外面的方法JmpedTest是否可以正確取得這個(gè)值
    Test() : m_nTest(3),m_nFirst(4)
    {}

    void TestThunk()
    {
   m_thunk.Init(pointer_cast<int>(&Test::Test2),this);
        FUNC1 f = (FUNC1)&m_thunk;
        f(1,2);
        cout << "Test::TestThunk()" << endl;
    }

int Test2(int a, int b)
{
   cout << a << " " << b << " " << m_nFirst << " " << m_nTest << endl;
   return 0;
}
};

int _tmain(int argc, _TCHAR* argv[])
{
    Test t;
    t.TestThunk();
    system("pause");
    return 0;
}

總結(jié):

這個(gè)明顯是暴力的去強(qiáng)制跳轉(zhuǎn),直接把指令寫入到數(shù)據(jù)段中,增加了出錯(cuò)的風(fēng)險(xiǎn),而且可移植性變的很差。所以盡量少用。

要弄清楚函數(shù)調(diào)用規(guī)則和堆棧的平衡。如果你用_cedcl規(guī)則的函數(shù)調(diào)用的話,就會(huì)出錯(cuò)啦。

學(xué)習(xí)代碼中只是處理了簡單的情況,還有幾種方式,例如不是強(qiáng)制跳轉(zhuǎn),而是用call的方式調(diào)用,也可以實(shí)現(xiàn)。對于其他的函數(shù)規(guī)則例如成員函數(shù)是_stdcall,他是參數(shù)壓棧的,這個(gè)THUNK的寫法也不一樣了。。

因?yàn)閿?shù)據(jù)段中用到了this,函數(shù)回調(diào)中會(huì)用到它,所以一定要保證這個(gè)this有效。特別是窗口回調(diào)函數(shù),如果釋放了變量,但是窗口沒有銷毀是很容易出問題的。窗口回調(diào)函數(shù)也有比較喜歡用一個(gè)靜態(tài)的分配器,通過窗口識別,把他分配到不同的成員處理函數(shù)中的方式。

這個(gè)只是初學(xué),原因是發(fā)現(xiàn)ATL的窗口回調(diào)是這樣做的。覺得很神奇,所以學(xué)習(xí)了一下,有不對的地方還望多多指教。嘻嘻。。。

找到的資料:

http://www.vckbase.com/document/viewdoc/?id=1821

http://www.codeproject.com/KB/cpp/GenericThunks.aspx

http://blog.csdn.net/superarhow/archive/2006/07/10/898261.aspx

http://www.cnblogs.com/homeofish/archive/2009/02/20/1395208.html

posted on 2010-12-11 16:14 shongbee2 閱讀(1612) 評論(0)  編輯 收藏 引用 所屬分類: c/c++

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲人成网站影音先锋播放| 欧美亚洲尤物久久| 亚洲制服欧美中文字幕中文字幕| 蜜臀久久99精品久久久久久9| 91久久夜色精品国产网站| 欧美电影免费观看大全| 亚洲天堂激情| 亚洲精品日韩一| 久久在线免费观看| 亚洲综合国产| 亚洲欧美在线免费观看| 最新日韩在线| 亚洲午夜精品在线| 亚洲欧美日韩另类| 久热re这里精品视频在线6| 99精品国产高清一区二区| 国产自产女人91一区在线观看| 欧美精品一区二区三区四区| 久久国产欧美精品| 午夜精彩国产免费不卡不顿大片| 午夜精品久久久久久久| 久久只有精品| 久久精品一区二区国产| 亚洲欧美久久久| 亚洲伊人一本大道中文字幕| 日韩视频不卡中文| 亚洲激情第一区| 一区二区三区.www| 最新国产拍偷乱拍精品 | 久久国产精品免费一区| 亚洲曰本av电影| 久久综合激情| 一本色道久久综合亚洲精品高清| 久久大香伊蕉在人线观看热2| 欧美久久久久久久| 在线看不卡av| 亚洲肉体裸体xxxx137| 亚洲第一二三四五区| 亚洲特色特黄| 亚洲欧美日韩国产综合| 欧美一区二区三区在| 欧美一区亚洲二区| 亚洲精品一区二区网址| 久久精品国产清自在天天线| 国产精品久久久久秋霞鲁丝 | 欧美午夜寂寞影院| 国产精品乱码久久久久久| 亚洲成人自拍视频| 久久精品国产久精国产爱| 亚洲精品日韩在线| 欧美电影在线观看完整版| 精品999日本| 亚洲欧洲日本mm| 久久亚洲国产精品一区二区| 亚洲视频免费| 久久久久久综合| 老司机成人在线视频| 国产伦精品一区二区三区免费迷| 国产一区视频网站| 欧美一区二区三区在线视频 | 亚洲国产高清自拍| 中文成人激情娱乐网| 亚洲黄色视屏| 一本色道久久综合狠狠躁的推荐| 男女激情久久| 亚洲国产99| 一区二区三区四区五区在线| 欧美精品粉嫩高潮一区二区| 国产精品黄色| 亚洲经典视频在线观看| 亚洲香蕉成视频在线观看| 久久久免费精品| 亚洲激情电影中文字幕| 欧美美女bbbb| 亚洲一级免费视频| 亚洲无线视频| 国产日韩精品久久| 一本色道久久综合亚洲91| 久久精品国产免费| 欧美一区二区精品久久911| 欧美高清视频www夜色资源网| 国产精品成人一区| 午夜精品久久久久久久99樱桃| 亚洲永久免费视频| 红桃视频国产一区| 亚洲欧美日韩一区二区三区在线| 一区二区三区欧美视频| 国产日韩亚洲欧美| 亚洲欧美日韩精品久久| 亚洲欧美视频在线| 亚洲国产精品黑人久久久 | 亚洲午夜免费福利视频| 国产亚洲日本欧美韩国| 亚洲在线视频网站| 性8sex亚洲区入口| 欧美偷拍一区二区| 亚洲第一精品电影| 日韩一级视频免费观看在线| 国产欧美日韩在线观看| 欧美激情一区在线| 狼人社综合社区| 在线视频日韩| 亚洲精品美女久久7777777| 麻豆精品在线观看| 亚洲一区二区三区三| 久久久久久伊人| 午夜精品区一区二区三| 免费一级欧美片在线观看| 亚洲电影av| 一区二区三区精品国产| 亚洲高清中文字幕| 亚洲夜间福利| 亚洲精品永久免费| 日韩视频在线播放| 激情久久五月| 亚洲无线视频| 一区二区三区国产盗摄| 久久综合久久综合这里只有精品 | 免费日本视频一区| 久久手机精品视频| 欧美不卡在线| 欲色影视综合吧| 欧美成人亚洲成人| 美国十次了思思久久精品导航| 一区二区三区四区蜜桃| 欧美成人激情视频| 欧美成人网在线| 好吊日精品视频| 午夜精品久久久久久久男人的天堂 | 久久精品国产2020观看福利| 国产精品v欧美精品v日韩| 最新中文字幕一区二区三区| 亚洲成人在线视频播放| 久久精品亚洲乱码伦伦中文| 久久精品国产欧美激情| 国产主播在线一区| 久久激情视频免费观看| 久久久欧美精品sm网站| 国产专区综合网| 久久久噜噜噜久久狠狠50岁| 老色鬼精品视频在线观看播放| 国内成人在线| 久久影视三级福利片| 欧美电影在线观看完整版| 亚洲国产综合视频在线观看| 免费亚洲一区| 亚洲精品久久久久久久久久久久久| 亚洲人成7777| 欧美日韩网站| 久久综合色一综合色88| 激情丁香综合| 免费不卡在线视频| 亚洲精品国产视频| 亚洲自拍高清| 国产一区999| 久久伊人免费视频| 亚洲国产婷婷| 国产在线一区二区三区四区| 久久国产精品黑丝| 欧美成人精品不卡视频在线观看| 亚洲高清中文字幕| 欧美日韩一区二区三区视频| 亚洲深夜福利网站| 久久蜜桃资源一区二区老牛| 日韩午夜在线播放| 国产伦精品一区二区三区在线观看| 久久精品道一区二区三区| 欧美激情bt| 小黄鸭精品密入口导航| 在线国产日韩| 欧美性色综合| 久久久噜噜噜久久中文字免| 日韩视频在线观看免费| 久久精品视频免费播放| 亚洲美女精品一区| 国产日韩欧美电影在线观看| 免费在线亚洲| 香蕉久久国产| 99热在线精品观看| 一区二区三区视频在线| 国产精品丝袜久久久久久app| 日韩亚洲不卡在线| 久久九九精品99国产精品| 国产欧美日韩视频一区二区| 久久一区中文字幕| 亚洲欧美日韩精品久久久久| 亚洲欧洲免费视频| 国产精品久线观看视频| 欧美女同在线视频| 日韩小视频在线观看专区| 嫩草影视亚洲| 久久9热精品视频| 国产欧美日韩在线播放| 欧美国产欧美亚洲国产日韩mv天天看完整 | 欧美jizzhd精品欧美巨大免费| 亚洲免费网站| 制服丝袜亚洲播放| 最新日韩在线视频| 亚洲高清在线视频| 美女精品网站|