先讲一个Windows窗口的问题。众所周知,Windows窗口API要求注册窗口,RegisterClass和CreateWindow函数注册和建立窗口,要在注册的时候填充WNDCLASSEX结构,写入自己实现的窗口消息处理函数,这个处理函数的原型:LRESULT CALLBACK WindowProc( HWND , UINT , WPARAM , LPARAM ); 这里存在一个小问题:如何将程序对象与系统对象(HWND类型)联系起来。(一个窗口要保存一些属于它自己的状态,要通过这个窗口句柄得到此状态的对象。)
MFC用全局字典,即有一个全局变量,记录了HWND到MFC窗口对象的一对一关系(当然,这里存在一个大问题,再次进行MFC窗口子类化,将会导致全局字典的错误。)
还有一个方法,就是将一个动态生成的函数指派给窗口超类,让系统回调这个动态生成的函数,而这个函数会将系统传过来的窗口句柄换成我们想要的任何指针。就是说,我们只要实现原型是这样的函数:LRESULT CALLBACK WindowProc2( struct SomeStruct * , UINT , WPARAM , LPARAM ); 就可以了,这样我们就得到了此窗口句柄对应的状态数据,而无需在全局设立字典。
于是,定义一个Thunk结构,用于存储动态生成的函数的内容。
struct cww_thunk
{
char bytes[16];
};
下面的函数填充这个结构,参数object就是指定的数据指针,proc是要实现的WindowProc2。_THUNK_PROTECTED是预定义宏,在Win7等Windows系统下,数据段的页属性已经没有了可执行位,需要修改页属性。
void __cww_thunk( struct cww_thunk *thk, u_long object, u_long proc )
{
/* mov dword ptr [esp+0x4], object */
thk->bytes[0] = 0xC7;
thk->bytes[1] = 0x44;
thk->bytes[2] = 0x24;
thk->bytes[3] = 0x04;
memcpy(thk->bytes + 4, &object, 4);
/* reli jmp proc */
thk->bytes[8] = 0xE9;
proc -= (u_long)thk + 13;
memcpy(thk->bytes + 9, &proc, 4);
memset(thk->bytes + 13, 0, sizeof(struct cww_thunk) - 13);
#if _THUNK_PROTECTED
{
u_long old;
VirtualProtectEx(GetCurrentProcess(), thk, sizeof(struct cww_thunk), PAGE_EXECUTE_READWRITE, &old);
}
#endif
}
这个函数生成两条指令,指令1:用指定的数据指针的数值填充进第1个参数的地址(覆盖HWND参数),指令2:跳转到指定的过程上。
下一篇讲通用Thunk的方法。
MFC用全局字典,即有一个全局变量,记录了HWND到MFC窗口对象的一对一关系(当然,这里存在一个大问题,再次进行MFC窗口子类化,将会导致全局字典的错误。)
还有一个方法,就是将一个动态生成的函数指派给窗口超类,让系统回调这个动态生成的函数,而这个函数会将系统传过来的窗口句柄换成我们想要的任何指针。就是说,我们只要实现原型是这样的函数:LRESULT CALLBACK WindowProc2( struct SomeStruct * , UINT , WPARAM , LPARAM ); 就可以了,这样我们就得到了此窗口句柄对应的状态数据,而无需在全局设立字典。
于是,定义一个Thunk结构,用于存储动态生成的函数的内容。
struct cww_thunk
{
char bytes[16];
};
下面的函数填充这个结构,参数object就是指定的数据指针,proc是要实现的WindowProc2。_THUNK_PROTECTED是预定义宏,在Win7等Windows系统下,数据段的页属性已经没有了可执行位,需要修改页属性。
void __cww_thunk( struct cww_thunk *thk, u_long object, u_long proc )
{
/* mov dword ptr [esp+0x4], object */
thk->bytes[0] = 0xC7;
thk->bytes[1] = 0x44;
thk->bytes[2] = 0x24;
thk->bytes[3] = 0x04;
memcpy(thk->bytes + 4, &object, 4);
/* reli jmp proc */
thk->bytes[8] = 0xE9;
proc -= (u_long)thk + 13;
memcpy(thk->bytes + 9, &proc, 4);
memset(thk->bytes + 13, 0, sizeof(struct cww_thunk) - 13);
#if _THUNK_PROTECTED
{
u_long old;
VirtualProtectEx(GetCurrentProcess(), thk, sizeof(struct cww_thunk), PAGE_EXECUTE_READWRITE, &old);
}
#endif
}
这个函数生成两条指令,指令1:用指定的数据指针的数值填充进第1个参数的地址(覆盖HWND参数),指令2:跳转到指定的过程上。
下一篇讲通用Thunk的方法。