西湖论剑2019-re

清明节的时候参加了一下西湖论剑杯,我主要做了 re。其实这次的 re 并不难,但是比赛的时候心态炸了,没能静下心来做题。不过我也好久没做题了,趁着这次比赛整理一下思路。

easyCpp

这是一道比较基本的 Cpp 逆向。

拖到 IDA 中看它的大概逻辑,处理后的 vector 和一个生成好的斐波那契数列进行比较。我们专注于它怎么处理输入就好啦。

稍微整理了一下逻辑,大概是这样的:

  • 初始化输入的数组(input)和斐波那契数列

  • 新建一个数列,我们可以称它为 global 数组。其中数据的初始化是这样的:

    • 首先 push_back input[0]

    • global[1] 开始按照 transform 初始化数据,transform 有一个内联的 lambda,分析之后可以写成这个样子:

      transform(input.begin()+1,input.end,back_insert(global),[](int x){return x+input[0];});

      也就是说输入的第二个数开始和输入的第一个数求和然后塞到 global 中。

  • 接下来它用 accumulate 函数做了一个对换的操作,其结果是倒转了 global 数组。

    PS:这里我刚开始没搞懂是什么意思,这里分析一下还是蛮有意思的。STL 里的骚操作不要太多,我感觉我根本不会 C++。。

  • 然后与之前初始化的斐波那契数列进行比较。

  • 成功了,输出答案,这一段我就没分析了。

综上,输入为

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 可以找到这里:

OnCommand: notifycode=0000 id=03e9,func= 0x00392420(Junk_Instruction.exe+ 0x002420 )

也就是弹窗函数的地址。我们在 IDA 中进入函数查看一下,地址就是 0x2420。

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 掉。

仔细阅读汇编代码可以发现一些有趣的汇编,摘录如下:

.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 掉它们。

提取这一段的特征如下:

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 写一下吧。。

文章作者: 40m41h42t
文章链接: http://qrzbing.cn/2019/04/10/WestLake2019-re/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 QRZ's Blog