总的来说这道题设计的比较巧妙吧。使用了声东击西的套路。首先放了一个烟雾弹,让人误以为关键点在某个地方,然而那里根本不是核心点。只是一个假“据点”。过多的在那里纠缠就只会浪费你的时间。
首先看下程序。没又任何保护可以直接调试:
然后ida打开。由于没有任何保护措施,所以直接搜索字符串,查找关键字符:
如上图所示,发现了关键位置。然后直接跟上去。可以发现关键逻辑点:
在这个关键跳转前面能发现三个函数。第一个是输入输出函数。用于获取用户的输入已经显示程序的信息。下面两个函数就是“加密”函数了。为什么要打引号?因为后面会降到。这只是个幌子,逗你玩的。在这里纠结那你就中计了:)
现在大概说一下目前看到的“关键逻辑”(假逻辑):
首先有一个变量:dword_41B034。他的初始值是2。然后跟进伪加密函数1可以发现一些逻辑:
如图可知,伪加密函数1的逻辑就是进行一系列的if判断然后如果满足条件则变量dword_41B034自减1。第二个“加密函数”也是一样的原理:
这两个伪加密函数最后就构成一个方程组。现在整理如下:
5 * (y - x) + y == 0x8F503A42
13 * (y - x) + x == 0xEF503A42
17 * (y - x) + y == 0xF3A94883
7 * (y - x) + x == 0x33A94883
看到scanf你想到了什么?作为一个搞安全的人。看到scanf第一反应应该是缓冲区溢出。这是本能反应。如果你没这个意识,这说明你还是需要再学习一个…..
既然想到了缓冲区溢出。那么就可以试着构造。溢出的原理很简单:
如上图所示。可以发现很多信息:
a) 函数依靠eax返回参数。Eax的值就是一个指针,指向一个buffer,这个buffer保存着我们输入的信息(就是“aaaabbbb”)
b) 函数的堆栈显示了输入信息所保存的方式。以及函数的返回地址。根据栈的排列方式不难想到,可以通过构造输入来覆盖返回地址,然后就达到了溢出的目的——让程序执行我们想让他执行的代码
c) 综合上图的“%s”可以发现,所有的输入是被当作字符串来保存的。因此输入的字母a和字母b都被当作了字符串。因此这里需要考虑ascii码的转换问题。同时数据在内存中是小端排序(这个是常识了)
接下来按照上述原理。直接构造输入。我们看到在程序清空堆栈之前,我们输入的信息保存的地址是0x18ff38.然后返回地址保存在0x18ff48。这两个地址相减得:0x10(十进制16)所以这很容易算出。如果要覆盖返回地址需要构造12字节的信息外加4个字节的返回地址。也就是说输入结构如下:A+B+C+返回地址。然后ida上可以看到成功分支的地址是:0x0040102F。因此对照ascii表构造输入如下:
如图所示这样构造其实也是可以解决问题的。但是比赛规则限制,正确的注册码只能由字母+数字组成。这说明此题还有其他的解。因此断定一定还有我们没有发现的逻辑。所以搜索特征值进行寻找。对于加密解密来说。一些经典的特征如下:
a) JMP EAX
b) shl等各自位移指令
c) XOR运算指令
上述这些指令都是加密算法精彩用到的。很容易就能找到关键点。(用最笨的方法。Ida直接向下翻,就可以找到啦:))其次,由于这是比赛。比赛的程序其实都很小。这其实大大降低了逆向的难度。不管你这个程序用了什么花哨的技术。你这么小一个程序,他逆向的难度和复杂度始终都不会高到哪里去。因此在这里多说几句。各位逆向界的新手们,千万要正确对待creakme比赛。不要以为自己能破解creakme。就沾沾自喜。就以为自己已经算是学会了,入门了。相信我。在一个只有几KB大小的creakme程序中。不管使用了多么复杂的反调试,反分析技术。相对都是很容易破解的。因为代码量级在那摆着。但是如果你真的进入这个行业接手真的破解项目。你会恍然大悟:什么ctf,什么creakme。简直就是too simple。Sometimes naive。现在随便一个程序都是几百兆,N个G。在这种量级的代码中进行逆向那才是真的复杂。即使作者不做任何保护。你分析它都非常困难。在海量的汇编代码中如果没有经验和技巧。很容易就迷失在代码的海洋中啦。如果作者又加上各自反调试。那难度呈指数级上涨。这才是真正的逆向工程。我们现在的crf比赛真的是小儿科,连入门都算不上:)
按照上文的方法。果然可以找到一段数据:
这其实就已经非常明显了。这么小的一个程序为什么会有一大段数据?(当然如果是商业级的软件,几百上千兆的程序,那这一点都不奇怪,很正常)可以猜测。这段数据要么就是程序运行时解密的密文数据。要么就是一段被ida当成数据的代码。然而,结果比想象中的要简单。这不是密文,而是明文的代码。只不过是被ida当成了数据。重新分析后如下:
如图可见。这段shellcode的代码地址是0x 00413131。而且可以看到很多跳转指令。熟悉的人一眼就明白。这里其实是花指令。而且是最简单的花指令。就是来回跳一跳,吓唬吓唬你而已。相信我。所有的花指令都是纸老虎。复杂的花指令可以用插件记录程序流程分析一下脱花。简单的花指令(就像本题中遇到的这种)根本不用鸟它。完全是战五渣。直接肛正面……
最后我整理了一下脱花后的代码如下:
如上文所属。整理下来,其实就是三个方程组:
(D1-D2)*4+D1+D3 = 0xEAF917E2
(D1-D2)*2+D1-D2+D1+D3 = 0xE8F508C8
(D1-D2)*2+D1-D2+D1-D3 = 0x0C0A3C68
其中D1,D2,D3就是我们输入的信息。大小为DWORD。
现在确定了这里才是真正的加密函数。那么要做的就是解方程并且构造输入信息。
首先解方程的方法很多。我是自己写的代码。代码如下:
如上文所属。整理下来,其实就是三个方程组:
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!