ATL Thunk机制学习

  ATL模板类库使用Thunk技术来实现与窗口消息相关联的HWND和负责处理消息的对象的this指针之间的映射。      ATL中窗口类注册时,窗口过程函数缺省值都是StartWindowProc,当创建窗口产生第一条消息时将调用此函数。 StartWindowProc是CWindowImplBase的一个静态成员函数,它的工作是建立CWindowImpl派生对象的HWND与对象的 this指针之间的映射。在新的HWND被缓存到WindowImpl派生对象的成员数据中之后,对象真正的窗口过程将替代 StartWindowProc窗口过程,并且窗口过程参数HWND被替换成对象指针值。

[cpp] view plain copy

//for X86) 
PVOID __stdcall __AllocStdCallThunk(VOID);  
VOID  __stdcall __FreeStdCallThunk(PVOID);  
#pragma pack(push,1) 
struct _stdcallthunk  
{  
 DWORD   m_mov;          // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd) 
 DWORD   m_this;         // 
 BYTE    m_jmp;          // jmp WndProc 
 DWORD   m_relproc;      // relative jmp 
 BOOL Init(DWORD_PTR proc, void* pThis)  
    {  
        m_mov = 0x042444C7;  //C7 44 24 0C 
        m_this = PtrToUlong(pThis);  
        m_jmp = 0xe9;  
        m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk)));  
 // write block from data cache and 
 //  flush from instruction cache 
        FlushInstructionCache(GetCurrentProcess(), this, sizeof(_stdcallthunk));  
 return TRUE;  
    }  
 //some thunks will dynamically allocate the memory for the code 
 void* GetCodeAddress()  
    {  
 return this;  
    }  
 void* operator new(size_t)  
    {  
 return __AllocStdCallThunk();  
    }  
 void operator delete(void* pThunk)  
    {  
        __FreeStdCallThunk(pThunk);  
    }  
};  
#pragma pack(pop)  

Init() 函数中这组汇编指令被初始化为下面的指令:

mov dword ptr [esp+0x4], pThis

jmp (int)proc - ((int)this+sizeof(_WndProcThunk))

它完成的功能是,用窗口类的指针 pThis 代替窗口句柄 hWndesp+0x4 中放的就是 hWnd ),然后跳转到传入的 proc 函数处( (int)proc - ((int)this+sizeof(_WndProcThunk))procthunk 之间的距离)。

[cpp] view plain copy

static LRESULT CALLBACK StartWindowProc(HWND hWnd, UINT uMsg,  
 WPARAM wParam, LPARAM lParam)  
    {  
        CContainedWindowT< TBase >* pThis = (CContainedWindowT< TBase >*)_AtlWinModule.ExtractCreateWndData();  
        ATLASSERT(pThis != NULL);  
 if(!pThis)  
        {  
 return 0;  
        }  
        pThis->m_hWnd = hWnd;  
 // Initialize the thunk.  This was allocated in CContainedWindowT::Create, 
 // so failure is unexpected here. 
        pThis->m_thunk.Init(WindowProc, pThis);  
        WNDPROC pProc = pThis->m_thunk.GetWNDPROC();  
        WNDPROC pOldProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pProc);  
#ifdef _DEBUG 
 // check if somebody has subclassed us already since we discard it 
 if(pOldProc != StartWindowProc)  
            ATLTRACE(atlTraceWindowing, 0, _T("Subclassing through a hook discarded./n"));  
#else 
        pOldProc;    // avoid unused warning 
#endif 
 return pProc(hWnd, uMsg, wParam, lParam);  
    }  

以下是关于Thunk技术测试的例子

[cpp] view plain copy

//涂远东 2009 09 17 深圳 
//声明函数类型。 
typedef void (*TESTFUN)(void*);  
//定义修改代码的结构。 
#pragma pack(push,1) 
struct Thunk  
{  
 DWORD   m_mov;          // 修改参数指令 
 DWORD   m_this;         //修改后的参数 
 BYTE    m_jmp;      // jmp TESTFUN,跳转指令。 
 DWORD   m_relproc;  // relative jmp,相对跳转的位置。 
 //初始化跳转代码。 
 void Init(TESTFUN pFun, void* pThis)  
    {  
 //设置参数指令 
        m_mov = 0x042444C7;  //C7 44 24 0C 
 //设置修改后的参数 
        m_this = PtrToUlong(pThis);  
 //设置跳转指针。 
        m_jmp = 0xe9;  
 //设置跳转的相对地址。 
        m_relproc = (int)pFun - ((int)this+sizeof(Thunk));  
 //把CPU里的缓冲数据写到主内存。 
        FlushInstructionCache(GetCurrentProcess(),  
 this, sizeof(Thunk));  
    }  
};  
#pragma pack(pop) 
//测试动态修改内存里的指令数据。 
class CTest  
{  
public:  
 //保存动态修改代码的内存。 
    Thunk m_Thunk;  
 //真实运行的函数。 
 static void TestFun(void* p)  
    {  
        CTest* pTest = (CTest*)p;  
        pTest->Print();  
    }  
 void Print()  
    {  
        printf("这里仅仅是一个测试/n TestFun函数的参数被修改了/n");  
    }  
};  
int main(int argc, char* argv[])  
{  
 //如下调用这个类: 
 //测试运行。 
    CTest Test;  
    Test.m_Thunk.Init(Test.TestFun,&Test);  
    TESTFUN pTestFun = (TESTFUN)&(Test.m_Thunk);  
 char* psz = "test";  
    pTestFun((void*)psz);   
 return 0;  
}  

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术之路

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则

用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeplex.com/ 文档也写的很详细...

2988
来自专栏debugeeker的专栏

adobe flash player升级coredump分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuzhina/article/detai...

631
来自专栏张善友的专栏

IBatisNet基础组件

DomSqlMapBuilder DomSqlMapBuilder,其作用是根据配置文件创建SqlMap实例。可以通过这个组件从Stream, Uri, Fil...

3165
来自专栏技术博客

Json.Net6.0入门学习试水篇

  JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。简单地说,JSON 可以将 JavaScript 对象中表示的...

1062
来自专栏张善友的专栏

Ibatisnet Quick Start

准备工作 1. 下载ibatis软件包(http://ibatis.apache.org/dotnetdownloads.html)。 2. 创建测试数据库,并...

1978
来自专栏林德熙的博客

win10 uwp 依赖属性

本文告诉大家如何使用依赖属性,包括在 UWP 和 WPF 如何使用。 本文不会告诉大家依赖属性的好处,只是简单告诉大家如何使用。

1012
来自专栏MasiMaro 的技术博文

ATL模板库中的OLEDB与ADO

上次将OLEDB的所有内容基本上都说完了,从之前的示例上来看OLEDB中有许多变量的定义,什么结果集对象、session对象、命令对象,还有各种缓冲等等,总体上...

1442
来自专栏進无尽的文章

编码篇-ARC下的内存泄漏

内存泄露是一个相对挺严重的问题,可是它的存在未引起足够的重视,如果程序运行时一直分配内存而不及时释放无用的内存,程序占用的内存越来越大,直到把系统分配给该APP...

1432
来自专栏程序员的SOD蜜

C#调用C和C++函数的一点区别

最近做U800电话的二次开发,需要调用厂商的C函数库来打电话,后来想加入通话录音功能,但发现程序默认生产的WAV文件过大,又找了个WAV转MP3的C++函数库程...

2626
来自专栏程序员的SOD蜜

PostgreSQL的.NET驱动程序Npgsql中参数对象的一个Bug

最近将公司的项目从SqlServer移植到PostgreSQL数据库上来,在调用数据库的存储过程(自定义函数)的时候,发现一个奇怪的问题,老是报函数无法找到。 ...

3027

扫码关注云+社区

领取腾讯云代金券