转载请注明出处。https://rhirufxmbcyj.gitlab.io
从看雪一篇帖子上看到的这个程序,没什么难度,就是比较绕。拿下来试一试。
程序:https://rhirufxmbcyj.gitlab.io/files/give_a_try.rar
准备工作
- 首先,打开程序看一看这是个什么类型的程序,是个name+password类型还是serial类型,心理也好有个底,这个程序只有一个编辑框,也就是个serial类型的。
- 然后使用PEiD等工具 进行查壳、查语言、查算法。
- 有壳先脱壳,尽量不干扰正常分析程序,能用工具脱就用工具脱,此程序无壳。
- C类语言大多都一样,不需要特殊的工具,像Delphi或VB或Net就需要使用特定的工具去分析了,这个程序是汇编写的,更容易分析,怪不得函数那么少。
- 使用PEid的插件Krypto ANALyzer查一下算法,也算是分析的时候考虑算法有个偏向。
分析
程序拖入IDA看一看,发现未知的函数只有几个,其他都是API,直接用IDA的f5一个函数一个函数看就找到了程序的校验算法那块,也就是sub_401103(char *a1),猜测参数1这个char *就是输入的字符串了。
这个是IDA的关键算法的伪代码。
1 | int __stdcall sub_401103(char *a1) |
设V6为未知数x,那么V7、V8、V9、Vn都可以根据V6得出,由于aZCeie数组里的值是固定的,那么根据上边的公式就可以得出i=0时V6的值了,开始写穷举代码。
1 | int v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21; |
得出v6 = 81FA5AF1。要求出第一个字符,有了第一个v6,根据公式v6 = (unsigned __int8)a1[i] * rand();所以还需要有rand()的值,rand()的值和srand里的参数有关。dword_40406C这处内存值通过动态调试得出。调试过程中有两个反调试函数,把push和call都nop掉,得出0x40406C = 0x31333359。
如果有了srand,那么rand就是固定了,a1[0]也就求出来了,也通过穷举方法,srand(dword_40406C ^ v2); v2是所有字符的累加值 还需要穷举 最小则是42个“ ”,最大则是42个“~”,以此得到穷举范围。
1 | for (int i = 1344; i <= 5292; i++) |
跑完这个发现并没有得到想象中的结果 所以某些地方出问题了 回头看汇编代码
1 | call ds : rand |
IDA的f5少算了一次运算,还是用汇编吧 避免编译器优化造成的错误 修改下第一次穷举代码
1 | unsigned int result; |
求得v6是0x19AF6A 继续用上边的穷举代码
1 | for (int i = 1344; i <= 5292; i++) |
马上得到了结果 而且结果只有一个 ‘f’ 字符总和为3681 这时可以按程序的执行流程顺序穷举出全部字符串了。
1 | unsigned int result = 0; |
跑完得到真正的结果 flag{wh3r3_th3r3_i5_@_w111-th3r3_i5_@_w4y}.