Rust:实现仅通过索引(序数)导出 DLL 函数的功能
在 Rust 中,可以通过手动控制导出来实现仅通过索引(序数)导出 DLL 函数的功能。以下是具体方法和完整步骤:
解决方案
通过结合 .def
文件(模块定义文件)和 MSVC 链接器参数来实现函数名隐藏,只暴露序数编号。
具体步骤
1. 创建 Rust 动态库项目
在 Cargo.toml
中配置 cdylib
类型:
[lib]
crate-type = ["cdylib"]
2. 编写 Rust 函数
使用 #[no_mangle]
和 extern "C"
定义导出函数:
// src/lib.rs
#[no_mangle]
pub extern "C" fn secret_function1() -> i32 {42
}#[no_mangle]
pub extern "C" fn secret_function2(x: i32) -> i32 {x * 2
}
3. 创建模块定义文件(.def
)
创建 exports.def
文件,用 NONAME
隐藏函数名并分配序数:
EXPORTS; 语法: 函数名 @序数 NONAMEsecret_function1 @1 NONAMEsecret_function2 @2 NONAME
4. 设置编译链接参数
修改 .cargo/config.toml
,添加 MSVC 链接器标志:
# .cargo/config.toml
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "link-args=/DEF:exports.def /EXPORT:NONE"]
注意:路径需根据项目结构调整(也可用绝对路径)
5. 编译项目
cargo build --release
生成的 target/release/your_lib.dll
将隐藏函数名。
验证导出结果
使用 dumpbin
工具检查导出表(确保 VS Developer Command Prompt 中运行):
dumpbin /EXPORTS target/release/your_lib.dll
输出应类似:
ordinal hint RVA name1 0 00001000 [NONAME]2 1 00001010 [NONAME]
从调用方通过索引加载
在 C/C++ 中通过序数加载函数(示例):
#include <windows.h>
#include <stdio.h>typedef int (*Func1)();
typedef int (*Func2)(int);int main() {HINSTANCE hDll = LoadLibraryA("your_lib.dll");if (!hDll) return 1;// 通过序数1加载第一个函数Func1 f1 = (Func1)GetProcAddress(hDll, (LPCSTR)1);// 通过序数2加载第二个函数Func2 f2 = (Func2)GetProcAddress(hDll, (LPCSTR)2);printf("f1: %d\n", f1()); // 输出 42printf("f2: %d\n", f2(10)); // 输出 20FreeLibrary(hDll);return 0;
}
关键点说明
-
NONAME
关键字
在.def
文件中强制使用序数导出,隐藏函数名称。 -
/EXPORT:NONE
禁止 Rust 的默认名称导出规则,确保只有.def
中的定义生效。 -
序数分配
序数必须唯一且从 1 开始(序数 0 保留)。 -
工具链限制
此方案仅适用于 MSVC 工具链(x86_64-pc-windows-msvc
)。如需 GNU 工具链,需改用dlltool
(流程较复杂)。
替代方案
如果需要在 GNU 工具链(如 x86_64-pc-windows-gnu
)实现:
- 使用
dlltool
生成.a
导入库 - 手动控制
.def
和dlltool
参数
但 GNU 方案较为复杂,推荐优先使用 MSVC 链。
通过以上步骤,可在 Rust 中编译出仅通过索引导出的 DLL 文件,有效隐藏内部符号名称。