• 二进制明文字符串加密:实现原理


    二进制明文字符串加密:实现原理

    背景

    这里就不多做解释了,明文字符串暴露就是最好的逆向分析指引。无论是恶意攻击样本还是有一定安全需求的组件,直接暴露程序中的明文字符串会大幅降低外部的分析成本。所以需要在编译出的二进制中隐藏字符串。不过需要明确下字符串加密的实现主要分两种,一种是借助llvm的pass去做的,另外一种是基于编译期模板的形式去做的,这里讨论的是后者。

    常规的字符串表示

    代码和16进制如下

    printf("conventional...\n");
    
    • 1

    在这里插入图片描述

    解决明文暴露的问题

    首先考虑转换下写法,代码和16进制如下

    char strArray[] = { 'c', 'o', 'n', 'v', 'e', 'n', 't',
        'i', 'o', 'n', 'a', 'l', '.', '.', '.', '\n', '\0' };
    printf(strArray);
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    这么写似乎和直接写字符串编译后没有区别?但是将字符串转为字符数组表示便于下面的加密逻辑

    char strArray[] = { 'c' + 1, 'o' + 1, 'n' + 1, 'v' + 1, 'e' + 1, 'n' + 1, 't' + 1,
        'i' + 1, 'o' + 1, 'n' + 1, 'a' + 1, 'l' + 1, '.' + 1, '.' + 1, '.' + 1, '\n' + 1, '\0' + 1 };
    for (unsigned int index = 0; index < sizeof(strArray); index++)
    {
        strArray[index] -= 1;
    }
    printf(strArray);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    可以看到实际每个字符以+1的模式存储,在使用前进行解密,从而实现明文字符串隐藏,同时这里的核心在于依靠编译器优化使得加密过程(这里指+1)在编译期可以直接算出加密后的结果,从而在编译完成的二进制中直接保存密文,而运行时进行解密从而使得实际结果保持不变。这里的两个需求非常重要,如果有一个不满足即会导致失败,这里将这两个总目标单独列出

    1. 编译期可以直接算出加密后的结果
    2. 运行时进行解密

    实现上述过程

    当然我们不可能每次隐藏字符串的时候都要手动去编写类似上述的代码,而希望有一个自动生成上述代码的方式。所以xorstr的核心目的就是如果将上面的写法用C++模板自动生成,并且确保总结出的两个核心目标{编译期可以直接算出加密后的结果}和{运行时进行解密}

    两个开源实现如:andrivet/ADVobfuscator以及JustasMasiulis/xorstr

    两个实现都稍显复杂,下面写个最小概念代码验证可行性

    **我们需要的使用形式:**我们希望使用起来和正常字符串使用差不多,代码如下

    #define Enc(str) EncryptString(str).decrypt()
    printf(Enc("encrypt string?"));
    
    • 1
    • 2

    因此我们需要EncryptString返回一个类的实例化对象,然后调用该对象的decrypt成员函数完成解密,代码如下

    template<size_t N>
    class EncryptString
    {
    public:
        template<size_t... Index>
        constexpr EncryptString(const char* plainString, std::index_sequence<Index...>)
            : encBuffer{ (plainString[Index] + 1)... }
        {}
    
        __forceinline char* decrypt()
        {
            for (auto index = 0; index < N; index++)
            {
                encBuffer[index] -= 1;
            }
            return encBuffer;
        }
    private:
        char encBuffer[N];
    };
    
    #define Enc(str) EncryptString<sizeof(str)>(str, std::make_index_sequence<sizeof(str)>{}).decrypt()
    
    printf(Enc("conventional...\n"));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    可以看到上述代码成功实现了我们的预定目标,实现方式概述:编译期求解字符串数组长度利用模板技术生成一个该字符串数组的加解密类,类的构造函数使用C++变参模板展开和std::index_sequence的方法实现对字符串中每个元素的提取和加密,同时保障上述过程编译时完成。同时解释下为什么这里既然所有字符串的decrypt的逻辑都是一样的,为什么还要把他实现成类的成员函数呢?因为如果设计成统一的外部解密函数会被反编译引用查找的方式找到统一的入口,大大降低分析的难度

    对抗

    介绍完原理下篇文章会详细介绍下:xorstr的还原 & xorstr还原的对抗

  • 相关阅读:
    第二章 进程与线程 五、线程(概念)
    webrtc媒体服务器介绍
    HTML跳动的爱心
    运动用品品牌排行榜,2022年值得买的运动用品推荐
    Panda3d 外部硬件接口介绍
    基于机器学习与大数据的糖尿病预测 计算机竞赛
    Javaweb-JSP详解Toretto
    学习笔记|矩阵按键控制原理|数值转化为键码|密码锁|STC32G单片机视频开发教程(冲哥)|第十四集:矩阵按键原理及实践
    VB-13
    C++奇迹之旅:深入思考拷贝构造函数
  • 原文地址:https://blog.csdn.net/Joliph/article/details/133966000