本帖最后由 wssbly 于 2019-1-21 11:55 编辑
学习一下!先API函数定位法,找调用,找到如下区域,感觉像是正确和错误的分支跳转,看跳转去向往上找:
往上拉发现下图所示指令,结合之前的jmp和call,遂推测0x00401F67处是爆破点,下断。
先不管爆破点向上的判断条件指令(爆破先),更改标志位F9跑起来发现如下图所示程序仍未现实破解,说明爆破点可能不止一个,于是重来一遍,这一次更改标志位后到达0x00402054处时选择F7跟进去。
0x004021EF到0x004021B9处存在一个和循环结构,在循环jmp的后一指令F4出来,走两步抵达0x004022C6,发现该位置的条件跳转指令跳过了很多指令,来到接近retn的地方——说明这里极有可能是我寻找的“爆破点2”,老样子,修改标志位,F9跑起来,这次OK了:
爆破结束,大体梳理好程序结构,来看序列号究竟是啥:
重新运行程序,来到爆破点1(把两个爆破点分别称为1和2),向上翻,此时中规中矩的方式是一点一点向上分析指令,现在为了节约时间,我直接上结果——应该能分析出三块:第一是如下的序列号长度要求,看图就能猜出来:序列号必须介于5与10之间(不包含,因为jle和jge)
第二是新的循环(0x00401E66到0x00401EF8),这个循环的作用是从序列号的第一个字符开始,依次累加输入序列号的每个字符所转换为的、相应ACSII码的十六进制值,最终把累加值放入0x004A8668的内存里。
第三是对于爆破点1的判断条件,这个条件简单来说就是要求序列号的第三位是“Y”,只要是Y即可。也就是说,随便胡乱输一通,只要位数在5-10之间且第三位为Y,那么就能跳转到0x00402054,进而进入第二循环体所在的子程序。
进入子程序后主要就是新的循环体。里面变得东西挺多,包括对FPU寄存器的修改等等,但是它对这个程序破解的最终作用貌似是在栈中生成一串数值,该值是程序一串固定值的每个字符ACSII码转换的十六进制值的累加值,如下图:
关键的是爆破点2的判断条件,从下图可以看到就是把栈中的值和内存中的值进行比较,相等才能显示破解成功,因为不等则跳转——这两个值分别是爆破点1之前的循环体的十六进制累加值结果和子程序中循环体的十六进制累加值结果
结论很简单:只要序列号长度在5和10之间、第三位固定位Y且满足各个字符累加值为1B3即可让程序显示“破解成功”——小技巧:因为1B3就是第二循环体得出的结果,所以可以直接从其中逆向得到字符串,为LVYU21,这个就是正确序列号的其中之一。
需要注意,该程序爆破点1之前的第一循环体最终将累加值赋给内存空间(0x004A8668),实测只要不关程序,该内存的值就会随着每次按验证按键的次数一直累加,但是第二循环体运算完在栈里的赋值永远是1B3——这就是为什么如果一开始输入错误,再输一个正确的字符串(LVYU21)也无法使程序提示“破解成功”的原因,特别是当内存中该值累加得大于1B3后,就别想该正确提示再出现了,重开程序吧。
还有一点,有时候会发现在程序显示”破解成功“后,再输入一串稍微修改后的字符串也不会让正确的提示消失,实测这是因为只要不进入0x0040204A所在的子程序,就不会导致正确提示消失,这应该是程序自己定义的(哪怕输入一串不符合要求的字符串也行,比如LVY)。所以,只要字符串的第三个Y不变或者干脆把字符串输入超过10或少于5,就能在已经提示”破解成功“的情况下,让该提示不消失。
大家共同探讨学习!
|