【注意】最后更新于 May 2, 2021,文中内容可能已过时,请谨慎食用。
清明节的时候参加了一下西湖论剑杯,我主要做了 re。其实这次的 re 并不难,但是比赛的时候心态炸了,没能静下心来做题。不过我也好久没做题了,趁着这次比赛整理一下思路。
easyCpp
这是一道比较基本的 Cpp 逆向。
拖到 IDA 中看它的大概逻辑,处理后的 vector 和一个生成好的斐波那契数列进行比较。我们专注于它怎么处理输入就好啦。
稍微整理了一下逻辑,大概是这样的:
-
初始化输入的数组(input)和斐波那契数列
-
新建一个数列,我们可以称它为 global 数组。其中数据的初始化是这样的:
-
接下来它用 accumulate
函数做了一个对换的操作,其结果是倒转了 global
数组。
PS:这里我刚开始没搞懂是什么意思,这里分析一下还是蛮有意思的。STL 里的骚操作不要太多,我感觉我根本不会 C++。。
-
然后与之前初始化的斐波那契数列进行比较。
-
成功了,输出答案,这一段我就没分析了。
综上,输入为
1
|
987 -377 -610 -754 -843 -898 -932 -953 -966 -974 -979 -982 -984 -985 -986 -986
|
的时候答案正确,结果是:flag{987-377-843-953-979-985}
Testre
这是一道水题,我上数据段直接看到了 base64 和 base58 的 table。然后它还有一个 fake_secret_makes_you_annoyed
,和输入进行了轮异或,果然没什么用。它的比较字符串提出来再 base58 decode 就可以得到 base58_is_boring
。
当然了。。能够一眼看出来是十分重要的。可能需要总结一下常见加密/编码函数在逆向时出现的关键函数/关键字符串了。
Junk_Instruction
一道 MFC 的逆向,输入 flag 然后检测,会弹出窗口。
通过 Xspy 可以找到这里:
1
|
OnCommand: notifycode=0000 id=03e9,func= 0x00392420(Junk_Instruction.exe+ 0x002420 )
|
也就是弹窗函数的地址。我们在 IDA 中进入函数查看一下,地址就是 0x2420。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
if ( sub_402600(v2 + 16) )
{
sub_401BF0(&v6, v3);
v7 = 0;
v9 = 0;
sub_403DC0(&v6, 129, 0);
v6 = &CCorrect::`vftable';
v16 = 0;
sub_40484E(&v6);
v6 = &CCorrect::`vftable';
v16 = 2;
sub_407B3B(&v10);
LOBYTE(v16) = 1;
v8 = &CBrush::`vftable';
sub_401580(&v8);
}
|
然后能看到判断函数的地址是 0x2600。
接着进去看到一堆奇怪的东西。里面规定了输入的长度是 38。
但是代码加花了,静态编译基本上看不出什么东西来。此时可以把花指令 patch 掉。
仔细阅读汇编代码可以发现一些有趣的汇编,摘录如下:
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
|
.text:00402B6E call $+5
.text:00402B73 pop eax
.text:00402B74 mov [ebp+var_28], eax
.text:00402B77 call loc_402B7F
.text:00402B77 ; ---------------------------------------------------------------------------
.text:00402B7C db 0EAh, 0EBh, 9
.text:00402B7F ; ---------------------------------------------------------------------------
.text:00402B7F
.text:00402B7F loc_402B7F: ; CODE XREF: sub_402AF0+87↑j
.text:00402B7F pop ebx
.text:00402B80 inc ebx
.text:00402B81 push ebx
.text:00402B82 mov eax, 11111111h
.text:00402B87 retn
.text:00402B88 ; ---------------------------------------------------------------------------
.text:00402B88 call loc_402B94
.text:00402B8D mov ebx, 33333333h
.text:00402B92 jmp short loc_402BA1
.text:00402B94 ; ---------------------------------------------------------------------------
.text:00402B94
.text:00402B94 loc_402B94: ; CODE XREF: sub_402AF0+98↑p
.text:00402B94 mov ebx, 11111111h
.text:00402B99 pop ebx
.text:00402B9A mov ebx, offset loc_402BA1
.text:00402B9F push ebx
.text:00402BA0 retn
.text:00402BA1 ; ---------------------------------------------------------------------------
.text:00402BA1
.text:00402BA1 loc_402BA1: ; CODE XREF: sub_402AF0+A2↑j
.text:00402BA1 ; DATA XREF: sub_402AF0+AA↑o
.text:00402BA1 mov ebx, 22222222h
|
基本上每一个带有 JUMPOUT()
的函数都会带上这么些个指令,我推测这一段指令就是万恶之源。我们可以用脚本 patch 掉它们。
提取这一段的特征如下:
1
2
3
4
|
E8 00 00 00 00 58 89 85 C4 FD FF FF E8 03 00 00
00 EA EB 09 5B 43 53 B8 11 11 11 11 C3 E8 07 00
00 00 BB 33 33 33 33 EB 0D BB 11 11 11 11 5B BB
75 29 40 00 53 C3 BB 22 22 22 22
|
其开头以 E8 00 00 00 00
开始,结尾以 22 22 22 22
结束,长度为 59。
接下来就可以 patch 掉了。
然后整个世界都变了,相比于之前来说很轻松的就能看出来它的逻辑了。通过一次 Map 的初始化和一次运算最后就能得到 flag 了。
总结
这次的比赛让我发现了自身的许多问题,有时间把 pwn 写一下吧。。
文章作者
QRZ
上次更新
2021-05-02
(0e7621f)