回调函数封装Thunk技术及反汇编技术.docx

上传人:b****1 文档编号:22994723 上传时间:2023-04-29 格式:DOCX 页数:12 大小:18.23KB
下载 相关 举报
回调函数封装Thunk技术及反汇编技术.docx_第1页
第1页 / 共12页
回调函数封装Thunk技术及反汇编技术.docx_第2页
第2页 / 共12页
回调函数封装Thunk技术及反汇编技术.docx_第3页
第3页 / 共12页
回调函数封装Thunk技术及反汇编技术.docx_第4页
第4页 / 共12页
回调函数封装Thunk技术及反汇编技术.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

回调函数封装Thunk技术及反汇编技术.docx

《回调函数封装Thunk技术及反汇编技术.docx》由会员分享,可在线阅读,更多相关《回调函数封装Thunk技术及反汇编技术.docx(12页珍藏版)》请在冰豆网上搜索。

回调函数封装Thunk技术及反汇编技术.docx

回调函数封装Thunk技术及反汇编技术

回调函数封装之Thunk技术

——将回调函数封装在类内(笔记)

回调函数的引入

//导入头文件

#include"ZThunk.h"

classCTimer

{

private:

//此对像必须声时为类的数据成员或者全局对像,以保证它的生命周期

ZThunkm_thunk;

public:

//安装定时器

voidSet()

{

//计算回调函数地址

void*pAddr=m_thunk.Callback(this,&CTimer:

:

TimerProc,ZThunk:

:

thisCALL);

//安装计时器

SetTimer(NULL,1,1000,(TIMERPROC)pAddr);  

}

//定时器回调函数,完全被封装成类成员函数!

voidTimerProc(HWNDhWnd,DWORDdwMsg,WPARAMwPa,LPARAMlPa)

{

//todosomething

}

};

下划线部分SetTimer(NULL,1,1000,(TIMERPROC)pAddr);原型

UINT_PTRSetTimer(   

HWNDhWnd,

UINT_PTRnIDEvent,

UINTuElapse,

TIMERPROClpTimerFunc

);、//详细参考可见MSDN

当定时时间到以后,系统会自动跳转至lpTimerFunc,执行对应的代码,一般称之为回调函数。

但是,若想将函数定义为类的成员函数,必须保证存在this指针(成员函数的默认参数,指向当前的对象)或将该函数定义为static函数(static函数是类的函数,因此不存在this指针)。

综上,解决方案如下:

方法一:

将回调函数定义为类的static函数:

static函数不存在this指针,因此不能访问非static成员函数(非static成员函数内部需要this指针)以及非static成员变量。

因此,该方法有一定的限制,不能对对象的状态做相应的更改。

代码如下:

classCTimer

{

public:

//typedefVOID(CALLBACK*TIMERPROC)(HWND,UINT,UINT,DWORD);

//安装定时器

voidSet()

{

//计算回调函数地址

void*pAddr=m_thunk.Callback(this,&CTimer:

:

TimerProc,ZThunk:

:

thisCALL);

//安装计时器

SetTimer(NULL,1,1000,(TIMERPROC)pAddr);  

}

//定时器回调函数,完全被封装成类成员函数!

staticvoidTimerProc(HWNDhWnd,DWORDdwMsg,WPARAMwPa,LPARAMlPa)

{

//todosomething

}

};

方法二:

利用Thunk技术

Thunk技术是利用嵌入一组动态ASM(汇编)指令,传递this指针,同时跳转至TimerProc。

下面反汇编一段简单程序:

C++源程序:

#include

usingnamespacestd;

classtest1

{

public:

intb;

chara;

charc[3];

voidfunc1(){

a='a';

cout<

}

};

intCompare(inta,intb,intc)

{

intx=a;

if(a>b){

returna;

}else{

returnb;

}

}

intmain()

{

inta=0;intb=1;intc=2;

a=Compare(a,b,b);

cout<

test1t1;

t1.func1();

return0;

}

//main函数部分的反汇编代码

intmain()

{

//利用保存调main函数的的ebp寄存器

004015D055         push    ebp

004015D18BEC        mov    ebp,esp

//将栈顶指针下移54h字节,预留一段栈内存用于存储局部变量

004015D383EC54      sub    esp,54h

//现场保护,将一系列寄存器入栈,程序结束后出栈

004015D653         push    ebx

004015D756         push    esi

004015D857         push    edi

004015D98D7DAC      lea    edi,[ebp-54h]

004015DCB915000000    mov    ecx,15h

004015E1B8CCCCCCCC   mov    eax,0CCCCCCCCh

004015E6F3AB        repstos  dwordptr[edi]

inta=0;

//局部变量存在栈中

004015E8C745FC00000000mov    dwordptr[ebp-4],0

intb=1;

004015EFC745F801000000mov    dwordptr[ebp-8],1

intc=2;

004015F6C745F402000000mov    dwordptr[ebp-0Ch],2

a=Compare(a,b,c);

//调用函数,利用堆栈传参数:

从右向左依次入栈:

c、b、a

004015FD8B45F8      mov    eax,dwordptr[ebp-0Ch]

0040160050         push    eax//c入栈

004016018B4DF8      mov    ecx,dwordptr[ebp-8]

0040160451         push    ecx//b入栈

004016058B55FC      mov    edx,dwordptr[ebp-4]

0040160852         push    edx//a入栈

00401609E89CFAFFFF   call    @ILT+165(Compare)(004010aa)//见下

0040160E83C40C      add    esp,0Ch//堆栈清理,将实参abc移除

004016118945FC      mov    dwordptr[ebp-4],eax

cout<

0040161468CD104000   push    offset@ILT+200(std:

:

endl)(004010cd)

004016198B45FC      mov    eax,dwordptr[ebp-4]

0040161C50         push    eax

0040161DB990BE4700   mov    ecx,offsetstd:

:

cout(0047be90)

00401622E8DDFAFFFF   call    @ILT+255(std:

:

basic_ostream

:

char_traits>:

:

operator<<)(00401104)

004016278BC8        mov    ecx,eax

00401629E8C1FBFFFF   call    @ILT+490(std:

:

basic_ostream

:

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;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 中医中药

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1