最近,看上一个Ring0级的库,可以支持win7 x64,不过我没有它提供的dll头文件,所以就只能抄起win32DAsm了~~~~
在调用这个库文件时,遇到了C/C++中调用汇编函数(木有C的源码)的问题,就整理这么一个东东,用汇编实现不同方式的调用函数
注:
//; 分号在asm是注释符,每行加个分号 用于VC排版
//eax,ecx,edx vc编译器中 由调用者保存,被调用者可以自由修改这里面的值
//edi,esi,ebx 由被调用者保存,使用前要先保存
//__declspec(naked) 这个会让vc编译对这个函数不为这个函数生成任何额外的代码
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;
}
}
|
也是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;
}
}
|
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;
}
}
|
传说中的”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;
};
|
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;
}
|