1.起因
这是学校的寒假作业,难度入门级,然而我也是菜鸟,所以看看到底谁更菜吧。分析过程很简单,大神请略过……
下载地址:https://wwe.lanzouy.com/iQm4H00mv4zi
2.查壳
破解好习惯先查壳,使用Die发现是无壳VC++程序。
3.静态分析
拖进IDA,查看伪代码,容易发现有反调试。输入为一个字符串和一个长整数,有三个判断条件。简单修改名称并添加注释,如图:
观察三个判断条件:
[C] 纯文本查看 复制代码 ((int (__cdecl **)(int *))dword_404380)[0](&int1)
((int (__cdecl **)(char *))dword_404380)[1]((char *)&int1 + 5)
((int (__cdecl **)(char *))dword_404380)[2]((char *)&int1 + 9)
前面的括号里应该是一个数组,下标0,1,2的元素是三个函数,后面的括号是函数的参数。至于函数到底在哪里,因为我实在不太会用IDA,所以用OD进行动态调试。
4.动态调试
拖进OD开始分析,刚才已经看到有反调试,代码位置如图:
而起始位置是00401000,相减算出偏移量是0x45C。OD的起始位置是01081000,加上偏移量,前往0108145C处,将je改为jmp,即可跳过反调试。
再回头看IDA的流程图,发现函数地址就储存在eax。
用同样的方法找到这一行代码在OD中的地址,F2下断点,运行程序,在断点停下然后F7步入,找到函数位置01081080,偏移量为0x80。然后继续寻找另外两个函数的偏移量,分别是0x130和0x230。5.分析算法每个函数的位置都找到了,下一步就是逐个分析算法。
函数1
回到IDA,先看第一个函数,在00401080处。F5看伪代码,发现循环和字符串比较。
我选择逆向分析,这个函数结束后的跳转是jz,而且跳转未实现才能进入下一个判断,所以eax等于1,即函数返回1。再看函数的返回值,v2 == 0必须为真,即v2等于0。那么if (v2)会被跳过。再往前看,v2是字符串比较的结果,搜索一下strcmp这个函数,得知字符串相同返回0,因此a1="pvlg`"。从前面的循环可以发现,a1的长度为5,每一位都和下标i进行异或运算。根据异或运算的性质,a^b^a=b,所以把字符串"pvlg`"的每一位与下标进行异或运算的结果就是a1。计算出结果是"pwndd"。
函数2
继续分析第二个函数,还是从返回值入手,过程跟上一个一样,就知道了a1这个字符串经过循环处理的结果是"evoL"。
分析这个循环,i的取值是[0,2),把下标i和3-i的值进行三次异或赋值运算,这是什么含义呢?假设有两个数a和b,进行以下运算:
[Asm] 纯文本查看 复制代码 a=a^b
b=a^b
a=a^b
相当于
[Asm] 纯文本查看 复制代码 b=a^b^b=a
a=a^b^a=b
也就是a和b的值互换,而不借助中间变量。其实我以前看到过这种方法,所以马上想到答案是"Love"。
函数3
来到00401230处,发现这个函数稍显复杂:
跟之前的思路一样,a1经过处理与"ZELDA"相同。分析这个循环,首先把int0向右移位,int0是什么?通过交叉引用,发现它就是输入的长整数。
第一次循环,v4=4,int0向右移动32位,也就是v2=int0/2^32;然后a1[0]=v2;最后v2左移32位,相当于int0=int0-v2*2^32。这时注意到输入的字符串应该长度为5,但循环只有4次,也就得到了a1[4]='A'。而循环中对字符串a1只有赋值操作,不依赖它原本的值,因此字符串的前4位可以是任意值,记作"????A"。所以我接下来的目的是找到长整数的值。继续逆向分析,从第四次循环开始,已知v2=(int)'D'=68,而每次循环v4自减,第四次循环时v4=1。那么68=int0/2^8,得出int0=17408。再依次分析第3,2,1次循环,最终得到int0=387709682688。其实可以写个循环来计算,不过次数很少,按计算器也方便。
总结
算法分析到这里就结束了。flag的字符串部分应该是"pwndd""Love""????A"(最后一个不唯一),而长整数就是387709682688。
6.未完待续
分析已经结束了,那么flag是不是"pwnddLoveZELDA 387709682688"呢?当然不是。因为每个函数都要求两个字符串完全相同,所以不能连在一起输入。如果分开输入,程序接受的第二个参数是长整型而不是字符串,也不能通过,于是陷入了两难的境地。但我觉得这就是正确答案,所以接下来用动态调试验证一下:
进入第一个函数,发现整个字符串都被传入了:
字符串比较不通过,但可以看到前5位是正确的:
为了继续验证,将此跳转改为nop,进入下一个函数:
可以看到字符串从第六位开始传入,依然是运行到比较处,发现前四位相同:
还是把下方的跳转改为nop,进入最后一个函数:
这时传入的字符串就满足条件了,因此只要我给出的长整数正确,那么第三个验证就能直接通过。F4直接来到验证处,查看结果:
完全正确!最后证明一下,返回之后的这个跳转我没有修改,可以直接来到成功提示:
程序运行效果如下:
最后
虽然无法直接运行程序得到成功提示,但通过最后的动态调试和分析,可以确定我的思路是正确的,对算法的分析也是正确的。尤其是第三个函数的验证能直接通过,有力证明了答案的正确性。现在我很好奇作者是如何用flag得到成功提示的,那实在太神奇了。感兴趣的小伙伴也可以动手尝试一下,看看能不能解决这个疑惑。
|