DES(Data Encryption Standard)是一种对称密钥的块加密算法,给定一个 64 位的明文和一个 64 位的密钥(有56位起作用,其他做奇偶效验),输出一个 64 位的密文。这个密文可以用相同的密钥解密。
DES 算法的入口参数有三个:Key、Data、Mode。
Key 为 7 个字节共 56 位,是 DES 算法的工作 密钥;
Data 为 8 个字节 64 位,是要被加密或被解密的 数据;
Mode 为 DES 的 工作方式,有两种:加密或解密。
DES 算法是在 Feistel network (费斯妥网络)的基础上执行的。
加密过程
令F 为轮函数 ;令K1,K2....... ,Kn分别为第1.2...n 轮的子密钥。那么基本构造过程如下:
(1)将明文信息均分为两块:L0 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)置换选择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[:off] 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位的子密钥。
框架
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[i]) 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[i] 处理6位a[i],得到4位输出 p = a[i] r = S_box[i][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]
加密和解密可以使用相同的算法。加密和解密唯一不同的是秘钥的次序是相反的。为每一轮产生秘钥的算法也是循环的。加密是秘钥循环左移,解密是秘钥循环右移。解密秘钥每次移动的位数是:0、1、2、2、2、2、2、2、1、2、2、2、2、2、2、1。
贴一下代码,方便查询和对比
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[:off] 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[i] 处理6位a[i],得到4位输出 p = a[i] r = S_box[i][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[i]) return bin2int(FP(r + l)) print(hex(DES(0x11aabbccddeeff01, 0xcafababedeadbeaf, 'encrypt'))) # 0x2973a7e54ec730a3 print(hex(DES(0x2973a7e54ec730a3, 0xcafababedeadbeaf, 'decrypt'))) # 0x11aabbccddeeff01
#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[i]); } 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[i]); } 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[i] = Data_in[i]; } } 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[i] = (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[i] = 0; } for(i = 0; i < Num / 4; i++) { Data_out[i] = 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[i] % 16 > 9) { Data_out[i] = Data_out[i] % 16 + '7'; } else Data_out[i] = Data_out[i] % 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[i] = ((Data_in[i / 4] - '0') >> (i % 4)) & 0x01; } else { Data_out[i] = ((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[i] = 0; } for(i = 0; i < Num; i++) { My_message[i / 8] |= Message_in[i] << (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[i] = Data_in[Table[i] - 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[i] = Message_out[i] ^ Message_in[i]; } } 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[i]); } 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[i], 28); Loop_bit(Key_bit_R, Move_Table[i], 28); TableReplace(SubKey[i], 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[i][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[i]); } 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[i]); 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[i]); 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); }
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[i] != byte_6040C0[i] ) 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[i] ^= 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[i] != byte_6040C0[i] ) 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[i] ^= flag[j] res = AES.new(aes_key, AES.MODE_ECB).decrypt(bytes(flag)) print(res)
GWHT{th1s_gam3_1s_s0_c00l_and_d}
随着计算机算力的增长,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的顺序执行解密-加密-解密的操作
#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[i] = In[ Table[i] - 1 ]; memcpy(Out, Tmp, len); } //异或运算 void Xor(bool *InA, const bool *InB, int len) { for(int i = 0; i < len; ++ i) InA[i] ^= InB[i]; } //循环左移 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[i] = (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] << (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[i][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[i]); MoveLeft(KR, 28, Table_Moveleft[i]); Transform((*pSubKey)[i], 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)[i]); Xor(Ri, Li, 32); memcpy(Li, tmp, 32); } } else //解密 { for(int i = 15; i >= 0; -- i) { memcpy(tmp, Li, 32); funF(Li, (*pSubKey)[i]); 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<<"Please input The string before encrypting: \n"; cin.getline(str, 255, '\n'); do { cout<<"Please input Key1(8 chars): \n"; cin>>key1; } while (strlen(key1) != 8); //key1为8个字符 cout<<"Please input Key2: \n"; cin>>key2; memset(key, 0, sizeof(key)); for (i = 0; i < 8; i ++) { key[i] = key1[i]; key[i + 8] = key2[i]; } 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; }
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())
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[i]+y*0x1010101)^(i+0x10+x))==b[i]) 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[i]; v19 = (v6 << 8) | v5 | (((unsigned __int8)v21[i + 6] | ((unsigned __int8)v21[i + 7] << 8)) << 16); sub_401500(v16, v15); v7 = v18; v22[i] = 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}