常见加解密算法04 - 分组密码DES

​各位才华横溢,风度翩翩的读者们,你们好。今天我们讨论一下DES算法以及逆向识别。

DES算法要比RC4复杂的多,但是幸运的是它的逆向识别比RC4要简单很多,当你了解DES大致的实现原理之后就明白为什么了。

DES算法介绍

DES算法,全称是数据加密标准(Data Encryption Standard),是一种对称密钥加密技巧,也就是说,加密和解密都用同一个密钥。它是分组加密算法。分组肯定要补位,关于补位的知识,我们后面再说。

DES算法实现过程

我找了一个讲的非常好的视频,还是一个妹子讲的:

www.bilibili.com/video/BV1KQ...

其实看视频就完全懂了,这里为了后面方便回忆,贴一张图:

ctf-wiki.org/crypto/bloc...

图中,加密过程分成了两部分。

第一部分是由一个64位的密钥来生成16个48位的子密钥。生成过程如下:

  • 先将密钥按照一定的规则打乱,其实是使用了一个置换表,这个置换表就是识别该算法的一个特征
  • 打乱时我们只保留了56位(剩下的8位用于奇偶校验,暂时不用管),分成两部分各28位。然后对这两个二进制流按一定的次数进行旋转,置换,再合并,得到一个48位的子密钥。搞16次,就得到了16个子密钥。这里也有一个置换表,是一个特征。
  • 密钥生成后,就可以与明文进行异或了。

第二部分是将明文使用密钥进行加密:

  • 将明文分成两个32位的,将右边的与子密钥进行异或,但是一个32位,一个48位,没法直接异或,所以采用一定的规则将32位的数据扩展成48位的 ,然后再进行异或。有兴趣的可以去看看视频,非常的精彩。
  • 异或的结果,分为 8 组,每一组是 6-bit 的数据,丢进对应的 S 盒 ,输出 4-bit 的信息。把这些输出收集起来,一共是 4*8 = 32 位,做一次置换 (P 置换),得到 32-bit 的结果。这个步骤里面的 S 盒与 P 置换都是算法识别的特征。而且为了加快DES速度,还可以将 S 与 P 进行合并。

好了,现在你已经精通DES算法了,去手搓一个吧。

DES算法识别

Java版

一般Java里面都是使用自带的 Cipher 库来实现,所以我们可以直接搜索相关类就行了,虽然有些App使用反射与字符串加密来隐藏实现,但是我们也可以直接使用 frida 等来进行Hook。

vbnet 复制代码
    /**
     * DES算法,加密
     *
     * @param data 待加密字符串
     * @param key  加密私钥,长度不能够小于8位
     * @return 加密后的字节数组,一般结合Base64编码使用
     */
    public static String encode(String key, byte[] data) {
        try {
            //Class CipherClass=MainActivity.class.getClassLoader().loadClass("javax.crypto.Cipher");
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(IVPARAMETERSPEC.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, getRawKey(key), iv);
            byte[] bytes = cipher.doFinal(data);
            return Base64.encodeToString(bytes, Base64.DEFAULT);
        } catch (Exception e) {
            return null;
        }
    }

Hook代码:

javascript 复制代码
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;
        };

        Java.use('javax.crypto.Cipher').init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (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;
        };

        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);

逻辑很直白,就不解释了,找到对应的函数干就完事了。

C版

贴部分代码,一个标准的DES实现里面的一些表:

less 复制代码
//IP初始置换表
int IP_Table[64] = {
        58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
        62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
        57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
        61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
};

//IP逆置换表
int IPR_Table[64] = {
        40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
        38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
        36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
        34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
};

//f函数中E位选择表(扩展置换表)
int E_Table[48] = {
        32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
        8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
        16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
        24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1
};
//f函数中的p表(4x8)
int P_Table[32] = {
        16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
        2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
};

int PC1_Table[56] = {
        57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18,
        10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
        63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
        14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4
};
//PC2选位表(密钥生成置换表2)
int PC2_Table[48] = {
        14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10,
        23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
        41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
        44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
};
//左移位数表
int LOOP_Table[16] = {
        1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};

// S盒
int S_Box[8][4][16] = {
        // S1
        14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
        0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
        4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
        15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
        //S2
        15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
        3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
        0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
        13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
        //S3
        10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
        13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
        13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
        1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
        //S4
        7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
        13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
        10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
        3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
        //S5
        2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
        14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
        4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
        11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
        //S6
        12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
        10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
        9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
        4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
        //S7
        4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
        13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
        1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
        6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
        //S8
        13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
        1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
        7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
        2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
};

上面的各种数组表,都是识别DES算法的特征。因为这些数组的值都是固定的,在编译之后也是按顺序储存在 data 段的。

所以,就有一些项目是根据特定的数据流来识别算法,比如:

github.com/polymorf/fi...

这是一个IDA插件,我们直接看它定义的一些DES识别规则:

ini 复制代码
rule DES_sbox
{ meta:
  author = "_pusher_"
  date = "2015-05"
  description = "DES [sbox]"
 strings:
  $c0 = { 00 04 01 01 00 00 00 00 00 00 01 00 04 04 01 01 04 00 01 01 04 04 01 00 04 00 00 00 00 00 01 00 00 04 00 00 00 04 01 01 04 04 01 01 00 04 00 00 04 04 00 01 04 00 01 01 00 00 00 01 04 00 00 00 }
 condition:
  $c0
}

这里定义了一个数据流:00 04 xx xx xx。

我们将它与标准算法的 S 盒比较,发现它并不一样,这是因为现在有一些实现为了速度,将 s 与 p 合并了,具体实现可参考如下项目代码:

github.com/weidai11/cr...

看里面的 spbox:

arduino 复制代码
const word32 RawDES::Spbox[8][64] = {
{
0x01010400,0x00000000,0x00010000,0x01010404, 0x01010004,0x00010404,0x00000004,0x00010000,
0x00000400,0x01010400,0x01010404,0x00000400, 0x01000404,0x01010004,0x01000000,0x00000004,
0x00000404,0x01000400,0x01000400,0x00010400, 0x00010400,0x01010000,0x01010000,0x01000404,
0x00010004,0x01000004,0x01000004,0x00010004, 0x00000000,0x00000404,0x00010404,0x01000000,
0x00010000,0x01010404,0x00000004,0x01010000, 0x01010400,0x01000000,0x01000000,0x00000400,
0x01010004,0x00010000,0x00010400,0x01000004, 0x00000400,0x00000004,0x01000404,0x00010404,
0x01010404,0x00010004,0x01010000,0x01000404, 0x01000004,0x00000404,0x00010404,0x01010400,
0x00000404,0x01000400,0x01000400,0x00000000, 0x00010004,0x00010400,0x00000000,0x01010004},
...
}

这个序列就与 findcrypt 里面定义的是一样的了。

知道了 findcrypt 的原理后,我们也可以添加一些标准的 DES 算法识别规则。

相关推荐
mldong1 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排2 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60612 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角3 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!3 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰4 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi5 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen6 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马7 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒7 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端