安卓逆向学习之DES加密算法

安卓逆向学习之DES加密算法

DES 算法简介

DES(Data Encryption Standard)是目前最为流行的加密算法之一。DES 是对称的,也就是说它使用同一个密钥来加密和解密数据。

DES 还是一种分组加密算法,该算法每次处理固定长度的数据段,称之为分组。DES 分组的大小是 64 位,如果加密的数据长度不是 64 位的倍数,可以按照某种具体的规则来填充位。

从本质上来说,DES 的安全性依赖于虚假表象,从密码学的术语来讲就是依赖于“混乱和扩散”的原则。混乱的目的是为隐藏任何明文同密文、或者密钥之间的关系,而扩散的目的是使明文中的有效位和密钥一起组成尽可能多的密文。两者结合到一起就使得安全性变得相对较高。

DES 算法具体通过对明文进行一系列的排列和替换操作来将其加密。过程的关键就是从给定的初始密钥中得到 16 个子密钥的函数。要加密一组明文,每个子密钥按照顺序(1-16)以一系列的位操作施加于数据上,每个子密钥一次,一共重复 16 次。每一次迭代称之为一轮。要对密文进行解密可以采用同样的步骤,只是子密钥是按照逆向的顺序(16-1)对密文进行处理。

DES 算法框架

以下是 DES 的算法框架

image-20220623145732624

从这个图中我们可以看到

明文传入 IP 进行置换 进行 16 次迭代 将 R16 与 L16 拼接 IP 逆置换 生成密文

  1. 从 64-bit 的主钥匙里面选取特定的 56 位,其余的位就没用了。于是我们现在手上有了一个 56 位的布尔数组。把它分成左、右两个半密钥,它们都是 28-bit 的布尔数组。
  2. 左、右两个半密钥都左旋(也就是循环左移。整个数组往左移,左边弹出去了的东西补到最右边去)一定位数,这个左移的位数也是指定的。有些轮次是 1 位,有些轮次是 2 位。
  3. 把左、右半密钥拼起来,再做一个置换,就得到了这一轮生成的子密钥。这个置换是从 56-bit 的数组里面选取指定的 48 位。所以现在每一轮都可以生成一个 48 位的子密钥。(注意,步骤 3 并不改变左右半密钥)。
  4. 重复 步骤 2、步骤 3 一共 16 次,于是得到了 16 个 48-bit 的子密钥。

现在我们手上有了 16 把子密钥。遂开始加密:

image-20220624170739003

  1. 输入的明文(长度为 64 的布尔数组)做一个置换(IP 置换)。仍然得到 64-bit 的数组(不然就丢失信息了!)
  2. 把得到的数组拆成左、右两半边。每边是 32 位长度。
  3. 每一轮迭代,都是接收一组 L, R,返回 L', R' ,作为下一轮迭代的 L, R . 迭代过程如下:
    L′=RR′=L⊕F(R,subkey)其中 F 函数(称为轮函数)是整个算法的核心,功能是:以一个子密钥,加密 32-bit 的信息。
  4. 利用之前得到的 16 个子密钥,执行步骤 3 一共 16 次。
  5. 将最终的 R 与 L 拼接,再做一次置换(FP 置换),即得到密文。

具体步骤

IP 置换

image-20220623151138027

本质上就是根据表进行位置交换

第 1 位 与 第 58 位进行交换

第 2 位 与 第 50 位进行交换

轮函数

E 扩展置换

image-20220623234120753

这步的目的是将 32 位的数据变成 48 位的与 48bit 的 Key 进行置换运算

举个例子

image-20220623234610302

例如 第二行 0001 -> 10010

1
2
3
第二位原数据 0001
第一位的数据1101的最后一位1变成了 1 -> 10001
第三位的数据0011的第一位0变成0 -> 100010

转变成了一个 48 位 bit 的数据在与 48 位的 key 进行 XOR 运算 得到运算结果后再进行 S 盒的压缩

S 盒压缩处理

image-20220623235225941

目的是将原来的 6 位的数据变成 4 位的数据

img

例如

P 盒置换

img

IP 逆置换

持续进行 16 次迭代 将迭代结果与 L16 R16 拼接进行 IP-1 逆运算 得到最终密文

密钥生成

img

img

红色标注就是较验证值 由 64 位去掉 8 位校验值=56 位

再通过 IV 偏移量 进行 16 次左移[内循环左移]

分为 C0 D0 进行左移动 1 位得到 C1 D1

​ C1 D1 左移动 1 位得到 C2 D2 以此类推

C1 D1 进行 PC-2 置换得到 48 位的 Key

img

img

Python 代码

编码(encode)与加密(encrypt)都是把一条数据变成另一条数据的函数,但它们有本质的区别——编码是公开的,是双向函数。任何人都可以把信息编码,也可以把信息解码。典型例子:十六进制编码、base64 编码,以及 rot13 “加密”。它虽然名字是加密,但它事实上是一个编码——任何知道算法规则的人,都可以进行解密。

加密是私密的。必须有密钥才能加密,也必须有密钥才能解密(公钥与私钥可能不同)。此外,优秀的加密算法,安全性应该依赖于密钥的保密程度、解密的难度,而无关乎算法的秘密——也就是说,任何人都知道这个加密、解密算法是怎样的,任何人都可以实现算法,但拿不到密钥就没法解密。典型的例子是 RSA、DES、AES 等密码体系。

应当意识到,编码是不可靠的。置换之类的简单编码方式尤其不可靠。作为密码的设计者,应当假设攻击者知道算法的每一处细节,也就是说,设计算法的时候,应该认为编码对信息安全完全没有帮助。

有了这些概念在心里,我们来详细讨论 DES 算法。

密钥生成

PC1

1
2
3
4
5
6
7
8
9
10
11
12
13
# 选择置换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]

PC2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 选择置换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]

生成密钥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 子密钥生成算法,由一个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

轮函数

E 置换

1
2
3
4
5
6
7
8
9
10
11
12
# 扩张置换,将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 盒压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 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

P 置换

1
2
3
4
5
6
7
8
9
10
11
12
13
# 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 函数

1
2
3
4
5
6
7
8
9
10
# 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

测试

运行结果

image-20220624002324704

image-20220624002556213

网站给出的结果一致 正确

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
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')))

总结

首先介绍几个重要的历史时间节点。

① 1973 年,美国国家标准局(NBS)向社会公开征集加密算法,一直盯加密算法标准。

② 1974 年,第二次征集。

③ 1975 年,选中 IBM 的算法,公布征求意见。

④ 1977 年 1 月 15 日正式颁布。

⑤ 1998 年底以后停用。

⑥ 1999 年颁布 3DES 为新标准。

标准加密算法的目标:

① 用于保护政府机构和商业部门的非机密的敏感数据。

② 用于加密保护静态存储和传输信道中的数据。

③ 安全使用 10~15 年。

DES 算法是单射的分组密码算法,是密码学发展的一个重要的阶段(现代密码学诞生的标志之一),对算法的标准化研究和分组密码的发展有重大意义。

目前攻击 DES 的最有效的办法是密钥穷举攻击。


参考文章/视频

https://www.bilibili.com/read/cv13258932

https://www.bilibili.com/video/BV1KQ4y127AT?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=28386b7c9a7b50491dec215cf2ec6197

https://www.cnblogs.com/idreamo/p/9333753.html

https://www.ruanx.net/des/

Hello Codecat-一个逆向资源分享网站

Hello Codecat-一个逆向资源分享网站

直达链接 🔗Hello Codecat

Why

最开始我只是想把自己在浏览 逆向资源 过程中,发现的有意思、高质量、容易上手的项目、工具收集起来,这样便于以后查找和学习。后来打算把这些有意思、有价值的开源项目分享给大家。最后就写了这个网站,便于查看和分享。

同时也为了帮助更多新手入门和少走一些弯路

阅读更多
【实战】某习通app逆向分析(二)发包参数加密分析、解密和数据采集
安卓逆向学习之Objection-Hook自吐请求抓包与辅助分析

安卓逆向学习之Objection-Hook自吐请求抓包与辅助分析

前言

证书校验

在许多业务非常聚焦比如行业应用、银行、公共交通、游戏等行业,C/S 架构中服务器高度集中,对应用的版本控制非常严格,这时候就会在服务器上部署对 app 内置证书的校验代码。
抓包出现如下提示时,我们确定出此 APP 为服务器校验 app 客户端证书

APP测试之安全机制问题及Bypass - 先知社区

阅读更多

安卓逆向学习之objection基础用法

0x1 介绍&安装 Objection

objection 是基于 frida 的命令行 hook 工具, 可以让你不写代码, 敲几句命令就可以对 java 函数的高颗粒度 hook, 还支持 RPC 调用

目前只支持 Java 层的 hook, 但是 objection 有提供插件接口, 可以自己写 frida 脚本去定义接口

比如葫芦娃大佬的脱壳插件, 实名推荐: FRIDA-DEXDump

官方仓库: objection

阅读更多
【实战】某习通app逆向分析(一)检测切屏、拍照、上传

【实战】某习通app逆向分析(一)检测切屏、拍照、上传

前言

Android 上的应用安装文件是 apk 格式,这个 apk 文件其实就是个归档文件压缩包,把应用相关的源码、资源文件、配置文件等乱七八糟的东西都简单粗暴的归档打包,其包内文件结构大体如下:

文件或目录 作用
META-INF/ 存放签名信息,用来保证 apk 包的完整性和系统的安全。
res/ 存放资源文件的目录
libs/ 若存在,则存放的是 ndk 编译出来的 so 文件库
AndroidManifest.xml 程序全局配置文件
classes.dex 最终生成的 dalvik 字节码
resources.ars 编译后的二进制资源文件。通常本地化、汉化资源存储在该文件文件中。

因此,我们将 apk 后缀改成 zip 压缩格式并直接解压就能得到 app 的 dalvik 指令集 dex 文件,然后再将其反编译为 smali 文件,还可以再反编译为 java 代码文件,这样就几乎等同于拿到了 app 就能拿到可读性还蛮清晰的源码,导致逆向破解的门槛几乎很低;没有做防范措施的 app 几乎等同于在裸奔,因此后来演变出来一些保护措施,让 apk 被反编译后获取的代码难理解。

阅读更多
安卓逆向学习之Frida-RPC远程调用与主动调用

安卓逆向学习之Frida-RPC远程调用与主动调用

准备阶段

Android 应用开发

image-20220614231424074

先准备一个工程,编写以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package cn.mineseb.rpc;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

private String total = "codecat";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

while (true){
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}

}

void fun(int x,int y){
Log.d("r0ysue.com",String.valueOf(x+y));
}
String fun(String x){
return x.toLowerCase();
}
void secret(){
total += "cool!!!";
Log.d("secret","this is secret func");
}
static void staticSecret(){
Log.d("staticSecret","this is static secret func");
}
}

将模拟器 adb 链接 studio 并且点击开始按钮后 出现 成功

image-20220615000228762

阅读更多
【实战】某大学信息门户登录分析

【实战】某大学信息门户登录分析

抓包参数分析

通过抓包分析 发现 在访问

1
http://authserver.tjut.edu.cn/authserver/login?service=http%3A%2F%2Fehall.tjut.edu.cn%2Flogin%3Fservice%3Dhttp%3A%2F%2Fehall.tjut.edu.cn%2Fnew%2Findex.html

时候 会设置两个 Cookies:route 和 JSESSIONID,当我们清空这两个 cookies 的时候点击登录发现会重定向,确定 ck 是服务器确认登录请求的值。用 python 模拟

阅读更多
安卓逆向学习之RSA加密算法

安卓逆向学习之RSA加密算法

RAS 算法简介

它是又罗纳德.李维斯特、阿迪.萨莫尔和伦纳德.阿德曼在 1977 年提出,当时他们三人在麻省理工学院工作,RSA 就是他们三大姓开头字母拼在一起组成的。

RSA 是最流行的非对称加密算法之一,也被称之为公钥加密。

阅读更多

【实战】人人直播 password 加密分析

分析

搜索 password

jadx 搜索 “password”

找到地点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
private void aCv() {
SettingManager.bmC().m16156rl("");
SettingManager.bmC().m16124tV(-1);
RSA.init();
aBB();
Variables.account = this.fgV.getText().toString().trim();
Variables.password = this.fgU.getText().toString().trim();
if (Variables.account == null || Variables.account.length() == 0) {
Toast.makeText(getActivity(), RenrenApplication.getContext().getResources().getString(C4591R.C4595string.v5_0_1_guide_register_account_no_null), 1).show();
} else if (Variables.password == null || Variables.password.length() == 0) {
Toast.makeText(getActivity(), RenrenApplication.getContext().getResources().getString(C4591R.C4595string.v5_0_1_guide_register_password_no_null), 1).show();
} else if (Methods.m13910tM(Variables.account)) {
Methods.showToast((CharSequence) getResources().getString(C4591R.C4595string.v5_0_1_guide_register_not_have_china), false);
} else if (Methods.m13910tM(Variables.password)) {
Methods.showToast((CharSequence) getResources().getString(C4591R.C4595string.v5_0_1_guide_register_pwd_have_china), false);
} else {
deY = RSA.bNv();
this.f14991n = RSA.bNx();
this.f14990e = RSA.bNw();
if (deY != null) {
try {
Variables.password = RSA.m10340P(Variables.password, this.f14991n, this.f14990e);
RSA.kJi = 1;
} catch (Exception e) {
e.printStackTrace();
}
} else {
Variables.password = Md5.toMD5(Variables.password);
RSA.kJi = 2;
}
if (Variables.account == null || Variables.account.length() <= 0 || Variables.password == null || Variables.password.length() <= 0) {
return;
}
if (this.fha == null) {
try {
this.fha = new Dialog(getActivity());
this.fha.setContentView(C4591R.layout.login_dialog);
((ImageView) this.fha.findViewById(C4591R.C4593id.image)).startAnimation(AnimationUtils.loadAnimation(getActivity(), C4591R.anim.login_dialog_scale));
} catch (Exception unused) {
}
}
this.fha.show();
if (RSA.kJi != 1) {
deY = null;
}
C12919ServiceProvider.m_RSA_login(Variables.account, Variables.password, 1, "", deY, getActivity(), this.dfg);
}
}


阅读更多