分组密码-DES

xiaoeryu Lv5

本章内容主要是DES加密的原理和如何在Java以及SO中识别DES算法

DES:DES算法为密码体制中的对称密码体制,属于分组密码,又被称为美国数据加密标准,是1972年美国IBM公司研制的对称密码体制加密算法。明文按64位进行分组,密钥长64位,而事实上是56位参与DES运算(第8、16、24、32、40、48、56、64位是校验位,使得每个密钥都有奇数个1)分组后的明文组和56位的密钥按位替代或交换的方法形成密文组的加密方法。

DES(数据加密标准)算法主要采用替换和移位的方式进行加密,他用56位(64位密钥只有56位有效)对64位二进制数据块进行加密,每次加密对64位的输入数据进行16轮编码,经过一系列替换和移位后,输入的64位原数据转换成完全不同的64位输出数据。DES作为分组密码,主要的实现过程由两部分组成,分别是密钥的生成以及明文的处理。其中每一个分组的明文的处理分为16轮进行,而对于每一轮都需要使用密钥编排部分生成的子密钥参与运算。

java代码中使用的DES

Android本身提供了用于加解密的类:javax.crypto.Cipher类,直接使用即可,其中的DES代表使用DES加密算法,CBC代表分组加密的工作模式,PKCS5Padding代表分组的填充方式;在家解密的过程中,主要的密钥和IV是在Cipher类中的init函数进行传递的,因此,只需要hook该函数即可获取到密钥和IV;

  • 在逆向过程中需要定位算法的时候,可以直接搜索“DES”、“Cipher”来定位到算法位置。但是如果代码使用了混淆、加壳、采用反射的方式(例如上图中注释的一行代码)调用Cipher的时候,我们使用静态的方式就无法直接定位到这些关键字符串所在的位置了。

  • 在这种情况下,对于Java实现的情况下我们可以使用xpose、frida去动态的hook关键函数,获取它的参数来拿到需要的内容

    例如:

    hook**Cipher.getInstance()**可以获取到加密模式和填充模式

    hook IvParameterSpec()拿到返回值可以获取到iv

    hook **cipher.init()**可以拿到密钥key

    对于Java部分来说,hook起来是相对比较容易的。困难的地方在于复杂的标准DES实现部分

复杂的标准DES实现部分

可以看到这个算法还是相对来说比较复杂的

在DES中去快速的识别确认是否是这个算法,有一个简单的办法是去定位DES加密流程中出现的一些常量表

明文处理流程中的常量表

在针对每一个明文分组即64位数据进行处理的流程中,需要有大量的常量表的参与,从而完成对明文的混淆、扩散处理。主要有:初始置换IP(8 × 8),以及与之对应的逆初始置换表(8 × 8);同时,还有在f函数中的扩展置换E表(8 × 6),用于将32位的输入,扩展成48位;加下来还有S-盒的参与,共计有8个S-盒(4 × 16)。这些常量都是快速判断DES加密算法的标志。

密钥编排流程中的常量表

在针对每一个明文分组即64位数据进行16轮的处理过程中每一轮都需要一个由原始56位密钥经过编排生成的48位子密钥的参与,该密钥编排流程中也同样出现了一些常量表的参与。主要有:初始置换PC-1表(7 × 8),以及PC-2表(6 × 8),这些常量也可以作为快速判断DES加密算法的标志。

DES的衍生算法

因为DES到了现如今仅仅56位的加密算法已经不足以确保安全性了,所以衍生出来双重和三重DES

衍生

在 DES 的基础上,衍生了以下两种加密方式

  • 双重 DES
  • 三种 DES
双重 DES

双重 DES 使用两个密钥,长度为 112 比特。加密方式如下

C=Ek2(Ek1(P))

但是双重 DES 不能抵抗中间相遇攻击,我们可以构造如下两个集合

I=Ek1(P)

J=Dk2(C)

即分别枚举 K1 和 K2 分别对 P 进行加密和对 C 进行解密。

在我们对 P 进行加密完毕后,可以对加密结果进行排序,这样的复杂度为2nlog(2n)=O(n2n)2

当我们对 C 进行解密时,可以每解密一个,就去对应的表中查询。

总的复杂度为还是O(n2n)

三重 DES

三重 DES 的加解密方式如下

C=Ek3(Dk2(Ek1(P)))

P=Dk1(Ek2(Dk3(C)))

在选择密钥时,可以有两种方法

  • 3 个不同的密钥,k1,k2,k3 互相独立,一共 168 比特。
  • 2 个不同的密钥,k1 与 k2 独立,k3=k1,112 比特。
攻击方法
  • 差分攻击
  • 线性攻击

实际的逆向分析

这里使用我们本地编写的一个标准DES加密作为例子进行分析,代码地址

Java层

java的静态分析:如果没有加保护的话,只需检索关键字字符串即可

对于使用了字符串加密,函数的调用使用了反射的方式,这种情况使用xposed、frida去hook相关的函数就可以了

function main(){
    Java.perform(function(){
        Java.use('javax.crypto.Cipher').getInstance.overload('java.lang.String').implementation = function(arg0){
            console.log('javax.crypto.Cipher.getInstance is called!', arg0);
            var result = this.getInstance(arg0);
            return result;
        };
        // cipher.init(Cipher.ENCRYPT_MODE, getRawKey(key), iv);
        Java.use('javax.crypto.Cipher').init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function(arg0, arg1, arg2){
            // console.log("javax.crypto.Cipher.init is called!", arg0, arg1, arg2);
            var mode = arg0;
            var key = arg1;
            var iv = arg2;
            var KeyClass = Java.use('java.security.Key');
            var keyobj = Java.cast(key, KeyClass);
            var key_bytes = keyobj.getEncoded();
            var IVClass = Java.use('javax.crypto.spec.IvParameterSpec');
            var ivobj = Java.cast(iv, IVClass);
            var iv_bytes = ivobj.getIV();
            console.log("javax.crypto.Cipher.init is called!", mode, JSON.stringify(key_bytes), JSON.stringify(iv_bytes));
            var result = this.init(arg0, arg1, arg2);
            return result;
        };
        // doFinal
        Java.use('javax.crypto.Cipher').doFinal.overload('[B').implementation = function(arg0){
            console.log("javax.crypto.Cipher.doFinal is called!", JSON.stringify(arg0));
            var data = arg0;
            var result = this.doFinal(arg0);
            console.log("javax.crypto.Cipher.doFinal is called!", JSON.stringify(data), "encrypt: ", JSON.stringify(result));
            return result;
        };
    })
}

setImmediate(main);
  • 代码非常简单,就是通过frida hook把这几个函数的参数以及**doFinal()**返回的加密后内容打印出来打印出来
  • 通过hook打印出了加密和填充模式、密钥Key、加密前的字符串、加密后的字符串

通过上面的实践可以知道,在java代码中去识别和获取到算法的内容是比较简单的,只需要去hook就ok了

在SO当中去识别DES

这里拿我们本地编译的未加保护的标准DES当作例子,用ida分析它的so文件

手动定位DES算法

例如这里我们可以直接在IDA搜索S盒中的16进制数字来定位

  • 在ida中alt+B可以直接搜索S盒的16进制数字,然后通过这个地址的交叉引用定位到算法所在的位置

或者,我们也可以通过使用findcrypt插件定位到算法位置

findcrypt插件定位算法

在使用插件快捷键ctrl+alt+F定位的时候发现无法定位到我们的算法位置,因为插件是按特征去识别算法来定位的,手动把算法的特征添加进去。

在ida的\plugins目录下找到findcrypt3.rules添加规则特征:将S盒处的16进制特征添加进去就可以了

然后重新识别就可以识别到算法位置

需要的话可以把P表、扩展置换表的特征都添加进去。跟添加S盒的方法是相同的。

  • 标题: 分组密码-DES
  • 作者: xiaoeryu
  • 创建于 : 2024-02-23 15:22:17
  • 更新于 : 2024-02-29 11:05:54
  • 链接: https://github.com/xiaoeryu/2024/02/23/分组密码-DES/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论