排行榜 统计
  • 文章总数:1135 篇
  • 评论总数:5 条
  • 分类总数:8 个
  • 最后更新:12月18日

C/C++ DLL 导出接口保护(匿名导出)

本文阅读 4 分钟
首页 安全资讯 正文

通常项目中导出 DLL 接口有两种方式:

1. __declspec(dllexport) 导出;

2.*.def 文件导出。

__declspec(dllexport) 导出

在 DLL 导出的函数名称前使用 _declspec(dllexport) 关键字导出函数接口。

test_dll.h
// test_dll.h
 
#ifdef __cplusplus
extern "C" {
#endif
#ifdef  DLL_EXPORT
#else
#define DLL_EXPORT _declspec(dllexport)
#endif
 
 
DLL_EXPORT int add(int a, int b);
DLL_EXPORT int sub(int a, int b);
 
 
#ifdef __cplusplus
}
#endif
test_dll.cpp
// test_dll.cpp
#include "test_dll.h"
int add(int a, int b)
{
    return a + b;
}
int sub(int a, int b)
{
    return a - b;
}


def 文件导出

由于  __declspec(dllexport) 导出接口需要定义一长串的内容,所以 MS 引入 def文件 来导出函数接口。 
使用 def文件 相对简单,只需要在项目中增加一个后缀名为 .def 的文件,按照格式将 DLL 需要导出的接口定义在 def 文件即可。

1.定义 .def 文件

test_dll.def

LIBRARY test_dll
EXPORTS
    add        @1
    sub        @2
  • DllName 表示 Dll 的文件名
  • @1 [可选] 指定导出的函数序号。导出多个函数时,需要开发者自定义序号,不能重复。

2.添加 def 文件至工程

3.设置工程属性

在 “属性”->“链接器”->“输入” 配置 模块定义文件 输入 def文件 名称。 

4.编译生成 DLL
使用工具查看导出函数名接口,导出接口的顺序与 def文件 定义的序号相同。

存在问题

  • 通过工具可以查看导出函数名,暴露接口函数功能。
  • 导出的函数名可以任意被访问和使用,即使业务功能被封装在内部代码经过加密处理,但外部接口是公开的,可以被正常调用。造成在不知情,被未经授权的开发者滥用,间接造成损失。

解决方案:导出匿名接口

导出匿名序号接口

匿名导出接口只需要在 def文件 的导出接口名称增加 NONAME 关键字即可,如下:

test_dll.def

LIBRARY test_dll
EXPORTS
    add        @1        NONAME
    sub        @2        NONAME
  • NONAME 表示不导出函数名称(匿名)。

使用 CFF Explorer 工具查看导出函数接口名称,函数名称已经被隐藏掉。

访问匿名序号接口

静态链接

和通常工程引用 DLL 的方法相同,引用 .h,链接 .lib,运行前将 .dll 文件拷贝至应用运行目录即可。

动态加载

通过动态加载的方式调用DLL接口。 
当导出序号时,定义函数指针变得复杂,只能通过静态反编译的方式确定参数数量和类型,定义函数指针。

#include <Windows.h>
// 定义函数指针
typedef int (*func_ptr)(int, int);
int dynamic_load_dll()
{
    // 动态加载 DLL 文件
    HINSTANCE hDll = LoadLibrary("test_dll.dll");
    // 定义函数指针变量
    func_ptr add_ptr, sub_ptr;
    if(hDll){
        add_ptr = (func_ptr)GetProcAddress(hDll, (LPCSTR)1);    // 根据导出序号加载函数(add)
        sub_ptr = (func_ptr)GetProcAddress(hDll, (LPCSTR)2);    // 根据导出需要加载函数(sub)
    }else{
        printf("Load DLL failed!\n");
        return 1;
    }
    if (add_ptr) {
        printf("%d\n", add_ptr(11, 4));
    } else {
        printf("GetProcAddress add function failed!\n");
        return 1;
    }
    if (sub_ptr) {
        printf("%d\n", sub_ptr(21, 4));
    } else {
        printf("GetProcAddress sub function failed!\n");
        return 1;
    }
    return 0;
}
注意:定义函数指针的类型声明必须与函数定义一致,否则无法正常使用。

小结

  • 静态链接的方式不变,不影响团队内部使用。
  • 匿名接口,不知情的开发者无法调用 DLL 接口功能,防止 DLL 功能被滥用。
  • 提高 DLL 破解难度,调用者无法通过函数名推测函数功能,增加分析接口功能、接口间业务逻辑关系的复杂程度。

匿名导出 DLL 接口能够一定程度提升安全性,但使用 IDA 等静态反编译工具逆向代码逻辑,能够找到函数的接口参数类型,并且大致了解接口内部实现逻辑(没有函数名,逆向分析的难度已经加大),如果要进一步提升 DLL 的安全性,需要使用加壳工具进行保护或采用其他的安全防护手段。

本文来自投稿,不代表本站立场,如若转载,请注明出处:https://typecho.firshare.cn/archives/569.html
免责声明:文章内容不代表本站立场,本站不对其内容的真实性、完整性、准确性给予任何担保、暗示和承诺,仅供读者参考,文章版权归原作者所有。避免网络欺诈,本站不倡导任何交易行为。如您私自与本站转载自公开互联网中的资讯内容中提及到的个人或平台产生交易,则需自行承担后果。本站在注明来源的前提下推荐原文至此,仅作为优良公众、公开信息分享阅读,不进行商业发布、发表及从事营利性活动。如本文内容影响到您的合法权益(内容、图片等),请及时联系本站,我们会及时删除处理。
-- 展开阅读全文 --
ANDROID AAB加固打包上架流程
« 上一篇 09-06
安卓APK加固应该怎么做
下一篇 » 09-06