0%

GeekChallenge2023逆向方向wp

参赛ID:1K0CT

Shift_jmp | 简单花指令

image-20240223003954050

将无条件跳转和loc_117Apatch为nop重构主函数

image-20231107160634631

简单的异或加密 解题Python脚本如下

1
2
3
4
5
6
7
key = [83,  88,  65, 120,  83,  54, 106, 100,  56, 100, 
111, 84, 120, 66, 81, 123, 120, 34, 77, 97,
39, 99, 115, 69, 45, 124, 69, 108, 44, 111,
47, 123, 94, 92]
for i in range(34):
print(chr(key[i] ^ i ^ 0), end='')
#SYC{W3lc0me_tO_th3_r3veR5e_w0r1d~}

点击就送的逆向题 | C源码的编译

.s文件是由C源码编译而来的汇编指令 它与.S文件的区别是它不支持预处理(#define…) gcc编译器可以将其经过链接等再编译为可执行文件 故可以先将.s文件编译为可执行文件再用IDA分析

clang click.s -o click

Kali虚拟机预装的gcc编译器用以上指令可以直接将.s文件编译为Linux可执行文件

image-20231107162004359

简单的位移加密 解题Python脚本如下

1
2
3
4
key = 'Z`J[X^LMNO`PPJPVQRSIUTJ]IMNOZKMM'
for each in key:
print(chr(ord(each) - 7), end='')
#SYCTQWEFGHYIICIOJKLBNMCVBFGHSDFF

Easymath | z3-solver简单使用

Exinfo查到无壳 64位win程序 IDA64打开

image-20231107162748924

  1. check函数逻辑简单 不分析(
  2. checkposition函数逻辑也简单 不分析(
  3. exchange函数
image-20231107163319234

​ 不理解 26行之前的代码作用 但是并不影响分析(

​ number在内存空间中每个有效元素之间插入三个0

image-20231107163601963

​ 与代码中的p_number[i] = number_[4 * v10]对应 最后一个循环的作用就是将字符在table中的位置映射到这些有效元素上

回到主函数 主要的比较逻辑就是经过映射后的position作为一个5阶方阵与另一个5阶方阵matrix用特殊的矩阵乘法法则相乘得到的矩阵与单位矩阵相比 以下是Python代码实现 其中n为矩阵的阶数

1
2
3
4
5
for i in range(n):
for j in range(n):
result[i][j] = 0
for k in range(n):
result[i][j] = (M1[i][k] * M2[k][j] + result[i][j]) % 0x20

除此之外主函数中还能看到为了防止出现多解给出的flag提示(动态调试时发现其为flag中的元素 重构为数组可以将变量名从内存地址变为数组) 可以使用爆破的方式解出flag 也可以使用z3-solver库解出

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
from z3 import *
table = '01234_asdzxcpoityumnbAOZWXGMY'
matrix = [18, 29, 16, 19, 27,
8, 31, 8, 23, 30,
29, 3, 28, 10, 21,
18, 29, 8, 16, 28,
11, 30, 7, 20, 7]
v7 = [1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1]
num = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 19, 22, 26, 27, 28, 29, 31, 32, 50, 51, 52, 53, 54, 55, 56]
for w in range(5):
mysolver = Solver()
position = IntVector('position', 5)
if w == 0:
mysolver.add(position[1] == 19)
elif w == 1:
mysolver.add(position[2] == 22)
elif w == 3:
mysolver.add(position[2] == 22)
mysolver.add(And([Or([position[i] == val for val in num]) for i in range(5)]))
for j in range(5):
expr = IntVal(0)
for k in range(5):
expr = (expr + position[k] * matrix[5 * k + j]) % 0x20
mysolver.add(expr == v7[5 * w + j])
result = mysolver.check()
if result == sat:
m = mysolver.model()
position_values = [m[position[i]].as_long() for i in range(5)]
for each in position_values:
print(table[num.index(each)], end='')
#xtd4co_ymiunbbx3Aypsmbzii
else:
print('No solution')

需要注意的是 可以一次性爆破全部不确定的22个元素 但这样会使复杂度提升到29^22(我猜的) 而每一次运算只用到position中的一行元素 故可以一行一行地爆破 可以将复杂度降低到5*29^5(我猜的)所以选择一行一行爆破(z3学习笔记另写)

luck | 简单爆破

Exinfo查到无壳 64位win程序 IDA64打开

image-20231107181356855

主函数定义了一个unsigned __int8类型地数组 这个数据类型的大小和char一样 qmemcpy(cmp_data, "\r\a", 2);这句实际上只将"\r\a"中地前两个字符复制到了cmp_data的末尾 数组名代表首元素地址 所以

cmp_data[0] = ‘\‘

cmp_data[1] = ‘r’

同理image-20231107181921189

"o96*#"添加到了cmp_data的第37个元素及之后

(其实最简单的方式是动态调试直接查看cmp_data中的数据)

主函数中的加密函数为用递归实现的等差数列求和image-20240223145103900

不管输入的数字是什么都会输出加密后的密文 根据flag前3个固定的字符爆破flag

1
2
3
4
5
6
7
key = [13,   7,  29]
for i in range(1000):
result = int(i * (i + 1) / 2) % 0xD3
if key[0] ^ (result) == ord('S') and key[1] ^ (result) == ord('Y') and key[2] ^ (result) == ord('C'):
print(i)
# 69
break

在程序中输入数字得到flag

砍树 | 安卓逆向

主函数的逻辑很简单image-20231107183128020

就是调用一个函数传入输入的字符串和key 判断返回值 这个函数在ezreeee库中 导出这个库用IDA分析

image-20231107183407674

在函数列表中检索I0o0I函数 传入的参数有4个 其中有2个是JNIEnvjobject JNI是为了让JAVA和C/C++互通构建的环境 jxxxxx是为了不与C原有的类型冲突 所以从下面的2个jstring_2unsigchar函数也可以看出a3a4分别为jeb伪代码中看到的两个参数与加密后的输入对比的是从内存0x14900中复制的35个字节型数据 以下是解密脚本

1
2
3
4
5
6
7
8
9
10
11
key = [0,  32,  32,  23,  27,  54,  14,  54,  38,  23, 
4, 42, 41, 7, 38, 21, 82, 51, 45, 15,
58, 39, 17, 6, 51, 7, 70, 23, 61, 10,
60, 56, 46, 34, 24, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0]
to_mod = "Sycloverforerver"
i = 0
for each in key:
print(chr(each ^ ord(to_mod[i % 7])), end='')
i += 1
#SYC{t@ke_thE_bul1_By_the_h0rns_TAT}SycloveSyclov

听说cpp很难? | C++逆向

Exinfo查到无壳 64位win程序 IDA64打开 伪代码如下

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
unsigned __int64 v4; // rbx
__int64 v5; // rax
_BYTE *addr_nowChar; // rax
unsigned int now_char; // ebx
_BYTE *v8; // rsi
int v9; // ebx
int v10; // ebx
__int64 v11; // rax
__int64 v12; // rax
__int64 m; // [rsp+20h] [rbp-60h] BYREF
__int64 k; // [rsp+28h] [rbp-58h] BYREF
char copy_flag[32]; // [rsp+30h] [rbp-50h] BYREF
char be_encoded[48]; // [rsp+50h] [rbp-30h] BYREF
_BYTE enc[32]; // [rsp+80h] [rbp+0h] BYREF
int v19[40]; // [rsp+A0h] [rbp+20h] BYREF
char input_flag[40]; // [rsp+140h] [rbp+C0h] BYREF
__int64 v21; // [rsp+168h] [rbp+E8h] BYREF
__int64 v22; // [rsp+170h] [rbp+F0h] BYREF
unsigned int len; // [rsp+17Ch] [rbp+FCh]
int v24; // [rsp+180h] [rbp+100h]
int v25; // [rsp+184h] [rbp+104h]
int j; // [rsp+188h] [rbp+108h]
int i; // [rsp+18Ch] [rbp+10Ch]

_main(argc, argv, envp);
v3 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "place input your flag:");
std::ostream::operator<<(v3, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);
std::string::basic_string(input_flag);
memset(v19, 0, sizeof(v19));
v19[0] = 0x4D;
v19[1] = 0x5F;
v19[2] = 0x3D;
v19[3] = 0xFFFFFF85;
v19[4] = 0x37;
v19[5] = 0x68;
v19[6] = 0x73;
v19[7] = 0x57;
v19[8] = 0x27;
v19[9] = 0x68;
v19[10] = 0x51;
v19[11] = 0x59;
v19[12] = 0x7F;
v19[13] = 0x26;
v19[14] = 0x6B;
v19[15] = 0x59;
v19[16] = 0x73;
v19[17] = 0x57;
v19[18] = 0x55;
v19[19] = 0x5B;
v19[20] = 0x59;
v19[21] = 0x6F;
v19[22] = 0x6A;
v19[23] = 0x59;
v19[24] = 0x27;
v19[25] = 0x57;
v19[26] = 0x72;
v19[27] = 0x57;
v19[28] = 0x4F;
v19[29] = 0x57;
v19[30] = 0x78;
v19[31] = 0x78;
v19[32] = 0xFFFFFF83;
std::operator>><char>(refptr__ZSt3cin, input_flag);
len = std::string::size(input_flag);
std::vector<int>::vector(enc);
for ( i = 0; ; ++i )
{
std::vector<int>::push_back(enc, &v19[i]);
if ( i > 34 )
break;
} // 复制v19到enc中
encode::encode(be_encoded, len, input_flag, 10);//
// be_encoded[0] = len(flag)
// be_encoded[8...] = input_flag
// be_encoded[40] = 10
// be_encoded[44] = 10
std::vector<char>::vector(copy_flag);
for ( j = 0; ; ++j )
{
v4 = j;
if ( v4 >= std::string::size(input_flag) )
break;
v5 = std::string::operator[](input_flag, j);// v5 = input_flag[j]
std::vector<char>::push_back(copy_flag, v5);
}
v25 = 0;
for ( k = std::vector<char>::begin(copy_flag);// for k in copy_flag
;
__gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator++(&k, 0i64) )
{
v21 = std::vector<char>::end(copy_flag);
if ( !__gnu_cxx::operator!=<char *,std::vector<char>>(&k, &v21) )// 读到最后一个字符时退出
break;
addr_nowChar = __gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&k);
*addr_nowChar += be_encoded[44];
now_char = *__gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&k);
v8 = __gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&k);
*v8 = text_67(be_encoded, now_char); // now_char += 10
// now_char = (10 ^ now_char) - 10
// now_char = v19[i]
}
v24 = 0;
for ( m = std::vector<char>::begin(copy_flag);
;
__gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator++(&m, 0i64) )
{
v22 = std::vector<char>::end(copy_flag);
if ( !__gnu_cxx::operator!=<char *,std::vector<char>>(&m, &v22) )
break;
v9 = *__gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&m);
if ( v9 == *std::vector<int>::operator[](enc, v24) )
++v25;
v10 = *__gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&m);
if ( v10 != *std::vector<int>::operator[](enc, v24) )
--v25;
++v24;
}
if ( v25 > 32 ) // v25统计正确的字符
{
v11 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "yes yes you are right!");
std::ostream::operator<<(v11, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);
}
if ( v25 <= 32 )
{
v12 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "no try again~");
std::ostream::operator<<(v12, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);
}
std::vector<char>::~vector(copy_flag);
encode::~encode(be_encoded);
std::vector<int>::~vector(enc);
std::string::~string(input_flag);
return 0;
}

分析C++逆向时XXXXXXXoperatorxxx(a, b) 可以看作abxxx操作

其中的主要逻辑是先对be_encoded进行encode操作 进入查看

image-20231107191204124

需要注意的是主函数中定义的be_encoded储存的数据长度为1byte 而C++中的双字类型长度为4bytes 所以其中的下标转化到原来的be_encoded中需要乘上4 text_67的逻辑很简单

1
2
3
4
5
__int64 __fastcall text_67(__int64 be_encoded, char now_char)
{
*(be_encoded + 40) = 9;
return (((*(be_encoded + 40) + 1) ^ now_char) - *(be_encoded + 40) - 1);
}

剩下的对比逻辑也很简单 以下是解密脚本

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
enc = [None] * 33
enc[0] = 77
enc[1] = 95
enc[2] = 61
enc[3] = 0x85
enc[4] = 55
enc[5] = 104
enc[6] = 115
enc[7] = 87
enc[8] = 39
enc[9] = 104
enc[10] = 81
enc[11] = 89
enc[12] = 127
enc[13] = 38
enc[14] = 107
enc[15] = 89
enc[16] = 115
enc[17] = 87
enc[18] = 85
enc[19] = 91
enc[20] = 89
enc[21] = 111
enc[22] = 106
enc[23] = 89
enc[24] = 39
enc[25] = 87
enc[26] = 114
enc[27] = 87
enc[28] = 79
enc[29] = 87
enc[30] = 120
enc[31] = 120
enc[32] = 0x83

for i in range(33):
print(chr(((enc[i] + 10) ^ 10) - 10), end='')
# SYC{Anma1nG_y0u_maKe_it_1alaIa~~}

flower-or-tea | 简单花指令和TEA加密

Exinfo查到无壳 32位win程序 IDA32打开 伪代码如下

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+0h] [ebp-1B0h]
char v5; // [esp+0h] [ebp-1B0h]
char v6; // [esp+0h] [ebp-1B0h]
char v7; // [esp+0h] [ebp-1B0h]
int v8[40]; // [esp+Ch] [ebp-1A4h]
unsigned int _54; // [esp+ACh] [ebp-104h]
int v10; // [esp+B0h] [ebp-100h]
char *v11; // [esp+B4h] [ebp-FCh]
int lenth; // [esp+B8h] [ebp-F8h]
char *v13; // [esp+BCh] [ebp-F4h]
int j; // [esp+C0h] [ebp-F0h]
int i; // [esp+C4h] [ebp-ECh]
int v16[40]; // [esp+CCh] [ebp-E4h]
int v17[4]; // [esp+16Ch] [ebp-44h] BYREF
int v18; // [esp+17Ch] [ebp-34h] BYREF
int v19; // [esp+180h] [ebp-30h]
char input[40]; // [esp+184h] [ebp-2Ch] BYREF
int v21; // [esp+1ACh] [ebp-4h]
int savedregs; // [esp+1B0h] [ebp+0h] BYREF

v17[0] = 32;
v17[1] = 27;
v17[2] = 39;
v17[3] = 44;
memset(input, 0, sizeof(input));
print(&Format, v4);
print(&byte_CA409C, v5);
print(&byte_CA4090, v6);
scanf(aS, input);
v11 = &input[1];
v13 = &input[strlen(input) + 1];
v10 = v13 - &input[1];
lenth = v13 - &input[1];
_54 = 54;
v16[0] = -1694939573;
v16[1] = -1005078370;
v16[2] = -1307072749;
v16[3] = -918836760;
v16[4] = -1795955634;
v16[5] = -1244910923;
v16[6] = 1146217516;
v16[7] = 2055874714;
v16[8] = 1405669384;
v16[9] = 1846639433;
v16[10] = -1677731948;
v16[11] = 1593781753;
v16[12] = 401024305;
v16[13] = -541222535;
v16[14] = -1886971078;
v16[15] = 1944634796;
v16[16] = -1299812186;
v16[17] = 1526113129;
v16[18] = 754440740;
v16[19] = 880502447;
v16[20] = -1178055328;
v16[21] = -1860267729;
v16[22] = -1118163045;
v16[23] = -879332550;
v16[24] = -979801922;
v16[25] = -1610607639;
v16[26] = -1053864284;
v16[27] = -561628656;
v16[28] = -1597713004;
v16[29] = 1132501052;
v16[30] = 2117039688;
v16[31] = -447882103;
v16[32] = 1059563152;
v16[33] = -1249037927;
v16[34] = 1615521047;
v16[35] = -1668269692;
v16[36] = -186628991;
v16[37] = 1022684671;
v16[38] = 0;
v16[39] = 0;
if ( v13 - &input[1] == 38 ) // flag长度为38
{
if ( input[0] == 83 && input[1] == 89 && input[2] == 67 )
{
if ( input[3] == 123 && input[37] == 125 )
{
for ( i = 0; i < lenth / 2; ++i )
{
v18 = input[i];
v19 = input[lenth - i - 1];
sub_CA10C3(_54, &v18, v17);
v8[2 * i] = v18; // 奇数元素
v8[2 * i + 1] = v19; // 偶数元素
} // target:v8 = v16
for ( j = 0; j < lenth && v8[j] == v16[j]; ++j )
;
if ( j == lenth )
{
print(aYesYes, v7);
system(aPause_1);
}
else
{
print(aWrong, v7);
system(aPause_2);
}
}
else
{
print(&byte_CA4060, v7);
system(aPause_0);
}
}
else
{
print(aFlag, v7);
system(aPause_3);
}
}
else
{
print(&byte_CA407C, v7);
system(Command);
}
return sub_CA15D1(&savedregs ^ v21);
}

核心逻辑就是输入的数据加密后调换顺序并和v16比较 这个加密算法(sub_CA10C3)就是TEA加密 特点是没有数据丢失 可以逆向

image-20240223145153828

需要注意的是传入的第二个参数是主函数中的&v18 打开v18栈中的位置 发现v19就是紧挨其的下一个image-20231107194847453

所以v5 = now_char[1];这句实际上取的是v19

另外一点需要注意的是数据的上限 unsigned int类型的数据最多储存4bytes 故每次执行都需要截去高32位

知道这些后就可以写出以下解密脚本

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
from z3 import *

key = [None] * 4
key[0] = 32
key[1] = 27
key[2] = 39
key[3] = 44

enc = [None] * 38
enc[0] = 0x9AF9464B
enc[1] = 0xC417B89E
enc[2] = 0xB217A713
enc[3] = 0xC93BA9E8
enc[4] = 0x94F3E44E
enc[5] = 0xB5CC2AB5
enc[6] = 0x4451E42C
enc[7] = 0x7A8A289A
enc[8] = 0x53C8D008
enc[9] = 0x6E117B49
enc[10] = 0x9BFFD794
enc[11] = 0x5EFF2DF9
enc[12] = 0x17E72531
enc[13] = 0xDFBD9979
enc[14] = 0x8F871B3A
enc[15] = 0x73E8C5AC
enc[16] = 0xB28670A6
enc[17] = 0x5AF6A369
enc[18] = 0x2CF7DA24
enc[19] = 0x347B66AF
enc[20] = 0xB9C84D60
enc[21] = 0x911E912F
enc[22] = 0xBD5A2F9B
enc[23] = 0xCB96733A
enc[24] = 0xC59968BE
enc[25] = 0xA00013E9
enc[26] = 0xC12F4EA4
enc[27] = 0xDE863A10
enc[28] = 0xA0C4D594
enc[29] = 0x4380983C
enc[30] = 0x7E2F7648
enc[31] = 0xE54DDC89
enc[32] = 0x3F27A690
enc[33] = 0xB58D3199
enc[34] = 0x604AE517
enc[35] = 0x9C903984
enc[36] = 0xF4E04481
enc[37] = 0x3CF4EDFF

flag = [None] * 38
lenth = 38

for i in range(19):
flag[i] = enc[2 * i]
flag[lenth - i - 1] = enc[2 * i + 1]
delta = 54 * 0x31415927
for _ in range(54):
delta -= 0x31415927
flag[i] -= (key[delta % 4] + delta) ^ (flag[lenth - i - 1] + ((flag[lenth - i - 1] >> 5) ^ (16 * flag[lenth - i - 1])))
flag[i] &= 0xFFFFFFFF
flag[lenth - i - 1] -= delta ^ (key[(delta >> 11) % 4] + delta) ^ (flag[i] + ((flag[i] >> 5) ^ (16 * flag[i])))
flag[lenth - i - 1] &= 0xFFFFFFFF
for each in flag:
print(chr(each), end='')
# SYC{D0_Yov_1ike_To_dRink_Flow3r_teA??}

rainbow | 控制流平坦化

关于去控制流平坦化参考 https://security.tencent.com/index.php/blog/msg/112

这里用别人的工具脚本去平坦化 去平坦化后的代码如下

image-20231107200704176

由于判断无用块的方法是排除法(排除掉序言块,分发器,预处理块和返回块) 而原程序中退出并不使用return而是call exit 所以被判定为无用块被删除 故判断长度和flag不正确就退出的块被删去 观察原来的代码 剩下的代码就是加密后的flag和v8~v11的数据进行对比 解密脚本如下

1
2
3
4
5
6
7
8
9
key = [101,  88,  65, 142,  80,  68, 123,  98,  87,  74, 
126, 84, 73, 108, 125, 132, 79, 91, 149, 96,
96, 100, 119, 72, 125, 77, 123, 159, 104, 60,
45, 98]
for i in range(32):
key[i] ^= i
key[i] -= 18 * (i % 3 == 0)
print(chr(key[i]), end='')
# SYC{TAke_1t_3asy_Just_a_STart!!}

mySelf | 简单SMC

Exinfo查到无壳 32位win程序 IDA32打开image-20231108082822264

VirtualProtect函数给予一段内存地址(函数)修改自身字节码的权限 基本上是SMC的标志

如果不使用静态分析的方法可以直接动态调试 这样的可操作性高 在SMC的函数下断点 动态调试到断点处 F7步进函数 之后查看汇编代码 将SMC后的函数重新识别为函数再反编译为伪代码分析

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
unsigned int __cdecl encode(int a1)
{
int v1; // ebx
int v2; // edi
unsigned int v3; // esi
int v4; // ebx
unsigned int result; // eax
unsigned int *v6; // [esp+Ch] [ebp-Ch]
unsigned int *v7; // [esp+10h] [ebp-8h]
int v8; // [esp+14h] [ebp-4h]

v1 = 0;
v8 = 0;
do
{
v2 = 0;
v3 = *(_DWORD *)(a1 + 4 * v1);
v7 = (unsigned int *)(a1 + 4 * v1);
v6 = (unsigned int *)(a1 + 4 * (v1 + 1));
v4 = 32;
result = *v6;
do
{
v2 -= 1640531527;
v3 += ((result >> 5) + 2) ^ (16 * result + 2) ^ (v2 + result);
result += ((v3 >> 5) + 4) ^ (16 * v3 + 3) ^ (v2 + v3);
--v4;
}
while ( v4 );
v8 += 2;
v1 = v8;
*v7 = v3;
*v6 = result;
}
while ( v8 < 8 );
return result;
}

又是一个TEA加密 但是其中用到了一个伪随机数 需要动态调试获得 这个TEA加密和上一题中的不同点是每次被加密的字符顺序不同 同时也要注意每次加密的数据长度是4bytes 而原来传入的是以1byte分割的数组的地址 而在x86程序中双字型数据使用小端序储存 知道了这些之后写出解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
o_key = [0xF0, 0xF9, 0xBD, 0xBD, 0xC4, 0x94, 0x61, 0xE2, 0x25, 0x91, 
0x79, 0x80, 0x19, 0xC2, 0x0F, 0x1F, 0x15, 0x18, 0x6A, 0xEB,
0xC5, 0x72, 0xF5, 0x84, 0x85, 0x3A, 0xCC, 0x40, 0xBB, 0x2A,
0xA3, 0xD2]
flag = []
for i in range(8):
flag.append(o_key[4 * i] | o_key[4 * i + 1] << 8 | o_key[4 * i + 2] << 16 | o_key[4 * i + 3] << 24)
for i in range(4):
v2 = 0x61C88647 * (-32)
for _ in range(32):
flag[2 * i + 1] -= ((flag[2 * i] >> 5) + 4) ^ (16 * flag[2 * i] + 3) ^ (v2 + flag[2 * i])
flag[2 * i + 1] &= 0xFFFFFFFF
flag[2 * i] -= ((flag[2 * i + 1] >> 5) + 2) ^ (16 * flag[2 * i + 1] + 2) ^ (v2 + flag[2 * i + 1])
flag[2 * i] &= 0xFFFFFFFF
v2 += 0x61C88647
for each in flag:
for i in range(4):
print(chr((each >> (8 * i)) & 0xFF), end='')

小黄鸭 | Python逆向

Exinfo 查到为Python3.7写的程序 可以先用pyinstxtractor将其反汇编生成.pyc文件 再用uncompyle6进行反编译生成.py文件

1
2
python pyinstxtractor.py duck.exe
uncompyle6.exe 1.pyc > duck.py

没有进行混淆

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
# --------duck.py----------
# uncompyle6 version 3.7.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.11.4 (tags/v3.11.4:d2340ef, Jun 7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)]
# Embedded file name: 1.py
import sys
arr = '~h|p4gs`gJdN`thPwR`jDn`te1w`2|RNH'
arr = list(arr)
print('快帮帮小黄鸭,找到它的钥匙')
a = input('请输入你找到的钥匙>:')
a = list(a)
if not ord(a[0]) != 83:
if ord(a[1]) != 89 or ord(a[2]) != 67:
print('不对喔~')
sys.exit(0)
a = a[::-1]
b = []
for i in range(len(a)):
if a[i].isalpha():
c = a[i]
c = chr(ord(c) + 13 - 26 if ord(c) + 13 > (90 if c <= 'Z' else 122) else ord(c) + 13)
b.append(chr(ord(c) + 2))
else:
b.append(chr(ord(a[i]) + 1))

if chr(ord(a[1])) != 's' or ord(a[2]) != 109 or chr(ord(a[17])) != 'C':
print('呀呀呀,怎么算出来不对呀?')
sys.exit(0)
cnt = 0
for i in range(len(b)):
if arr[i] == b[i]:
cnt += 1

if cnt == len(b):
print('密码正确啦!!!,快去告诉小黄鸭吧~')
else:
print('密码不对喔~,请再想想吧')
# okay decompiling 1.pyc

简单的ROT13加密 写出脚本解密

1
2
3
4
5
6
7
8
9
10
11
12
key_o = '~h|p4gs`gJdN`thPwR`jDn`te1w`2|RNH'
key_o = list(key_o)
key_o = key_o[::-1]
key = [ord(val) for val in key_o]
for each in key:
if(chr(each - 2).isalpha()):
c = each -2
c += 13 if ord('a') <= c and c <= ord('m') or ord('A') <= c and c <= ord('M') else (-13)
print(chr(c), end='')
else:
print(chr(each - 1), end='')
# SYC{1_h0pe_yOu_ChAse_YoUr_dr3ams} 我Chase你的梦!

浪漫至死不渝 | JS逆向

源码对变量名进行了混淆 去除混淆并对网页元素进行删去 得到主要逻辑

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
 let k={
a:0,b:1,c:2,d:3,e:4,f:5,g:6,h:7,i:8,j:9,k:10,l:11,m:12,n:13,o:14,p:15,q:16,r:17,s:18,t:19,u:20,v:21,w:22,x:23,y:24,z:25,
A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9,K:10,L:11,M:12,N:13,O:14,P:15,Q:16,R:17,S:18,T:19,U:20,V:21,W:22,X:23,Y:24,Z:25
}
let a=[
{name:"A",yin:7, str:,
{name:"B" ,yin:24, str:,
{name:"C" ,yin:1, str:,
{name:"D" ,yin:4, str:,
{name:"E" ,yin:5, str:,
{name:"F" ,yin:2, str:,
{name:"G" ,yin:6, str:,
{name:"H" ,yin:5, str:,
{name:"I" ,yin:8, str:,
{name:"J" ,yin:3, str:,
{name:"K" ,yin:9, str:,
{name:"L" ,yin:8, str:,
{name:"M" ,yin:11, str:,
{name:"N" ,yin:3, str:,
{name:"O" ,yin:7, str:,
{name:"P" ,yin:14, str:,
{name:"Q" ,yin:15, str:,
{name:"R" ,yin:16, str:,
{name:"S" ,yin:17, str:,
{name:"T" ,yin:18, str:,
{name:"U" ,yin:8, str:,
{name:"V" ,yin:20, str:,
{name:"W" ,yin:3, str:,
{name:"X" ,yin:2, str:,
{name:"Y" ,yin:23, str:,
{name:"Z" ,yin:24, str:
];

let target_2=0;
let key = new Array(125, 130, 131, 122, 117, 110, 123, 125, 130, 131, 122, 117, 110, 123, 99, 99, 99, 99);
let s = "";
let target = false;

function fl(e)
{
if(clickb==true)
{
const num_of_fence = 3;
const fence_key = '53X211WH04N';
const Text1 = decryptRailFence(fence_key, num_of_fence); //Text1 = '510321H4XWN'
let key = e.key;
if((key<='z'&&key>='a')||(key<='Z'&&key>='A'))
{
let p=a[k[key]];
s += p.name;
const intArr = [];
for (let i = 0; i < s.length; i++)
{
const charCode = s.charCodeAt(i);
intArr.push(charCode); //intArr记录s中每个字符对应的ASCII值
}
if (s.length % 18 == 0)
{
let len = s.length / 18;
for (let i = 0; i < len; i++)
{
for (let control = 0; control < 18; control++)
{
if (control < 14)
{
intArr[i + control] ^= Text1.charCodeAt(control % 7);
intArr[i + control] += 10;
}
else
{
intArr[i+ control] ^= Text1.charCodeAt(control -7);
intArr[i + control] += 99;
}
}
}
for (let i = 0; i < s.length; i++)
{
let cnt = 0;
if (intArr[i] == key[0])
{
for (let control = 0; control < key.length && i+ control < intArr.length; control++)
{
if (intArr[i + control] == key[control])
{
cnt++;
}
}
}
if (cnt >= 18) {
target = true;
break;
}
}


}
if(target&&target_2==0)
{
print "ok"
}
}
}

主要是先用栅栏密码对key进行加密 然后对输入逐位字符进行加密 写出解密脚本

1
2
3
4
5
6
7
8
key = [125, 130, 131, 122, 117, 110, 123, 125, 130, 131, 122, 117, 110, 123, 99, 99, 99, 99]
Text1 = '5201314WXHN'
for i in range(18):
if i < 14:
print(chr((key[i] - 10) ^ ord(Text1[i % 7])), end='')
else:
print(chr((key[i] - 99) ^ ord(Text1[i - 7])), end='')
# FJIAXUEFJIAXUEWXHN

寻找初音未来 | Go语言逆向 | 一坨go史

go语言所有的字符串在内存上都是连续的 所以对一个字符串对应的长度和偏移量非常重要

附件放到虚拟机中打开 发现提示输入初音未来色 上网搜索知道是39C5BB 用IDA打开 发现这个输入通过某些加密算法得到key

再用该key进行RC4加密直接动态调试得到key为CCCCCCCCCCCCCCCCCC 用python内置的RC4解密算法进行解密

1
2
3
4
5
6
7
8
9
10
11
from Crypto.Cipher import ARC4

ciphertext = b'\x25\x6F\x3D\x6C\xF9\xE0\xCF\x3F\x2E\x24\xC6\x7B\x81\xBF\x55\x4F\x0D\x99\x87\x47\x48\xF7\xB9\x98\xFB\x1B\x22\xEC\x84\x23\xFD\xB2'
key = b'CCCCCCCCCCCCCCCCCC'

cipher = ARC4.new(key)
plaintext = cipher.decrypt(ciphertext=ciphertext)

print(plaintext)

# b'SYC{N0thing_1s_sEriOus_But_MIku}'

AES! AES? | AES算法魔改

Exinfo查到无壳 64位win程序 IDA64打开 伪代码中看到核心加密逻辑是Shiftrow()transform()伪代码如下

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
void __fastcall ShiftRow(__int64 flag)
{
int v1; // [rsp+0h] [rbp-10h]
int j; // [rsp+4h] [rbp-Ch]
int i; // [rsp+8h] [rbp-8h]
int k; // [rsp+Ch] [rbp-4h]

for ( i = 0; i <= 15; i += 4 )
{
for ( j = 0; j < i / 4; ++j )
{
v1 = *(4i64 * i + flag);
for ( k = 0; k <= 2; ++k )
*(4i64 * (i + k) + flag) = *(flag + 4 * (i + k + 1i64));
*(4i64 * (i + k) + flag) = v1;
}
}
}
__int64 __fastcall tansform(__int64 flag)
{
__int64 result; // rax
unsigned __int8 v2; // dl
int v3; // eax
_DWORD *v4; // rcx
int v5[20]; // [rsp+0h] [rbp-60h]
int k; // [rsp+50h] [rbp-10h]
int j; // [rsp+54h] [rbp-Ch]
int i; // [rsp+58h] [rbp-8h]
int v9; // [rsp+5Ch] [rbp-4h]

v9 = 0;
for ( i = 0; i <= 15; ++i )
{
result = i;
v5[i] = *(4i64 * i + flag);
}
for ( j = 0; j <= 3; ++j )
{
for ( k = 0; k <= 3; ++k )
{
*(flag + 4i64 * v9) = v5[4 * k + j];
v2 = S[*(4i64 * v9 + flag)];
v3 = v9++;
v4 = (flag + 4i64 * v3);
result = v2;
*v4 = v2;
}
}
return result;
}
// 整体代码如下 其中还有一个异或加密 用来实现AES算法
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Str[48]; // [rsp+20h] [rbp-60h] BYREF
int flag1[16]; // [rsp+50h] [rbp-30h] BYREF
_BYTE flag2[96]; // [rsp+90h] [rbp+10h] BYREF
char v7[8]; // [rsp+F0h] [rbp+70h] BYREF
__int64 v8; // [rsp+F8h] [rbp+78h]
int v9; // [rsp+100h] [rbp+80h]
char v10[20]; // [rsp+110h] [rbp+90h] BYREF
char v11[15]; // [rsp+124h] [rbp+A4h] BYREF
char v12[13]; // [rsp+133h] [rbp+B3h] BYREF
__int64 v13[3]; // [rsp+140h] [rbp+C0h] BYREF
int v14; // [rsp+158h] [rbp+D8h]
__int16 v15; // [rsp+15Ch] [rbp+DCh]
char v16[16]; // [rsp+160h] [rbp+E0h] BYREF
int v17[40]; // [rsp+170h] [rbp+F0h] BYREF
char v18[176]; // [rsp+210h] [rbp+190h] BYREF
__int16 v19; // [rsp+2C0h] [rbp+240h]
char v20; // [rsp+2C2h] [rbp+242h]
int v21; // [rsp+2C4h] [rbp+244h]
int _32; // [rsp+2C8h] [rbp+248h]
int i2; // [rsp+2CCh] [rbp+24Ch]
int cnt; // [rsp+2D0h] [rbp+250h]
int i1; // [rsp+2D4h] [rbp+254h]
int nn; // [rsp+2D8h] [rbp+258h]
int mm; // [rsp+2DCh] [rbp+25Ch]
int jj; // [rsp+2E0h] [rbp+260h]
int kk; // [rsp+2E4h] [rbp+264h]
int ii; // [rsp+2E8h] [rbp+268h]
int n; // [rsp+2ECh] [rbp+26Ch]
int m; // [rsp+2F0h] [rbp+270h]
int k; // [rsp+2F4h] [rbp+274h]
int j; // [rsp+2F8h] [rbp+278h]
int i; // [rsp+2FCh] [rbp+27Ch]

(_main)(argc, argv, envp);
memset(v18, 0, sizeof(v18));
v19 = 0;
v20 = 0;
memset(v17, 0, sizeof(v17));
v17[0] = 0xE0;
v17[1] = 0xFFFFFF05;
v17[2] = 0xFFFFFF6E;
v17[3] = 0xFFFFFFC2;
v17[4] = 0xFFFFFF6E;
v17[5] = 0xFFFFFF99;
v17[6] = 0xFFFFFF68;
v17[7] = 0x45;
v17[8] = 0xFFFFFF7D;
v17[9] = 0xFFFFFF1F;
v17[10] = 0xFFFFFF3F;
v17[11] = 0xFFFFFFF9;
v17[12] = 0xFFFFFF97;
v17[13] = 0xFFFFFF76;
v17[14] = 0x3B;
v17[15] = 0x92;
v17[16] = 0x2F;
v17[17] = 0xFFFFFF44;
v17[18] = 0xFFFFFF06;
v17[19] = 0xFFFFFF67;
v17[20] = 0xFFFFFFA8;
v17[21] = 0xFFFFFFEB;
v17[22] = 0xFFFFFFEC;
v17[23] = 0x4A;
v17[24] = 0xFFFFFF6F;
v17[25] = 0xFFFFFFE8;
v17[26] = 0xFFFFFF35;
v17[27] = 0xFFFFFFF9;
v17[28] = 0xFFFFFFAC;
v17[29] = 0xFFFFFFA7;
v17[30] = 0x8C;
v17[31] = 0x71;
qmemcpy(v16, "nyi", 3);
v16[3] = -125;
v16[4] = 121;
v16[5] = 127;
v16[6] = 105;
v16[7] = 117;
v16[8] = 121;
v16[9] = 120;
v16[10] = -127;
v16[11] = 105;
v16[12] = 93;
v16[13] = 99;
v16[14] = 77;
v16[15] = 73;
v13[0] = 0x78732A6F6D6B767Ai64;
v13[1] = 0x7C7F79832A7E7F79i64;
v13[2] = 0x142A44716B76702Ai64;
v14 = 0;
v15 = 0;
v10[0] = -125;
v10[1] = 111;
v10[2] = 125;
v10[3] = 43;
v10[4] = 42;
v10[5] = -125;
v10[6] = 121;
v10[7] = 127;
v10[8] = 42;
v10[9] = 107;
v10[10] = 124;
v10[11] = 111;
v10[12] = 42;
v10[13] = 124;
v10[14] = 115;
v10[15] = 113;
v10[16] = 114;
v10[17] = 126;
v10[18] = 43;
v10[19] = 20;
qmemcpy(v11, "\nxy", 3);
v11[3] = -120;
v11[4] = 126;
v11[5] = 124;
v11[6] = -125;
v11[7] = 42;
v11[8] = 107;
v11[9] = 113;
v11[10] = 107;
v11[11] = 115;
v11[12] = 120;
v11[13] = -120;
v11[14] = 20;
qmemcpy(v12, "\nxy", 3);
v12[3] = -120;
v12[4] = 10;
for ( i = 0; i <= 23; ++i )
*(v13 + i) -= 10;
for ( j = 0; j <= 39; ++j )
v10[j] -= 10;
*v7 = 0i64;
v8 = 0i64;
v9 = 0;
printf("%s", v13);
scanf("%s", Str);
_32 = strlen(Str);
v21 = strlen(v7);
if ( _32 != 32 )
{
printf("%s", &v12[1]);
exit(0);
}
for ( k = 0; k <= 15; ++k )
{
v16[k] -= 10;
v7[k] = v16[k];
}
if ( Str[5] != '.' || Str[10] != 'l' || Str[17] != '0' )
{
printf("%s", &v12[1]);
exit(0);
}
for ( m = 0; m <= 15; ++m )
v18[m] = v7[m]; // v18 = do_you_konw_SYC?
for ( n = 1; n <= 10; ++n )
{
for ( ii = 0; ii <= 31; ++ii )
v18[16 * n + ii] = v18[16 * n - 16 + ii] ^ S[v18[16 * n - 16 + ii]];
}
HIBYTE(v19) = '9';
kk = 0;
for ( jj = 0; jj < _32; ++jj )
flag1[jj] = S[Str[jj]];
for ( kk = 0; kk <= 0; ++kk )
{
ShiftRow(flag1); // 将第n行的前(n - 1)个元素向右移动(5 - n)格
ShiftRow(flag2);
tansform(flag1);
tansform(flag2);
for ( mm = 0; mm <= 15; ++mm )
{
flag1[mm] ^= v18[16 * kk + mm]; // 将flag与key1异或
flag1[mm + 16] ^= v18[16 * kk + mm];
}
for ( nn = 0; nn < _32; ++nn )
flag1[nn] = S[flag1[nn]]; // 将flag中的值替换为S中以flag的值为索引的值
}
ShiftRow(flag1);
ShiftRow(flag2);
for ( i1 = 0; i1 <= 15; ++i1 ) // 将flag与key2异或
{
flag1[i1] ^= v18[16 * kk + i1];
flag1[i1 + 16] ^= v18[16 * kk + i1];
}
cnt = 0;
for ( i2 = 0; i2 <= 31; ++i2 )
{
if ( flag1[i2] == v17[i2] )
++cnt;
}
if ( cnt <= 31 )
printf("%s", &v11[1]);
else
printf("%s", v10);
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
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
S = [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_ = "do_you_konw_SYC?"
v17 = [None] * 32
v17[0] = 0xE0
v17[1] = 0x05
v17[2] = 0x6E
v17[3] = 0xC2
v17[4] = 0x6E
v17[5] = 0x99
v17[6] = 0x68
v17[7] = 0x45
v17[8] = 0x7D
v17[9] = 0x1F
v17[10] = 0x3F
v17[11] = 0xF9
v17[12] = 0x97
v17[13] = 0x76
v17[14] = 0x3B
v17[15] = 0x92
v17[16] = 0x2F
v17[17] = 0x44
v17[18] = 0x06
v17[19] = 0x67
v17[20] = 0xA8
v17[21] = 0xEB
v17[22] = 0xEC
v17[23] = 0x4A
v17[24] = 0x6F
v17[25] = 0xE8
v17[26] = 0x35
v17[27] = 0xF9
v17[28] = 0xAC
v17[29] = 0xA7
v17[30] = 0x8C
v17[31] = 0x71

def shift_row(flag:list) -> list:
for i in range(0, 4):
for _ in range(i):
for k in range(3):
flag[4 * i + 3 - k], flag[4 * i + 3 - k - 1] = flag[4 * i + 3 - k - 1], flag[4 * i + 3 - k]
return flag


key1 = []
for each in key_:
key1.append(ord(each) & 0xFF)
key2 = []
for i in range(16):
key2.append(key1[i] ^ S[key1[i]] & 0xFF)

flag_after = []
for i in range(32):
temp = v17[i] ^ key2[i % 0x10]
flag_after.append(temp)

flag = shift_row(flag_after[0 :16])
flag += shift_row(flag_after[16:32])

for i in range(32):
flag[i] = S.index(flag[i])
flag[i] ^= key1[i % 0x10]

flag1 = [None] * 16
flag2 = [None] * 16
cnt = 0
for i in range(4):
for j in range(4):
flag1[4 * j + i] = S.index(flag[cnt])
cnt += 1
for i in range(4):
for j in range(4):
flag2[4 * j + i] = S.index(flag[cnt])
cnt += 1

flag_last = shift_row(flag1)
flag_last += shift_row(flag2)

for each in flag_last:
t = S.index(each)
try:
print(chr(t), end='')
except:
pass
# SYC{0.o_Thls_1s_n0t_A3s_(q^_^p)}

另外 不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key

ezandroid | 安卓逆向

apk用jeb打开 有两个主要活动如下

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
package com.example.babyapk_1;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import b.f;

public class MainActivity extends f {
public class b {
public int[] a;

public b() {
this.a = new int[]{2023708229, -158607964, 0x81963FFA, 0x458FAC58};
}
}

public EditText o;

@Override // b.f
public void onCreate(Bundle bundle0) {
new Handler();
super.onCreate(bundle0);
this.setContentView(0x7F0B001C); // layout:activity_main
Button button0 = (Button)this.findViewById(0x7F080057); // id:button
this.o = (EditText)this.findViewById(0x7F080097); // id:edit_text
button0.setOnClickListener(new View.OnClickListener() {
@Override // android.view.View$OnClickListener
public void onClick(View view0) {
String s = MainActivity.this.o.getText().toString();
if(s.length() > 24) {
MainActivity.this.o.setText("");
s = MainActivity.this.o.getText().toString();
}

if(((s.length() == 0 ? 1 : 0) | (s.length() % 24 == 0 ? 0 : 1)) != 0) {
int v = 24 - s.length() % 24;
StringBuilder stringBuilder0 = new StringBuilder(s);
for(int v1 = 0; v1 < v; ++v1) {
stringBuilder0.append('X');
}

s = stringBuilder0.toString();
}

StringBuilder stringBuilder1 = new StringBuilder();
StringBuilder stringBuilder2 = new StringBuilder();
for(int v2 = 0; v2 < s.length(); ++v2) {
if(v2 % 2 == 0) {
stringBuilder1.append(s.charAt(v2));
}
else {
stringBuilder2.append(s.charAt(v2));
}
}

b mainActivity$b0 = new b(MainActivity.this);
String s1 = stringBuilder2.toString();
byte[] arr_b = stringBuilder1.toString().getBytes();
int v3 = arr_b.length >> 2;
int[] arr_v = new int[v3];
int v5 = 0;
for(int v4 = 0; v4 < arr_b.length; v4 += 4) {
int v6 = arr_b[v4 + 3];
if(v6 < 0) {
v6 += 0x100;
}

int v7 = arr_b[v4 + 2];
if(v7 < 0) {
v7 += 0x100;
}

int v8 = arr_b[v4 + 1];
if(v8 < 0) {
v8 += 0x100;
}

arr_v[v5] = v6 | v7 << 8 | v8 << 16 | arr_b[v4] << 24;
++v5;
}

int v9 = arr_v[0];
int v10 = arr_v[1];
int v11 = arr_v[2];
int[] arr_v1 = mainActivity$b0.a;
int key_0 = arr_v1[0];
int key_1 = arr_v1[1];
int key_2 = arr_v1[2];
int key_3 = arr_v1[3];
int v16 = 0;
for(int v17 = 0; v17 < 0x20; ++v17) {
v16 += -1640531527;
v9 += (v10 << 4) + key_0 ^ v10 + v16 ^ (v10 >> 5) + key_1;
v10 += (v9 << 4) + key_2 ^ v9 + v16 ^ (v9 >> 5) + key_3;
}

arr_v[0] = v9;
for(int v18 = 0; v18 < 0x20; ++v18) {
v16 += -1640531527;
v11 += (v10 << 4) + key_0 ^ v10 + v16 ^ (v10 >> 5) + key_1;
v10 += (v11 << 4) + key_2 ^ v11 + v16 ^ (v11 >> 5) + key_3;
}

arr_v[1] = v11;
arr_v[2] = v10;
byte[] arr_b1 = new byte[v3 << 2];
int v20 = 0;
for(int v19 = 0; v19 < v3; ++v19) {
arr_b1[v20 + 3] = (byte)(arr_v[v19] & 0xFF);
arr_b1[v20 + 2] = (byte)(arr_v[v19] >> 8 & 0xFF);
arr_b1[v20 + 1] = (byte)(arr_v[v19] >> 16 & 0xFF);
arr_b1[v20] = (byte)(arr_v[v19] >> 24 & 0xFF);
v20 += 4;
}

for(int v21 = 0; v21 < new byte[]{-91, -8, -110, -55, -49, 75, 0x73, 13, -76, (byte)0x8F, 102, 80}.length; ++v21) {
System.out.println(((int)arr_b1[v21]));
}

int v23 = 1;
for(int v22 = 0; true; ++v22) {
byte[] arr_b2 = new byte[]{-91, -8, -110, -55, -49, 75, 0x73, 13, -76, (byte)0x8F, 102, 80};
if(v22 >= arr_b2.length) {
break;
}

if(arr_b1[v22] != arr_b2[v22]) {
Toast.makeText(MainActivity.this, "You\'re Wrong!\n", 0).show();
v23 = 0;
}
}

Intent intent0 = new Intent(MainActivity.this, MainActivity2.class);
intent0.putExtra("ad@#E!@a123", s1);
intent0.putExtra("eCAS213@!@3", arr_b1);
if(v23 == 1) {
MainActivity.this.startActivity(intent0);
}
}
});
}
}
package com.example.babyapk_1;

import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import b.f;

public class MainActivity2 extends f {
@Override // b.f
public void onCreate(Bundle bundle0) {
super.onCreate(bundle0);
this.setContentView(0x7F0B001D); // layout:activity_main2
Intent intent0 = this.getIntent();
byte[] arr_b = intent0.getStringExtra("ad@#E!@a123").getBytes();
byte[] arr_b1 = intent0.getByteArrayExtra("eCAS213@!@3");
int v1 = 1;
for(int v = 0; v < arr_b1.length; ++v) {
arr_b1[v] = (byte)(arr_b1[v] ^ arr_b[v % arr_b.length]);
if(arr_b1[v] != new int[]{-107, -106, 0xFFFFFFA1, 0xFFFFFF8D, 0xFFFFFF89, 0x7F, 26, 0x79, -62, -20, 86, 9}[v]) {
Toast.makeText(this, "N0T Right,Maybe try more harder?\n", 0).show();
v1 = 0;
}
}

if(v1 == 1) {
Toast.makeText(this, "Congratulations!", 1).show();
}
}
}

当MainActivity1中的匹配通过后才会进行MainActivity2的另一部分加密 主要是分奇偶索引进行TEA或异或加密 据此写出解密脚本

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

int main(){
unsigned char arr_even[12] = {-107, -106, 0xFFFFFFA1, 0xFFFFFF8D, 0xFFFFFF89, 0x7F, 26, 0x79, -62, -20, 86, 9};
unsigned char key_even[12] = {-91, -8, -110, -55, -49, 75, 0x73, 13, -76, 0x8F, 102, 80};
char flag_even[12];
for(int i = 0; i < 12; i++){
flag_even[i] = arr_even[i] ^ key_even[i];
}
int flag_odd_[3];
for(int i = 0; i < 12; i += 4){
flag_odd_[(int)(i / 4)] = key_even[i + 3] | key_even[i + 2] << 8 | key_even[i + 1] << 16 | key_even[i] << 24;
}
int TEA_key[4] = {2023708229, -158607964, 0x81963FFA, 0x458FAC58};
int delta = -1640531527 * 0x40;
for(int i = 0; i < 0x20; i++){
flag_odd_[2] -= (flag_odd_[1] << 4) + TEA_key[2] ^ flag_odd_[1] + delta ^ (flag_odd_[1] >> 5) + TEA_key[3];
flag_odd_[1] -= (flag_odd_[2] << 4) + TEA_key[0] ^ flag_odd_[2] + delta ^ (flag_odd_[2] >> 5) + TEA_key[1];
delta += 1640531527;
}
for(int i = 0; i < 0x20; i++){
flag_odd_[2] -= (flag_odd_[0] << 4) + TEA_key[2] ^ flag_odd_[0] + delta ^ (flag_odd_[0] >> 5) + TEA_key[3];
flag_odd_[0] -= (flag_odd_[2] << 4) + TEA_key[0] ^ flag_odd_[2] + delta ^ (flag_odd_[2] >> 5) + TEA_key[1];
delta += 1640531527;
}
unsigned char flag_odd[12];
for(int i = 3; i >= 0; i--){
flag_odd[3 - i] = (flag_odd_[0] >> i * 8);
}
for(int i = 3; i >= 0; i--){
flag_odd[7 - i] = (flag_odd_[2] >> i * 8);
}
for(int i = 3; i >= 0; i--){
flag_odd[11 - i] = (flag_odd_[1] >> i * 8);
}
printf("SYC{");
for(int i = 0; i < 24; i++){
if(i % 2 == 0){
printf("%c", flag_odd[(int)(i / 2)]);
}
else{
printf("%c", flag_even[(int)(i / 2)]);
}
}
printf("}");
// SYC{T00nV3tD3F34Tint0vict0rY}
return 0;
}

是男人就来扎针 | Unity引擎游戏逆向

Unity引擎游戏的控制逻辑一般存放在.\*_Data\Managed\Assembly-CSharp.dll中 用dnspy打开 主要逻辑如下

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
using System;
using System.Collections;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

// Token: 0x02000002 RID: 2
public class GameManager : MonoBehaviour
{
// Token: 0x06000002 RID: 2 RVA: 0x000020A9 File Offset: 0x000004A9
public static string RemoveDash(string str)
{
str = str.Replace("-", string.Empty);
return str;
}

// Token: 0x06000003 RID: 3 RVA: 0x000020C0 File Offset: 0x000004C0
public static string what(string str)
{
MD5 md = MD5.Create();
byte[] bytes = Encoding.Default.GetBytes(str);
byte[] value = md.ComputeHash(bytes);
return GameManager.RemoveDash(BitConverter.ToString(value));
}

// Token: 0x06000004 RID: 4 RVA: 0x000020F4 File Offset: 0x000004F4
private void Start()
{
this.startPoint = GameObject.Find("StartPoint").transform;
this.spawnPoint = GameObject.Find("SpawnPoint").transform;
this.mainCamera = Camera.main;
this.SpawnPin();
}

// Token: 0x06000005 RID: 5 RVA: 0x00002134 File Offset: 0x00000534
private void Update()
{
if (this.score == 30)
{
string text = string.Empty;
for (int i = 0; i < this.magicc.Length; i++)
{
char c = (char)this.magicc[i];
text += c;
}
this.flagText.text = text;
this.nothing += GameManager.what(this.flagText.text);
this.nothing += '\0';
}
if (this.score == 40)
{
string text2 = string.Empty;
for (int j = 0; j < this.magic.Length; j++)
{
char c2 = (char)this.magic[j];
text2 += c2;
}
this.flagText.text = text2;
text2 += '\0';
}
if (this.score == 100)
{
this.scoreText.text = "!";
this.flagText.text = this.nothing;
}
if (this.isGameOver)
{
return;
}
if (Input.GetMouseButtonDown(0))
{
this.score++;
this.scoreText.text = this.score.ToString();
this.currentPin.StartFly();
this.SpawnPin();
for (int k = 0; k < this.magicc.Length; k++)
{
this.magicc[k] ^= this.score;
this.magic[k] ^= this.score;
}
}
}

// Token: 0x06000006 RID: 6 RVA: 0x000022F6 File Offset: 0x000006F6
private void SpawnPin()
{
this.currentPin = UnityEngine.Object.Instantiate<GameObject>(this.pinPrefab, this.spawnPoint.position, this.pinPrefab.transform.rotation).GetComponent<Pin>();
}

// Token: 0x06000007 RID: 7 RVA: 0x00002329 File Offset: 0x00000729
public void GameOver()
{
if (this.isGameOver)
{
return;
}
GameObject.Find("Circle").GetComponent<RotateSelf>().enabled = false;
base.StartCoroutine(this.GameOverAnimation());
this.isGameOver = true;
}

// Token: 0x06000008 RID: 8 RVA: 0x00002360 File Offset: 0x00000760
private IEnumerator GameOverAnimation()
{
for (;;)
{
this.mainCamera.backgroundColor = Color.Lerp(this.mainCamera.backgroundColor, Color.red, this.speed * Time.deltaTime);
this.mainCamera.orthographicSize = Mathf.Lerp(this.mainCamera.orthographicSize, 4f, this.speed * Time.deltaTime);
if (Mathf.Abs(this.mainCamera.orthographicSize - 4f) < 0.01f)
{
break;
}
yield return 0;
}
yield return new WaitForSeconds(0.2f);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
yield break;
}

// Token: 0x04000001 RID: 1
private Transform startPoint;

// Token: 0x04000002 RID: 2
private Transform spawnPoint;

// Token: 0x04000003 RID: 3
private Pin currentPin;

// Token: 0x04000004 RID: 4
private bool isGameOver;

// Token: 0x04000005 RID: 5
private int score;

// Token: 0x04000006 RID: 6
private Camera mainCamera;

// Token: 0x04000007 RID: 7
private string nothing = string.Empty;

// Token: 0x04000008 RID: 8
private int[] magicc = new int[]
{
75,
109,
102,
63,
107,
112,
63,
108,
124,
112,
109,
122,
63,
43,
47,
63,
111,
112,
118,
113,
107,
108,
62
};

// Token: 0x04000009 RID: 9
private int[] magic = new int[]
{
124,
90,
81,
8,
92,
71,
8,
90,
77,
73,
75,
64,
8,
25,
24,
24,
8,
88,
71,
65,
70,
92,
91,
9
};

// Token: 0x0400000A RID: 10
public Text scoreText;

// Token: 0x0400000B RID: 11
public Text flagText;

// Token: 0x0400000C RID: 12
public GameObject pinPrefab;

// Token: 0x0400000D RID: 13
public float speed = 3f;
}

可以看到游戏前30次每刷新一次分数就会与明文进行异或 最后进行md5加密然后在分数达到100时显示 显然不可能点到100 但是可以先点到30 再用CE改到100 写出解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
import hashlib
key = [ 75, 109, 102, 63, 107, 112, 63, 108, 124, 112, 109, 122, 63, 43, 47, 63, 111, 112, 118, 113, 107, 108, 62]
convt = hashlib.md5()
for i in range(1, 31):
for j in range(len(key)):
key[j] ^= i
flag = ''
for each in key:
flag += chr(each)
convt.update(flag.encode())
print(convt.hexdigest().upper())
# CBDDD133B60130856D3C695D9E5ED6A5

babycode | 不知道什么加密

IDA64打开 伪代码如下 其中两个简单加密算法不展示

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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int v3; // eax
int v5; // [rsp+4h] [rbp-13Ch]
int i; // [rsp+8h] [rbp-138h]
int j; // [rsp+Ch] [rbp-134h]
int k; // [rsp+10h] [rbp-130h]
char enc[4]; // [rsp+20h] [rbp-120h] BYREF
int v10[33]; // [rsp+24h] [rbp-11Ch]
char input[113]; // [rsp+B7h] [rbp-89h] BYREF
unsigned __int64 v12; // [rsp+128h] [rbp-18h]

v12 = __readfsqword(0x28u);
v5 = 1;
puts("please input your flag:");
__isoc99_scanf("%s", &input[9]);
*enc = 120;
v10[0] = 91;
v10[1] = 86;
v10[2] = 122;
v10[3] = 93;
v10[4] = 84;
v10[5] = 37;
v10[6] = 49;
v10[7] = 32;
v10[8] = 104;
v10[9] = 61;
v10[10] = 100;
v10[11] = -110;
v10[12] = 118;
v10[13] = 99;
v10[14] = 123;
v10[15] = 89;
v10[16] = 87;
v10[17] = 33;
v10[18] = -124;
v10[19] = 87;
v10[20] = 118;
v10[21] = -121;
v10[22] = 114;
v10[23] = -124;
v10[24] = -123;
v10[25] = 112;
v10[26] = -98;
v10[27] = 79;
v10[28] = 112;
v10[29] = 114;
v10[30] = -124;
v10[31] = 87;
v10[32] = -120;
strcpy(input, "fuwafuwa");
for ( i = 0; i < strlen(&input[9]); ++i )
input[i + 9] = (16 * input[i + 9]) | (input[i + 9] >> 4) & 0xF;// 互换输入中的高/低四位
encode(&input[9]);
for ( j = 0; j < strlen(&input[9]); ++j )
{
input[j + 9] += j;
input[j + 9] ^= input[j % 8] % 32 + 1;
ceasar(input, j); // 向后移j位的凯撒加密
v3 = strlen(input);
reverse(input, v3); // 对称对换key(0->8, 1->7...
}
for ( k = 0; k <= strlen(enc); ++k )
{
if ( input[k + 9] != *&enc[4 * k] )
{
v5 = 0;
puts("no way!");
break;
}
}
if ( v5 == 1 )
puts("your input is flag!");
return 0LL;
}
char *__fastcall encode(const char *input)
{
unsigned __int8 v2; // [rsp+17h] [rbp-49h]
int i; // [rsp+18h] [rbp-48h]
int v4; // [rsp+1Ch] [rbp-44h]
char *ptr_input; // [rsp+20h] [rbp-40h]
char *key2; // [rsp+28h] [rbp-38h]
char *ptr_input_; // [rsp+30h] [rbp-30h]
unsigned __int64 j; // [rsp+38h] [rbp-28h]
size_t len_of_input; // [rsp+40h] [rbp-20h]
__int64 max_key; // [rsp+48h] [rbp-18h]
char *v11; // [rsp+50h] [rbp-10h]
unsigned __int64 last; // [rsp+58h] [rbp-8h]

len_of_input = strlen(input);
max_key = 3 * ((len_of_input >> 1) + 1);
v11 = malloc(max_key + len_of_input);
key2 = &v11[max_key]; // 输入的首地址
last = &v11[max_key - 1 + len_of_input]; // v11的最后一个元素
ptr_input = &v11[max_key];
memcpy(&v11[max_key], input, len_of_input); // 最后的一段元素和输入相同
while ( ptr_input <= last )
{
if ( *ptr_input )
{
v2 = 0;
for ( ptr_input_ = ptr_input; ptr_input_ <= last; ++ptr_input_ )
{
v4 = v2 << 8;
v2 = *ptr_input_ & 0x3F;
*ptr_input_ = (*ptr_input_ + v4) >> 6;
}
*--key2 = off_55B72E249018[v2];
}
else
{
++ptr_input;
}
}
for ( i = 0; i <= 19; ++i )
key += rand() % 100;
key = key % 0x20 + 1;
for ( j = 0LL; j < &v11[max_key] - key2; ++j )
input[j] = key ^ key2[j];
input[j] = 0;
return v11;
}

核心加密是一个base64的换表 要注意的是其中的key动态调试出来并不正确 因为程序会检测动态调试改变key的初值

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
.init_array:000055B72E248D80                               _init_array segment qword public 'DATA' use64
.init_array:000055B72E248D80 assume cs:_init_array
.init_array:000055B72E248D80 ;org 55B72E248D80h
.init_array:000055B72E248D80 00 62 24 2E B7 55 00 00 dq offset sub_55B72E246200
.init_array:000055B72E248D88 09 62 24 2E B7 55 00 00 dq offset sub_55B72E246209
.init_array:000055B72E248D88 _init_array ends
...
.text:000055B72E246209 sub_55B72E246209 proc near ; DATA XREF: .init_array:000055B72E248D88↓o
.text:000055B72E246209
.text:000055B72E246209 var_4= dword ptr -4
.text:000055B72E246209
.text:000055B72E246209 ; __unwind {
.text:000055B72E246209 F3 0F 1E FA endbr64
.text:000055B72E24620D 55 push rbp
.text:000055B72E24620E 48 89 E5 mov rbp, rsp
.text:000055B72E246211 C7 45 FC 00 00 00 00 mov [rbp+var_4], 0
.text:000055B72E246218 BB 00 00 00 00 mov ebx, 0 ; ptrace_request
.text:000055B72E24621D B9 00 00 00 00 mov ecx, 0 ; pid
.text:000055B72E246222 BA 00 00 00 00 mov edx, 0 ; addr
.text:000055B72E246227 B8 1A 00 00 00 mov eax, 1Ah
.text:000055B72E24622C CD 80 int 80h ; LINUX - sys_ptrace
.text:000055B72E24622C
.text:000055B72E24622E 89 45 FC mov [rbp+var_4], eax
.text:000055B72E246231 8B 45 FC mov eax, [rbp+var_4]
.text:000055B72E246234 48 98 cdqe
.text:000055B72E246236 48 83 F8 FF cmp rax, 0FFFFFFFFFFFFFFFFh
.text:000055B72E24623A 75 0A jnz short loc_55B72E246246
.text:000055B72E24623A
.text:000055B72E24623C C7 05 CA 2D 00 00 08 00 00 00 mov cs:key, 8
.text:000055B72E24623C
.text:000055B72E246246
.text:000055B72E246246 loc_55B72E246246: ; CODE XREF: sub_55B72E246209+31↑j
.text:000055B72E246246 90 nop
.text:000055B72E246247 5D pop rbp
.text:000055B72E246248 C3 retn
.text:000055B72E246248 ; } // starts at 55B72E246209
.text:000055B72E246248
.text:000055B72E246248 sub_55B72E246209 endp

所以用key在(1, 31)且加密后密文都在表中爆破出key为21 据此写出解密脚本

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
![image-20231128164528686](C:\Users\Orink\AppData\Roaming\Typora\typora-user-images\image-20231128164528686.png)def emulator(key:list, time:int):
for i in range(8):
key[i] = 97 + (key[i] - 97 + time) % 26

def antiemulator(key:list, time:int):
for i in range(8):
key[i] = 97 + (key[i] - 97 - time) % 26

table = [ord(val) for val in 'i5jLW7S0GX6uf1cv3ny4q8es2Q+bdkYgKOIT/tAxUrFlVPzhmow9BHCMDpEaJRZN']
enc = [val & 0xFF for val in [120, 91, 86, 122, 93, 84, 37, 49, 32, 104, 61, 100, -110, 118, 99, 123, 89, 87, 33, -124, 87, 118, -121, 114, -124, -123, 112, -98, 79, 112, 114, -124, 87, -120]]
key_0 = 'fuwafuwa'
key = [ord(val) for val in key_0]
for i in range(34):
emulator(key, i)
key = key[::-1]
for i in range(33, -1, -1):
key = key[::-1]
antiemulator(key, i)
enc[i] ^= key[i % 8] % 32 + 1
enc[i] -= i
for i in range(34):
enc[i] ^= 21
enc = [table.index(val) for val in enc]
flag = ''
for each in enc:
flag += "{:06b}".format(each)
flag = flag[4:]
flag = [flag[8 * i:8 * i + 8] for i in ra![image-20231128164528686](C:\Users\Orink\AppData\Roaming\Typora\typora-user-images\image-20231128164528686.png)nge(len(enc))]
print("SYC{", end='')
for each in flag:
try:
print(chr(int(each[4:8] + each[0:4], 2)), end='')
except:
pass
print("}", end='')
# SYC{HbwKqCOAOVXdHAbG0HeinZkez}

yakvm | 虚拟机逆向

main为生成的虚拟机 IDA打开发现是无符号go 用go paraser恢复符号 搜索有关操作码的函数

image-20231128164544215

发现有一个ShowOpcodes函数 但 调试时不进过该函数 交叉引用找到调用它的函数

image-20231128164844486

在判断处下断点 调试时通过改ZF寄存器步进ShowOpcodes函数 得到可读操作码 根据官方文档分析如下

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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
2:6->2:9         0:OP:type                 byte
2:4->2:119 1:OP:type slice
2:11->2:13 2:OP:push 137
2:15->2:17 3:OP:push 108
2:19->2:21 4:OP:push 159
2:23->2:25 5:OP:push 114
2:27->2:29 6:OP:push 185
2:31->2:32 7:OP:push 90
2:34->2:36 8:OP:push 174
2:38->2:39 9:OP:push 68
2:41->2:43 10:OP:push 160
2:45->2:46 11:OP:push 81
2:48->2:50 12:OP:push 179
2:52->2:53 13:OP:push 41
2:55->2:57 14:OP:push 186
2:59->2:60 15:OP:push 89
2:62->2:64 16:OP:push 168
2:66->2:67 17:OP:push 78
2:69->2:71 18:OP:push 229
2:73->2:75 19:OP:push 121
2:77->2:79 20:OP:push 149
2:81->2:83 21:OP:push 106
2:85->2:87 22:OP:push 147
2:89->2:91 23:OP:push 103
2:93->2:95 24:OP:push 156
2:97->2:99 25:OP:push 114
2:101->2:103 26:OP:push 133
2:105->2:106 27:OP:push 98
2:108->2:110 28:OP:push 146
2:112->2:114 29:OP:push 116
2:116->2:118 30:OP:push 181
2:4->2:119 31:OP:typedslice 29
2:4->2:119 32:OP:list 1
2:0->2:0 33:OP:pushleftr 1 arg_1 = key_list[29]
2:0->2:0 34:OP:list 1
2:0->2:119 35:OP:assign
3:0->3:4 36:OP:pushid print
3:6->3:25 37:OP:push please input flag:
3:5->3:26 38:OP:call vlen:1
3:0->3:26 39:OP:pop
4:4->4:6 40:OP:pushid get
4:7->4:8 41:OP:call vlen:0
4:4->4:8 42:OP:list 1
4:0->4:0 43:OP:pushleftr 2
4:0->4:0 44:OP:list 1 arg_2 = input[29]
4:0->4:8 45:OP:assign
5:18->8:0 46:OP:new-scope 2 {
5:3->5:5 47:OP:pushid len
5:7->5:7 48:OP:pushr 2
5:6->5:8 49:OP:call vlen:1 len(arg_2)
5:12->5:14 50:OP:pushid len
5:16->5:16 51:OP:pushr 1 len(arg_1)
5:15->5:17 52:OP:call vlen:1
5:3->5:17 53:OP:gt len(arg_1) > len(arg_2) -> goto globle63
5:18->8:0 54:OP:jmpf -> 63
5:18->8:0 55:OP:new-scope 3 {
6:1->6:7 56:OP:pushid println
6:9->6:32 57:OP:push input string too long! printf(too long!)
6:8->6:33 58:OP:call vlen:1
6:1->6:33 59:OP:pop
7:1->7:6 60:OP:return
5:18->8:0 61:OP:end-scope }
5:18->8:0 62:OP:jmp -> 63 globle63:
5:18->8:0 63:OP:end-scope }
12:8->21:0 64:OP:push function params[1] codes[54] (copy)
12:8->21:0 65:OP:list 1
12:0->12:4 66:OP:pushleftr 8 arg_8 = func1
12:0->12:4 67:OP:list 1
12:0->21:0 68:OP:assign
25:4->25:8 69:OP:pushr 8
25:10->25:10 70:OP:pushr 2
25:9->25:11 71:OP:call vlen:1 arg2 = func1(arg_2)
25:4->25:11 72:OP:list 1
25:0->25:0 73:OP:pushleftr 2
25:0->25:0 74:OP:list 1
25:0->25:11 75:OP:assign
28:9->33:0 76:OP:push function params[0] codes[40] (copy)
28:9->33:0 77:OP:list 1
28:0->28:4 78:OP:pushleftr 14
28:0->28:4 79:OP:list 1
28:0->33:0 80:OP:assign
36:0->36:4 81:OP:pushr 14
36:5->36:6 82:OP:call vlen:0 func2()
36:0->36:6 83:OP:pop
38:11->48:0 84:OP:push function params[0] codes[52] (copy)
38:11->48:0 85:OP:list 1
38:0->38:7 86:OP:pushleftr 19
38:0->38:7 87:OP:list 1
38:0->48:0 88:OP:assign
51:14->53:0 89:OP:new-scope 24
51:3->51:10 90:OP:pushr 19
51:11->51:12 91:OP:call vlen:0
51:14->53:0 92:OP:jmpf -> 100
51:14->53:0 93:OP:new-scope 25
52:1->52:5 94:OP:pushid print
52:7->52:24 95:OP:push yes! you get it!
52:6->52:25 96:OP:call vlen:1
52:1->52:25 97:OP:pop
51:14->53:0 98:OP:end-scope
51:14->53:0 99:OP:jmp -> 108
51:14->53:0 100:OP:end-scope
51:14->53:0 101:OP:new-scope 26
53:6->55:0 102:OP:new-scope 27
54:1->54:5 103:OP:pushid print
54:7->54:24 104:OP:push no this not flag
54:6->54:25 105:OP:call vlen:1
54:1->54:25 106:OP:pop
53:6->55:0 107:OP:end-scope
51:14->53:0 108:OP:end-scope

//func1 start
anonymous
13:1->19:1 1:OP:new-scope 6 {
13:1->19:1 2:OP:pushleftr 5 arg_5 = arg_3 = arg_2
13:18->13:18 3:OP:pushr 3
13:1->19:1 4:OP:fast-assign
13:1->19:1 5:OP:enter-for-range -> 47 for i in range(47)
13:1->19:1 6:OP:range-next
13:5->13:5 7:OP:pushleftr 6
13:8->13:8 8:OP:pushleftr 7
13:5->13:8 9:OP:list 2
13:1->19:1 10:OP:assign
13:20->19:1 11:OP:new-scope 7 {
14:16->16:2 12:OP:new-scope 8 {
14:5->14:5 13:OP:pushr 6
14:9->14:9 14:OP:push 2
14:5->14:9 15:OP:mod
14:14->14:14 16:OP:push 0
14:5->14:14 17:OP:eq arg_6 % 2 == 0 goto global31
14:16->16:2 18:OP:jmpf -> 31
14:16->16:2 19:OP:new-scope 9 else{
15:10->15:10 20:OP:pushr 7
15:14->15:17 21:OP:push 240
15:10->15:17 22:OP:xor
15:10->15:17 23:OP:list 1
15:3->15:3 24:OP:pushr 3
15:5->15:5 25:OP:pushr 6 arg_3[arg_6] = arg_7 ^ 240
15:3->15:6 26:OP:list 2
15:3->15:6 27:OP:list 1
15:3->15:17 28:OP:assign
14:16->16:2 29:OP:end-scope }
14:16->16:2 30:OP:jmp -> 44
14:16->16:2 31:OP:end-scope global31: }
14:16->16:2 32:OP:new-scope 10 {
16:9->18:2 33:OP:new-scope 11 {
17:10->17:10 34:OP:pushr 7 arg_7 ^ 15
17:14->17:17 35:OP:push 15
17:10->17:17 36:OP:xor
17:10->17:17 37:OP:list 1
17:3->17:3 38:OP:pushr 3
17:5->17:5 39:OP:pushr 6 arg_3[arg_6] = arg_7 ^ 15
17:3->17:6 40:OP:list 2
17:3->17:6 41:OP:list 1
17:3->17:17 42:OP:assign
16:9->18:2 43:OP:end-scope }
14:16->16:2 44:OP:end-scope }
13:20->19:1 45:OP:end-scope }
13:1->19:1 46:OP:exit-for-range -> 6
13:1->19:1 47:OP:pop
13:1->19:1 48:OP:end-scope }
20:8->20:8 49:OP:pushr 3
20:8->20:8 50:OP:list 1
20:1->20:8 51:OP:return
//func1 end

//func2 start
anonymous
28:15->33:0 0:OP:new-scope 13
29:1->32:1 1:OP:new-scope 14
29:1->32:1 2:OP:pushleftr 10
29:18->29:18 3:OP:pushr 2 arg_10 = arg_2
29:1->32:1 4:OP:fast-assign
29:1->32:1 5:OP:enter-for-range -> 36
29:1->32:1 6:OP:range-next
29:5->29:5 7:OP:pushleftr 11 arg_12 = arg_11 ?
29:8->29:8 8:OP:pushleftr 12
29:5->29:8 9:OP:list 2
29:1->32:1 10:OP:assign
29:20->32:1 11:OP:new-scope 15 {
30:6->30:6 12:OP:pushr 11 arg_13 = arg_11 * 2
30:10->30:10 13:OP:push 2
30:6->30:10 14:OP:mul
30:6->30:10 15:OP:list 1
30:2->30:2 16:OP:pushleftr 13
30:2->30:2 17:OP:list 1
30:2->30:10 18:OP:assign
31:11->31:11 19:OP:pushr 12 //用与或非实现异或
31:10->31:11 20:OP:not arg_12 ^ arg_13 -> (~v12 | v13) and (v12 | ~v13)
31:15->31:15 21:OP:pushr 13
31:10->31:15 22:OP:and
31:21->31:21 23:OP:pushr 12
31:26->31:26 24:OP:pushr 13
31:25->31:26 25:OP:not
31:21->31:26 26:OP:and
31:9->31:27 27:OP:or
31:9->31:27 28:OP:list 1
31:2->31:2 29:OP:pushr 2
31:4->31:4 30:OP:pushr 11
31:2->31:5 31:OP:list 2
31:2->31:5 32:OP:list 1
31:2->31:27 33:OP:assign
29:20->32:1 34:OP:end-scope }
29:1->32:1 35:OP:exit-for-range -> 6
29:1->32:1 36:OP:pop
29:1->32:1 37:OP:end-scope
28:15->33:0 38:OP:end-scope
28:9->33:0 39:OP:return
//func2 end

//func3 start 判断函数
anonymous
38:17->48:0 0:OP:new-scope 17
39:21->41:1 1:OP:new-scope 18
39:4->39:6 2:OP:pushid len
39:8->39:8 3:OP:pushr 2
39:7->39:9 4:OP:call vlen:1
39:14->39:16 5:OP:pushid len
39:18->39:18 6:OP:pushr 1
39:17->39:19 7:OP:call vlen:1
39:4->39:19 8:OP:neq
39:21->41:1 9:OP:jmpf -> 16
39:21->41:1 10:OP:new-scope 19
40:9->40:13 11:OP:push false
40:9->40:13 12:OP:list 1
40:2->40:13 13:OP:return
39:21->41:1 14:OP:end-scope
39:21->41:1 15:OP:jmp -> 16
39:21->41:1 16:OP:end-scope
42:1->46:1 17:OP:new-scope 20
42:1->46:1 18:OP:pushleftr 16
42:18->42:18 19:OP:pushr 2
42:1->46:1 20:OP:fast-assign
42:1->46:1 21:OP:enter-for-range -> 45
42:1->46:1 22:OP:range-next
42:5->42:5 23:OP:pushleftr 17
42:8->42:8 24:OP:pushleftr 18
42:5->42:8 25:OP:list 2
42:1->46:1 26:OP:assign
42:20->46:1 27:OP:new-scope 21
43:14->45:2 28:OP:new-scope 22
43:5->43:5 29:OP:pushr 18
43:10->43:10 30:OP:pushr 1
43:12->43:12 31:OP:pushr 17
43:11->43:13 32:OP:push false
43:11->43:13 33:OP:iterablecall off:1 op1: - op2: -
43:5->43:13 34:OP:neq
43:14->45:2 35:OP:jmpf -> 42
43:14->45:2 36:OP:new-scope 23
44:10->44:14 37:OP:push false
44:10->44:14 38:OP:list 1
44:3->44:14 39:OP:return
43:14->45:2 40:OP:end-scope
43:14->45:2 41:OP:jmp -> 42
43:14->45:2 42:OP:end-scope
42:20->46:1 43:OP:end-scope
42:1->46:1 44:OP:exit-for-range -> 22
42:1->46:1 45:OP:pop
42:1->46:1 46:OP:end-scope
47:8->47:11 47:OP:push true
47:8->47:11 48:OP:list 1
47:1->47:11 49:OP:return
38:17->48:0 50:OP:end-scope
38:11->48:0 51:OP:return
//func3 end

据此写出解密脚本

1
2
3
4
5
6
7
8
9
10
11
key = [137, 108, 159, 114, 185, 90, 174, 68, 160, 81, 179, 41, 186, 89, 168, 78, 229, 121, 149, 106, 147, 103, 156, 114, 133, 98, 146, 116, 181]
for i in range(29):
if i % 2 == 0:
key[i] ^= 2 * i
key[i] ^= 240
else:
key[i] ^= 2 * i
key[i] ^= 15
for each in key:
print(chr(each), end='')
# yak{A_RE@LW0RLD_5TACKB@SE_VM}