探索黑客技术攻防,实战研究与安全创新

导航菜单

一次失败的追码经历

我玩黑客也已经有7年多,学到了不少东西,最近因为比较悠闲,可以将自己学到的东西用于实践了,于是就从网上找了几个crakeme来练手,成功的爆破了几个,轻松的度过了破解的第一阶段,进入了第二阶段的追码,前面几个都容易的追到了注册码,但世事难料,过早的高兴让却我跌人了失败的深渊,我到底遇到了什么样的难题,又是如何克服困难的呢?大家请听我慢慢的道来!

和其他刚入门的人一样,拿到了crackme后当然是先来查壳了,哈哈,没有加壳,这可是个好消息,对于一般的壳,我还是能够脱掉的,但如过遇到了ZP那种强壳,脱起来就比较麻烦了。既然没有壳那我们就省去脱壳的这个步骤了。接下来就进入第二步——收集信息,我们运行crackme,用户名就填写kkkk,注册码填写123,点击注册,这时会弹出一个对话框,上面有字符“No”。

QQ截图20170304153101.png

既然有提示信息和对话框,那就好办了,我们进入第三步——使用OD载入程序,先来尝试一下超级字符串查找,嗯,人品不错,提示信息马上映入了眼帘,我们双击进入,就来到了下面的代码处。

不难看出0040561B处就是关键跳了,要爆破的话我们直接修改这里就行了,但我们的目的是追码,所以就继续往上看,在0040560F处这里就是关键CALL了,当我进入这个CALL后,悲剧发生了。我进入后,里面又有几个CALL,我依次遍历,可就是不见注册码的身影,真是奇怪呀,我又连续追了5、6次,浪费了近一个小时,然后就是盯着电脑发呆,然后……

这事并没有就此结束,否则的话,也就不会有本文了,呵呵,那么我在遇到困难后是如何去做的呢?我到网上开始寻找答案,在各大论坛漫无目的的转着,企图寻找出问题的答案,不知是因为我太笨还是别的什么原因,我不但没有找到问题的答案,相反却还受到了极大的刺激,网上的高手们动辄就贴出了一大堆的代码,然后分析的我一句都看不懂,让我很是受伤,我不禁自叹!当刺激变成了动力,我便做出了决定,我也要来个算法分析,充当一下高手,其实做出这样的决定是经过认真考虑的。首先,只有通过算法分析我才能搞清楚为什么追不到注册码。其次,破解的第三个阶段就是算法分析,我可以更快的进行自我提高。说干就干,开始吧,我们先来看关键跳的上一句,它测试ECX是否等于0,如果不等于就跳向注册失败,它的上一句也就是00405616这句,是将堆栈[ebp-2C]与ECX进行异或运算,在这里我们就得充分发挥一下自己的想象力了,我是这样想的,它们俩进行异或,然后再测试,再跳转,这肯定是有原因的,我猜想这两个数据肯定与我们输入的用户名和注册码有关,来验证一下我们的猜想吧。我们选择00405616这行,按F2下断,然后再按F9运行程序,断下来了,看到了[ebp-2C]=[0012FC48]=8F7998399以及ECX=0000007B。

记下8F799839和0000007B这两个数字,然后按CTRL+F2重新运行,再按F9运行,注册窗口出现了,这次我们注册名填写ssss,注册号就填写123,然后点击注册,程序在相同的地方断了下来,我们再来看看,这时[ebp-2C]=[0012FC48]=996C56C2,ecx=0000007B,与上次不一样了吧。

这就证明了我的猜想是正确的,既然叫逆向工程,那我们就逆着向上来看ECX的值到底是什么?通过00405614这句,我们知道ECX的值来自EAX,我们再继续往上就来到了关键CALL,看来必须进入CALL中才能找出问题的答案,那我们就按F7进入吧,进入后经过我的定位。

在004041E3指令中,堆栈ebp-8储存的是“0123”,也就是我们输入的注册码,程序在它前面加了一个0,这应该也是程序的一种算法吧。下一句004041E6,是将字符串“0123”的第一个字符“0”送入EBX中,接着与EDX相加,因为EDX的值为0,字符“0”的十六进制为30,这条指令执行完后,EDX=30,下一句EDX中的值减30,执行完后EDX=0,然后EAX增一,与ECX相比,ECX储存的是字符串的个数,不相等就跳上去执行,直到读完所有的字符。这里我使用一段C语句来表示:这样大家们就能理解了吧,运行结果为EDX=0000007B,这个数字很眼熟吧,我们就见到过这个数字,这并不是偶然,那个数字就是从这里算出来,然后传到了那里。现在我们终于明白了,那个ECX是通过我们输入的注册码,使用上面的算法算出来的,这已经能够解释我为什么追不到注册码了,因为在关键CALL里并没有进行明码比较,而是通过这种算法算出一个值,再去与用户名算出的值进行异或,通过测试异或得到的值来判断程序该跳到哪儿。现在我们已经追到ECX了,接下来就该去追[ebp-2C]了,这是一个乏味而又漫长的过程,我们应该做好心理准备。那么我们应该如何去追[ebp-2C]呢,我就给大家介绍一个笨方法吧,这种方法需要极大的耐心,但却很有效,如果哪位高手有更好的方法,别忘了告诉我啊!好了,废话少说,我们开始吧!

我们按CTRL+F2重新运行,来到堆栈窗口,单击右键,选择前往表达式,然后在对话框内输入0012FC48,为什么要输入这个数字呢?其实它就是ebp-2C。然后再单击右键,选择锁定堆栈,这样0012FC48这项就不在动了,我们边单步边注意这项数值的变化,有CALL就按F7步入,经过了N次F7,2N次的F8后,我们来到了下面的代码处:

 这段代码依次将67452301、EFCDAB89、98BADCFE、10325476、C3D2E1FO这几组数字压入堆栈0012FC48、0012FC4C、0012FC50、0012FC54、0012FC58中,但用户名并没有出现,我们继续按F8,又经过了N次F7,2N次F8后,我们来到了代码处:这里依次是将堆栈0012fc48、0012fc4c、0012fc50、0012fc54、0012fc58中的值加上EAX、EDX、ECX、EBX、EDX中的值。我们先不追EAX、EDX、ECX、EBX、EDX中的数值,因为堆栈0012fc48中的值还没有变成8F7998399,我们还得追下去才行。继续吧,又经过了N次F7,2N次F8后,我们来到了代码处:在这里堆栈0012fc48中的值变成了8F7998399,那我们就来分析一下吧!EAX在这里充当循环寄存器,也就是这段代码循环4次,第二句代码等价于将数值0012fc4c放入EDX,第三句就是将堆栈0012fc4c里存放的数值BCED02DC送入ECX,第四句就是将ECX中的数与堆栈[ebp-2C]=[0012fc48]进行异或后,存入堆栈中。这样经过4轮运算,堆栈0012fc48中的值就变成了8F7998399。这里我们要注意第一次将数值0012fc4e送入ECX,然后EDX加4,第二次我们将0012fc50送入……也就是说,我们将依次把堆栈0012fc4c、0012fc50、0012fc54、0012fd58储存的数值送入ECX与堆栈0012fc48的数值进行异或运算后,堆栈0012fc48中的值就变成了8F7998399,到这里我们就大概明白了堆栈00l2fc48中的值是如何变成8F7998399的,但我们还没有弄清楚输入的用户名与堆栈0012fc48中的值8F7998399有什么关系,既然问题还没有完全弄清楚,那就来继续吧!

通过上面的代码,我们意识到只有弄清楚堆栈0012fc48、0012fc4c、0012fc50、0012fc54、0012fc58中的值,才能完全清楚程序的算法。本来我们报清楚堆栈0012fc48、0012fc4c、0012fc50、0012fc54、0012fc58中的数值的,因为我们刚才分析过,它们中储存的本来不过是几个固定的数值,但后来发生了变化,分别加上了寄存器EAX、EDX、ECX、EBX、EDX中的数值,看来我们要想弄清楚,就必须得去追这些寄存器的值了,我们把004052DA处的代码往前翻,这段代码很长很长,从0040426B到004052DA,有106f句,这里的106f是十六进制,想像一下换成十进制会有多少啊!鉴于代码比较长,而且通过我的粗略分析,这段代码的算法毫无规律可言,在此就不贴出代码了,只说一下我分析的结果,分析后发现要想完全弄懂,还得去追堆栈0012FA64到0012FBA4中的值,那么我们就按上面介绍的方法去追吧,不知又经过了多少次F7和F8,我们来到了如下代码处:横线以上的部分产生了堆栈0012FA64到0012FAA4中的值,横线以下的部分产生了堆栈0012FA A8到0012FBA4中的值,我们分析一下它们是如何产生的吧!先分析横线以上部分,很显然ECX充当循环寄存器,然后会将堆栈edx+ecx*4中的值送入EAX,因为是第一次循环,这里OD上显示EDX=0012fbd4,也就是说第一次会将堆栈0012fbd4中的值送入EAX,接下来的这个运算符我也没有见过,那么就来参考一下8068汇编手册吧。手册上说运算符bswap的功能是交换32位寄存器里字节的顺序,在这里EAX中的值是65486944,执行这条指令后,EAX中的值变为了446948654,果真如此,字节65移到了最后面,44移到了最前面,69移到……弄清楚这个运算符后我们接着来分析,然后会把运算后的数送入堆栈ebx =0012FA64,然后EBX加4,ECX增1,然后比较,在进入下一次循环。其实就是把堆栈0012fbd4到0012FC10中的值取出,运算后放入堆栈0012FA64到0012FAA4中去。

接着再来看横线下面的部分,ECX充当循环寄存器,然后会把堆栈ebx-C=0012FA9C中的值进入EAX,然后与下面几个堆栈中的值进行异或运算,其实前3个堆栈中的值都是0,只有堆栈ebx-40= 0012fa6c值不为空,而0与一个不为0的值异或,结果相当于将那个值送入EAX,接下来的这个运算符我又没有见过,那么还是来参考一下8068汇编手册吧。手册上说ROL的功能是循环左移,现在终于弄清楚了,我们继续下一句吧,接着就把EAX中的值送入堆栈ebx,第一次的时候ehx=0012FAA8,然后EBX加4,ECX减l,然后比较,在进入下一次循环。其实就是把堆栈0012fa6c中的值取出,运算后放入堆栈0012FAA8到0012FBA4中去。这段代码弄清楚了,但新的麻烦又来了,因为我们并不知道堆栈0012fbd4到0012FC10中的值是如何产生的,而且我前面说过了算法与用户名有关,也没有弄清楚它们之间到底有什么关系,这就意味着我们必须再去追堆栈0012fbd4到0012FCl0中的值是如何得到的,大家们是否已经感到厌烦了,我只能鼓舞大家挺住,因为我们马上就要搞清楚了,也就是说这是我们的最后一追了。来开始吧,不知又经过了……我们来到了象征着胜利的代码处:

在这里我们输入的用户名终于露面了,堆栈eax=00BE0045储存的字符串“DiKchlkkkk”就是我们输入的用户名前面再加上“DiKeN”,还记得我们上面分析过的程序,就是在我们输入的注册码前面加0吗?这和那里一样,也是程序的一种算法。在这里程序将堆栈00BE0045储存的字符串的第一个字符即“D”送入BL,然后再送入堆栈edx =0012fod4中,然后ESI增l,EDX增1,ECX减1,然后进入下一次循环,将第二个字符即“i”送入堆栈edx+1=0012fbd5中……依次将字符串中的字符送人堆栈0012fbd4到0012FC10中,现在我们终于将整个算法分析清楚了,我们来整理一下,从哪儿开始的还记得吗?我们是从“00405616   334D D4 xor ecx,dword ptr ss:[ebp-2C]”这句代码开始我们的追踪之旅的,我们开始追的是ECX,发现它是通过我们输人的注册码算出的,具体算法很简单,是注册码中的每个字符的十六进制减30,然后求和。弄清楚ECX后,我们开始追堆栈ebp-2C中的数值,也就是0012FC48中的数值,整个过程很浪费时间,但经过我们的努力,最终还是弄清楚了。大概是这样的,程序先将我们输入的用户名前面加上字符串“DiKeN”组成一个新字符串,然后把新字符串取出经过一系列运算后,放入了新的堆栈中,关键算法在我没贴出来的那段超长代码中,它将字符串运算后的值和前面压入堆栈的5组固定数值做为参数,经过一系列毫后规律的运算后,再把运算后的新的5组数值送入原来的堆栈中,然后又把后4组数值与第一组进行异或运算,最后得到了我们追踪的堆栈0012FC48中的数值,然后程序会将由用户名算得的数值和由注册码算得的数值进行异或运算,判断程序是跳向注册成功,还是失败。

分析完了整个算法后,我感慨巨多啊!首先,我觉的这个程序对大家们来说练爆破还可以,但要练算法分析,那就是遇上了巨大的灾难,而我恰好很不幸,直接就掉进了灾难里,幸亏我看多了,遇到这种情况,能够镇定下来。原因有两点:一、这个程序的算法的分支很多,绕来绕去,容易让大家们迷失在代码森林中,这是主要原因,程序的开发者通常使用这种方法来阻碍破解者调试代码,浪费他们的时间。二、对于算法的核心部分我没有贴出来,因为其超长且毫无规律,这会让大家们信心全无,这种算法真是变态啊!不知是哪个高人写的,建议大家们找一些算法代码集中且有规律的crackme来开始练手。这个crackme花费了我三个晚上的时间,但我觉的很值,收获很大,进一步提高了自己的水平,相信经过这次的历练,以后再看高手们写的文章就能懂一些了,祝愿所有人天天都会有进步!

本文内容所提及均为本地测试或经过目标授权同意,旨在提供教育和研究信息,内容已去除关键敏感信息和代码,以防止被恶意利用。文章内提及的漏洞均已修复,作者不鼓励或支持任何形式的非法破解行为。