(4/100) linux系统对动态链接库进行加密保护

写在前面:因为产品开发需求,需要对一些关键函数进行加密保护。一般地,这些关键函数会被编译成动态库加载,但是动态库依然存在被反编译的风险,因此需要对动态库进行加密操作。本文是基于linux系统下的ros开发环境进行的,如果想在wins系统下对动态库进行加密操作,可以参考博客:开发笔记:如何对【动态链接库】文件进行加密保护?
动态链接库加密原理及过程: 主要分为两步:在编译时加密;在运行时解密。
具体是:开发人员在编译的时候对重要的动态库进行加密操作,加密后的动态库放在产品上去运行。每次运行时,按照加密时的策略进行解密操作,这个时候可以得到解密出来的动态库文件,这个文件与加密前的文件是完全一致的。因为在解密后我们我们需要立刻将动态库加载进内存中去,加载完毕后立即删除解密文件。这样系统中只会看到加密后的动态库文件。

比如我们要对以下求和功能的文件进行加密:
encrypt.h头文件

#include <iostream>
class EncryptDemo
{
public:
    EncryptDemo();
    ~EncryptDemo();
public:
    int Add(int a, int b);
};
#ifdef __cplusplus
extern "C" 
{
#endif
int Initialize(int a, int b);
#ifdef __cplusplus
}
#endif

encrypt.cpp文件

#include "encrypt_demo/encrypt.h"
EncryptDemo::EncryptDemo()
{
    std::cout << "EncryptDemo" << std::endl;
}
EncryptDemo::~EncryptDemo()=default;
int EncryptDemo::Initialize()
{
    EncryptDemo ed;
    std::cout << ed.Add(1,3) << std::endl;
}

1. 加密过程示例:

在cmakeList.txt中进行加密操作

add_library(${PROJECT_NAME}_shared SHARED
  src/encrypt.cpp
)
add_dependencies(${PROJECT_NAME}_shared ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(${PROJECT_NAME}_shared
  ${catkin_LIBRARIES}
  dl #重要,解密时需要加上
)

## Encrypting ##
set(FUNC_ENC ${CATKIN_DEVEL_PREFIX}/lib/libxxx_shared_enc.so)
add_custom_target(xxx_shared_enc 
  ALL #[ALL] - 该目标会被添加到默认的构建目标,使得每次都被运行
  COMMAND openssl enc -des  -e  -a   -salt -pbkdf2 -in ${CATKIN_DEVEL_PREFIX}/lib/libxxx_shared.so -out  ${CATKIN_DEVEL_PREFIX}/lib/libxxx_shared_enc.so  -k  1234
  #[COMMAND] - 指定要在构建时执行的命令
  COMMENT "Encrypting"  #[COMMENT] - 在构建时执行命令之前显示给定信息
  DEPENDS  ${PROJECT_NAME}_shared #[DEPENDS] - 指定命令所依赖的文件
)

简单解析一下以上cmake命令:
add_library()是对求和的cpp文件装载成动态链接库;
libxxx.so是要加密的动态库,libxxx_enc.so是加密后的动态库。xxx是可以是PROJECT_NAME,即libxxx.so${PROJECT_NAME}_shared 等价。

set(FUNC_ENC ${CATKIN_DEVEL_PREFIX}/lib/libxxx_shared_enc.so)

这句话是定义一下加密后的动态库的路径,即把${CATKIN_DEVEL_PREFIX}/lib/路径下的加密库文件libxxx_shared_enc.so定义为FUNC_ENC

add_custom_target()命令是生成target,关键字ALL是每次都被运行。
关键字COMMAND是指执行的操作,这里就是加密操作,利用openssl中的des加密算法进行加密,-in 后的是被加密文件,-out是加密输出文件,-k 是加密的密钥。
关键字DEPEND是指所依赖的文件,这里是依赖加密前的动态库来构建target(加密后的动态库)

基于openssl的加密算法有很多种,这里不再赘述,本文使用的是较为简单的des加密算法。

2. 解密过程示例:

解密过程是在程序运行的时候执行的。
没有加密操作前,我们只需要增加一个main函数,示例化一个对象即可

int main(int argc, char *argv[])
{
    EncryptDemo ed;
    std::cout << ed.Add(1,3) << std::endl;
    return 0;
}

而增加加密后,则需要修改为:

int main(int argc, char *argv[])
{
	cadbot::encrypt_decode();
    return 0;
}

其中,encrypt_decode()函数在encrypt_decode.cpp中定义

encrypt_decode.cpp文件:

namespace cadbot {

bool file_exists(const std::string &path){
    struct stat buffer;
    return (stat(path.c_str(), &buffer) == 0);
}

std::string find_shared_library(const std::string &lib_name)
{
    std::vector<std::string> search_paths;

    const char* env_path = std::getenv("LD_LIBRARY_PATH");
    if(env_path != nullptr){
        std::stringstream ss(env_path);
        std::string path;
        while(std::getline(ss, path, ':')){
            search_paths.push_back(path);
        }
    }

    search_paths.push_back("/lib");
    search_paths.push_back("/install/lib");
    search_paths.push_back("/home/robot/install/lib");

    for(const auto &path : search_paths){
        std::string full_path = path + "/" + lib_name;
        if(file_exists(full_path)){
            return path;
        }
    }
    return "";
}

extern "C"  void encrypt_decode()
{
    std::string lib_name = "libxxx_shared_enc.so";
    std::string lib_path = find_shared_library(lib_name);

    if (!lib_path.empty()) {
        ROS_INFO("[encrypt] shared library: %s --- path: %s\n",lib_name.c_str(), lib_path.c_str());
    } else {
        ROS_INFO("[encrypt] Not find shared library: %s\n",lib_name.c_str());
    }

    std::string encrypt_lib_path = lib_path + std::string("/");
    std::string enc_encrypt_path = catbot_lib_path + "libencrypt_shared_enc.so";
    std::string dec_encrypt_path = catbot_lib_path + "libencrypt_shared_dec.so";
    ROS_INFO("[encrypt] enc_catbot_path = %s\n",enc_encrypt_path.c_str());

    int ret = std::system((std::string("openssl enc -des  -d  -a   -salt -pbkdf2 -in ") + enc_encrypt_path + std::string(" -out  ") + dec_encrypt_path + std:: string("  -k  1234")).c_str());
    void *handle = dlopen(dec_encrypt_path.c_str(), RTLD_NOW);
    if (handle == NULL) {
        //打开动态库出错
        fprintf(stderr, "dlopen error %s\n", dlerror());
    } else {
        ret = std::system((std::string("rm -rf ") + dec_encrypt_path).c_str());
        typedef void(*init_t)();
        init_t Initialize = (init_t) dlsym(handle,"Initialize");
        Initialize();
        dlclose(handle); //关闭句柄
    }
}
}

encrypt_decode.cpp文件主要有三个函数,其中file_exists()和find_shared_library()主要是用来在环境中寻找指定的动态库文件。encrypt_decode()函数才是解密操作过程。

int ret = std::system((std::string("openssl enc -des  -d  -a   -salt -pbkdf2 -in ") + enc_encrypt_path + std::string(" -out  ") + dec_encrypt_path + std:: string("  -k  1234")).c_str());

这句指令是对加密的库文件进行解密操作,解密密钥与加密时的“1234”相同。
之后利用dlopen函数打开解密后的动态库并立即load进内存中。
加载完成后删除文件:ret = std::system((std::string("rm -rf ") + dec_encrypt_path).c_str());
最后通过Initialize函数实例化对象,执行正常函数功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值