:
char_traits>:
:
operator<<)(004011ef)
test1t1;
t1.func1();
0040162E8D4DEC lea ecx,[ebp-14h]//利用ecx传递this指针
00401631E850FBFFFF call @ILT+385(test1:
:
func1)(00401186)
return0;
0040163633C0 xor eax,eax//自身异或结果为0,利用eax传递main函数的返回值
}
004016385F pop edi
004016395E pop esi
0040163A5B pop ebx
0040163B83C454 add esp,54h//清理调用main函数时开辟的堆栈内存
0040163E3BEC cmp ebp,esp
00401640E87BF30100 call __chkesp(004209c0)
004016458BE5 mov esp,ebp
004016475D pop ebp
00401648C3 ret
call指令
00401609E89CFAFFFF call @ILT+165(Compare)(004010aa)
@ILT+165(?
Compare@@YAHHHH@Z):
004010AAE9D1040000 jmp Compare(00401580)
call指令做两件事:
1)将函数调用的返回地址,也就是下一条指令的内存地址入栈,此处是0x0040160E
2)直接跳转至004010AA执行,这是一条jmp跳转指令,目的地址如下:
目的地址=源地址(EIP)+偏移量
此处源地址是什么呢?
是004010AA?
此处的源地址应该是(004010AA+5),因为程序在执行的时候,首先需要读取指令,当该条指令(5字节指令:
E9D1040000)读完以后,EIP已经变为(004010AA+5),因此:
目的地址=(004010AA+5)+偏移量
该条jmp指令中的E9为跳转指令的机器码,D1040000被解释为偏移量,根据高位存储在高地址区的原则,偏移量被解释为:
0x000004D1,因此程序跳转至:
(0x004010AA+5)+0x000004D1=0x00401580
当根据对象名.成员函数名 方式访问成员函数时,vc6采用寄存器ecx传递this指针的值,也就是如果需要传递this参数,只需要在函数调用之前,将this指针的值存入ecx寄存器即可。
启发:
函数调用的顺序是,先将实参入栈,然后再调用call指令,同时将返回地址入栈(此处由于是基于某个框架的,涉及到消息队列的处理,因此返回地址尚未搞明白。
。
。
。
。
。
暂时忽略吧)。
根据定时器举例:
SetTimer(NULL,1,1000,(TIMERPROC)pAddr);
typedefVOID(CALLBACK*TIMERPROC)(HWND,UINT,UINT,DWORD);
voidTimerProc(HWNDhWnd,DWORDdwMsg,WPARAMwPa,LPARAMlPa)
当定时时间到了以后,系统会
1)、首先将(HWND,UINT,UINT,DWORD)四个参数入栈,也就是在参数入栈的过程中,具体的执行函数是没有影响的;
2)、参数入栈完毕以后,然后将函数转到(TIMERPROC)pAddr地址执行对应的函数。
根据普通函数的启发,可以将汇编代码,此处说明c/c++的默认调用方式_cdecl情况:
//Encodedmachineinstruction Equivalentassemblylanguatenotation
//--------------------------- -------------------------------------
//B9?
?
?
?
?
?
?
?
mov ecx,pthis ; Loadecxwiththispointer
//E9?
?
?
?
?
?
?
?
jmp targetaddr; Jumptotargetmessagehandler
将代码的机器码(16进制)写入数组当中,然后将数组地址强制转换为TIMERPROC类型,(TIMERPROC)pAddr,系统会自动跳转至pAddr执行代码。
利用ecx寄存器传递this指针参数,然后跳转至TimerProc函数处理,此时作为成员函数的TimerProc就已经有了对应的this指针参数。
Thunk技术实现代码
#ifndef_ZTHUNK
#define_ZTHUNK
classZThunk
{
private:
unsignedcharm_ThiscallCode[10];
unsignedcharm_StdcallCode[16];
ZThunk*m_pThunk;
boolm_bDEP;
public:
ZThunk(){
//检查数据段执行代码保护是否存在DataExecutionPrevention(DEP)
m_bDEP=IsProcessorFeaturePresent(12/*PF_NX_ENABLED*/);
//在当前进程的4GB虚拟地址空间内申请一段区域,用于赋予指定内存段可执行的属性,从而在数据段执行汇编代码
if(m_bDEP){
m_pThunk=(ZThunk*)VirtualAlloc(NULL,sizeof(ZThunk),MEM_COMMIT,PAGE_EXECUTE_READWRITE);
}else{
m_pThunk=this;
}
}
~ZThunk()
{
if(m_bDEP&&m_pThunk){
boolbSuccess=VirtualFree(m_pThunk, //Baseaddressofblock
sizeof(ZThunk), //Bytesofcommittedpages
MEM_RELEASE); //Decommitthepages
if(bSuccess)m_pThunk=NULL;
}
}
enumCALLINGCONVENTIONS
{
STDCALL=1,
THISCALL=2
};
public:
template
void*Callback(void*pThis,TMemberOffset,CALLINGCONVENTIONSCallingConvention=STDCALL)
{
//thesecodesonlyuseinstdcall
if(CallingConvention==THISCALL){
//Encodedmachineinstruction Equivalentassemblylanguatenotation
//--------------------------- -------------------------------------
//B9?
?
?
?
?
?
?
?
mov ecx,pthis ;Loadecxwiththispointer
//E9?
?
?
?
?
?
?
?
jmp targetaddr;Jumptotargetmessagehandler
charBuf[33]={0};
sprintf(Buf,"%d",MemberOffset);
unsignedlongJmpAddr=(unsignedlong)atol(Buf)-(unsignedlong)&(m_pThunk->m_ThiscallCode[0])-10;
m_pThunk->m_ThiscallCode[0]=0xB9;//mov ecx,pThis 指令机器码
m_pThunk->m_ThiscallCode[5]=0xE9;//jmp xxxxxx 指令机器码
*((unsignedlong*)&(m_pThunk->m_ThiscallCode[1]))=(unsignedlong)pThis;//this指针
*((unsignedlong*)&(m_pThunk->m_ThiscallCode[6]))=JmpAddr;//偏移地址
return(void*)(m_pThunk->m_ThiscallCode);
}
//thesecodesonlyuseinthiscall
elseif(CallingConvention==STDCALL){
//Encodedmachineinstruction Equivalentassemblylanguatenotation
//--------------------------- -------------------------------------
//FF3424push dwordptr[esp]Save(orduplicate) ;theReturnAddrintostack
//C7442404?
?
?
?
?
?
?
?
mov dwordptr[esp+4],pthis;Overwitetheold ;ReturnAddrwith'thispointer'
//E9?
?
?
?
?
?
?
?
jmp targetaddr ;Jumptotargetmessagehandler
charBuf[33]={0};
sprintf(Buf,"%d",MemberOffset);
unsignedlongJmpAddr=(unsignedlong)atol(Buf)-(unsignedlong)&m_StdcallCode[0]-16;
m_StdcallCode[11]=0xE9;
*((unsignedlong*) &m_StdcallCode[0])=0x002434FF;
*((unsignedlong*) &m_StdcallCode[3])=0x042444C7;
*((unsignedlong*) &m_StdcallCode[7])=(unsignedlong)pThis;