各位才华横溢,风度翩翩的读者们,你们好。今天我们讨论一下DES算法以及逆向识别。
DES算法要比RC4复杂的多,但是幸运的是它的逆向识别比RC4要简单很多,当你了解DES大致的实现原理之后就明白为什么了。
DES算法介绍
DES算法,全称是数据加密标准(Data Encryption Standard),是一种对称密钥加密技巧,也就是说,加密和解密都用同一个密钥。它是分组加密算法。分组肯定要补位,关于补位的知识,我们后面再说。
DES算法实现过程
我找了一个讲的非常好的视频,还是一个妹子讲的:
其实看视频就完全懂了,这里为了后面方便回忆,贴一张图:
图中,加密过程分成了两部分。
第一部分是由一个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 段的。
所以,就有一些项目是根据特定的数据流来识别算法,比如:
这是一个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 合并了,具体实现可参考如下项目代码:
看里面的 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 算法识别规则。