逆向常见算法之DES/3DES
逆向常见算法之DES/3DES
衡阳信安 2023-10-21 00:00 发表于湖南
DES
DES(Data Encryption Standard)是一种对称密钥的块加密算法,给定一个 64 位的明文和一个 64 位的密钥(有56位起作用,其他做奇偶效验),输出一个 64 位的密文。这个密文可以用相同的密钥解密。
DES 算法的入口参数有三个:Key、Data、Mode。
Key 为 7 个字节共 56 位,是 DES 算法的工作 密钥;
Data 为 8 个字节 64 位,是要被加密或被解密的 数据;
Mode 为 DES 的 工作方式,有两种:加密或解密。
算法原理
Feistel
DES 算法是在 Feistel network (费斯妥网络)的基础上执行的。
加密过程
令F 为轮函数 ;令K1,K2....... ,Kn分别为第1.2...n 轮的子密钥。那么基本构造过程如下:
(1)将明文信息均分为两块 0 R0
(2)在每一轮中,进行如下运算 (i为当前轮数):
Li+1 = Ri Ri+1=Li⊕F(Ri,Ki)
所得的结果即为 Ri+1,Li+1)
解密过程
对于密文(Rn+1,Ln+1)我们将i由n向0进行,即,i=n.n-1,.....,0。然后对密文进行加密的逆向操作,如下
(1) Ri= Li+1.
(2)L= Ri+1⊕F(Li+1 Ki)
所得结果为(L0,RO)即原来的明文信息
1.子密钥生成
1)置换选择PC1:去除第8位奇偶校验位,将其余56位密钥位打乱重排,前28位C0,后28位D0。
# 选择置换1,输入 key 为长度为 64 的 0/1 数组
# 从64位输入密钥中选择56位,分为左右两个28位半密钥
def PC1(key):
pc1_l = [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]
pc1_r = [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]
return [key[x-1] for x in pc1_l], [key[x-1] for x in pc1_r]
2)循环左移:将Ci-1和Di-1进行循环左移变化得到Ci和Di,位数不变。
# 循环左移off位
def leftRotate(a, off):
return a[off:] + a[ ff]
assert leftRotate([0, 1, 0, 1, 1], 2) == [0, 1, 1, 0, 1]
# 子密钥生成算法,由一个64位主密钥导出16个48位子密钥
def keyGen(key):
assert len(key) == 64
l, r = PC1(key)
off = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
res = []
for x in range(16):
l = leftRotate(l, off[x])
r = leftRotate(r, off[x])
res.append(PC2(l + r))
return res
3)置换选择PC2:将Ci与Di合成56位中间数据,并从56位中间数据中选择一个48位的子密钥Ki(i:1~16)。这个过程中,既置换了每位的顺序,又获得了子密钥Ki,因此也可以称为压缩置换。
# 选择置换2
# 从56位的密钥中选取48位子密钥
def PC2(key):
assert len(key) == 56
pc2 = [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]
return [key[x-1] for x in pc2]
4)循环2、3过程,直至生成16个48位的子密钥。
2. 迭代加密
框架
def DES(plain, key, method):
subkeys = keyGen(int2bin(key, 64))
if method == 'decrypt':
subkeys = subkeys[::-1]
m = IP(int2bin(plain, 64))
l, r = np.array(m, dtype=int).reshape(2, -1).tolist()
for i in range(16):
l, r = goRound(l, r, subkeys)
return bin2int(FP(r + l))
1)初始置换IP,目的是将输入的64位数据块按位重新组合,并把输出分为L0、R0两部分,每部分各长32位,置换规则如下表所示:
# 初始置换
def IP(a):
ip = [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]
return [a[x-1] for x in ip]
testM = [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1]
assert IP(testM) == [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0]
2)16轮feistel结构迭代(轮函数F)
Feistel加密过程:
1. 先对其进行扩展置换,使其变为 48 位的数据,
2. 然后生成的数据再与子密钥进行异或运算,得到一组新的 48 位数据
3. 再以异或运算后的 48 位数据进行 S 盒代替,将 48 位的数据,转换为 32 位的数据,
4. 再进行 P 盒置换,生成 32 位的数据
5. 最后将 P 盒置换生成的数据与本轮运算时输入的 L 进行异或运算,生成新的 R。
6. 而新的 L 是直接由本轮的 R 进行替换
a.选择运算E(也称扩展置换):将Ri-1(即输入A)扩展从32位扩展为48位,32位->48位
通过扩展置换E,数据的右半部分Ri-1从32位扩展到48位。扩展置换改变了位的次序,重复了某些位。
作用:产生与秘钥相同长度的数据以进行异或运算,R0是32位,子秘钥是48位,所以R0要先进行扩展置换之后与子秘钥进行异或运算;提供更长的结果,使得在替代运算时能够进行压缩。
b.密钥异或:48位中间结果与48位子密钥按位异或;引入密钥。
def goRound(l, r, subKey):
return r, binXor(l, Feistel(r, subKey))
# F函数,用于处理一个半块
def Feistel(a, subKey):
assert len(a) == 32
assert len(subKey) == 48
t = binXor(Expand(a), subKey)
t = S(t)
t = P(t)
return t
c.S盒代换:8个S盒,每个6位输入,4位输出,混淆作用,48位->32位
# 扩张置换,将32位的数据扩展到48位
def Expand(a):
assert len(a) == 32
e = [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]
return [a[x-1] for x in e]
# S盒变换,输入48位,输出32位
def S(a):
assert len(a) == 48
S_box = [[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],
[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],
[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],
[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],
[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],
[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],
[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],
[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]]
a = np.array(a, dtype=int).reshape(8, 6)
res = []
for i in range(8):
# 用 S_box 处理6位a,得到4位输出
p = a
r = S_box[bin2int([p[0], p[5], p[1], p[2], p[3], p[4]])]
res.append(int2bin(r, 4))
res = np.array(res).flatten().tolist()
assert len(res) == 32
return res
Rn扩展置换之后与子秘钥Kn异或以后的结果作为输入块,功能是把48位数据压缩为32位数据,由8个不同的代替盒(S盒)完成。每个S盒有6位输入,4位输出。48位的输入块被分成8个6位的分组,每一个分组对应一个S盒代替操作。经过S盒代替,得到8个4位分组结果—32位。
注:每一个S-盒的输入数据是6位,输出数据是4位,但是每个S盒自身是64位!!
d.置换P:将S盒输出的32位数据打乱重排,扩散作用,32位->32位;结果为一轮f函数输出
# P置换
def P(a):
assert len(a) == 32
p = [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]
return [a[x-1] for x in p]
异或:置换P输出与Ln-1异或得到Rn,一轮Feistle结构结束。由此,Ri、Li根据Ri-1、Li-1计算得出,并作为新一轮计算的输入。新一轮轮函数:扩展置换E->密钥异或->S盒运算->置换P->异或;总计16轮结束,最终生成L16和R16。
注:最后一轮的左右两部分不交换也不再运算,而是直接合并在一起R16L16,作为逆初始置换(FP置换)的输入块。
3)FP置换:初始置换IP的逆置换
# 最终置换
def FP(a):
fp = [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]
return [a[x-1] for x in fp]
3.解密过程
加密和解密可以使用相同的算法。加密和解密唯一不同的是秘钥的次序是相反的。为每一轮产生秘钥的算法也是循环的。加密是秘钥循环左移,解密是秘钥循环右移。解密秘钥每次移动的位数是:0、1、2、2、2、2、2、2、1、2、2、2、2、2、2、1。
程序实现
贴一下代码,方便查询和对比
python
from functools import reduce
import numpy as np
# 整数转二进制数组,指定位长 n,大端序
def int2bin(a, n):
assert 0<=n and a < 2**n
res = np.zeros(n, dtype = int)
for x in range(n):
res[n-x-1] = a % 2
a = a // 2
return res.tolist()
assert int2bin(0x1a, 10) == [0, 0, 0, 0, 0, 1, 1, 0, 1, 0]
# 二进制数组转整数,大端序
def bin2int(a):
return reduce(lambda x,y: x*2+y, a)
assert bin2int([0, 0, 0, 0, 0, 1, 1, 0, 1, 0]) == 0x1a
# 循环左移off位
def leftRotate(a, off):
return a[off:] + a[ ff]
assert leftRotate([0, 1, 0, 1, 1], 2) == [0, 1, 1, 0, 1]
# 异或
def binXor(a, b):
assert len(a) == len(b)
return [x^y for x, y in zip(a, b)]
assert binXor([1, 1, 0, 1], [0, 1, 1, 0]) == [1, 0, 1, 1]
# 初始置换
def IP(a):
ip = [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]
return [a[x-1] for x in ip]
testM = [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1]
assert IP(testM) == [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0]
# 最终置换
def FP(a):
fp = [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]
return [a[x-1] for x in fp]
# 选择置换1
# 从64位输入密钥中选择56位,分为左右两个28位半密钥
def PC1(key):
pc1_l = [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]
pc1_r = [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]
return [key[x-1] for x in pc1_l], [key[x-1] for x in pc1_r]
testKey = [0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1]
testL, testR = PC1(testKey)
assert testL + testR == [1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1]
# 选择置换2
# 从56位的密钥中选取48位子密钥
def PC2(key):
assert len(key) == 56
pc2 = [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]
return [key[x-1] for x in pc2]
# 子密钥生成算法,由一个64位主密钥导出16个48位子密钥
def keyGen(key):
assert len(key) == 64
l, r = PC1(key)
off = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
res = []
for x in range(16):
l = leftRotate(l, off[x])
r = leftRotate(r, off[x])
res.append(PC2(l + r))
return res
assert keyGen(testKey)[-1] == [1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1]
# S盒变换,输入48位,输出32位
def S(a):
assert len(a) == 48
S_box = [[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],
[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],
[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],
[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],
[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],
[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],
[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],
[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]]
a = np.array(a, dtype=int).reshape(8, 6)
res = []
for i in range(8):
# 用 S_box 处理6位a,得到4位输出
p = a
r = S_box[bin2int([p[0], p[5], p[1], p[2], p[3], p[4]])]
res.append(int2bin(r, 4))
res = np.array(res).flatten().tolist()
assert len(res) == 32
return res
# 扩张置换,将32位的半块扩展到48位
def Expand(a):
assert len(a) == 32
e = [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]
return [a[x-1] for x in e]
# P置换
def P(a):
assert len(a) == 32
p = [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]
return [a[x-1] for x in p]
# F函数,用于处理一个半块
def Feistel(a, subKey):
assert len(a) == 32
assert len(subKey) == 48
t = binXor(Expand(a), subKey)
t = S(t)
t = P(t)
return t
def goRound(l, r, subKey):
return r, binXor(l, Feistel(r, subKey))
def DES(plain, key, method):
subkeys = keyGen(int2bin(key, 64))
if method == 'decrypt':
subkeys = subkeys[::-1]
m = IP(int2bin(plain, 64))
l, r = np.array(m, dtype=int).reshape(2, -1).tolist()
for i in range(16):
l, r = goRound(l, r, subkeys)
return bin2int(FP(r + l))
print(hex(DES(0x11aabbccddeeff01, 0xcafababedeadbeaf, 'encrypt')))
# 0x2973a7e54ec730a3
print(hex(DES(0x2973a7e54ec730a3, 0xcafababedeadbeaf, 'decrypt')))
# 0x11aabbccddeeff01
c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
/*------------------------
定义枚举型全局变量
------------------------*/
typedef enum
{
false = 0,
true = 1
} bool;
// 十六轮子密钥
static bool SubKey[16][48] = {0};
/*---------------------*/
/*-------------------------------------------------------------
各种置换表
-------------------------------------------------------------*/
// IP置换表
const char 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-1置换表
const char 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
};
// E扩展表
static char 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
};
// PC1置换表
static char 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表
static char 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,34,33,48,
44,49,39,56,34,53,46,42,50,36,29,32
};
// 移位表
static char Move_Table[16] = {
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};
// S盒
static char 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, 0, 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, 0, 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
};
//P置换表
static char 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
};
/*-------------------------------------------------------------------*/
/*-----------------------------自定义函数-----------------------------*/
void SetKey(char My_key[8]); //生成16轮的子密钥;
void ByteToBit(bool* Data_out, char* Data_in, int Num); //字节转换成位;
void Change_bit(bool* Data_out, int Num);//二进制的位置进行转换;
void BitToByte(char My_message[8], bool* Message_in, int Num); //位转换成字节;
void TableReplace(bool* Data_out, bool* Data_in, const char* Table, int Num); //各种表的置换算法;
void Bitcopy(bool* Data_out, bool* Data_in, int Num); //二进制数组的拷贝
void Loop_bit(bool* Data_out, int movstep, int len); //左移位;
void Run_Des(char My_message[8], char HexMssage[16]);//des的轮加密算法
void Xor(bool* Message_out, bool* Message_in, int Num); //执行异或
void S_change(bool* Data_out, bool* Data_in); // S盒变换;
void HexToBit(bool* Data_out, char* Data_in, int Num); // 十六进制转二进制
void BitToHex(char* Data_out, bool* Data_in, int Num); //二进制转换成十六进制;
void Run_desDes(char My_message[8], char HexMessage[16]);// DES轮解密算法;
/*--------------------------*/
/*--------------------------主函数----------------------------------*/
int main()
{
int i = 0, j;
char My_key[8] = {0}; //记录加密密钥;
char You_key[8] = {0}; //解密密钥
char My_message[8] = {0}; //明文
char Message_hex[16] = {0};//16进制的密文
printf("请输入你要加密的内容(8 Byte):\n");
gets(My_message);
printf("请输入你的加密密钥:\n");
gets(My_key);
i = strlen(My_key);
while(i != 8)
{
printf("请输入加密密钥(8 Byte)\n");
gets(My_key);
i = 0;
i = strlen(My_key);
}
SetKey(My_key); //生成16轮的加密子密钥;
Run_Des(My_message, Message_hex); //des的轮加密过程
printf("经过加密的密文为:\n");
for(i = 0; i < 16; i++)
{
printf("%c ", Message_hex);
}
printf("\n");
printf("请输入你的解密密钥(8 Byte):\n");
gets(You_key);
i = strlen(You_key);
while(i != 8)
{
printf("请输入解密密钥(8 Byte)\n");
gets(You_key);
i = 0;
i = strlen(You_key);
}
SetKey(You_key); //生成16轮的解密子密钥;
Run_desDes(My_message, Message_hex);//解密;
printf("解密结果为:\n");
for(i = 0; i < 8; i++)
{
printf("%c ", My_message);
}
printf("\n");
return 0;
}
/*--------------------具体函数定义----------------------*/
void Bitcopy(bool* Data_out, bool* Data_in, int Num) //二进制数组拷贝
{
int i = 0;
for(i = 0; i < Num; i++)
{
Data_out = Data_in;
}
}
void Change_bit(bool* Data_out, int Num) //二进制的位置进行转换;
{
int i, j;
static bool Temp[8] = {0};
for(i = 0; i < Num / 8; i++)
{
Bitcopy(Temp, Data_out, Num / 8);
for(j = 0; j < Num / 8; j++)
{
Data_out[j] = Temp[Num / 8 - 1 - j];
}
Data_out += Num / 8;
}
}
void ByteToBit(bool* Data_out, char* Data_in, int Num) //字节转位
{
int i, j;
for(i = 0; i < Num; i++)
{
Data_out = (Data_in[i / 8] >> (i % 8)) & 0x01;
}
//Change_bit(Data_out,Num);
}
void BitToHex(char* Data_out, bool* Data_in, int Num) //二进制转十六进制
{
int i;
for(i = 0; i < Num / 4; i++)
{
Data_out = 0;
}
for(i = 0; i < Num / 4; i++)
{
Data_out = Data_in[4 * i] + Data_in[4 * i + 1] * 2 + Data_in[4 * i + 2] * 4 + Data_in[4 * i + 3] * 8;
if(Data_out % 16 > 9)
{
Data_out = Data_out % 16 + '7';
}
else
Data_out = Data_out % 16 + '0';
}
}
void HexToBit(bool* Data_out, char* Data_in, int Num) //十六进制转二进制
{
int i;
for(i = 0; i < Num; i++)
{
if(Data_in[i / 4] <= '9')
{
Data_out = ((Data_in[i / 4] - '0') >> (i % 4)) & 0x01;
}
else
{
Data_out = ((Data_in[i / 4] - '7') >> (i % 4)) & 0x01;
}
}
}
void BitToByte(char My_message[8], bool* Message_in, int Num) //位转换成字节
{
int i = 0;
for(i = 0; i < (Num / 8); i++)
{
My_message = 0;
}
for(i = 0; i < Num; i++)
{
My_message[i / 8] |= Message_in << (i % 8);
}
}
void TableReplace(bool* Data_out, bool* Data_in, const char* Table, int Num) // 置换算法
{
int i = 0;
static bool Temp[256] = {0};
for(i = 0; i < Num; i++)
{
Temp = Data_in[Table - 1];
}
Bitcopy(Data_out, Temp, Num);
}
void Loop_bit(bool* Data_out, int movstep, int len)
{
static bool Temp[256] = {0};
Bitcopy(Temp, Data_out, movstep);
Bitcopy(Data_out, Data_out + movstep, len - movstep);
Bitcopy(Data_out + len - movstep, Temp, movstep);
/*Temp=Data_out;
Temp[movstep]='\0';
Data_out=Data_out+movstep;
Data_out+(len-movstep)=Temp;*/
}
void Xor(bool* Message_out, bool* Message_in, int Num)//执行异或
{
int i;
for(i = 0; i < Num; i++)
{
Message_out = Message_out ^ Message_in;
}
}
void SetKey(char My_key[8])
{
int i, j;
static bool Key_bit[64] = {0}; //Key的二进制缓存;
static bool* Key_bit_L, * Key_bit_R;
Key_bit_L = &Key_bit[0]; //key的左边28位;
Key_bit_R = &Key_bit[28]; //key的右边28位;
ByteToBit(Key_bit, My_key, 64);
/* Change_bit(Key_bit,64) ;//二进制的位置进行转换;
for(i=0;i<64;i++)
{
printf("%d ",Key_bit);
}
printf("\n");
printf("\n");*/
TableReplace(Key_bit, Key_bit, PC1_Table, 56);//pc-1 置换
for(i = 0; i < 16; i++)
{
Loop_bit(Key_bit_L, Move_Table, 28);
Loop_bit(Key_bit_R, Move_Table, 28);
TableReplace(SubKey, Key_bit, PC2_Table, 48);//pc-2置换
}
}
void S_change(bool* Data_out, bool* Data_in) //S盒变换
{
int i;
int r = 0, c = 0;//S盒的行和列;
for(i = 0; i < 8; i++, Data_in = Data_in + 6, Data_out = Data_out + 4)
{
r = Data_in[0] * 2 + Data_in[5] * 1;
c = Data_in[1] * 8 + Data_in[2] * 4 + Data_in[3] * 2 + Data_in[4] * 1;
ByteToBit(Data_out, &S_Box[r][c], 4);
}
}
void F_change(bool Data_out[32], bool Data_in[48]) // f函数;
{
int i;
static bool Message_E[48] = {0}; //存放E置换的结果;
TableReplace(Message_E, Data_out, E_Table, 48);//E表置换
Xor(Message_E, Data_in, 48);
S_change(Data_out, Message_E); // S盒变换
TableReplace(Data_out, Data_out, P_Table, 32); //P置换
}
void Run_Des(char My_message[8], char HexMssage[16])//des轮加密算法;
{
int i;
static bool Message_bit[64] = {0};
static bool* Message_bit_L = &Message_bit[0], * Message_bit_R = &Message_bit[32];
static bool Temp[32] = {0};
ByteToBit(Message_bit, My_message, 64);
/*Change_bit(Message_bit,64) ;//二进制的位置进行转换;
for(i=0;i<64;i++)
{
printf("%d ",Message_bit);
}
printf("\n");
printf("\n");*/
TableReplace(Message_bit, Message_bit, IP_Table, 64);
for(i = 0; i < 16; i++)
{
Bitcopy(Temp, Message_bit_R, 32);
F_change(Message_bit_R, SubKey);
Xor(Message_bit_R, Message_bit_L, 32);
Bitcopy(Message_bit_L, Temp, 32);
}
TableReplace(Message_bit, Message_bit, IPR_Table, 64);
BitToHex(HexMssage, Message_bit, 64);//二进制转换成十六进制;
}
void Run_desDes(char My_message[8], char HexMessage[16])// DES轮解密算法;
{
int i = 0;
static bool Message_bit[64] = {0};
static bool* Message_bit_L = &Message_bit[0], * Message_bit_R = &Message_bit[32];
static bool Temp[32] = {0};
HexToBit(Message_bit, HexMessage, 64);
TableReplace(Message_bit, Message_bit, IP_Table, 64);
for(i = 15; i >= 0; i--)
{
Bitcopy(Temp, Message_bit_L, 32);
F_change(Message_bit_L, SubKey);
Xor(Message_bit_L, Message_bit_R, 32);
Bitcopy(Message_bit_R, Temp, 32);
}
TableReplace(Message_bit, Message_bit, IPR_Table, 64);
BitToByte(My_message, Message_bit, 64);
}
题目练习
[羊城杯 2020]babyre
64位elf文件
findcrypt发现有aes
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int i; // [rsp+0h] [rbp-140h]
char v5[16]; // [rsp+A0h] [rbp-A0h] BYREF
char s[64]; // [rsp+B0h] [rbp-90h] BYREF
char v7[72]; // [rsp+F0h] [rbp-50h] BYREF
unsigned __int64 v8; // [rsp+138h] [rbp-8h]
v8 = __readfsqword(0x28u);
sub_402563();
__isoc99_scanf("%39s", s);
if ( (unsigned int)strlen(s) != 16 )
{
puts("Wrong!");
exit(0);
}
DES_string_to_key();
if ( !(unsigned int)DES_set_key_checked() )
{
memset(v5, 0, 8uLL);
DES_ncbc_encrypt();
for ( i = 0; i <= 15; ++i )
{
if ( v7 != byte_6040C0 )
puts("wrong!");
}
if ( (*(unsigned int (__fastcall **)(char *, char *))byte_40272D)(s, v7) )
puts("Correct!");
else
puts("Wrong!");
exit(0);
}
puts("convert to key_schedule failed.");
return 0xFFFFFFFFLL;
}
main函数里有des,byte_40272D是经过sub_402563smc的代码
先动调出sub_40272D。这里使用了openssl的动态加密算法库,需要在本地也安装上
wget https://www.openssl.org/source/openssl-1.0.0.tar.gz
#解压
tar -vxzf openssl-1.0.0.tar.gz
cd openssl-1.0.0
mkdir build
#创建版本信息文件(内容在下一步),无此文件链接时会报找不到版本信息
vim openssl.ld
#进入vim输入
OPENSSL_1.0.0 {
global:
*;
};
OPENSSL_1.0.1 {
new*;
}OPENSSL_1.0.0;
OPENSSL_1.0.1p {
new*;
}OPENSSL_1.0.0;
#esc :wq
#配置
./config no-asm shared -Wl,--version-script=openssl.ld --prefix=./build
#编译
make -j`nproc`
sudo cp libcrypto.so.1.0.0 /usr/lib/
环境配置好后开始动调
在 sub_402563(a1, a2, a3);处打一个断点,F4运行到这,再向下一步,byte_40272D就解密了
点进去c强制分析,在开头p生成函数
void __fastcall __noreturn sub_40272D(__int64 a1)
{
int i; // [rsp+18h] [rbp-128h]
int j; // [rsp+1Ch] [rbp-124h]
int k; // [rsp+24h] [rbp-11Ch]
int m; // [rsp+28h] [rbp-118h]
char v5[192]; // [rsp+30h] [rbp-110h] BYREF
char s[16]; // [rsp+F0h] [rbp-50h] BYREF
_BYTE v7[56]; // [rsp+100h] [rbp-40h] BYREF
unsigned __int64 v8; // [rsp+138h] [rbp-8h]
v8 = __readfsqword(0x28u);
__isoc99_scanf("%40s", s);
if ( (unsigned int)strlen(s) != 32 )
{
puts("Wrong!");
exit(0);
}
sub_400C91(v5, a1);
sub_401B8E(v5, s);
sub_401B8E(v5, v7);
for ( i = 0; i <= 31; ++i )
{
for ( j = 0; i / 4 > j; ++j )
s ^= s[j];
}
for ( k = 1; k <= 31; ++k )
byte_6040D0[k - 1] = (2 * (s[k - 1] ^ 0x13) + 7) ^ ((unsigned __int8)s[k - 1] % 9u + s[k] + 2);
if ( v7[15] == 0xC4 )
{
for ( m = 0; m <= 30; ++m )
;
}
}
其中 sub_400C91(v5, a1); sub_401B8E(v5, s); sub_401B8E(v5, v7);是AES加密
所以总体的逻辑就是先进行des加密,正确后进入sub_40272D,des的结果作为key,再输入并进行aes加密
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+0h] [rbp-140h]
char v5[128]; // [rsp+10h] [rbp-130h] BYREF
char v6[16]; // [rsp+90h] [rbp-B0h] BYREF
char v7[16]; // [rsp+A0h] [rbp-A0h] BYREF
char s[64]; // [rsp+B0h] [rbp-90h] BYREF
char v9[72]; // [rsp+F0h] [rbp-50h] BYREF
unsigned __int64 v10; // [rsp+138h] [rbp-8h]
v10 = __readfsqword(0x28u);
sub_402563(argc, argv, envp);
__isoc99_scanf("%39s", s);
if ( (unsigned int)strlen(s) != 16 )
{
puts("Wrong!");
exit(0);
}
DES_string_to_key("this is my key", v6);
if ( !(unsigned int)DES_set_key_checked(v6, v5) )
{
memset(v7, 0, 8uLL);
DES_ncbc_encrypt(s, v9, 60LL, v5, v7, 1LL);
for ( i = 0; i <= 15; ++i )
{
if ( v9 != byte_6040C0 )
puts("wrong!");
}
sub_40272D((__int64)s);
}
puts("convert to key_schedule failed.");
return -1;
}
先来des:
des的key并不是所给的字符串'this is my key',而是经过一些汇编代码处理字符串得到的。在scanf处下一个断点,之后F7过检测,让程序运行到下一个if结构中。这时v6中的也不是key,在汇编界面找到对应的寄存器中才是正确的key
des的密文就是byte_6040C0 = [0x0A, 0xF4, 0xEE, 0xC8, 0x42, 0x8A, 0x9B, 0xDB, 0xA2, 0x26, 0x6F, 0xEE, 0xEE, 0xE0, 0xD8, 0xA2]
分别用ECB模式和CBC模式解DES,两次解密结果的拼接即为第一次正确的输入
from Crypto.Cipher import DES
key=b'\xAD\x52\xF2\x4C\xE3\x2C\x20\xD6'
des_ecb=DES.new(key,DES.MODE_ECB)
des_cbc=DES.new(key,DES.MODE_CBC,key)
cipher=b'\x0A\xF4\xEE\xC8\x42\x8A\x9B\xDB\xA2\x26\x6F\xEE\xEE\xE0\xD8\xA2'
m1=des_ecb.decrypt(cipher)
m2=des_cbc.decrypt(cipher)
print(m1)
print(m2)
#th1s1sth3n1c3k3y
接下来进入aes:
input在传入后先经过aes,之后经过异或等处理,key已知,但是找不着enc了。。
再次查看汇编,在enc在两段有反编译的代码中间,byte_604100 = [ 0xBD, 0xAD, 0xB4, 0x84, 0x10, 0x63, 0xB3, 0xE1, 0xC6, 0x84,0x2D, 0x6F, 0xBA, 0x88, 0x74, 0xC4, 0x90, 0x32, 0xEA, 0x2E, 0xC6, 0x28, 0x65, 0x70, 0xC9, 0x75, 0x78, 0xA0, 0x0B, 0x9F, 0xA6, 0x00, 0x30, 0xE4, 0xD2, 0xC3, 0xEF, 0x75, 0xED, 0xA8, 0xE1, 0xA1, 0x73, 0x81, 0xE2, 0xE9, 0xAB, 0xC8, 0xBF, 0xCA, 0x52, 0xE8, 0xED, 0x6B, 0xA2, 0x39, 0x86, 0x21, 0xD0, 0xF6, 0x50, 0x3E, 0xF3, 0x5C]
在函数最后提示了 if ( v7[15] == 0xC4 )
res = "BDADB4841063B3E1C6842D6FBA8874C49032EA2EC6286570C97578A00B9FA6"
res = bytearray.fromhex(res)
aes_key = b'th1s1sth3n1c3k3y'
flag = [0] * 32
flag[31] = 0xc4
flags = []
def deep_search(flag, deep):
if deep == 0:
flags.append(flag.copy())
else:
for i in range(0xff):
if ((2 * (i ^ 0x13) + 7) ^ (i % 9 + flag[deep] + 2)) & 0xff == res[deep-1]:
flag[deep-1] = i
deep_search(flag, deep-1)
deep_search(flag, 31)
print(flags, "\nfind solver number is: ", len(flags))
for flag in flags:
for i in range(31, -1, -1):
for j in range(int(i/4)):
flag ^= flag[j]
res = AES.new(aes_key, AES.MODE_ECB).decrypt(bytes(flag))
print(res)
GWHT{th1s_gam3_1s_s0_c00l_and_d}
3DES
随着计算机算力的增长,DES加密56位的秘钥容易受到穷举攻击。因此,3DES作为替换升级的方案应运而生。
算法原理
加密
3DES(DESede , TripleDES)也是三重DES,是为了增加DES的强度,将DES重复3次所得到的一种加密算法。
三重DES的密钥长度为3cheng56=168 bit,进行加密-解密-加密的过程,设Ek()和Dk()分别代表DES算法的加密和解密过程,k代表DES算法使用的秘钥,M代表明文,C代表密文,则3DES加密为:C = Ek3(Dk2(Ek2(Dk1(C))))
可以使秘钥k1 = k3 ,即使用两个有效的秘钥,以降低对计算机算力的要求(DES_EDE2)
当三重DES中所有密钥都相同时,三重DES就等于普通的DES了,这样3DES就与DES兼容了
解密
解密过程和加密过程正好相反,以密钥3-密钥2-密钥1的顺序执行解密-加密-解密的操作
程序实现
c
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <memory.h>
using namespace std;
typedef bool
(*PSubKey)[16][48];
// 初始置换
const static char Table_IP[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
};
// 末置换
const static char Table_InverseIP[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
};
// 扩展置换
static const char Table_E[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
};
// 密钥初始置换
const static char Table_PC1[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
};
// 左移运算
const static char Table_Moveleft[16] =
{
1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1
};
// 密钥压缩置换
const static char Table_PC2[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
};
// S盒
const static char Box_S[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
};
// P盒置换
const static char Table_P[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
};
static bool SubKey[2][16][48];//两个密钥的16轮子密钥
static char Tmp[256], deskey[16];
#define ENCRYPT 0
#define DECRYPT 1
//通用置换函数
void Transform(bool *Out, bool *In, const char *Table, int len)
{
for(int i = 0; i < len; ++ i)
Tmp = In[ Table - 1 ];
memcpy(Out, Tmp, len);
}
//异或运算
void Xor(bool *InA, const bool *InB, int len)
{
for(int i = 0; i < len; ++ i)
InA ^= InB;
}
//循环左移
void MoveLeft(bool *In, int len, int loop)
{
memcpy(Tmp, In, loop);
memcpy(In, In + loop, len - loop);
memcpy(In + len - loop, Tmp, loop);
}
//字节转换成位
void Byte2Bit(bool *Out, const char *In, int bits)
{
for(int i = 0; i < bits; ++ i)
Out = (In[i >> 3] >> (i & 7)) & 1;
}
//位转换字节
void Bit2Byte(char *Out, const bool *In, int bits)
{
memset(Out, 0, bits >> 3);
for(int i = 0; i < bits; ++ i)
Out[i >> 3] |= In << (i & 7);
}
//S 盒置换
void funS(bool Out[32], const bool In[48])
{
for(char i = 0, j, k; i < 8; ++ i, In += 6, Out += 4)
{
j = (In[0] << 1) + In[5];
k = (In[1] << 3) + (In[2] << 2) + (In[3] << 1) + In[4];
Byte2Bit(Out, &Box_S[j][k], 4);
}
}
//F 函数
void funF(bool In[32], const bool Ki[48])
{
static bool MR[48];
Transform(MR, In, Table_E, 48);
Xor(MR, Ki, 48);
funS(In, MR);
Transform(In, In, Table_P, 32);
}
//生成子密钥
void MakeSubKey(PSubKey pSubKey, const char Key[8])
{
static bool K[64], *KL = &K[0], *KR = &K[28];
Byte2Bit(K, Key, 64);
Transform(K, K, Table_PC1, 56);
for(int i = 0; i < 16; ++ i)
{
MoveLeft(KL, 28, Table_Moveleft);
MoveLeft(KR, 28, Table_Moveleft);
Transform((*pSubKey), K, Table_PC2, 48);
}
}
//生成密钥
void MakeKey(const char* Key, int len)
{
memset(deskey, 0, 16);
memcpy(deskey, Key, len > 16 ? 16 : len);
MakeSubKey(&SubKey[1], &deskey[8]);
}
//一重DES加/解密
void DES(char Out[8], char In[8], const PSubKey pSubKey, bool Type)
{
static bool M[64], tmp[32], *Li = &M[0], *Ri = &M[32];
Byte2Bit(M, In, 64);
Transform(M, M, Table_IP, 64);
if( Type == ENCRYPT ){ //加密
for(int i = 0; i < 16; ++ i)
{
memcpy(tmp, Ri, 32);
funF(Ri, (*pSubKey));
Xor(Ri, Li, 32);
memcpy(Li, tmp, 32);
}
}
else //解密
{
for(int i = 15; i >= 0; -- i)
{
memcpy(tmp, Li, 32);
funF(Li, (*pSubKey));
Xor(Li, Ri, 32);
memcpy(Ri, tmp, 32);
}
}
Transform(M, M, Table_InverseIP, 64);
Bit2Byte(Out, M, 64);
}
bool DoDES(char *Out, char *In, long datalen, const char *Key, int keylen, bool Type)
{
if( !( Out && In && Key && (datalen=(datalen+7)&0xfffffff8) ) )
return false;
MakeKey(Key, keylen);
// 3次DES 加密:加(key0)-解(key1)-加(key0) 解密:解(key0)-加(key1)-解(key0)
for(long i = 0, j = datalen >> 3; i < j; ++ i, Out += 8, In += 8)
{
DES(Out, In, &SubKey[0], Type);
DES(Out, Out, &SubKey[1], !Type);
DES(Out, Out, &SubKey[0], Type);
}
return true;
}
int main(int argc,char *argv[])
{
int i;
char key[255];
char buf[255];
char str[8];
char key1[8];
char key2[8];
cout<<" lease input The string before encrypting: \n";
cin.getline(str, 255, '\n');
do
{
cout<<" lease input Key1(8 chars): \n";
cin>>key1;
} while (strlen(key1) != 8); //key1为8个字符
cout<<" lease input Key2: \n";
cin>>key2;
memset(key, 0, sizeof(key));
for (i = 0; i < 8; i ++)
{
key = key1;
key[i + 8] = key2;
}
memset(buf, 0, sizeof(buf));
strcpy(buf, str);
cout<<"\nBefore encrypting\n";
cout<<buf<<endl;
DoDES(buf, buf, sizeof(str), key, sizeof(key), ENCRYPT);
cout<<"\nAfter encrypting\n";
cout<<buf<<endl;
DoDES(buf, buf, sizeof(str), key, sizeof(key), DECRYPT);
cout<<"\nAfter decrypting\n";
cout<<buf<<endl;
return 0;
}
python
from libdes import DES_Encrypt, DES_Decrypt
def validate_des_key(key: bytes) -> bool:
for keyByte in key:
binStr: str = "{0:0>8b}".format(keyByte)
if sum([1 if b == '1' else 0 for b in binStr]) % 2 == 0:
return False
return True
if __name__ == '__main__':
plaintextHex: str = input('plaintext:')
key1Hex: str = input('key1:')
if not validate_des_key(bytes.fromhex(key1Hex)):
raise Exception('Parity check failed on the key.')
key2Hex: str = input('key2:')
if not validate_des_key(bytes.fromhex(key2Hex)):
raise Exception('Parity check failed on the key.')
key3Hex: str = input('key3:')
if not validate_des_key(bytes.fromhex(key3Hex)):
raise Exception('Parity check failed on the key.')
ciphertext1: bytes = DES_Encrypt(
bytes.fromhex(plaintextHex),
bytes.fromhex(key1Hex),
)
ciphertext2: bytes = DES_Decrypt(
ciphertext1,
bytes.fromhex(key2Hex),
)
ciphertext3: bytes = DES_Encrypt(
ciphertext2,
bytes.fromhex(key3Hex),
)
print('ciphertext:', ciphertext3.hex())
plaintext3: bytes = DES_Decrypt(
ciphertext3,
bytes.fromhex(key3Hex),
)
plaintext2: bytes = DES_Encrypt(
plaintext3,
bytes.fromhex(key2Hex),
)
plaintext1: bytes = DES_Decrypt(
plaintext2,
bytes.fromhex(key1Hex),
)
print('plaintext:', plaintext1.hex())
题目练习
[强网杯] just re
32位exe文件
ida打开,findcrypt插件发现des加密
int __cdecl main(int argc, const char **argv, const char **envp)
{
char ArgList[100]; // [esp+4h] [ebp-68h] BYREF
puts(" # ###### ");
puts(" # # # #### ##### # # ###### ");
puts(" # # # # # # # # ");
puts(" # # # #### # ###### ##### ");
puts("# # # # # # # # # ");
puts("# # # # # # # # # # ");
puts(" ##### #### #### # # # ###### ");
sub_401CE0("%s", (char)ArgList);
if ( sub_401610(ArgList) && sub_4018A0(ArgList) )
{
puts("congrats!");
sub_401CA0("flag{%.26s}\n\n", (char)ArgList);
return 0;
}
else
{
puts("sorry..");
return 0;
}
}
main函数看不出什么,先从des的表开始
按x查找,找到连续使用三次des的函数
操作太多了,考虑使用z3或者动调
找到3des之后追不了了,查看汇编
上面的一段很像smc,解密之后应该可以把3des和main函数联系起来
回到main函数分析,sub_4018A0会jumpout到sub_401610的 WriteProcessMemory(CurrentProcess, sub_4018A0, &xmmword_405018, 0x60u, 0);
int __thiscall sub_401610(_BYTE *this)
{
unsigned int v2; // eax
int v3; // esi
char v4; // dh
unsigned __int8 v5; // dl
unsigned int v6; // eax
int v7; // ecx
__m128i v8; // xmm5
char v9; // dl
unsigned __int8 v10; // dh
unsigned __int8 v11; // cl
char v12; // ch
unsigned __int8 v13; // ch
char v14; // dl
unsigned __int8 v15; // dh
int v16; // esi
__m128i v17; // xmm0
__m128i v18; // xmm0
int v19; // esi
__m128i v20; // xmm4
int v21; // ecx
unsigned __int64 v22; // rax
HANDLE CurrentProcess; // eax
unsigned __int64 v25; // [esp-54h] [ebp-60h]
unsigned int v26; // [esp-20h] [ebp-2Ch]
unsigned __int8 v27; // [esp-1h] [ebp-Dh]
v25 = __rdtsc();
v2 = 0;
v3 = 0;
while ( 1 )
{
v4 = this[v3];
if ( v4 >= '0' && v4 <= '9' ) // 数字
{
v5 = v4 - 65;
goto LABEL_6;
}
v5 = v4 - 'A'; // A-F = 0-5
if ( (unsigned __int8)(v4 - 'A') > 0x19u ) // a-z
break;
LABEL_6:
v2 *= 16;
if ( (unsigned __int8)(v4 - 48) <= 9u )
{
v6 = v2 - 48;
LABEL_10:
v2 = v4 + v6;
goto LABEL_11;
}
if ( v5 <= 0x19u )
{
v6 = v2 - 55; // A-Z = 10-15
goto LABEL_10;
}
LABEL_11:
if ( ++v3 >= 8 )
{
v7 = 1;
goto LABEL_14;
}
}
v7 = 0;
LABEL_14:
v8 = _mm_shuffle_epi32(_mm_cvtsi32_si128(v2), 0);
if ( !v7 )
return 0;
v9 = this[8];
v10 = 0;
if ( v9 < 48 || v9 > 57 )
{
v11 = v9 - 65;
v27 = v9 - 65;
if ( (unsigned __int8)(v9 - 65) <= 0x19u )
goto LABEL_19;
LABEL_33:
v16 = 0;
goto LABEL_34;
}
v11 = v9 - 65;
v27 = v9 - 65;
LABEL_19:
v12 = this[9];
if ( v12 >= 48 && v12 <= 57 )
{
v13 = v12 - 65;
goto LABEL_23;
}
v13 = v12 - 65;
v27 = v11;
if ( v13 > 0x19u )
goto LABEL_33;
LABEL_23:
if ( (unsigned __int8)(v9 - 48) > 9u )
{
if ( v27 > 0x19u )
v14 = 0;
else
v14 = 16 * (v9 - 7);
}
else
{
v14 = 16 * v9;
}
v15 = this[9] - 48;
if ( v15 <= 9u )
goto LABEL_32;
if ( v13 > 0x19u )
{
v15 = 0;
LABEL_32:
v10 = v14 + v15;
v16 = 1;
goto LABEL_34;
}
v16 = 1;
v10 = v14 + this[9] - 55;
LABEL_34:
v17 = _mm_cvtsi32_si128((char)v10);
v18 = _mm_unpacklo_epi8(v17, v17);
v26 = _mm_shuffle_epi32(_mm_unpacklo_epi16(v18, v18), 0).m128i_u32[0];
if ( v16 )
{
v19 = 0;
if ( dword_4053C4 >= 2 )
{
v19 = 16;
v20 = _mm_mullo_epi32(_mm_cvtepu8_epi32(_mm_cvtsi32_si128(v26)), (__m128i)xmmword_404380);
xmmword_405018 = (__int128)_mm_xor_si128(
_mm_add_epi32((__m128i)xmmword_404340, v8),
_mm_add_epi32(v20, (__m128i)xmmword_405018));
xmmword_405028 = (__int128)_mm_xor_si128(
_mm_add_epi32(_mm_add_epi32((__m128i)xmmword_404350, (__m128i)xmmword_404340), v8),
_mm_add_epi32(v20, (__m128i)xmmword_405028));
xmmword_405038 = (__int128)_mm_xor_si128(
_mm_add_epi32(_mm_add_epi32((__m128i)xmmword_404360, (__m128i)xmmword_404340), v8),
_mm_add_epi32(v20, (__m128i)xmmword_405038));
xmmword_405048 = (__int128)_mm_xor_si128(
_mm_add_epi32(_mm_add_epi32((__m128i)xmmword_404370, (__m128i)xmmword_404340), v8),
_mm_add_epi32(v20, (__m128i)xmmword_405048));
}
do
{
*((_DWORD *)&xmmword_405018 + v19) = (v19 + v2) ^ (16843009 * v10 + *((_DWORD *)&xmmword_405018 + v19));
++v19;
}
while ( v19 < 24 );
v21 = 0;
while ( *((_BYTE *)&xmmword_405018 + v21) == *((_BYTE *)&loc_404148 + v21) )
{
if ( ++v21 >= 96 )
{
v22 = __rdtsc();
if ( HIDWORD(v22) > HIDWORD(v25) || (unsigned int)(v22 - v25) >= 0xFFFFFF )
MEMORY[0] = 0;
CurrentProcess = GetCurrentProcess();
WriteProcessMemory(CurrentProcess, sub_4018A0, &xmmword_405018, 96u, 0);
return 1;
}
}
}
return 0;
}
WriteProcessMemory:将数据写入指定进程中的内存区域。要写入的整个区域必须可访问,否则操作将失败。
要写入的sub_4018A0就是smc处理过的函数,开始动调
先在 main函数的 if ( sub_401610(&v4) && sub_4018A0(&v4) )处打一个断点,然后输点什么进去(比如12345678),接着一直F7到这个循环
这段的意思是(0x12345678+0x10+i)^(90*1010101+ds:[405058+4 *i])将计算的结果给405058+4 * i,判断405058处地址与404188处地址是否相等
from z3 import *
a=[0x1e47913f,0x1e87963c,0xfa0b0acd,0x035b0958,0xf5e74cf4,0xfa1261dc,0x854b2f05,0xf852ed82]
b=[0x24448840,0x24848d4c,0x000001fc,0x0f50006a,0x1c244411,0x000f58e8,0x8d406a00,0x02482484]
solver=z3.Solver()
x=BitVec('x',32)
y=BitVec('y',32)
for i in range(8):
solver.add(((a+y*0x1010101)^(i+0x10+x))==b)
solver.add(x>0)
solver.add(y>0,y<0xff)
if (solver.check()==z3.sat):
print(solver.model())
else:
print("unsat")
a和b分别是下图的esi和esp
得到 [y = 25, x = 321135112],结果转换16进制前10个字符为1324220819
rdtsc:可用来获得程序或者一段代码运行的时间,这里有反调试的作用
在重进填数之前先把 v23 = __rdtsc();nop了,这样sub_401610就通了
WriteProcessMemory写入的新机器码没有显示,使用od
先去掉所有的int3和rdtsc
再通过搜索popa指令找到WriteProcessMemory
在上面有一个mov dword ptr ds:[eax],eax判断输入,会结束程序,需要nop掉
接下来F4到retn处,sub_4018A0就显露出来了。oddump出来分析
int __thiscall sub_4018A0(int this)
{
size_t v1; // kr04_4
signed int v2; // kr08_4
int v3; // edi
int i; // esi
int v5; // edx
int v6; // eax
int v7; // ecx
char v8; // ah
int v9; // eax
__int128 v11; // [esp+8h] [ebp-270h] BYREF
int v12; // [esp+18h] [ebp-260h]
int v13; // [esp+1Ch] [ebp-25Ch]
__int128 Src[2]; // [esp+28h] [ebp-250h] BYREF
char v15[128]; // [esp+50h] [ebp-228h] BYREF
char v16[128]; // [esp+D0h] [ebp-1A8h] BYREF
char v17[128]; // [esp+150h] [ebp-128h] BYREF
int v18; // [esp+1D0h] [ebp-A8h] BYREF
int v19; // [esp+1D4h] [ebp-A4h]
int v20[6]; // [esp+1D8h] [ebp-A0h] BYREF
char v21[64]; // [esp+1F0h] [ebp-88h] BYREF
char v22[68]; // [esp+230h] [ebp-48h] BYREF
strcpy((char *)Src, "AFSAFCEDYCXCXACNDFKDCQXC");
v11 = *(_OWORD *)(this + 10);
memset(v21, 0, sizeof(v21));
memset(v22, 0, 0x40u);
v1 = strlen((const char *)Src);
memcpy(v20, Src, v1);
memset((char *)v20 + v1, 0, 24 - v1);
v18 = v20[0];
v19 = v20[1];
sub_401000(&v18, v17);
v18 = v20[2];
v19 = v20[3];
sub_401000(&v18, v16);
v18 = v20[4];
v19 = v20[5];
sub_401000(&v18, v15);
v2 = strlen((const char *)&v11);
memcpy(v21, &v11, v2);
v3 = 8 * (v2 / 8) + 8;
memset(&v21[v2], (char)(8 - v2 % 8), 8 - v2 % 8);
if ( v3 > 0 )
{
for ( i = 0; i < v3; i += 8 )
{
v5 = (unsigned __int8)v21[i + 4];
v6 = (unsigned __int8)v21[i + 5];
v18 = ((unsigned __int8)v21[i + 3] << 24) | ((unsigned __int8)v21[i + 2] << 16) | ((unsigned __int8)v21[i + 1] << 8) | (unsigned __int8)v21;
v19 = (v6 << 8) | v5 | (((unsigned __int8)v21[i + 6] | ((unsigned __int8)v21[i + 7] << 8)) << 16);
sub_401500(v16, v15);
v7 = v18;
v22 = v18;
v22[i + 1] = BYTE1(v7);
v22[i + 2] = BYTE2(v7);
v22[i + 3] = HIBYTE(v7);
HIWORD(v7) = HIWORD(v19);
v8 = BYTE1(v19);
v22[i + 4] = v19;
v22[i + 5] = v8;
v22[i + 6] = BYTE2(v7);
v22[i + 7] = HIBYTE(v7);
}
}
*(_QWORD *)&v11 = 0xFACE0987E6A97C50ui64;
v9 = 0;
*((_QWORD *)&v11 + 1) = 0x6C97BB90CF0DD520i64;
v12 = -1326018416;
v13 = -391862661;
while ( *((_BYTE *)&v11 + v9) == v22[v9] )
{
if ( ++v9 >= 16 )
return 1;
}
return 0;
}
3des加密,密钥为AFSAFCEDYCXCXACNDFKDCQXC
密文
解密后将两段合起来即可
flag{13242208190dcc509a6f75849b}
来源:【https://xz.aliyun.com/】,感谢【4444】
|