【为美好CTF献上祝福】 ISCTF2024 逆向笔记
很显然,我并没有参加ISCTF2024,去网上找套ISCTF2024的题,解压出来发下只缺了Re的题。东拼西凑找到的这些题,所以题目顺序包不是对的。
py不好,会被ban
扔进ida里,查看string看到这些东西

看来是用 Pyinstaller 打包的 python 程序。
掏出我们的 pyinstxtractor 。
找到我们的 main 函数

成功得到了 main.pyc , 再用 pycdc.exe
得到 python 伪源码
# Source Generated with Decompyle++
# File: main.pyc (Python 3.12)
def verify(o0000, len2, language2):
enc = [
25,
85,
88,
62,
105,
93,
110,
124,
1,
97,
46,
47,
75,
5,
116,
48,
2,
25]
for i in range(len2 - 1):
o0000[i] = o0000[i] ^ o0000[i + 1]
o0000.reverse()
for ii in range(len2 - 1):
o0000[ii] = o0000[ii] ^ o0000[ii + 1]
for iii in range(len2):
pass
for None in range(len2):
if not o0000[iiii] != enc[iiii]:
continue
return False
return True
def main():
print('Y0u k0nw what is the best language in the world???')
language = input()
OO000 = []
language1 = []
OOO00 = ''
# WARNING: Decompyle incomplete
if __name__ == '__main__':
main()
return None
写个逆向脚本就是了。。。吗?
# WARNING: Decompyle incomplete 噔噔咚,编译未完成。
而且伪代码里:
for iii in range(len2):
pass
这一段也很怪,像没反编译出来。而且函数里的 languge2竟然没有用到。
上 pycads ,得到内容如下:
main.pyc (Python 3.12)
[Code]
File Name: main.py
Object Name: <module>
Qualified Name: <module>
Arg Count: 0
Pos Only Arg Count: 0
KW Only Arg Count: 0
Stack Size: 2
Flags: 0x00000000
[Names]
'verify'
'main'
'__name__'
[Locals+Names]
[Constants]
[Code]
File Name: main.py
Object Name: verify
Qualified Name: verify
Arg Count: 3
Pos Only Arg Count: 0
KW Only Arg Count: 0
Stack Size: 9
Flags: 0x00000003 (CO_OPTIMIZED | CO_NEWLOCALS)
[Names]
'range'
'reverse'
'len'
[Locals+Names]
'o0000'
'len2'
'language2'
'enc'
'i'
'ii'
'iii'
'iiii'
[Constants]
None
(
25
85
88
62
105
93
110
124
1
97
46
47
75
5
116
48
2
25
)
1
False
True
[Disassembly]
0 RESUME 0
2 BUILD_LIST 0
4 LOAD_CONST 1: (25, 85, 88, 62, 105, 93, 110, 124, 1, 97, 46, 47, 75, 5, 116, 48, 2, 25)
6 LIST_EXTEND 1
8 STORE_FAST 3: enc
10 LOAD_GLOBAL 1: NULL + range
20 LOAD_FAST 1: len2
22 LOAD_CONST 2: 1
24 BINARY_OP 10 (-)
28 CALL 1
36 GET_ITER
38 FOR_ITER 19 (to 78)
42 STORE_FAST 4: i
44 LOAD_FAST 0: o0000
46 LOAD_FAST 4: i
48 BINARY_SUBSCR
52 LOAD_FAST 0: o0000
54 LOAD_FAST 4: i
56 LOAD_CONST 2: 1
58 BINARY_OP 0 (+)
62 BINARY_SUBSCR
66 BINARY_OP 12 (^)
70 LOAD_FAST 0: o0000
72 LOAD_FAST 4: i
74 STORE_SUBSCR
78 JUMP_BACKWARD 21 (to 38)
80 END_FOR
82 LOAD_FAST 0: o0000
84 LOAD_ATTR 3: reverse
104 CALL 0
112 POP_TOP
114 LOAD_GLOBAL 1: NULL + range
124 LOAD_FAST 1: len2
126 LOAD_CONST 2: 1
128 BINARY_OP 10 (-)
132 CALL 1
140 GET_ITER
142 FOR_ITER 19 (to 182)
146 STORE_FAST 5: ii
148 LOAD_FAST 0: o0000
150 LOAD_FAST 5: ii
152 BINARY_SUBSCR
156 LOAD_FAST 0: o0000
158 LOAD_FAST 5: ii
160 LOAD_CONST 2: 1
162 BINARY_OP 0 (+)
166 BINARY_SUBSCR
170 BINARY_OP 12 (^)
174 LOAD_FAST 0: o0000
176 LOAD_FAST 5: ii
178 STORE_SUBSCR
182 JUMP_BACKWARD 21 (to 142)
184 END_FOR
186 LOAD_GLOBAL 1: NULL + range
196 LOAD_FAST 1: len2
198 CALL 1
206 GET_ITER
208 FOR_ITER 30 (to 270)
212 STORE_FAST 6: iii
214 LOAD_FAST 0: o0000
216 LOAD_FAST 6: iii
218 COPY 2
220 COPY 2
222 BINARY_SUBSCR
226 LOAD_FAST 2: language2
228 LOAD_FAST 6: iii
230 LOAD_GLOBAL 5: NULL + len
240 LOAD_FAST 2: language2
242 CALL 1
250 BINARY_OP 6 (%)
254 BINARY_SUBSCR
258 BINARY_OP 25 (^=)
262 SWAP 3
264 SWAP 2
266 STORE_SUBSCR
270 JUMP_BACKWARD 32 (to 208)
272 END_FOR
274 LOAD_GLOBAL 1: NULL + range
284 LOAD_FAST 1: len2
286 CALL 1
294 GET_ITER
296 FOR_ITER 15 (to 328)
300 STORE_FAST 7: iiii
302 LOAD_FAST 0: o0000
304 LOAD_FAST 7: iiii
306 BINARY_SUBSCR
310 LOAD_FAST 3: enc
312 LOAD_FAST 7: iiii
314 BINARY_SUBSCR
318 COMPARE_OP 55 (!=)
322 POP_JUMP_IF_TRUE 1 (to 326)
324 JUMP_BACKWARD 15 (to 296)
326 POP_TOP
328 RETURN_CONST 3: False
330 END_FOR
332 RETURN_CONST 4: True
[Exception Table]
[Code]
File Name: main.py
Object Name: main
Qualified Name: main
Arg Count: 0
Pos Only Arg Count: 0
KW Only Arg Count: 0
Stack Size: 6
Flags: 0x00000003 (CO_OPTIMIZED | CO_NEWLOCALS)
[Names]
'print'
'input'
'len'
'ord'
'verify'
[Locals+Names]
'language'
'OO000'
'language1'
'OOO00'
'len1'
'itemm'
'item'
[Constants]
None
'Y0u k0nw what is the best language in the world???'
''
'is_.py_not_py'
'goOo0od!!!'
'So,th3n???'
True
'very goOo0od!!!'
'What a pity!!!T_T'
'NoOo0o!!!T_T'
[Disassembly]
0 RESUME 0
2 LOAD_GLOBAL 1: NULL + print
12 LOAD_CONST 1: 'Y0u k0nw what is the best language in the world???'
14 CALL 1
22 POP_TOP
24 LOAD_GLOBAL 3: NULL + input
34 CALL 0
42 STORE_FAST 0: language
44 BUILD_LIST 0
46 STORE_FAST 1: OO000
48 BUILD_LIST 0
50 STORE_FAST 2: language1
52 LOAD_CONST 2: ''
54 STORE_FAST 3: OOO00
56 LOAD_FAST 0: language
58 LOAD_CONST 3: 'is_.py_not_py'
60 COMPARE_OP 40 (==)
64 POP_JUMP_IF_FALSE 130 (to 326)
66 LOAD_GLOBAL 1: NULL + print
76 LOAD_CONST 4: 'goOo0od!!!'
78 CALL 1
86 POP_TOP
88 LOAD_GLOBAL 1: NULL + print
98 LOAD_CONST 5: 'So,th3n???'
100 CALL 1
108 POP_TOP
110 LOAD_GLOBAL 3: NULL + input
120 CALL 0
128 STORE_FAST 3: OOO00
130 LOAD_GLOBAL 5: NULL + len
140 LOAD_FAST 3: OOO00
142 CALL 1
150 STORE_FAST 4: len1
152 LOAD_FAST 0: language
154 GET_ITER
156 LOAD_FAST_AND_CLEAR 5: itemm
158 SWAP 2
160 BUILD_LIST 0
162 SWAP 2
164 FOR_ITER 13 (to 192)
168 STORE_FAST 5: itemm
170 LOAD_GLOBAL 7: NULL + ord
180 LOAD_FAST 5: itemm
182 CALL 1
190 LIST_APPEND 2
192 JUMP_BACKWARD 15 (to 164)
194 END_FOR
196 STORE_FAST 2: language1
198 STORE_FAST 5: itemm
200 LOAD_FAST 3: OOO00
202 GET_ITER
204 LOAD_FAST_AND_CLEAR 6: item
206 SWAP 2
208 BUILD_LIST 0
210 SWAP 2
212 FOR_ITER 13 (to 240)
216 STORE_FAST 6: item
218 LOAD_GLOBAL 7: NULL + ord
228 LOAD_FAST 6: item
230 CALL 1
238 LIST_APPEND 2
240 JUMP_BACKWARD 15 (to 212)
242 END_FOR
244 STORE_FAST 1: OO000
246 STORE_FAST 6: item
248 LOAD_GLOBAL 9: NULL + verify
258 LOAD_FAST 1: OO000
260 LOAD_FAST 4: len1
262 LOAD_FAST 2: language1
264 CALL 3
272 LOAD_CONST 6: True
274 IS_OP 0 (is)
276 POP_JUMP_IF_FALSE 12 (to 302)
278 LOAD_GLOBAL 1: NULL + print
288 LOAD_CONST 7: 'very goOo0od!!!'
290 CALL 1
298 POP_TOP
300 RETURN_CONST 0: None
302 LOAD_GLOBAL 1: NULL + print
312 LOAD_CONST 8: 'What a pity!!!T_T'
314 CALL 1
322 POP_TOP
324 RETURN_CONST 0: None
326 LOAD_GLOBAL 1: NULL + print
336 LOAD_CONST 9: 'NoOo0o!!!T_T'
338 CALL 1
346 POP_TOP
348 RETURN_CONST 0: None
350 SWAP 2
352 POP_TOP
354 SWAP 2
356 STORE_FAST 5: itemm
358 RERAISE 0
360 SWAP 2
362 POP_TOP
364 SWAP 2
366 STORE_FAST 6: item
368 RERAISE 0
[Exception Table]
160 to 196 -> 350 [2]
208 to 244 -> 360 [2]
'__main__'
None
[Disassembly]
0 RESUME 0
2 LOAD_CONST 0: <CODE> verify
4 MAKE_FUNCTION 0
6 STORE_NAME 0: verify
8 LOAD_CONST 1: <CODE> main
10 MAKE_FUNCTION 0
12 STORE_NAME 1: main
14 LOAD_NAME 2: __name__
16 LOAD_CONST 2: '__main__'
18 COMPARE_OP 40 (==)
22 POP_JUMP_IF_FALSE 8 (to 40)
24 PUSH_NULL
26 LOAD_NAME 1: main
28 CALL 0
36 POP_TOP
38 RETURN_CONST 3: None
40 RETURN_CONST 3: None
[Exception Table]
唉,需要读汇编了。
拿到 language1

拿到language2

解题脚本:
#include<bits/stdc++.h>
using namespace std;
char enc[] = {25,85,88,62,105,93,110,124,1,97,46,47,75,5,116,48,2,25,0};
char* lang1 = "is_.py_not_py";
void reverseStr(char* s, int n) {
for (int i = 0, j = n - 1; i < j; ++i, --j) {
char temp = s[i];
s[i] = s[j];
s[j] = temp;
}
}
signed main(){
int len=18;
for (int i=0;i<len;++i) {
enc[i]^=lang1[i%13];
}
for (int i=len-2;i>=0;--i) {
enc[i]^=enc[i+1];
}
reverseStr(enc, 18);
for (int i=len-2;i>=0;--i) {
enc[i]^=enc[i+1];
}
for (int i=0;i<len;++i) {
cout<<enc[i];
}
return 0;
}
MIPS
题目提示:MIPS指令集编译为可执行文件即可正常分析,魔改RC4
知识盲区了。
根据新生赛的教训,先bdfs一下题目名字。
MIPS 是一种CPU性能指标?
MIPS架构。。。?
先放过这个题等等再回来做。
桀桀桀
抽象神人题目,成功卡了我近两周,但是道好题。
扔进ida后看到一堆函数,直接 shift+F12 查字符串。 果然一堆互动。

有理由怀疑这题是比赛的前几道题,根据字符串追踪到主函数。
稍微改改变量名,得到如下代码:
点击查看代码
__int64 sub_411E40()
{
int v0; // edx
__int64 v2; // [esp-8h] [ebp-17Ch]
char v3; // [esp+0h] [ebp-174h]
char v4; // [esp+0h] [ebp-174h]
int i; // [esp+D0h] [ebp-A4h]
int j; // [esp+D0h] [ebp-A4h]
size_t k; // [esp+D0h] [ebp-A4h]
unsigned int Seed; // [esp+F4h] [ebp-80h]
int v9; // [esp+100h] [ebp-74h]
char a[4]; // [esp+10Ch] [ebp-68h] BYREF
__int16 v11; // [esp+110h] [ebp-64h]
char Str[4]; // [esp+11Ch] [ebp-58h] BYREF
int v13; // [esp+120h] [ebp-54h]
int v14; // [esp+124h] [ebp-50h]
int v15; // [esp+128h] [ebp-4Ch]
int v16; // [esp+12Ch] [ebp-48h]
int v17; // [esp+130h] [ebp-44h]
int v18; // [esp+134h] [ebp-40h]
int v19; // [esp+138h] [ebp-3Ch]
char v20; // [esp+13Ch] [ebp-38h]
_DWORD v21[10]; // [esp+148h] [ebp-2Ch]
int savedregs; // [esp+174h] [ebp+0h] BYREF
sub_411357(&unk_41D0A5);
v21[0] = 2063192753;
v21[1] = 592530563;
v21[2] = -1096176201;
v21[3] = 371522280;
v21[4] = -683475309;
v21[5] = -145612880;
v21[6] = -394232486;
v21[7] = 1866595368;
v21[8] = 0;
*(_DWORD *)Str = 0;
v13 = 0;
v14 = 0;
v15 = 0;
v16 = 0;
v17 = 0;
v18 = 0;
v19 = 0;
v20 = 0;
*(_DWORD *)a = 0;
v11 = 0;
Seed = 0;
puts("Hello Ctfer~~~~");
putchar();
puts("Welcome to ISCTF 2024!!!");
putchar();
puts("This is a easy challenge");
putchar();
puts("Just do it");
putchar();
puts("Now!Please send me what you think is the solution to the equation below.");
putchar();
scanf("%5s", (char)a);
if ( (a[1] * a[0]) << 10 == 6204416
&& (a[1] + 1) * (a[1] - 1) == 6888
&& a[2] + a[3] == 151
&& (a[2] << 11) - a[3] == 137132
&& a[3] + a[2] + a[1] + a[0] + (char)v11 == 377 )
{
puts("Yeah Yeah Yeah");
putchar();
puts("You are a qualified college student");
putchar();
puts("Something has changed........");
putchar();
for ( i = 0; i < 5; ++i )
Seed += a[i] ^ 0xA1;
sub_4110E1("Now,Give me what you think is right:", v3);
scanf("%32s", (char)Str);
j_strlen(Str);
for ( j = 0; j < 4; ++j )
sub_4111B3(&Str[8 * j], &dword_41B000);
srand(Seed);
putchar();
rand();
v9 = putchar();
sub_41128A(Str, v9);
for ( k = 0; k < j_strlen(Str) >> 2; ++k )
{
if ( !sub_41100A(*(_DWORD *)&Str[4 * k], v21[k]) )
{
sub_4110E1("Emmmmmm,It is not right........see you again~~~", v4);
goto LABEL_19;
}
}
puts("Good!!!");
putchar();
puts("Man! You've completed the challenge");
putchar();
puts("Welcome again to ISCTF2024");
putchar();
}
else
{
puts("Nononononono~~~");
putchar();
puts("You look like something is wrong");
putchar();
}
LABEL_19:
sub_411203(&savedregs, &dword_4122DC, 0, v0);
return v2;
}
a 是个长度为 5 的字符串。(合理猜测是 ISCTF )
发现他让我们先解方程求出 a 。

(a[1]+1)*(a[1]-1) == 6888 ----> a[1] == 83 ---> a[1] 为 S
(a[1] *a[0]) <<10 == 6204416 -----> a[0]==73 ---> a[0] 为 I
还有 a[2],a[3] 的初中二元一次方程组,阿巴阿巴阿巴。
a[2] = 67 -----> C
a[3] = 84 ----T
V11=70 ----- > F
所以 a 为 ISCTF
接着计算 seed

支持C++脚本喵
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int a[5]={73,83,67,84,70};
signed main(){
int seed=0;
for (int i=0;i<5;++i) {
seed+=a[i] ^ 0xA1 ;
}
cout<<seed;
return 0;
}
seed == 1176
再往后看,重头戏来了,核心的加密部分。

先让我们输入一个长度为 32 的字符串 str
然后
for ( j = 0; j < 4; ++j )
sub_4111B3(&Str[8 * j], &dword_41B000);`
点进去后却看到了这个

再往后看。

这个函数,点进去后发现这函数是判断相等的。

合理推测之前现实 JUMPOUT 的函数才是加密的大头。
应该是使用了花指令。
TAB 一下看汇编果然是花指令

采用 jz jnz 形式的花指令,NOP掉之后重构函数就好了。
(有的版本的ida没法NOP,很难绷,找学长重新下了个ida)
重构函数之后也终于拿到了加密函数,
int __usercall JUMPOUT_w_0@<eax>(int a1@<edi>, unsigned int *a2, int *a3)
{
int v4; // [esp+D0h] [ebp-68h]
int v5; // [esp+DCh] [ebp-5Ch]
int v6; // [esp+E8h] [ebp-50h]
int v7; // [esp+F4h] [ebp-44h]
int i; // [esp+100h] [ebp-38h]
int n1131796; // [esp+10Ch] [ebp-2Ch]
int v10; // [esp+118h] [ebp-20h]
unsigned int v11; // [esp+124h] [ebp-14h]
unsigned int v12; // [esp+130h] [ebp-8h]
v12 = *a2;
v11 = a2[1];
v10 = 0;
n1131796 = -1640531527;
MEMORY[8] = a1;
v7 = *a3;
v6 = a3[1];
v5 = a3[2];
v4 = a3[3];
for ( i = 0; i < 32; ++i )
{
v10 += n1131796;
v12 += (v6 + (v11 >> 5)) ^ (v10 + v11) ^ (v7 + 16 * v11);
v11 += (v4 + (v12 >> 5)) ^ (v10 + v12) ^ (v5 + 16 * v12);
if ( i == 15 )
{
v7 = a3[2];
v6 = a3[3];
v5 = *a3;
v4 = a3[1];
}
else if ( i == 23 )
{
n1131796 = 1131796;
}
}
*a2 = v12;
a2[1] = v11;
return 4;
}
TEA加密。
解密函数如下:
void TEA_decode(uint32_t* v){
unsigned int sum = 8*0x114514 + 0x9E3779B9 * 24;
uint32_t key[4] = {0x6fc6, 0x69d3, 0x68d5, 0x73cc};
uint32_t key1,key2,key3,key4;
key1 = key[2];
key2 = key[3];
key3 = key[0];
key4 = key[1];
uint32_t delta = 0x114514;
for(int i = 31; i >= 0; i--){
if(i == 23){
delta = 0x9e3779b9;
}
if(i == 15){
key1 = key[0];
key2 = key[1];
key3 = key[2];
key4 = key[3];
}
v[1] -= ((v[0] << 4) + key3) ^ (v[0] + sum) ^ ((v[0] >> 5) + key4);
v[0] -= ((v[1] << 4) + key1) ^ (v[1] + sum) ^ ((v[1] >> 5) + key2);
sum -= delta; //阴间TEA
}
}
TEA 加密完后用之前求到的 seed, rand() 出一个随机值,不知道拿来干什么了。
然后将加密后的 flag 传给了j_strlen_w ()这个函数。
最开始没看懂这个函数在干什么,现在来看这玩意儿就是楚生诈骗函数。
点进去这个函数,看到如下:

再点进去

再点

再点

难道这真的是个没什么用的垃圾函数吗?
整个题目最阴间的部分《骗过ida》
我们在第二层return j_strlen(Str); 这个地方查看汇编

因为需要call函数了, esp 的值加 4 ,
然后将 eax 对应的值给了地址 [ebp+var_20]
这里 eax 的值其实就是 strlen 的值。
call 的这个函数明显有问题:

我们需要动调一下查看这个 call 的函数。
先在 add esp,4 这个地方设个断点。
开始动调。
读汇编发现有一串汇编 ida没有认出这是个函数,所以之前不显示,需要手动构造函数。
得到伪代码:
int i = 0;
int v1 = 0;
while(i < strlen(flag)){
if(i % 2){
v1 = 0x3C ^ flag[i];
}
else{
v1 = 0x26 ^ flag[i];
}
flag[i] = v1;
i++;
}
这个题终于可以结束了,跟之前的脚本合一起求解flag为
ISCTF{y0U_kN0w_RAnD0m_ANd_73a!!}

浙公网安备 33010602011771号