汇编展示函数调用方式

最近,看上一个Ring0级的库,可以支持win7 x64,不过我没有它提供的dll头文件,所以就只能抄起win32DAsm了~~~~

在调用这个库文件时,遇到了C/C++中调用汇编函数(木有C的源码)的问题,就整理这么一个东东,用汇编实现不同方式的调用函数
注:
//; 分号在asm是注释符,每行加个分号 用于VC排版
//eax,ecx,edx vc编译器中 由调用者保存,被调用者可以自由修改这里面的值
//edi,esi,ebx 由被调用者保存,使用前要先保存
//__declspec(naked) 这个会让vc编译对这个函数不为这个函数生成任何额外的代码

1. __cdecl :

C和C++缺省调用方式。函数的参数自右向左通过栈传递,由调用者把参数弹出栈。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void __declspec ( naked ) __cdecl cpuid_c( int * /*regs*/ , int /*op*/ )
{
     __asm
     {
         push        ebp;
         mov     ebp,esp;   
         push        ebx;
         push        esi;
     }
     __asm
     {
         //op => eax;
         mov     eax,[ebp+0Ch];
         //regs => esi;
         mov     esi,[ebp+8];
         cpuid;
         mov     [esi],eax;
         mov     [esi+1*TYPE int ],ebx;
         mov     [esi+2*TYPE int ],ecx;
         mov     [esi+3*TYPE int ],edx;
     }
     __asm
     {
         pop     esi;
         pop     ebx;
         mov     esp, ebp;
         pop     ebp;
         ret;
     }
}
2.     _stdcall:

也是pascal调用,winapi就是这种函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,和__cdecl 比起来,最后的ret 变成了ret 8, 因为函数传进来了8个字节的参数。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void __declspec ( naked ) _stdcall cpuid_s( int * /*regs*/ , int /*op*/ )
{
     __asm
     {
         push        ebp;
         mov     ebp,esp;   
         push        ebx;
         push        esi;
     }
     __asm
     {
         //op => eax;
         mov     eax,[ebp+0Ch];
         //regs => esi;
         mov     esi,[ebp+8];
         cpuid;
         mov     [esi],eax;
         mov     [esi+1*TYPE int ],ebx;
         mov     [esi+2*TYPE int ],ecx;
         mov     [esi+3*TYPE int ],edx;
     }
     __asm
     {
         pop     esi;
         pop     ebx;
         mov     esp, ebp;
         pop     ebp;
         ret 8;
     }
}
3.    _fastcall:

fast call 它是通过寄存器(ECX、EDX)来传送前两个双字(DWORD)或更小的参数的,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void __declspec ( naked ) _fastcall cpuid_f( int * /*regs*/ , int /*op*/ ,
         int unused=0xbad)
{ //regs 在ecx,op在edx,unused通过堆栈传过来
 
     __asm
     {
         push        ebp;
         mov     ebp,esp;   
         push        ebx;
         push        esi;
     }
     __asm
     {
         //op => eax;
         mov     eax,edx;
         //regs => esi;
         mov     esi,ecx;
         cpuid;
         mov     [esi],eax;
         mov     [esi+1*TYPE int ],ebx;
         mov     [esi+2*TYPE int ],ecx;
         mov     [esi+3*TYPE int ],edx;
     }
     __asm
     {
         pop     esi;
         pop     ebx;
         mov     esp, ebp;
         pop     ebp;
         ret 4;
     }
}
4.    thiscall:

传说中的”thiscall“。C++在调用自己成员函数时采用这种,成员函数是定参的时候, this指针通过ecx传到函数里面,其它和_stdcall一样,成员函数是不定参的时候,this指针在最后面入栈,其它和__cdecl一样,不过naked不能用在成员函数上,所以用汇编实现这种函数就写不了了,可以写一个调用的例子(用远程线程,调用别人的C++成员函数就是这么做的~~~)
先定义一个类,调用在下面的main函数中:

?
1
2
3
4
5
6
7
8
9
10
11
class CTest
{
public :
     CTest(){}
     int  Add( int a)
     {
         return a+num;
     }
private :
     int num;
};
最后,附上main函数
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int main()
{
     int regs[4]={0};
     int op=0;
     char vid[13]={0};
     const int choose =3;
 
     switch (choose)
     {
     case 1:
         cpuid_c(regs,op);
         break ;
     case 2:
         cpuid_s(regs,op);
         break ;
     case 3:
         cpuid_f(regs,op);
         break ;
     }
 
     *(( int *)vid)=regs[1];
     *(( int *)vid+1)=regs[3];
     *(( int *)vid+2)=regs[2];
 
     printf ( "cpuid: %s\n" ,vid);
 
     //传说中的thiscall
 
     int a[1] ={0x100};
     int b=0xda;
     //这样是编译不过的
     //b=a.Add(b)
     __asm
     {
         push [b];
         lea ecx,[a];把a当成CTest对象
         call CTest::Add;
         mov [b],eax
     }
 
     printf ( "b=0x%x\n" ,b);
 
     return 0;
}

你可能感兴趣的:(C)