DASCTF_Oct_re

这两道题中都有加密的知识点,第一道题的SM4的解密在最后一步仍然有问题,希望大佬能够指点指点(●’◡’●)

马猴烧酒

知识点

  1. 魔改SM4
  2. 魔改base64

用ida6将文件载入其中

image-20220318181826345

定位到相应的代码的位置,这段代码开始的位置就有一段flag{this_is_fake_flag}的flag

image-20220318182418204

密钥的生成

第一个函数是获取时间戳,第二个函数是对时间戳base64的变表加密,第三个异或运算得到密钥

获取时间戳

使用0x61624B82 演变得到一串数组

image-20220319151621929

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
key = 0x61624B82
a13 = 0
a14 = 0
while key:
a13 = key%10
key = key//10
a14 = 87
if a13<=9:
a14 = 48
print(chr(a13+a14),end='')

obj = '0181383361'
for i in range(len(obj)):
print(obj[len(obj)-1-i],end='')

得到的结果是 1633831810 这个字符串

魔改的base64加密

用标准的base64加密这个字符串的结果是 MTYzMzgzMTgxMA==

分析本代码之中的base64加密 可以知道如果位数不是3的倍数位也是使用“=”

base64的变表

1
abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ=

利用base64的变表和标准表的对应关系得到该代码base64加密的结果

1
2
3
4
5
6
7
8
9
oldkey = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
newkey = 'abcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZ='
theMap = {}
for i in range(len(oldkey)):
theMap[oldkey[i]] = newkey[i]
advert = 'MTYzMzgzMTgxMA=='
for i in range(len(advert)):
print(theMap[advert[i]],end='')
结果是:mtyNmN6Nmt6Lma==

image-20220318191421527

image-20220318191436020

异或运算

将上面base64的加密结果和字符串 “flag{this_is_fake_flag}”进行异或运算

image-20220319154418390

脚本(密钥):

1
2
3
4
5
tem1 = 'mtyNmN6Nmt6Lma=='
tem2 = 'flag{this_is_fake_flag}'
for i in range(len(tem1)):
print('0x'+hex(ord(tem1[i])^ord(tem2[i]))[2:].zfill(2),end=',')
[0x0b,0x18,0x18,0x29,0x16,0x3a,0x5e,0x27,0x1e,0x2b,0x5f,0x3f,0x32,0x07,0x5c,0x56]

得到的这个数组将会作为SM4加密的key

SM4加密

  1. 识别出SM4加密
  2. 找到SM4加密魔改的地方

加密之后的结果和相应的数组对比

image-20220318193700160

需要解密的对象是:

1
enc = [0xf7,0xeb,0x5e,0x87,0x17,0x9c,0x74,0x94,0x44,0xb5,0xf5,0x12,0xf9,0x74,0x15,0x5f]

轮函数每次会处理128bite的数据,会将这128bite的数据分成4个32bite的数据

SM4加密,每组加密的是128bit,每32bit加密之后的顺序发生了变化的

image-20220318193418765

对每个8bit都用了s盒进行了处理,在内存之中找到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
{
{0x48, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05},
{0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99},
{0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62},
{0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6},
{0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8},
{0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35},
{0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87},
{0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E},
{0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1},
{0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3},
{0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F},
{0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51},
{0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8},
{0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0},
{0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84},
{0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0xD6}
}

static const unsigned long CK[32] =
{
0xF4BFE18F, 0xA8AA055C, 0x8B266D2B, 0xB3819D47, 0x0B1B3A85, 0xF7DB86B6, 0xC3279F82, 0x39D9C102,
0xBEA224C9, 0xE75D4DAC, 0xAC61726C, 0x6F98AA6F, 0xFA2ADA4E, 0x6A7CFF92, 0xA8066E7B, 0x7BE32F9F,
0x8CD0FED3, 0x4B98AF71, 0x790C2CBC, 0xBF880433, 0xAA46F582, 0x69C17A2C, 0x80BBD5E4, 0x24A02531,
0x293D87B3, 0x75F159AD, 0xB750AE9D, 0x9886928C, 0x05577A22, 0xB425E19F, 0x124D4F63, 0xE26F66D1
};

SM4解密脚本

网上找一个SM4的c语言脚本,修改里面的Sbox,CK参数,以及将上面得到的key和解密的对象引入(但是我从网上找了两个解密的脚本,修改了里面相应的参数之后,仍然得不到想要的结果,并且在内存之中也没能找到修改之后的CK参数,希望指点指点)

SM.h头部文件

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
#ifndef __lk_sm4_h__
#define __lk_sm4_h__

#ifdef __cpluscplus
extern "C" {
#endif

#include <strings.h>
#include <string.h>

#ifdef __cpluscplus
}
#endif

#define LK_WORD_SIZE 32
#define LK_GCHAR_SIZE 16
#define LK_GWORD_SIZE 4

typedef void LK_VOID;
typedef int LK_INT;
typedef unsigned int UINT;
#ifdef i386
typedef unsigned long long UWORD;
#else
typedef unsigned long UWORD;
#endif
typedef unsigned char UCHAR;

//大端转化
#define LK_GE(c, i) (\
(c[(i)+0] << 24) | ((c[(i)+1] << 16)) |\
(c[(i)+2] << 8) | (c[(i)+3]) )
//小端转化
#define LK_LE(c, n, i) {\
c[(i)+0] = ((n) >> 24) & 0x000000ff;\
c[(i)+1] = ((n) >> 16) & 0x000000ff;\
c[(i)+2] = ((n) >> 8) & 0x000000ff;\
c[(i)+3] = (n) & 0x000000ff; }
//循环左移
#define LOOPSHFT(LK_LSHIFA, LK_LSHIFN) (\
((LK_LSHIFA) << (LK_LSHIFN)) | \
((LK_LSHIFA) >> (LK_WORD_SIZE - (LK_LSHIFN))))

//线性变换函数L' L'(B)=B^(B<<<13)^(B<<<23);
#define LK_L0(LK_L0B) (\
(LK_L0B)^(LOOPSHFT((LK_L0B), 13))^(LOOPSHFT((LK_L0B), 23)) )

//线性变换函数L L(B) = B^(B<<<2)^(B<<<10)^(B<<<18)^(B<<<24)
#define LK_L1(LK_L1B) (\
(LK_L1B)^(LOOPSHFT((LK_L1B), 2))^(LOOPSHFT((LK_L1B), 10))\
^(LOOPSHFT((LK_L1B), 18))^(LOOPSHFT((LK_L1B), 24)) )

//非线性变换 τ函数 (b0 , b1, b2 , b3 ) = τ (A) = ( Sbox (a0 ), Sbox (a1 ), Sbox (a2 ), Sbox (a3 ) )
#define LK_ST(LK_STA) ({\
LK_INT LK_i;\
UINT LK_STB = LK_STA;\
UCHAR *pcr = (UCHAR *)(&LK_STB);\
for (LK_i = 0; LK_i < 4; LK_i++) {\
UCHAR high_bval = ((pcr[LK_i] >> 4) & 0x0f);\
UCHAR low_bval = (pcr[LK_i] & 0x0f);\
*(pcr+LK_i) = lk_sbox[high_bval][low_bval];\
}\
LK_STB;\
})

//合成置换 T T(.)=L(τ(.))
#define LK_T1(LK_TA) (\
LK_L1(LK_ST(LK_TA)) )

//合成置换 T' T'(.)=L'(τ(.))
#define LK_T0(LK_TA) (\
LK_L0(LK_ST(LK_TA)) )

//密钥扩展算法生成轮密钥
#define LK_SM4_INIT(lk_context, ekey) {\
UINT k[36], mk[4];\
LK_INT i;\
lk_sm4_context_t *t = (lk_sm4_context_t *)(lk_context);\
memset(t, 0,sizeof(lk_sm4_context_t));\
for (i = 0; i < 4; i++) {\
mk[i] = LK_GE(ekey, i * 4);\
k[i] = mk[i]^lk_fk[i];\
}\
for (i = 0; i < 32; i++) {\
k[i+4] = (k[i] ^ LK_T0(k[i+1]^k[i+2]^k[i+3]^(lk_ck[i])));\
t->e_rk[i] = k[i+4];\
t->d_rk[31 - i] = t->e_rk[i];\
}\
}

//加密函数
#define LK_SM4_ENC_F(lk_context) {\
LK_INT i;\
UINT x[36];\
lk_sm4_context_t *t = (lk_sm4_context_t *)(lk_context);\
for (i = 0; i < 4; i++) {\
x[i] = LK_GE(t->buf, i * 4);\
}\
for (i = 0; i < 32; i++) {\
x[i + 4] = x[i]^(LK_T1(x[i+1]^x[i+2]^x[i+3]^t->e_rk[i]));\
}\
LK_LE(t->ebuf, x[35], 0)\
LK_LE(t->ebuf, x[34], 4)\
LK_LE(t->ebuf, x[33], 8)\
LK_LE(t->ebuf, x[32], 12)\
}
//解密函数
#define LK_SM4_DEC_F(lk_context) {\
LK_INT i;\
UINT x[36];\
lk_sm4_context_t *t = (lk_sm4_context_t *)(lk_context);\
for (i = 0; i < 4; i++) {\
x[i] = LK_GE(t->ebuf, i * 4);\
}\
for (i = 0; i < 32; i++) {\
x[i + 4] = x[i]^LK_T1(x[i+1]^x[i+2]^x[i+3]^t->d_rk[i]);\
}\
LK_LE(t->buf, x[35], 0)\
LK_LE(t->buf, x[34], 4)\
LK_LE(t->buf, x[33], 8)\
LK_LE(t->buf, x[32], 12)\
}

typedef struct lk_sm4_context_s
{
//需要加密的明文,加密之前需要填充
UCHAR buf[LK_GCHAR_SIZE];
//加密之后的密文,如果是解密的话,解密之前需要填充
UCHAR ebuf[LK_GCHAR_SIZE];
//原密钥经过扩展之后的轮密钥,初始化接口自行填充
UINT e_rk[LK_WORD_SIZE];
//解密过程需要的轮密钥,初始化接口自行填充
UINT d_rk[LK_WORD_SIZE];
} lk_sm4_context_t;
#ifdef __cpluscplus
extern "C" {
#endif

extern LK_VOID lk_sm4_enc(LK_VOID *p_context);
extern LK_VOID lk_sm4_dec(LK_VOID *p_context);

#ifdef __cpluscplus
}
#endif

#endif

main.cpp

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
#include <stdio.h>
#include "SM4.h"
//非线性转化用到的sbox对照表
UCHAR lk_sbox[LK_GCHAR_SIZE][LK_GCHAR_SIZE] = {
{0x48, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05},
{0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99},
{0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62},
{0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6},
{0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8},
{0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35},
{0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87},
{0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E},
{0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1},
{0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3},
{0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F},
{0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51},
{0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8},
{0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0},
{0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84},
{0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0xD6}
};
//系统参数FK
UINT lk_fk[4] = {
0xA3B1BAC6,0x56AA3350,
0x677D9197,0xB27022DC
};
//固定参数CK
UINT lk_ck[32] = {

0xF4BFE18F, 0xA8AA055C, 0x8B266D2B, 0xB3819D47, 0x0B1B3A85, 0xF7DB86B6, 0xC3279F82, 0x39D9C102,
0xBEA224C9, 0xE75D4DAC, 0xAC61726C, 0x6F98AA6F, 0xFA2ADA4E, 0x6A7CFF92, 0xA8066E7B, 0x7BE32F9F,
0x8CD0FED3, 0x4B98AF71, 0x790C2CBC, 0xBF880433, 0xAA46F582, 0x69C17A2C, 0x80BBD5E4, 0x24A02531,
0x293D87B3, 0x75F159AD, 0xB750AE9D, 0x9886928C, 0x05577A22, 0xB425E19F, 0x124D4F63, 0xE26F66D1

};

LK_VOID lk_sm4_init(LK_VOID *p_context, UCHAR *ekey)
{
//进行密钥扩展,生成轮转密钥rk
LK_SM4_INIT(p_context, ekey)
}

LK_VOID lk_sm4_enc(LK_VOID *p_context)
{
LK_SM4_ENC_F(p_context)
}

LK_VOID lk_sm4_dec(LK_VOID *p_context)
{
LK_SM4_DEC_F(p_context)
}


LK_INT main(LK_VOID)
{
LK_INT i;
lk_sm4_context_t context;
UCHAR ekey[16] = { 0x0b,0x18,0x18,0x29,0x16,0x3a,0x5e,0x27,0x1e,0x2b,0x5f,0x3f,0x32,0x07,0x5c,0x56 };
UCHAR data[16] = { 0xf7,0xeb,0x5e,0x87,0x17,0x9c,0x74,0x94,0x44,0xb5,0xf5,0x12,0xf9,0x74,0x15,0x5f };
//初始化
lk_sm4_init(&context, ekey);



// //待加密数据填充
// memcpy(context.buf, data, sizeof(data));
// //对数据进行加密
// lk_sm4_enc(&context);
// //打印密文值
// printf("enc data:");
// for (i = 0; i < LK_GCHAR_SIZE; i++)
// printf(" %02x ", context.ebuf[i]);
// printf("\n");

//待解密的对象
printf("enc data:");
for (i = 0; i < sizeof(data) ; i++)
printf(" %02x ", data[i]);
printf("\n");
//对数据进行解密
memcpy(context.ebuf, data, sizeof(data));
lk_sm4_dec(&context);
//打印解密数据
printf("dec data:");
for (i = 0; i < LK_GCHAR_SIZE; i++)
printf(" %02x ", context.buf[i]);
printf("\n");
return 0;
}

结果:

image-20220320092347815

misc DASCTF Oct X writeup | jxswcy’s blog

魔法叠加

知识点:

  • pyc文件头部(py3.7)
  • base91解密

修改pyc头部

文件头部修改成如图所示 这里是python3.7

这个pyc是python3.7的版本 ,所以修改magic头部为 42 0D,后面34 改为32,因为要减去两个多出来的字节

image-20220319095943868

在网上找到一个pyc的文件头部(对比着来修改)image-20220319102000542

unemployee6反编译

修改完pyc的文件头部之后,用unemployee6进行反编译(看到下面的命名方式,可以自行修改一下命名)

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
# uncompyle6 version 3.8.1.dev0
# Python bytecode 3.7.0 (3394)
# Decompiled from: Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: ./2.py
# Compiled at: 2021-10-20 11:56:04
# Size of source mod 2**32: 1928 bytes
import struct
O0O00O00O00O0O00O = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$', '%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"']

def encode(O000O00000OO00OOO):
""""""
OOOO00OOO00O000OO = 0
OOOOOOOOOO00O0OOO = 0
OO0OOO000000OOOOO = ''
for O0O0OO0OOOOOOOO00 in range(len(O000O00000OO00OOO)):
O000O0OOOOO00O0O0 = O000O00000OO00OOO[O0O0OO0OOOOOOOO00:O0O0OO0OOOOOOOO00 + 1]
OOOO00OOO00O000OO |= struct.unpack('B', O000O0OOOOO00O0O0)[0] << OOOOOOOOOO00O0OOO
OOOOOOOOOO00O0OOO += 8
if OOOOOOOOOO00O0OOO > 13:
OO00O0OO00OOO000O = OOOO00OOO00O000OO & 8191
if OO00O0OO00OOO000O > 88:
OOOO00OOO00O000OO >>= 13
OOOOOOOOOO00O0OOO -= 13
else:
OO00O0OO00OOO000O = OOOO00OOO00O000OO & 16383
OOOO00OOO00O000OO >>= 14
OOOOOOOOOO00O0OOO -= 14
OO0OOO000000OOOOO += O0O00O00O00O0O0O0[(OO00O0OO00OOO000O % 91)] + O0O00O00O00O0O0O0[(OO00O0OO00OOO000O // 91)]

if OOOOOOOOOO00O0OOO:
OO0OOO000000OOOOO += O0O00O00O00O0O0O0[(OOOO00OOO00O000OO % 91)]
if OOOOOOOOOO00O0OOO > 7 or OOOO00OOO00O000OO > 90:
OO0OOO000000OOOOO += O0O00O00O00O0O0O0[(OOOO00OOO00O000OO // 91)]
return OO0OOO000000OOOOO


O0O00O00O00O0O0O0 = []
OO000O00O00O0O0O0 = []
O0O0O0O0000O0O00O = input('plz input O0O0O0O0000O0O00O:\n')
for i in range(0, 52):
O0O00O00O00O0O0O0 = O0O00O00O00O0O00O[i:] + O0O00O00O00O0O00O[0:i]
O0O0O0O0000O0O00O = encode(O0O0O0O0000O0O00O.encode('utf-8'))

dic = open('./00.txt', 'a')
dic.write(O0O0O0O0000O0O00O)
dic.close
# okay decompiling D:\re\buuctf\buu\ħ������\magic.pyc

上面给的数组的长度是91 并且每次模的对象也是91,所以这里应该是[base91](aberaud/base91-python: A python implementation of Base91 as described on http://base91.sourceforge.net/ (github.com))

整理得到

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
import struct
key = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$', '%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"']

def encode(num):
""""""
tem1 = 0
tem2 = 0
temstr = ''
for i in range(len(num)):
tem = num[i:i + 1]
tem1 |= struct.unpack('B', tem)[0] << tem2
tem2 += 8
if tem2 > 13:
tem0 = tem1 & 8191
if tem0 > 88:
tem1 >>= 13
tem2 -= 13
else:
tem0 = tem1 & 16383
tem1 >>= 14
tem2 -= 14
temstr += key2[(tem0 % 91)] + key2[(tem0 // 91)]

if tem2:
temstr += key2[(tem1 % 91)]
if tem2 > 7 or tem1 > 90:
temstr += key2[(tem1 // 91)]
return temstr


key2 = []
OO000O00O00O0O0O0 = []
text = input('plz input text:\n')
for i in range(0, 52):
key2 = key[i:] + key[0:i]
text = encode(text.encode('utf-8'))

dic = open('./00.txt', 'a')
dic.write(text)
dic.close

base91的解密

加密解密base91的官方文档(注意每个函数参数类型)

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
import struct

base91_alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$',
'%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=',
'>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"']

decode_table = dict((v,k) for k,v in enumerate(base91_alphabet))

def decode(encoded_str):
''' Decode Base91 string to a bytearray '''
v = -1
b = 0
n = 0
out = bytearray()
for strletter in encoded_str:
if not strletter in decode_table:
continue
c = decode_table[strletter]
if(v < 0):
v = c
else:
v += c*91
b |= v << n
n += 13 if (v & 8191)>88 else 14
while True:
out += struct.pack('B', b&255)
b >>= 8
n -= 8
if not n>7:
break
v = -1
if v+1:
out += struct.pack('B', (b | v << n) & 255 )
return out

def encode(bindata):
''' Encode a bytearray to a Base91 string '''
b = 0
n = 0
out = ''
for count in range(len(bindata)):
byte = bindata[count:count+1]
b |= struct.unpack('B', byte)[0] << n
n += 8
if n>13:
v = b & 8191
if v > 88:
b >>= 13
n -= 13
else:
v = b & 16383
b >>= 14
n -= 14
out += base91_alphabet[v % 91] + base91_alphabet[v // 91]
if n:
out += base91_alphabet[b % 91]
if n>7 or b>90:
out += base91_alphabet[b // 91]
return out

通过上面的循环我们可以知道一共进行了52次循环,所以利用对应每次变换得到的表进行每次的base91的解密

脚本:

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
import struct
base91_alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$',
'%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=',
'>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"']

def decode(encoded_str):
''' Decode Base91 string to a bytearray '''
v = -1
b = 0
n = 0
out = b''
for strletter in encoded_str():
if not strletter in decode_table:
continue
c = decode_table[strletter]
if(v < 0):
v = c
else:
v += c*91
b |= v << n
n += 13 if (v & 8191)>88 else 14
while True:
out += struct.pack('B', b&255)
b >>= 8
n -= 8
if not n>7:
break
v = -1
if v+1:
out += struct.pack('B', (b | v << n) & 255 )
return out

with open("D:\\re\\buuctf\\buu\魔法叠加\\00.txt",'rb') as f:
data = f.read()
for i in range(51,-1,-1):
tem = base91_alphabet[i:] + base91_alphabet[0:i]
decode_table = dict((v, k) for k, v in enumerate(tem))
data = decode(data.decode()) # 该函数之中传入的参数应该是字符串型 但是我们得到的是byte类型 所以需要decoed() 将byte转换成string类型的数据
print(i)
print(data)

注意byte 和 石头人相互转换的方法:

1
2
3
4
5
# str to bytes
str.encode(s)

# bytes to str
bytes.decode(b)

因为解密的长度太长了,所以就直接用别人跑出来的结果

image-20220319124833684