[逆向学习笔记]上海全国大学生网络安全邀请赛-pluzz

0x00前言

这回上海大学生网络安全邀请赛就看了这题,实在是没空。不过这次我作为跑龙套的re手思路最清晰的一回了,值得记录下来。

0x01思路

作为汇编基础不扎实的跑龙套re手,拿到题目当然是仍经IDA来F5一波。

main函数的逻辑很简单,if判断为真就在sub_401BA0输出flag。通过OD动调得知v11就是通过输入的v9作为种子什么的,经过sub_401BA0函数获得uuid。

if里第一个函数简单的判断输入长度是否为16位,然后是一个while循环,我一开始navie的认为只能输入a-f的字符,后来才发现输入1-9的时候类型转无符号型,结果也会比&大,以后遇到类型转换一点要注意了。

第二个函数没看懂是什么操作,但谷歌了一下%02X格式化输出位两位16的意思,动调的结果也确实如此。值得注意的是sub_401B60的两参数是传引用进去的,在执行完这个函数之后第一个参数的值发生了改变,所以一个函数除了看返回值外还要看它的参数是否为指针或者引用。太菜了现在才领悟到这个道理。

第三个函数IDA的伪代码也没看懂是什么操作,好在这个函数在这之前与输入的字符串关。第一个参数为取指针,直接动调一下看结果,出来了一串好像是int型的东西,后一个函数会用到先dump下来。

第四个函数这回看懂了,就是将转换后的16进制与上一个函数的结果的第i位和前i位的和进行异或,先把要异或的数dump下来备用。返回值恒为真,先放一边。

第五个函数尤为关键,跟第一个函数一样决定了能不能夺得flag。传入8个字节异或后的结果,每字节不能打大于9且要满足7个判断语句。这里按照它的逻辑把八位数爆出来就行了,但是一开始用python写的脚本一堆问题,整得我在比赛结束前没做出来。最后用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
#include<iostream>
#include<string>
#include<ios>
using namespace std;

long long int dword_40F020 = 0x8A;
long long int dword_40F024 = 0x01A1;
long long int dword_40F028 = 0x012A;
long long int dword_40F02C = 0x0269;
long long int dword_40F030 = 0x0209;
long long int dword_40F034 = 0x68;
long long int dword_40F038 = 0x039F;
long long int dword_40F03C = 0x02C8;
long long int dword_40F040 = 0x00FF;

int test(int v);
void flag(int v);

int main()
{
int v = 0;
int i;
for (i = 10000000; i <= 99999999; ++i)
{
int j = i;
while (j != 0)
{
if (test(j % 10) == 1)
{
cout << i << endl;
flag(i);
return 0;
}
j /= 10;
}
dword_40F020 = 0x8A;
dword_40F024 = 0x01A1;
dword_40F028 = 0x012A;
dword_40F02C = 0x0269;
dword_40F030 = 0x0209;
dword_40F034 = 0x68;
dword_40F038 = 0x039F;
dword_40F03C = 0x02C8;
dword_40F040 = 0x00FF;
}

return 0;
}

int test(int v)
{
int result = 0;
switch (v)
{
case 0:
dword_40F028 &= dword_40F038;
dword_40F02C *= dword_40F028;
goto LABEL_4;
case 1:
if (!dword_40F02C)
goto LABEL_6;
dword_40F028 /= dword_40F02C;
dword_40F024 += dword_40F034;
goto LABEL_4;
case 2:
dword_40F030 ^= dword_40F034;
dword_40F03C += dword_40F020;
goto LABEL_4;
case 3:
dword_40F03C -= dword_40F030;
dword_40F030 &= dword_40F024;
goto LABEL_4;
case 4:
dword_40F034 *= dword_40F020;
dword_40F02C -= dword_40F038;
goto LABEL_4;
case 5:
dword_40F020 ^= dword_40F02C;
dword_40F038 -= dword_40F03C;
goto LABEL_4;
case 6:
if (!dword_40F03C)
goto LABEL_6;
dword_40F034 |= dword_40F024 / dword_40F03C;
dword_40F024 /= dword_40F03C;
goto LABEL_4;
case 7:
dword_40F038 += dword_40F028;
dword_40F034 |= dword_40F024;
goto LABEL_4;
case 8:
dword_40F020 *= dword_40F02C;
dword_40F030 -= dword_40F03C;
goto LABEL_4;
case 9:
dword_40F028 += dword_40F034;
dword_40F02C ^= dword_40F030;
LABEL_4:
result = ((dword_40F038 == 231) + (dword_40F034 == 14456)+ (dword_40F030 == 14961)+ (dword_40F02C == -13264)+ (dword_40F028 == 16)+ (dword_40F024 == 104)+ (dword_40F020 == -951) == 7);
if (dword_40F03C != -239)
goto LABEL_6;
break;
default:
LABEL_6:
return 0;
break;
}
return result;
}

void flag(int v)
{
int a[] = { 0x7C,0xAB,0x2D,0x91,0x2F,0x98,0xED,0xA9 };
int j = 0;
while (v)
{
for (int i = 0; i <= 256; ++i)
{
if (v % 10 == (i ^ a[j]))
{
printf("%02x", i);
break;
}
}
v /= 10;
j += 1;
}
}

运行结果:

27059416
7aaa29982a98eaab

输入到题目文件中:

Plz solve the puzzle:
7aaa29982a98eaab
Congrats!
flag{5cb92582-66a8-e5b7-d3bf-3b99df8ac7f0}

0X02总结

  • 应当注意类型装换,以及转换后的字节大小
  • 除了函数的返回值,还可以从参数的引用或指针获取结果
  • 可以通过动态调试函数的结果得知函数的功能

评论

Your browser is out-of-date!

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

×