Chinaunix首页 | 论坛 | 博客
  • 博客访问: 577929
  • 博文数量: 158
  • 博客积分: 4380
  • 博客等级: 上校
  • 技术积分: 2356
  • 用 户 组: 普通用户
  • 注册时间: 2006-09-21 10:45
文章分类

全部博文(158)

文章存档

2012年(158)

我的朋友

分类: C/C++

2012-11-14 16:42:45

// 理论上而言调用一个类的成员函数只需要知道类实例的地址和类成员函数的地址和类成员函数参数类型等,而不需要知道类的类型,但除了使用汇编之外却别无它法
#include
#include
using namespace std;

// 参数分别是 类实例地址, 类成员函数地址, 类成员函数参数
// 之所以使用...作参数,因为了去掉一些类型转换
__declspec(naked) bool print( ... )
{
    __asm {
        // 压入了3个字节长度的变量,以后的esp要加上12
        push ebx
        push esi
        push edi
        mov eax, dword ptr [(esp+12)+4] // 将对象地址保存到EAX
        mov ebx, dword ptr [(esp+12)+8] // 将类成员地址保存到EBX
        mov ecx, dword ptr [(esp+12)+0] // 此函数的返回地址
        mov cl, byte ptr [ecx+2]   // __cdecl返回地址处是个add ebp,?语句,取得?,即参数长度
        movzx ecx, cl              // 将cl扩展到ecx中
        sub ecx, 8                 // 对象地址4字节长+成员函数地址4字节长
        sub esp, ecx
        // memcpy( esp, esp+ecx+12, ecx )
        shr ecx, 2                 // ecx /= 4
        lea esi, dword ptr [(esp+12)+ecx*4+12]
        lea edi, dword ptr [esp]
        rep movsd
        mov ecx, eax         // 将类对象地址传入ecx,这是thiscall调用约定
        call ebx             // call 成员函数
        pop edi
        pop esi
        pop ebx
        ret
    }
}

// 以下为测试代码
struct cls1
{
    int i;
    cls1():i(1234){}
    virtual bool memfun( const char* str )
    {
        cout << i << ' ' << str << endl;
        return true;
    }
};
struct cls2 : cls1
{
    int j;
    cls2():j(4321){}
    virtual bool memfun( const char* str )
    {
        cout << i << ' ' << j << ' ' << str << endl;
        return false;
    }
};

int main()
{
    cls1 a;
    cls2 b;
    bool f1 = print( &a, cls1::memfun, "base" );
    cout << f1 << endl;
    bool f2 = print( &b, cls1::memfun, "derive" ); // 这里没有使用cls2::memfun,可以体现多态
    cout << f2 << endl;

    system( "Pause" );
    return 0;
}
阅读(1246) | 评论(7) | 转发(0) |
给主人留下些什么吧!~~

网友评论2012-11-14 16:46:24

kaby
偶前阵子作inverse engineering的时候。。。好像都是ecx寻址,不管是变量还是虚汗数。。。普通函数好像直接就把指向堆里的this指针固化在opcode里面了

网友评论2012-11-14 16:45:54

邓子国
再说CALL后面也不一定马上就是add esp,?啊。可以多编译几个文件试一下。有的后面先保存返回值EAX,有的进行其他操作,然后才ADD esp,xxx。我的作法是做一个简单的查找反汇编add esp,xxx代码的语句。这句有2种机器码方式:一个是后面的立即数不大于0xff的,一个是大于的。以下是我的代码:
LookForAddEsp:
mov ecx,[ebx];
cmp cx,0xc483; //add esp,0xxh
jne Next01;
shr ecx,16;
and ecx,0xff;
jmp TransParams
Next01:
cmp cx,0xc481; //add esp,0?????xxxh
jne Next02;
mov ecx,[ebx+2]
jmp TransParams
Next02:
inc ebx; <

网友评论2012-11-14 16:45:31

邓子国
__cdecl返回地址处是个add ebp,?语句,取得?,即参数长度
笔误吧,是add esp,?

另外,因为我以前写过LoadLibraryAndCallApi的类,也用了堆栈来复制原参数表中的所有参数。调试时我测试的结果是,VC6.0调用不定参数的函数时,会自动在第一个参数前另加上一个系统参数。并且将此函数当作__cdecl方式来调用。转换过程:CallFun(1,2)-->CallFun(dwSysParam,1,2);

网友评论2012-11-14 16:45:07

fei
不错,有点创意

网友评论2012-11-14 16:44:51

ilovevc
不好。不如codeproject上的FastDelegate,汇编毕竟过于底层了。