一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

也說說Thunk - 桃之夭夭,灼灼其華.

 ShaneWu 2009-11-18

面向?qū)ο笫莻€(gè)好東西,用接近世界的方式抽象程序世界,直觀。

全局函數(shù)(或許我應(yīng)該特指Windows API)也是好東西,要什么調(diào)什么,毫不含糊.

那么,當(dāng)他們走到一起,矛盾就產(chǎn)生

類時(shí)刻保護(hù)著自己的成員,以至于為每一個(gè)方法加入一個(gè)指向自己的指針.

比如有以下類

 

1class TestClass()
2{
3    void Func();
4}
;

 

則Func被編譯器安插了this以針,以便Func內(nèi)部可以訪問類TestClass的成員變量,即Func變?yōu)槿缦聵幼?/p>

 

1void Func(TestClass* this);

 

在實(shí)際的開發(fā)中,使用API時(shí)常常會(huì)要求我們提供回調(diào)函數(shù),比如SetTimer,我們需要設(shè)置向這個(gè)API提供一個(gè)如下類型的函數(shù)指針:

 

1typedef VOID (CALLBACK* TIMERPROC)(HWND, UINT, UINT_PTR, DWORD);

 

假如我們有如下類

1class TestClass()
2{
3    void OnTimeProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
4    {
5        //do something
6    }

7}

8
9

 

 

并希望將成員函數(shù)

 

1void OnTimeProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime );


設(shè)為API:

 

1UINT_PTR
2WINAPI
3SetTimer(
4    __in_opt HWND hWnd,
5    __in UINT_PTR nIDEvent,
6    __in UINT uElapse,
7    __in_opt TIMERPROC lpTimerFunc); 
8
9

 

的第四個(gè)參數(shù),以便定時(shí)器的時(shí)間到時(shí),我們的類成員函數(shù)TestFunc:OnTimerProc被調(diào)用。

根據(jù)最前面對(duì)Func的分析,在編譯時(shí),OnTimerProc會(huì)被安插this指針,變成如下形式:

 

1void OnTimeProc(TestClass *this, HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime );

 

很顯然,我們無法直接設(shè)置。

那我們應(yīng)該怎么做呢,在完成這個(gè)任務(wù)之前,讓我們先看一下一個(gè)稍簡(jiǎn)單一點(diǎn)的例子,用以說明Thunk原理。

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

這樣說起來就很簡(jiǎn)單.

相信對(duì)于后一個(gè)操作:將一個(gè)函數(shù)指針指向這幾個(gè)字節(jié)的第一個(gè)字節(jié)我們都應(yīng)該會(huì):

比如有結(jié)構(gòu)體:

 

1typedef struct thunk
2{
3    DWORD    dwMovEsp;
4    DWORD    dwThis;
5    BYTE    bJmp;
6    DWORD    dwRealProc; 
7
8}
THUNK; 
9

 

函數(shù)指針:

 

1typedef void (*FUNC)(DWORD dwThis); 

 

則如下代碼將一個(gè)thunk的結(jié)構(gòu)體強(qiáng)轉(zhuǎn)為FUNC型的函數(shù)指針:

 

1THUNK testThunk; 
2
3FUNC fun = (FUNC)&testThunk; 
4
5fun(NULL);//先設(shè)為NULL 
6

 

這樣,系統(tǒng)便會(huì)把testThunk所指向的內(nèi)存加載到緩沖中。

現(xiàn)在的總是是將這個(gè)結(jié)構(gòu)體設(shè)為多少比較好?

在x86 指令集中,我們可以查到:

匯編指令JMP為0xe9

所以,我們寫下如下函數(shù)用于設(shè)置這個(gè)結(jié)構(gòu)體的值:

 

1void Init(DWORD proc,void* pThis)
2    {
3        dwMovEsp = 0x042444C7;  //C7 44 24 04
4        dwThis = (DWORD)pThis;
5        bJmp = 0xe9;
6        dwRealProc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(thunk)));
7        FlushInstructionCache(GetCurrentProcess(),this,sizeof(thunk));
8    }

 

前兩行用于將pThis指針壓棧,接下來的兩句用于設(shè)置跳轉(zhuǎn)的相對(duì)地址。最后一個(gè)是更新緩存(說實(shí)話,我個(gè)人覺得這句在這種情況下是可有可無的,但也可能是我認(rèn)識(shí)不夠深,望指教)。

整個(gè)代碼如下:

Code

 

測(cè)試成功,接下來是將thunk技術(shù)應(yīng)用到實(shí)際中,就是一開始提出的問題。

首先,要使用定時(shí)器的功能,肯定要調(diào)用API:SetTimer,而調(diào)用這個(gè)API需要一個(gè)如下簽名的函數(shù)指針:typedef VOID (CALLBACK* TIMERPROC)(HWND, UINT, UINT_PTR, DWORD);因此,我們要做的,就是利用Thunk技術(shù),讓這個(gè)回調(diào)函數(shù)調(diào)用我們的類的成員方法。

我們可以用一個(gè)代理類來完成這一系列的工作,然后我們的真正的業(yè)務(wù)邏輯類就繼承自這個(gè)代理類。

現(xiàn)在想想這個(gè)代理類要完成這個(gè)任務(wù)需要那些數(shù)據(jù)?

首先,他要知道當(dāng)他被API回調(diào)時(shí),他應(yīng)該調(diào)用哪一個(gè)類的成員方法,類的面員方法的函數(shù)指針時(shí)需要指定類類型。如下所示:

 

1void (Base:: *  )( HWND , UINT , UINT , DWORD ); 

 

看到這里,相信任何一個(gè)初級(jí)的剛?cè)腴T的c++程序員都可以快速的寫下以下類:

 

 1class SimpleTest;
 2class SimpleTimerAdapter
 3{
 4public:
 5    CALLBACKThunk thunk;
 6    typedef void (SimpleTest::*func)(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime );
 7    typedef func MemberCallBackType;
 8    MemberCallBackType mTimerProc; 
 9
10    void Init(TIMERPROC proc, void* pThis,int nPos = 0)
11    {
12        assert(pThis != NULL);
13        if(pThis)
14        {
15            thunk.m_mov = 0x042444C7//C7 44 24 04, here 04 is first param ,08 is second
16            thunk.m_this = (DWORD)pThis;
17            thunk.m_jmp = 0xe9;
18            thunk.m_relproc = (int)proc - ((int)this + sizeof(CALLBACKThunk));
19        }

20    }
 
21
22    TIMERPROC MakeCallback(MemberCallBackType lpfn,void* pThis, int nPos = 0)
23    {
24        assert(pThis);
25        if (pThis)
26        {
27            Init(DefaultCallBackProc, pThis ,nPos);
28            mTimerProc = lpfn;
29            return (TIMERPROC)&thunk;
30        }

31        return NULL;
32    }
 
33
34    UINT_PTR SetTimer(UINT uElapse, MemberCallBackType lpTimerFunc)
35    {
36        return ::SetTimer(NULL, 0, uElapse, MakeCallback(lpTimerFunc,this));
37    }
 
38
39    BOOL KillTimer(UINT_PTR uIDEvent)
40    {
41        return ::KillTimer(NULL, uIDEvent);
42    }
 
43
44    static void CALLBACK DefaultCallBackProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
45    {
46        (BaseType(hwnd)->*MemberFuncType(hwnd))(0, uMsg, idEvent, dwTime);
47    }
 
48
49    static SimpleTest* BaseType(void* pThis)
50    {
51        return reinterpret_cast<SimpleTest*>(pThis);
52    }
 
53
54    template <class T>
55    static MemberCallBackType MemberFuncType(T pThis)
56    {
57        return reinterpret_cast<SimpleTest*>(pThis)->mTimerProc;
58    }

59}

60
61  
62
63  
64
65class SimpleTest : public SimpleTimerAdapter
66{
67public:
68    bool mQuit; 
69
70    void TimerProc2(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
71    {
72        mQuit = true;
73        KillTimer( idEvent);
74        printf("good! %d\n", idEvent);
75    }

76}

77
78
79 
80

 

然后在MAIN中寫下測(cè)試代碼:

 

 1int main(void)
 2{
 3    SimpleTest a;
 4    a.mQuit = false;
 5    SetTimer(NULL, 01000, a.MakeCallback(&SimpleTest::TimerProc2,&a)); 
 6
 7    MSG msg;
 8    while(!a.mQuit && GetMessage(&msg, 000) )
 9    {
10        printf("before dispatch!\n");
11        DispatchMessage(&msg);
12    }
 
13
14    system("pause");
15    return 0;
16}
 
17
18

 

以上方法確實(shí)可以完成任務(wù),但僅限于完成這一個(gè)任務(wù)而已,

甚至,在一個(gè)類中SimpleTimeAdapter中寫出了這樣的代碼:

 

Code

 

我寫出這段代碼只是為了更清楚的顯示代理類是如何工作的,除此之外,以上代碼,沒有任何作用,為真正的純垃圾代碼

為了抽象出一個(gè)中間代理類,我們需要用到模板,對(duì)于上面提到的定義問題,用模板可以很輕松的解決。同時(shí),把最基本的內(nèi)容從代理類中抽象出來。于是得以以下三個(gè)類:

 

 

 1/*
 2* class Base,最終的功能類,目地的要跳轉(zhuǎn)到Base的成員函數(shù)中去
 3* class Impl,中間類,界于Adapt與Base之間
 4* MemberCallBackType Base的成員函數(shù)
 5* CallBackType,我們給API的回調(diào)函數(shù)
 6*/

 7template <class Base, class Impl, class MemberCallBackType, class CallBackType>
 8class CallBackAdapter
 9{
10protected:
11    typedef CallBackAdapter<Base, Impl, MemberCallBackType, CallBackType> SelfType;
12    typedef MemberCallBackType BaseMemberCallBackType; 
13
14    CALLBACKThunk thunk; 
15
16    void Init(CallBackType proc, SelfType* pThis,int nPos = 0)
17    {
18        thunk.m_mov = 0x042444C7//C7 44 24 04, here 04 is first param ,08 is second
19        thunk.m_this = (DWORD)pThis;
20        thunk.m_jmp = 0xe9;
21        thunk.m_relproc = (int)proc - ((int)this + sizeof(CALLBACKThunk));
22    }
 
23
24    CallBackType _CallBackProcAddress(void){
25        return (CallBackType)&thunk;
26    }

27public:
28    template <class T>
29    static Base* BaseType(T pThis){
30        return reinterpret_cast<Base*>(pThis);
31    }
 
32
33    template <class T>
34    static MemberCallBackType MemberFuncType(T pThis){
35        return reinterpret_cast<SelfType*>(pThis)->mTimerProc;
36    }
 
37
38    MemberCallBackType mTimerProc; 
39
40    operator CallBackType()
41
42        Init(&Impl::DefaultCallBackProc, this);
43        mTimerProc = &Base::TimerProc;
44        return (CallBackType)&thunk;
45    }

46    CallBackType MakeCallback(MemberCallBackType lpfn,int nPos = 0)
47
48        Init(&Impl::DefaultCallBackProc, this,nPos);
49        mTimerProc = lpfn;
50        return (CallBackType)&thunk;
51    }

52}

53
54 
55
56 
57
58 
59
60template <class Base>
61class TimerAdapter : public CallBackAdapter<
62                            Base,
63                            TimerAdapter<Base>,
64                            void (Base:: *  )( HWND , UINT , UINT , DWORD ),
65                            void (CALLBACK *)( HWND , UINT , UINT , DWORD )>
66{
67public:
68    typedef typename TimerAdapter<Base>::BaseMemberCallBackType MemCallBackType; 
69
70    UINT_PTR SetTimer(UINT uElapse, MemCallBackType lpTimerFunc)
71    {
72        return ::SetTimer(NULL, 0, uElapse, MakeCallBackProc(lpTimerFunc));
73    }
 
74
75    BOOL KillTimer(UINT_PTR uIDEvent)
76    {
77        return ::KillTimer(NULL, uIDEvent);
78    }
 
79
80    static void CALLBACK DefaultCallBackProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime )
81    {
82        (BaseType(hwnd)->*MemberFuncType(hwnd))(0, uMsg, idEvent, dwTime);
83        //(Base*)(hwnd)->*(reinterpret_cast<Base*>(hwnd)->mTimerProc)(0, uMsg, idEvent, dwTime);
84    }
 
85
86}

87
88

測(cè)試代碼如下:

 

 1int main(void)
 2{
 3    Test a;
 4    printf("timer id is %d", a.SetTimer(100&Test::TimerProc2));
 5    a.mQuit = false;
 6    SetTimer(NULL, 0100, a.MakeCallback(&Test::TimerProc2)); 
 7
 8    //SimpleTest a;
 9    //a.mQuit = false;
10    //SetTimer(NULL, 0, 1000, a.MakeCallback(&SimpleTest::TimerProc2,&a)); 
11
12    MSG msg;
13    while(!a.mQuit && GetMessage(&msg, 000) )
14    {
15        printf("before dispatch!\n");
16        DispatchMessage(&msg);
17    }
 
18
19    system("pause");
20    return 0;
21}

22

 

參考:

ATL Under the HOOK Part 5 : http://www./KB/atl/atl_underthehood_5.aspx

還有一篇也是CodeProject上的,但由于看文章的時(shí)間太久了,今天再去找時(shí)沒有找到。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    欧美成人一区二区三区在线| 99久久精品免费精品国产| 激情亚洲内射一区二区三区| 美女被啪的视频在线观看| 一区二区三区免费公开| 亚洲国产av精品一区二区| 国产又爽又猛又粗又色对黄| 精品国产一区二区欧美| 久久福利视频视频一区二区| 蜜桃av人妻精品一区二区三区| 国产精品视频久久一区| 亚洲国产性生活高潮免费视频| av一区二区三区天堂| 亚洲综合激情另类专区老铁性| 国产又粗又猛又爽又黄的文字| 日韩成人中文字幕在线一区| 一二区中文字幕在线观看 | 欧美一级内射一色桃子| 人妻久久一区二区三区精品99| 老司机精品国产在线视频| 成人精品视频一区二区在线观看| 午夜福利直播在线视频| 久一视频这里只有精品| 在线免费看国产精品黄片| 免费观看日韩一级黄色大片| 一区二区欧美另类稀缺| 丰满少妇被猛烈撞击在线视频| 日韩一区二区免费在线观看| 中文字幕日韩一区二区不卡| 欧洲日本亚洲一区二区| 国产91色综合久久高清| 国产人妻精品区一区二区三区| 亚洲国产精品国自产拍社区| 日本久久中文字幕免费| 久久99夜色精品噜噜亚洲av| 国产一区二区三区草莓av| 免费特黄欧美亚洲黄片| 日韩精品综合免费视频| 久热在线视频这里只有精品| 国产不卡视频一区在线| 日韩中文字幕狠狠人妻|