normal2

静态分析,修饰一下

步入关键加密位置(太长了就不丢图了)

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
__int64 __fastcall encode1(unsigned __int8 *Buffer, __int64 a2)
{
unsigned __int8 *B; // rbx
__int64 s; // rsi
unsigned __int8 *v4; // r8
__int64 v5; // r9
__int64 v6; // rcx
__int64 s1; // rdi
__int64 v8; // rbp
unsigned __int8 *B1; // r8
__int64 v10; // r9
unsigned __int8 *B2; // rcx
__int64 v12; // rdx
__int64 v13; // rax
unsigned __int8 v14; // cl
unsigned __int8 v15; // al
unsigned __int8 v16; // cl
unsigned __int8 v17; // al
unsigned __int8 v18; // cl
unsigned __int8 v19; // al
unsigned __int8 v20; // cl
unsigned __int8 v21; // al
unsigned __int8 *v22; // rax
__int64 v23; // r8
__int64 v24; // rdx
unsigned __int8 *v25; // r8
__int64 v26; // r9
unsigned __int8 *v27; // rcx
__int64 v28; // rdx
__int64 v29; // rax
__int64 v30; // rdx
unsigned __int8 v31; // cl
unsigned __int8 v32; // al
unsigned __int8 v33; // cl
unsigned __int8 v34; // al
unsigned __int8 v35; // cl
unsigned __int8 v36; // al
unsigned __int8 v37; // cl
__int64 v38; // rcx
__int64 result; // rax

B = Buffer; // 地址赋予
s = a2 - Buffer; // 地址减法
v4 = Buffer;
v5 = 4i64;
do
{
v6 = 4i64; // 循环16遍
do
{
*v4 ^= v4[s]; // 实际是异或string[i],根据v3变化,v3是两地址减法,而地址实际上是变化的
++v4;
--v6;
}
while ( v6 );
--v5;
}
while ( v5 );
s1 = s + 16; // string[16]开始捏
v8 = 9i64; // 循环144
do
{
B1 = B;
v10 = 4i64;
do
{
B2 = B1; // 循环16次
v12 = 4i64;
do
{
v13 = *B2; // Buffer地址起始赋予
B2 += 4; // Buffer加4
*(B2 - 4) = data4[v13]; // Buffer起始赋值 data4[*B2]
--v12;
}
while ( v12 );
++B1;
--v10;
}
while ( v10 );
v14 = B[1];
B[1] = B[5];
B[5] = B[9];
B[9] = B[13];
v15 = B[10];
B[13] = v14;
v16 = B[2];
B[2] = v15;
v17 = B[14];
B[10] = v16;
v18 = B[6];
B[6] = v17;
v19 = B[15];
B[14] = v18;
v20 = B[3];
B[3] = v19;
B[15] = B[11];
v21 = B[7];
B[7] = v20;
B[11] = v21;
encode3(B, 0i64, B1); // 此时B1是从 B[4]开始的
v22 = B;
v23 = 4i64;
do
{
v24 = 4i64;
do
{
*v22 ^= v22[s1];
++v22;
--v24;
}
while ( v24 );
--v23;
}
while ( v23 );
s1 += 16i64;
--v8;
}
while ( v8 );
v25 = B;
v26 = 4i64;
do
{
v27 = v25;
v28 = 4i64;
do
{
v29 = *v27;
v27 += 4;
*(v27 - 4) = data4[v29];
--v28;
}
while ( v28 );
++v25;
--v26;
}
while ( v26 );
v30 = 4i64;
v31 = B[1];
B[1] = B[5];
B[5] = B[9];
B[9] = B[13];
v32 = B[10];
B[13] = v31;
v33 = B[2];
B[2] = v32;
v34 = B[14];
B[10] = v33;
v35 = B[6];
B[6] = v34;
v36 = B[15];
B[14] = v35;
v37 = B[3];
B[3] = v36;
B[15] = B[11];
B[11] = B[7];
B[7] = v37;
do
{
v38 = 4i64;
do
{
result = B[s + 160]; // db BE开始 其实也就是string1[160+i]
*B++ ^= result;
--v38;
}
while ( v38 );
--v30;
}
while ( v30 );
return result;
}

好长,仔细看一下就是,Buffer内容异或string1,之后进行一次9次的循环,用加密过的内容做序号,赋值Buffer以data4的内容,再打乱顺序,再进行一个异或和位移的加密,然后Buffer内容再次异或不同位置的string1,结束循环。之后再用data4的内容替换,打乱顺序后再和Sting1异或,返回值。这题它后16位是不加密的。

中间藏了个小的加密

这题整体的加密是输入小于等于16位字符串都可以扩容到16位,然后后16位的对比数据提示我们这题的字符串长度是32位。

此处的加密有几个疑问点

1,是否为某种加密的魔改方案

2,是否为不可逆加密

3,如果不可逆,应该如何逆推出flag

CTF-Reverse-常用算法特征 | X Mεl0n | 随手记 (zrzz.site)

[原创]逆向中常见的Hash算法和对称加密算法的分析-密码应用-看雪论坛-安全社区|安全招聘|bbs.pediy.com

结果是aes的加密(愚昧的我没有搜索出来也没有发现,也没有意识到),s盒没有被魔改,似乎列混淆那里有一点魔改

学习下gf(2^8)域上的乘法,还有矩阵的逆推。

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
#这是爆破脚本一号,同样能得出结果,但是因为其又臭又长,被我舍弃了
import threading
data2='934d8706bed74cd6eea683c7be86b2eb32616562363039383965386433333531'
string1=[0x1B, 0x2E, 0x35, 0x46, 0x58, 0x6E, 0x72, 0x86, 0x9B, 0xA7, 0xB5, 0xC8, 0xD9, 0xEF, 0xFF, 0x0C, 0xC5, 0x38, 0xCB, 0x73,
0x9D, 0x56, 0xB9, 0xF5, 0x06, 0xF1, 0x0C, 0x3D, 0xDF, 0x1E,0xF3, 0x31, 0xB5, 0x35, 0x0C, 0xED, 0x28, 0x63, 0xB5, 0x18,
0x2E, 0x92, 0xB9, 0x25, 0xF1, 0x8C, 0x4A, 0x14, 0xD5, 0xE3,0xF6, 0x4C, 0xFD, 0x80, 0x43, 0x54, 0xD3, 0x12, 0xFA, 0x71,
0x22, 0x9E, 0xB0, 0x65, 0xD6, 0x04, 0xBB, 0xDF, 0x2B, 0x84, 0xF8, 0x8B, 0xF8, 0x96, 0x02, 0xFA, 0xDA, 0x08, 0xB2, 0x9F,
0xF6, 0x33, 0x60, 0x88, 0xDD, 0xB7, 0x98, 0x03, 0x25, 0x21, 0x9A, 0xF9, 0xFF, 0x29, 0x28, 0x66, 0x73, 0x07, 0x53, 0x9E,
0xAE, 0xB0, 0xCB, 0x9D, 0x8B, 0x91, 0x51, 0x64, 0x74, 0xB8,0x79, 0x02, 0x5F, 0xB1, 0x24, 0x0C, 0xF1, 0x01, 0xEF, 0x91,
0x7A, 0x90, 0xBE, 0xF5, 0x0E, 0x28, 0xC7, 0xF7, 0xEB, 0x77,0x4C, 0xA7, 0x1A, 0x76, 0xA3, 0x36, 0x60, 0xE6, 0x1D, 0xC3,
0x6E, 0xCE, 0xDA, 0x34, 0x7B, 0x20, 0x54, 0x38, 0x61, 0x56,0xF7, 0x0E, 0x01, 0xB0, 0xEA, 0xCD, 0x6F, 0x7E, 0x30, 0xF9,
0xBE, 0x24, 0xCD, 0x90, 0xDF, 0x72, 0x3A, 0x9E, 0xDE, 0xC2,0xD0, 0x53, 0xB1, 0xBC, 0xE0, 0xAA, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xEE, 0x17, 0x14, 0x8A, 0xF7, 0x7F, 0x00, 0x00]

data4=[0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D
, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2
, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB,
0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D,
0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9,
0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB]
def paixu (decode1):
copy=[]
for x in decode1:
copy.append(x)
decode1[5]=copy[1]
decode1[10]=copy[2]
decode1[15]=copy[3]
decode1[9]=copy[5]
decode1[14]=copy[6]
decode1[3]=copy[7]
decode1[13]=copy[9]
decode1[2]=copy[10]
decode1[7]=copy[11]
decode1[1]=copy[13]
decode1[6]=copy[14]
decode1[11]=copy[15]
def jiansuo (deccode1):
for i in range(16):
for a in range(len(data4)):
if data4[a]==deccode1[i]:
deccode1[i]=a
break
def decode0 (decode1):
print("start!")
for v3 in range(0,255):
for v4 in range(0,255):
for v6 in range(0, 255):
for v5 in range(0, 255):
v7=v4^v3^v6^v5
if decode1[0] == (v7 ^ v3 ^ (2 * (v4 ^ v3)) ^ (27 * ((v4 ^ v3) >> 7)))&0xff and decode1[1] ==( v7 ^ v4 ^ (2 * (v6 ^ v4)) ^ (27 * ((v6 ^ v4) >> 7)))&0xff and decode1[2] == (v7 ^ v6 ^ (2 * (v6 ^ v5)) ^ (27 * ((v6 ^ v5) >> 7)))&0xff and decode1[3] == (v7 ^ v5 ^ (2 * (v5 ^ v3)) ^ (27 * ((v5 ^ v3) >> 7)))&0xff:
decode1[0]=v3
decode1[1]=v4
decode1[2]=v6
decode1[3]=v5
return print("next!")
def decode2 (decode1):
print("running now!")
for v3 in range(0,255):
for v4 in range(0,255):
for v6 in range(0, 255):
for v5 in range(0, 255):
v7=v4^v3^v6^v5
if decode1[0] == (v7 ^ v3 ^ (2 * (v4 ^ v3)) ^ (27 * ((v4 ^ v3) >> 7)))&0xff and decode1[1] ==( v7 ^ v4 ^ (2 * (v6 ^ v4)) ^ (27 * ((v6 ^ v4) >> 7)))&0xff and decode1[2] == (v7 ^ v6 ^ (2 * (v6 ^ v5)) ^ (27 * ((v6 ^ v5) >> 7)))&0xff and decode1[3] == (v7 ^ v5 ^ (2 * (v5 ^ v3)) ^ (27 * ((v5 ^ v3) >> 7)))&0xff:
decode1[4]=v3
decode1[5]=v4
decode1[6]=v6
decode1[7]=v5
return print("next!")
def decode3 (decode1):
for v3 in range(0,255):
for v4 in range(0,255):
for v6 in range(0, 255):
for v5 in range(0, 255):
v7=v4^v3^v6^v5
if decode1[0] == (v7 ^ v3 ^ (2 * (v4 ^ v3)) ^ (27 * ((v4 ^ v3) >> 7)))&0xff and decode1[1] ==( v7 ^ v4 ^ (2 * (v6 ^ v4)) ^ (27 * ((v6 ^ v4) >> 7)))&0xff and decode1[2] == (v7 ^ v6 ^ (2 * (v6 ^ v5)) ^ (27 * ((v6 ^ v5) >> 7)))&0xff and decode1[3] == (v7 ^ v5 ^ (2 * (v5 ^ v3)) ^ (27 * ((v5 ^ v3) >> 7)))&0xff:
decode1[8]=v3
decode1[9]=v4
decode1[10]=v6
decode1[11]=v5
return
def decode4 (decode1):
print("end!")
for v3 in range(0,255):
for v4 in range(0,255):
for v6 in range(0, 255):
for v5 in range(0, 255):
v7=v4^v3^v6^v5
if decode1[0] == (v7 ^ v3 ^ (2 * (v4 ^ v3)) ^ (27 * ((v4 ^ v3) >> 7)))&0xff and decode1[1] ==( v7 ^ v4 ^ (2 * (v6 ^ v4)) ^ (27 * ((v6 ^ v4) >> 7)))&0xff and decode1[2] == (v7 ^ v6 ^ (2 * (v6 ^ v5)) ^ (27 * ((v6 ^ v5) >> 7)))&0xff and decode1[3] == (v7 ^ v5 ^ (2 * (v5 ^ v3)) ^ (27 * ((v5 ^ v3) >> 7)))&0xff:
decode1[12]=v3
decode1[13]=v4
decode1[14]=v6
decode1[15]=v5
return
def decode ():
thread1=threading.Thread( decode0(decode1))
thread2=threading.Thread( decode2(decode1))
thread3=threading.Thread( decode3(decode1))
thread4=threading.Thread(decode4(decode1))
thread1.start()
thread2.start()
thread3.start()
thread4.start()

print(len(string1))
data2_l=list(data2)
decode1=[]
double=data2_l[::2]
single=data2_l[1::2]
for i in range(len(double)):
a=ord(double[i])
if a<=57 and a>=48:
a-=48
else:
a=a-87
double[i]=a
a=ord(single[i])
if a<=57 and a>=48:
a-=48
else:
a=a-87
single[i]=a
c=double[i]*16+single[i]
decode1.append(c)
data=''
for x in decode1:
data+=chr(x)
print(data)
for i in range(16):
decode1[i] = (decode1[i] ^ string1[160 + i]) & 0xff
paixu(decode1)
jiansuo(decode1)

for x in range(9):
for i in range(16):
decode1[i] = (decode1[i] ^ string1[(9-x)*16 + i]) & 0xff
decode()
paixu(decode1)
jiansuo(decode1)
for i in range(16):
decode1[i] = (decode1[i]^string1[i])&0xff
flag=''
for x in decode1:
flag+=chr(x)
print(flag)

得知是aes之后了解了一下aes的运作原理。

aes整体分成4个部分

  1. 拓展密钥异或 (加轮密钥)
  2. s盒字节代换 可魔改(此处开始循环
  3. 行位移
  4. 列混淆(重点) 应该可魔改
  5. 拓展密钥异或(此处循环结束

其中,包含列混淆的循环只有9次

而整体循环有10次

aes 的逆向只需要按照操作取反即可

不过比较麻烦的是列混淆这一块

因为其他操作都可以通过列表方式取数据逆向

而列混淆是将数据分成 4x4 的矩阵存入状态矩阵,然后状态矩阵和固定矩阵进行gf(2^8)域上的乘法运算

{ v3,v4,v6,v5} {2,3,1,1}

{ v3,v4,v6,v5} X {1,2,3,1}

{ v3,v4,v6,v5} {1,1,2,3}

{ v3,v4,v6,v5} {3,1,1,2}

运算出来是:(2v3)^(3v4)^v6^v5

而此处的乘法是gf域内的乘法

乘1 即为它本身

乘2即为 ((v3<<1)&0xff)^(27*(v3>>7))

左移1,如果溢出,那么再异或上 1B (此处为aes算法规定)

乘3为 (2*v3)^v3

列混淆的取反操作,是加密后的矩阵和固定矩阵的逆矩阵相乘

比如

{2,3,1,1} {0e,0b,0d,09} (剩下照着规律推)

{1,2,3,1}

{1,1,2,3}

{3,1,1,2} 的逆矩阵为

而e b d 9 的操作

有限域GF(2^8)的四则运算及拉格朗日插值_luotuo44的博客-CSDN博客_有限域gf

混淆乘法实现

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
void mixColumns(int a[4][4], int encode){
//encode 为1代表列混淆,为0代表逆向列混淆
for (int i = 0; i < 4; ++i) {
int temp0 = a[0][i];
int temp1 = a[1][i];
int temp2 = a[2][i];
int temp3 = a[3][i];
if (encode) {
a[0][i] = aes_multiple(temp0, 2) ^ aes_multiple(temp1, 3) ^ temp2 ^ temp3;
a[1][i] = temp0 ^ (aes_multiple(temp1, 2)) ^ (temp2 ^ aes_multiple(temp2, 2)) ^ temp3;
a[2][i] = temp0 ^ temp1 ^ (aes_multiple(temp2, 2)) ^ (temp3 ^ aes_multiple(temp3, 2));
a[3][i] = temp0 ^ (aes_multiple(temp0, 2)) ^ temp1 ^ temp2 ^ aes_multiple(temp3, 2);
}else{
a[0][i] = aes_multiple(temp0, 14) ^ aes_multiple(temp1, 11) ^ aes_multiple(temp2, 13) ^ aes_multiple(temp3, 9);
a[1][i] = aes_multiple(temp0, 9) ^ aes_multiple(temp1, 14) ^ aes_multiple(temp2, 11) ^ aes_multiple(temp3, 13);
a[2][i] = aes_multiple(temp0, 13) ^ aes_multiple(temp1, 9) ^ aes_multiple(temp2, 14) ^ aes_multiple(temp3, 11);
a[3][i] = aes_multiple(temp0, 11) ^ aes_multiple(temp1, 13) ^ aes_multiple(temp2, 9) ^ aes_multiple(temp3, 14);
}
}
}

//AES乘法计算
int aes_multiple(int a, int le){
int thr = le & 0x8;
int sec = le & 0x4;
int fir = le & 0x2;
int fir_mod = le % 2;
int result = 0;
if (thr){
int b = a;
for (int i = 1; i <=3 ; ++i) {
b = b<<1;
if (b >= 256)
b = b ^ 0x11b;
}
b = b % 256;
result = result ^ b;
}
if (sec){
int b = a;
for (int i = 1; i <=2 ; ++i) {
b = b<<1;
if (b >= 256)
b = b ^ 0x11b;
}
b = b % 256;
result = result ^ b;
}
if (fir){
int b = a << 1;
if (b >= 256)
b = b ^ 0x11b;
b = b % 256;
result = result ^ b;
}
if (fir_mod)
result = result ^ a;
return result;
}

鉴于本题没有魔改,用crypto库写个简单的

关于crypto 包的安装 :如何解决检索不到库的问题

from Crypto.Cipher import AES
ImportError: No module named ‘Crypto’
pip install pycryptodome
Windows安装后需要将Python下site-packages 下的 crypto 目录的 c 改为 C (大写)
再次 from Crypto.Cipher import AES 就可以正常使用了

关于crypto包的使用

AES — PyCryptodome 3.14.1 文档

最终写出脚本

1
2
3
4
5
6
from Crypto.Cipher import AES
decodedata = '934d8706bed74cd6eea683c7be86b2eb'
key = '1b2e3546586e72869ba7b5c8d9efff0c'
aeskey = AES.new(bytes.fromhex(key), AES.MODE_ECB) #初始化加密器
decode = aeskey.decrypt(bytes.fromhex(decodedata)) #加密器解密
print(decode+bytes.fromhex('32616562363039383965386433333531'))