CGCTF-re部分题解

CG-CTF是南邮的平台,题目都很不错,这里记录一下部分题解决题目的思路和方法

Hello,RE!

打开程序,发现输入flag,用OD打开,注意到是将输入的字符串和flag直接进行比较,所以单步调试直接找到flag
Hello,RE!-1

ReadAsm2

这题考的是直接读汇编的能力,代码贴在下面

1
2
3
4
5
6
7
8
9
int main(int argc, char const *argv[])
{
char input[] = {0x0, 0x67, 0x6e, 0x62, 0x63, 0x7e, 0x74, 0x62, 0x69, 0x6d,
0x55, 0x6a, 0x7f, 0x60, 0x51, 0x66, 0x63, 0x4e, 0x66, 0x7b,
0x71, 0x4a, 0x74, 0x76, 0x6b, 0x70, 0x79, 0x66 , 0x1c};
func(input, 28);
printf("%s\n",input+1);
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
00000000004004e6 <func>:
4004e6: 55 push rbp
4004e7: 48 89 e5 mov rbp,rsp
4004ea: 48 89 7d e8 mov QWORD PTR [rbp-0x18],rdi
4004ee: 89 75 e4 mov DWORD PTR [rbp-0x1c],esi
4004f1: c7 45 fc 01 00 00 00 mov DWORD PTR [rbp-0x4],0x1
4004f8: eb 28 jmp 400522 <func+0x3c>
4004fa: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
4004fd: 48 63 d0 movsxd rdx,eax
400500: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18]
400504: 48 01 d0 add rax,rdx
400507: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
40050a: 48 63 ca movsxd rcx,edx
40050d: 48 8b 55 e8 mov rdx,QWORD PTR [rbp-0x18]
400511: 48 01 ca add rdx,rcx
400514: 0f b6 0a movzx ecx,BYTE PTR [rdx]
400517: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
40051a: 31 ca xor edx,ecx
40051c: 88 10 mov BYTE PTR [rax],dl
40051e: 83 45 fc 01 add DWORD PTR [rbp-0x4],0x1
400522: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
400525: 3b 45 e4 cmp eax,DWORD PTR [rbp-0x1c]
400528: 7e d0 jle 4004fa <func+0x14>
40052a: 90 nop
40052b: 5d pop rbp
40052c: c3 ret

发现这段汇编的作用就是实现input[i]^i,所以实现上面代码输出得到flag

1
2
3
4
5
6
7
8
9
10
11
s = [0x0, 0x67, 0x6e, 0x62, 0x63, 0x7e, 0x74, 0x62, 0x69, 0x6d,
0x55, 0x6a, 0x7f, 0x60, 0x51, 0x66, 0x63, 0x4e, 0x66, 0x7b,
0x71, 0x4a, 0x74, 0x76, 0x6b, 0x70, 0x79, 0x66, 0x1c]
for i in range(len(s)):
s[i] ^= i

out = ''
for i in range(len(s)):
out += chr(s[i])

print(out)

输出flag为:

1
flag{read_asm_is_the_basic}

Py交易

本题是python的反编译,使用在线工具或uncompyle6将pyc文件反编译,得到代码

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


def encode(message):
s = ''
for i in message:
x = ord(i) ^ 32
x = x + 16
s += chr(x)

return base64.b64encode(s)


correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
flag = ''
print 'Input flag:'
flag = raw_input()
if encode(flag) == correct:
print 'correct'
else:
print 'wrong'
import base64


def encode(message):
s = ''
for i in message:
x = ord(i) ^ 32
x = x + 16
s += chr(x)

return base64.b64encode(s)


correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
flag = ''
print 'Input flag:'
flag = raw_input()
if encode(flag) == correct:
print 'correct'
else:
print 'wrong'

研究代码,发现我们只要将correct逆向解密,就可以得到正确的flag,所以得到如下exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import base64

correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
correct = str(base64.b64decode(correct)) # ^SdVkT#S ]`Y\\!^)\x8f\x80ism
# print(correct)
correct = '^SdVkT#S ]`Y\\!^)\x8f\x80ism'
flag = ''

for s in correct:
x = ord(s)
x -= 16
i = chr(x ^ 32)
flag += i
print(flag)

输出flag为:

1
nctf{d3c0mpil1n9_PyC}

WxyVM

下载下来不知道是什么文件,记事本打开,开头ELF,所以直接拖进IDA反编译,main函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char v4; // [rsp+Bh] [rbp-5h]
signed int i; // [rsp+Ch] [rbp-4h]

puts("[WxyVM 0.0.1]");
puts("input your flag:");
scanf("%s", &byte_604B80);
v4 = 1;
sub_4005B6();
if ( strlen(&byte_604B80) != 24 )
v4 = 0;
for ( i = 0; i <= 23; ++i )
{
if ( *(&byte_604B80 + i) != dword_601060[i] )
v4 = 0;
}
if ( v4 )
puts("correct");
else
puts("wrong");
return 0LL;
}

一开始认为需要输入一个24位的flag,然后和dword_601060每一位都相等即可,但是中间的sub_4005B6()函数对我们输入的flag还进行了一些变换,所以需要把dword_601060对应的进行反变换才能得到应该输入的正确的flag

sub_4005B6()函数如下:

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
__int64 sub_4005B6()
{
unsigned int v0; // ST04_4
__int64 result; // rax
signed int i; // [rsp+0h] [rbp-10h]
char v3; // [rsp+8h] [rbp-8h]

for ( i = 0; i <= 14999; i += 3 )
{
v0 = byte_6010C0[i];
v3 = byte_6010C0[i + 2];
result = v0;
switch ( v0 )
{
case 1u:
result = byte_6010C0[i + 1];
*(&byte_604B80 + result) += v3;
break;
case 2u:
result = byte_6010C0[i + 1];
*(&byte_604B80 + result) -= v3;
break;
case 3u:
result = byte_6010C0[i + 1];
*(&byte_604B80 + result) ^= v3;
break;
case 4u:
result = byte_6010C0[i + 1];
*(&byte_604B80 + result) *= v3;
break;
case 5u:
result = byte_6010C0[i + 1];
*(&byte_604B80 + result) ^= *(&byte_604B80 + byte_6010C0[i + 2]);
break;
default:
continue;
}
}
return result;
}

用IDA将byte_6010C0导出,用python进行逆向处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
s = [0xc4, 0x34, 0x22, 0xb1, 0xd3, 0x11, 0x97, 0x7, 0xdb, 0x37, 0xc4, 0x6, 0x1d, 0xfc, 0x5b, 0xed, 0x98, 0xdf, 0x94,0xd8, 0xb3, 0x84, 0xcc, 0x8]
# dword与char比较,只取最后一位一个字节

with open('export_results.txt') as f: #文件里是导出的byte_6010C0
t = f.read().split(' ')
# print(len(t))
for i in range(5000):
v0 = int(t[3 * (4999 - i)], 16)
v3 = int(t[3 * (4999 - i) + 2], 16)
result = int(t[3 * (4999 - i) + 1], 16)
if v0 == 1:
s[result] -= v3
elif v0 == 2:
s[result] += v3
elif v0 == 3:
s[result] ^= v3
elif v0 == 4:
s[result] /= v3
elif v0 == 5:
s[result] ^= s[v3]
else:
continue
print(''.join([str(chr(int(i) % 128)) for i in s]))

这里有两件事需要注意,第一是dword只需要取最后一个字节,第二是逆运算时要从最后三位向前计算,最后输出flag:

1
nctf{Embr4ce_Vm_j0in_R3}

maze

极其无聊的迷宫题……

找到地图、起点终点和上下左右分别对应的键就可以了

1
nctf{o0oo00O000oooo..OO}

WxyVM2

拖进IDA反编译,发现函数非常大,操作特别多,但是仔细看发现最后要验证的都是byte类型,所有对dword的操作都是无效的,所以把所有操作拷贝到文件(export_results.txt)中,先进行过滤操作,过滤出需要的操作,最后的验证过程如下:

1
2
3
4
5
6
7
8
9
10
for ( i = 0; i <= 24; ++i )
{
if ( *(&byte_694100 + i) != dword_694060[i] )
v4 = 0;
}
if ( v4 )
puts("correct");
else
puts("wrong");
return 0LL;

所以操作之后的数组和dword_694060数组的每个数的最后一位相同,将数组导出,进行逆运算得到原来输入的flag,python代码如下:

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

with open('export_results.txt') as f:
s = ''
a = f.readline()
while a:
if re.match(r'(.*)byte_6941(.*);', a):
s += a
a = f.readline()
with open('s.txt', 'w+') as ff:
s = s.split('\n')
n = len(s)
# print(len(s))
# print(s)
for i in range(n):
ff.write(s[n - 1 - i].strip() + '\n')

ss = [0xC0, 0x85, 0xF9, 0x6C, 0xE2, 0x14, 0xBB, 0xe4, 0xd, 0x59, 0x1c, 0x23, 0x88, 0x6e, 0x9b, 0xca, 0xba, 0x5c, 0x37,
0xfff, 0x48, 0xd8, 0x1f, 0xab, 0xa5]

with open('s.txt') as f:
a = f.readline()
while a:
# print(re.match(r'(.*)byte_6941(.*);', a).groups())
if not re.match(r'(.*)byte_6941(.*);', a).group(1).strip():
s = re.match(r'(.*)byte_6941(.*);', a).group(2).split(' ')
# print(s)
i = int(s[0], 16)
# print(i)
i1 = str(ss[i])
if s[1] == '+=':
sub = '-'
elif s[1] == '-=':
sub = '+'
else:
sub = '^'

if re.match(r'(.*)u', s[2]):
s2 = re.match(r'(.*)u', s[2]).group(1)
else:
s2 = s[2]
exp = i1 + sub + s2
ss[i] = eval(exp) % 128
else:
s = re.match(r'(.*)byte_6941(.*);', a).group(2).split(' ')
# print(s)
i = int(s[0], 16)
# print(i)
i1 = str(ss[i])
if re.match(r'(.*)byte_6941(.*);', a).group(1).strip() == '++':
sub = '-'
if re.match(r'(.*)byte_6941(.*);', a).group(1).strip() == '--':
sub = '+'
exp = i1 + sub + '1'
ss[i] = eval(exp) % 128
a = f.readline()
# print(ss)
print(''.join([str(chr(i % 128)) for i in ss]))

这里要注意一点,python没有++和–操作,所以直接++和–的传唤算不出正确值,在这里卡了一会儿才发现问题。

运行程序输出flag:

1
nctf{th3_vM_w1th0ut_dAta}

你大概需要一个优秀的mac

这是一个macos程序,没法直接打开,所以还是拖进IDA反编译,发现需要输入一个56位的flag,然后经过几个函数处理之后与一个数组进行比较,相同则输入的是正确的flag,所以还是一样进行一个逆运算

反编译出来的代码如下:

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
  scanf("%s", &v4);
if ( strlen(&v4) != 56 )
{
puts((const char *)err);
exit(0);
}
__strcpy_chk(input, &v4, 100LL);
func1((__int64)input);
xfun1();
xfun2();
xfun3();
xfun4();
xfun5();
check();
//func1
for ( i = 0; i < 57; ++i ) //这里应该是56?
{
*(_BYTE *)(a1 + i) ^= 0xDEu;
}
//xfun1
for ( i = 0; i < 10; ++i )
{
input[i] ^= 0xADu;
}
//xfun2
for ( i = 0; i < 10; ++i )
{
input[i + 10] ^= 0xBEu;
}
//xfun3
for ( i = 0; i < 10; ++i )
{
input[i + 20] ^= 0xEFu;
}
//xfun4
for ( i = 0; i < 10; ++i )
{
input[i + 30] ^= 0xABu;
}
//xfun5
for ( i = 0; i < 16; ++i )
{
input[i + 40] ^= 0xEFu;
}
//check
memcpy(v2, &byte_100000ED0, 0xE0uLL);
for ( i = 0; i < 56; ++i )
{
if ( (char)input[i] != v2[i] )
{
puts((const char *)err);
exit(0);
}
}

所以将byte_100000ED0处的数组导出,进行逆运算,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
s = [0x15, 0x1F, 0x12, 0x14, 0x08, 0x3A, 0x46, 0x2C, 0x07, 0x1B, 0x51, 0x13, 0x3F, 0x57, 0x08, 0x05, 0x3F, 0x30, 0x32,
0x51, 0x52, 0x02, 0x6E, 0x78, 0x16, 0x7C, 0x6E, 0x61, 0x70, 0x48, 0x1C, 0x3B, 0x32, 0x2A, 0x13, 0x45, 0x07, 0x2A,
0x18, 0x0C, 0x6E, 0x41, 0x70, 0x04, 0x06, 0x6E, 0x5C, 0x00, 0x42, 0x45, 0x70, 0x5A, 0x02, 0x04, 0x0E, 0x4C]
for i in range(10):
s[i]^=0xAD
for i in range(10,20):
s[i]^=0xBE
for i in range(20,30):
s[i]^=0xEF
for i in range(30,40):
s[i]^=0xAB
for i in range(40,56):
s[i]^=0xEF
for i in range(56):
s[i]^=0xDE
print(''.join([str(chr(i%128)) for i in s]))

运行程序输出flag:

1
flag{I5_th1s_7he_PR1c3_I'M_PAyiNG_f0r_my_pA57_m1stAk35?}
BUUCTF-re部分题解 bugku-re部分题解

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×