调用约定 (Calling Conventions) (__cdecl、__stdcall、__fastcall) C++ 函数名修饰 (Name Mangling)。
代码防护思路:配置文件简单加密,避免字符串硬编码。
VS “dumpbin -exports ***.dll”。
格式为 function。
格式为 _function@number。
格式为 @function@number。
对于 C++ 编译器的函数名修饰规则:不管 __cdecl, __fastcall 还是 __stdcall 调用方式,函数修饰名都是以 "?" 开始,后面是函数在名字,再后面是函数返回类型和参数类型按照代号拼出的参数表。 对于 __stdcall 方式,参数表的开始标示是 "@@YG”,对于 __cdecl 方式则是 "@@YA”,对于 __fastcall 方式则是 "@@YI”。 参数表后以 "@Z” 标示整个名字的结束,如果该函数无参数,则以 "Z” 标识结束。
__declspec(dllexport) int add(int, int);
__declspec(dllexport) int __cdecl add(int, int);
__declspec(dllexport) int __stdcall add(int, int);
__declspec(dllexport) int __fastcall add(int, int);
__stdcall 和 __fastcall 编译出来的函数名还是和原始的函数名不同。 就拿 __stdcall 来说,它以 C 编译导出的时候,会在函数前面加入下划线,并在函数后面加入 @ 和参数总大小的字节数。
extern "C" __declspec(dllexport) int add(int, int);
extern "C" __declspec(dllexport) int __cdecl add(int, int);
extern "C" __declspec(dllexport) int __stdcall add(int, int);
extern "C" __declspec(dllexport) int __fastcall add(int, int);
Tool to demangle C++ symbols .
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#include <cxxabi.h>
static void demangle(const char* mangled_name, bool quiet) {
int status = 0;
const char* realname = abi::__cxa_demangle(mangled_name, 0, 0, &status);
switch (status) {
case 0:
if (quiet) {
puts(realname);
}
else {
printf("%s %s\n", realname, mangled_name);
}
break;
case -1:
printf("FAIL: failed to allocate memory while demangling %s\n",
mangled_name);
break;
case -2:
printf("FAIL: %s is not a valid name under the C++ ABI mangling rules\n",
mangled_name);
break;
default:
printf("FAIL: some other unexpected error: %d\n", status);
break;
}
free((void*)realname);
}
内部源码实现:Demangler for MSVC symbols
一般本机上都有,用 Everything 检索本地:undname.exe。
template <class R>
DLLTEST_API void create_func(R(_stdcall* func_ptr)(void)) {
}
template <class R>
DLLTEST_API void create_func(R(_cdecl* func_ptr)(void)) {
}
这两个函数编译出来,x86:
No Address Hint Ordinal Name
1. $11159 - 1 ??$create_func@X@@YAXP6AXXZ@Z
2. $112FD 1 2 ??$create_func@X@@YAXP6GXXZ@Z
x64 是编译错误:
严重性 代码 说明 项目 文件 行 禁止显示状态
错误 C2995 “void create_func(R (__cdecl *)(void))”: 函数模板已经定义
在设计调用约定时,x64 体系结构利用机会清除了现有 Win32 调用约定(如 __stdcall、__cdecl、__fastcall、_thiscall 等)的混乱。在 Win64 中,只有一个本机调用约定。 而 __cdecl 之类的修饰符都被编译器忽略。除此之外,减少调用约定行为还为可调试性带来了好处。 from
原来 64 位平台下只有一种变形的 __fastcall 的调用约定,前 4 参数则先放入 ecx、edx、r8、r9 寄存器,更多的参数放入栈区。这个时候我们要注意的是,在 64 位下,系统还是为前 4 个参数预留了栈区空间(每个栈空间大小为 8 字节,共 32 字节大小),然后将基存器的值放入所预留的栈区空间。为什么系统要多此一举呢?我们都知道寄存器传递参数速度要远大于栈区传值,而将寄存器中的值再放入栈区预留空间,这是为了防止在传递参数的过程中,寄存器需要接收其他的值而导致参数无法传递,或者其他值无法接收的情况。 from
查看一下:
"MSVC\bin\Hostx64\x64\dumpbin.exe" /exports E:\kpdf\DllTest64.dll
ordinal hint RVA name
1 0 00011172 ??$create_func@X@@YAXP6AXXZ@Z = @ILT+365(??$create_func@X@@YAXP6AXXZ@Z)
1 0 00011172 ??$create_func@X@@YAXP6AXXZ@Z = @ILT+365(??$create_func@X@@YAXP6AXXZ@Z)
1 0 000110F0 ??$create_func1@X@@YAXP6AXXZ@Z = @ILT+235(??$create_func1@X@@YAXP6AXXZ@Z)
2 1 0001100A ??$create_func2@X@@YAXP6AXXZ@Z = @ILT+5(??$create_func2@X@@YAXP6AXXZ@Z)
1 0 000110F0 ??$create_func1@X@@YAXP6AXXZ@Z = @ILT+235(??$create_func1@X@@YAXP6AXXZ@Z)
2 1 0001100A ??$create_func2@X@@YAXP6AXXZ@Z = @ILT+5(??$create_func2@X@@YAXP6AXXZ@Z)
1 0 000110EB ??$create_func3@X@@YAXP6AXXZ@Z = @ILT+230(??$create_func3@X@@YAXP6AXXZ@Z)
2 1 00011131 ??$create_func4@X@@YAXP6AXXZ@Z = @ILT+300(??$create_func4@X@@YAXP6AXXZ@Z)
你会发现两种写法,生成的 签名是一样的,所以链接肯定不成功。