副标题#e#
Windows API常常需要回调函数,而在C++开拓中面向工具当行其道,若能让C++类的成员函数成为回调函数,的确就是大善!可是C++成员函数都隐含了一个this指针用于指向当前的工具。要实现回调确实不容易。
我约莫一年前就打仗到Thunk技能,甚至也看过操作Thunk实现将成员函数酿成回调函数的例子。可是我实在没相识过C++汇编后的样子,很容易钻了牛角尖,看都看不懂,直接用他们的措施又不敢,究竟堕落伍欠长处理惩罚。前端时间偶然想起Thunk技能,对未懂技能老这样悬着很大概影响本身的措施员生涯的,于是刻意闭关参悟(没步伐,资质差啊),终于弄大白了。那种感受啊,就像诚信礼佛的人溘然见到如来一样,可能换了贴近本身的比喻:就像千年色鬼见到美男一样的欢快。 我忍不住的仿照小说中的修真人士突悟大道后的叹息:本来如此!
下面的分享一下我的收获,根基上是进出门径的写给初学者的,大侠千万要止步,小弟皮薄!
稍微研究了一下C++汇编后的代码,一般挪用C++的成员函数之前,都是利用ECX寄存器生存工具的指针,亏得C++成员函数的挪用约定__thiscall的参数压栈顺序和仓库均衡的维护都是和回调函数的挪用约定__stdcall一样,所以只需要结构汇编将工具指针生存在ECX寄存器后JMP到成员函数的执行地点就可以了。先写个C++布局拼凑这两条汇编码:
#pragma pack( push, 1 )
struct MemFunToStdCallThunk
{
BYTE m_mov;
DWORD m_this;
BYTE m_jmp;
DWORD m_relproc;
BOOL Init( DWORD_PTR proc, void* pThis )
{
m_mov = 0xB9;
m_this = PtrToUlong(pThis);
m_jmp = 0xe9;
m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(MemFunToStdCallThunk)));
::FlushInstructionCache( ::GetCurrentProcess(), this, sizeof(MemFunToStdCallThunk) );
return TRUE;
}
void* GetCodeAddress()
{
return this;
}
};
#pragma pack( pop )
这个布局相当于两条汇编语句:
mov ecx, pThis
jmp [偏移地点]
利用:
class CTestClass
{
private:
int m_nBase;
MemFunToStdCallThunk m_thunk;
void memFun( int m, int n )
{
int nSun = m_nBase + m + n;
CString str;
str.Format( _T("%d"), nSun );
AtlMessageBox( NULL, _U_STRINGorID( str ) );
}
public:
CTestClass()
{
m_nBase = 10;
}
void Test()
{
//UnionCastType:操作连系将函数指针转换成DWORD_PTR
m_thunk.Init( UnionCastType<DWORD_PTR>(&CTestClass::memFun), this );
StdCallFun fun = (StdCallFun)m_thunk.GetCodeAddress();
ATLASSERT( fun != NULL );
fun( 1, 3 );
}
};
#p#副标题#e#
MemFunToStdCallThunk的Init要领接管成员函数指针和工具指针后就结构成2条汇编码,当挪用fun(1,3)时,
首先将参数3和1压入仓库,之后跳转到m_thunk处,也就是那结构的2条汇编码处,将工具指针生存到ECX寄存器,之后跳转到指定的成员函数处执行。一切OK了。
UnionCastType要领的代码如下:
template< typename TDst, typename TSrc >
TDst UnionCastType( TSrc src )
{
union
{
TDst uDst;
TSrc uSrc;
}uMedia;
uMedia.uSrc = src;
return uMedia.uDst;
}