使用内联汇编动态调用DLL
1.首先我们必须知道C语言的调用约定为
__cdecl(即参数从右向左依次进栈,由调用者还原堆栈).
2.一条push指令最多压入4个字节,当不足4个字节时应补齐4个字节,超过4个字节时应该由低位到高位依次压栈.
3.pop指令也和push一样一次只能弹出4个字节.
4.我们需要一个CallStruct类型来储存一个参数.
1
class
CallStruct
2 {
3 public :
4 INT Integer;
5 BOOL Bool;
6 DOUBLE Real;
7 WCHAR String[MAX_STRING];
8
9 enum TYPE
10 {
11 ctInteger,
12 ctString,
13 ctBool,
14 ctReal,
15 ctVoid,
16 }Type;
17
18 CallStruct() : Integer( 0 ),Bool(FALSE),Real( 0 )
19 {
20 String[ 0 ] = 0 ;
21 }
22
23 CallStruct( const NAutoPtr < VirtualMachine::VarClass >& Var) : Integer(Var -> Integer),Bool(Var -> Bool),Real(Var -> Real),Type((TYPE)Var -> Type)
24 {
25 if (Type == ctString) wcscpy(String,Var -> String);
26 }
27 };
5.我们需要一个列表来存放参数和一个返回值对象来存放返回值.
2 {
3 public :
4 INT Integer;
5 BOOL Bool;
6 DOUBLE Real;
7 WCHAR String[MAX_STRING];
8
9 enum TYPE
10 {
11 ctInteger,
12 ctString,
13 ctBool,
14 ctReal,
15 ctVoid,
16 }Type;
17
18 CallStruct() : Integer( 0 ),Bool(FALSE),Real( 0 )
19 {
20 String[ 0 ] = 0 ;
21 }
22
23 CallStruct( const NAutoPtr < VirtualMachine::VarClass >& Var) : Integer(Var -> Integer),Bool(Var -> Bool),Real(Var -> Real),Type((TYPE)Var -> Type)
24 {
25 if (Type == ctString) wcscpy(String,Var -> String);
26 }
27 };
1
List
<
NAutoPtr
<
CallStruct
>>
VarList;
2 NAutoPtr < CallStruct > Return;
6.最后我们需要一个HMODULE和一个FARPROC分别存放dll的句柄和函数地址.
2 NAutoPtr < CallStruct > Return;
1
HMODULE hModule;
2 FARPROC FunctionPtr;
7.然后我们添加几个功能函数.
2 FARPROC FunctionPtr;
1
BOOL AddVar(NAutoPtr
<
VirtualMachine::VarClass
>&
Var);
2 BOOL SetReturnType(CallStruct::TYPE Type);
3 BOOL SetLibName(LPTSTR Name);
4 BOOL SetFunctionName(LPTSTR Name);
注意:GetProcAddress第二个参数只接受LPCSTR类型的字符串,应此如果传入的是Unicode编码的字符必须将其转换成ANSI的.2 BOOL SetReturnType(CallStruct::TYPE Type);
3 BOOL SetLibName(LPTSTR Name);
4 BOOL SetFunctionName(LPTSTR Name);
8.我们添加一个函数Run用于调用函数.
1
BOOL CallMacro::Run()
2 {
3 if (FunctionPtr == 0 || Return.Buffer() == 0 ) return FALSE;
4 union RealStruct
5 {
6 double Real;
7 struct
8 {
9 int Head,Tail;
10 };
11 };
12 NAutoPtr < CallStruct > cs;
13 int Integer;
14 BOOL Bool;
15 RealStruct Real; // Push指令一次只能压入4字节
16 LPTSTR String;
17
18 int iEsp;
19 __asm mov int ptr[iEsp],esp; // 保存esp
20 for ( int i = 0 ;i < VarList.Size();i ++ )
21 {
22 cs = VarList[i];
23 Integer = cs -> Integer;
24 Bool = cs -> Bool;
25 Real.Real = cs -> Real;
26 String = cs -> String;
27 switch (cs -> Type)
28 {
29 case CallStruct::ctInteger:
30 __asm push Integer;
31 break ;
32 case CallStruct::ctString:
33 __asm push String;
34 break ;
35 case CallStruct::ctBool:
36 __asm push Bool;
37 break ;
38 case CallStruct::ctReal:
39 __asm push Real.Tail;
40 __asm push Real.Head;
41 break ;
42 }
43 }
44 FARPROC proc = FunctionPtr;
45 int Head,Tail;
46 __asm
47 {
48 call proc
49 mov int ptr[Head],edx
50 mov int ptr[Tail],eax
51 }
52 switch (Return -> Type)
53 {
54 case CallStruct::ctInteger:
55 Return -> Integer = Tail;
56 break ;
57 case CallStruct::ctString:
58 wcscpy(Return -> String,(LPCTSTR)Tail);
59 break ;
60 case CallStruct::ctBool:
61 Return -> Bool = Tail;
62 break ;
63 case CallStruct::ctReal:
64 __asm fstp [Real.Real];
65 Return -> Real = Real.Real;
66 break ;
67 }
68 // __declspec调用约定,需要手工还原堆栈
69 __asm mov esp, int ptr[iEsp];
70 return TRUE;
71 }
Run函数首先检查是否已经装载了DLL并获得了函数地址,以及返回值类型是否已经定义.
2 {
3 if (FunctionPtr == 0 || Return.Buffer() == 0 ) return FALSE;
4 union RealStruct
5 {
6 double Real;
7 struct
8 {
9 int Head,Tail;
10 };
11 };
12 NAutoPtr < CallStruct > cs;
13 int Integer;
14 BOOL Bool;
15 RealStruct Real; // Push指令一次只能压入4字节
16 LPTSTR String;
17
18 int iEsp;
19 __asm mov int ptr[iEsp],esp; // 保存esp
20 for ( int i = 0 ;i < VarList.Size();i ++ )
21 {
22 cs = VarList[i];
23 Integer = cs -> Integer;
24 Bool = cs -> Bool;
25 Real.Real = cs -> Real;
26 String = cs -> String;
27 switch (cs -> Type)
28 {
29 case CallStruct::ctInteger:
30 __asm push Integer;
31 break ;
32 case CallStruct::ctString:
33 __asm push String;
34 break ;
35 case CallStruct::ctBool:
36 __asm push Bool;
37 break ;
38 case CallStruct::ctReal:
39 __asm push Real.Tail;
40 __asm push Real.Head;
41 break ;
42 }
43 }
44 FARPROC proc = FunctionPtr;
45 int Head,Tail;
46 __asm
47 {
48 call proc
49 mov int ptr[Head],edx
50 mov int ptr[Tail],eax
51 }
52 switch (Return -> Type)
53 {
54 case CallStruct::ctInteger:
55 Return -> Integer = Tail;
56 break ;
57 case CallStruct::ctString:
58 wcscpy(Return -> String,(LPCTSTR)Tail);
59 break ;
60 case CallStruct::ctBool:
61 Return -> Bool = Tail;
62 break ;
63 case CallStruct::ctReal:
64 __asm fstp [Real.Real];
65 Return -> Real = Real.Real;
66 break ;
67 }
68 // __declspec调用约定,需要手工还原堆栈
69 __asm mov esp, int ptr[iEsp];
70 return TRUE;
71 }
然后根据类型依次将函数压栈.
然后调用call指令并保存返回值.(这里需要注意的是 当返回值类型为double或float类型时必须使用fstp指令从FPU寄存器栈的栈顶的值取出来)
最后还原堆栈.
然后我们来测试一下.
创建一个名为TestDLL的DLL工程并添加函数Test.
1
TESTDLL_API
double
Test(
double
d,
double
*
d1,WCHAR
*
lpBuffer)
2 {
3 if (d == 123.456789 ) MessageBox( 0 ,lpBuffer,L "" , 0 );
4 * d1 = 789.654 ;
5 return 77777 ;
6 }
2 {
3 if (d == 123.456789 ) MessageBox( 0 ,lpBuffer,L "" , 0 );
4 * d1 = 789.654 ;
5 return 77777 ;
6 }
然后创建一个测试工程,添加相关文件.
在_tmain中添加以下代码.
1
int
_tmain(
int
argc, _TCHAR
*
argv[])
2 {
3 double d;
4 CallMacro cm;
5 NAutoPtr < VirtualMachine::VarClass > Var;
6
7 Var = new VirtualMachine::VarClass;
8 Var -> Type = VirtualMachine::VarClass::vtString;
9 wcscpy(Var -> String,L " aaaaa " );
10 cm.AddVar(Var);
11
12 Var = new VirtualMachine::VarClass;
13 Var -> Type = VirtualMachine::VarClass::vtInteger;
14 Var -> Integer = (INT) & d;
15 cm.AddVar(Var);
16
17 Var = new VirtualMachine::VarClass;
18 Var -> Type = VirtualMachine::VarClass::vtReal;
19 Var -> Real = 123.456789 ;
20 cm.AddVar(Var);
21
22 cm.SetLibName(L " TestDll.dll " );
23 cm.SetFunctionName(L " Test " );
24 cm.SetReturnType(CallMacro::CallStruct::ctReal);
25 cm.Run();
26
27 wprintf(L " %f %f\n " ,d,cm.Return -> Real);
28 system( " pause " );
29 return 0 ;
30 }
2 {
3 double d;
4 CallMacro cm;
5 NAutoPtr < VirtualMachine::VarClass > Var;
6
7 Var = new VirtualMachine::VarClass;
8 Var -> Type = VirtualMachine::VarClass::vtString;
9 wcscpy(Var -> String,L " aaaaa " );
10 cm.AddVar(Var);
11
12 Var = new VirtualMachine::VarClass;
13 Var -> Type = VirtualMachine::VarClass::vtInteger;
14 Var -> Integer = (INT) & d;
15 cm.AddVar(Var);
16
17 Var = new VirtualMachine::VarClass;
18 Var -> Type = VirtualMachine::VarClass::vtReal;
19 Var -> Real = 123.456789 ;
20 cm.AddVar(Var);
21
22 cm.SetLibName(L " TestDll.dll " );
23 cm.SetFunctionName(L " Test " );
24 cm.SetReturnType(CallMacro::CallStruct::ctReal);
25 cm.Run();
26
27 wprintf(L " %f %f\n " ,d,cm.Return -> Real);
28 system( " pause " );
29 return 0 ;
30 }
编译并运行,可以看到Test函数调用成功,并成功输出d的值和返回值.
最后给出 完整代码.