拿到程序之后拖进ida,发现是一个控制流平坦化的混淆,而且其中夹杂了大量的辣鸡代码。
于是考虑跑一个trace,来分析程序的流程。
首先利用deflat.py,可以获得控制流平坦化中每一个基本块对应的地址。
获得地址之后,使用odscript来记录程序的执行流程,这里我程序的base是0x12B0000
拿到trace之后可以开始分析一些比较可疑的地方,首先是这里:
在trace记录中搜索,这个基本块也是只执行了一次,所以这里应该是获取用户输入的地方。
可以对这里下断点进行验证:
之后继续对trace进行分析,发现从12BD5A8
开始是一个循环,一共执行了6遍,于是猜测这里是对输入的字符串进行了一定的处理
而这个循环中包含了另一个可疑的区块:
这里使用了%d%n对输入的字符串进行了处理,于是猜测之后可能是直接使用%d录入的输入数字,于是对%d对应的内存区域下内存访问断点。后面会在这里断下来:
同样在trace里面查看,这里同样也执行了6次,这个基本块大致功能就是用snprintf(猜测)打印输入的字符串到另一个区域,相当于复制了一份输入,但是对那一片内存区域下访问断点之后没有断下来,所以猜测这里是一个没有任何作用的拷贝操作。
同样断下来的还有几个地方,但是经过分析之后都是一些没有任何作用的操作。
最后终于发现了一个有意义的操作:
在这里0x93fe40是输入的数字转成int之后储存的地址,这里把这个数作为索引到0x93f000取了一个数,并且以64位的大小累加到了[esi+0x3024]这个内存区域,于是对这个地方下一个内存访问断点。
最后可以在这里断下来:
在trace中查看发现这个基本块只执行了一次,并且在格式错误的情况下不会执行到,所以这里极有可能是验证的位置。这里调用了一个函数,并且把返回值与0x93f580这个地址进行了比较,于是这里尝试手动让这个条件成立:
可以发现程序成功输出了答案正确的提示,所以是验证函数没跑了。
于是我们需要分析的就是8A1020这个函数,直接看没法看出这个函数直接的作用。
分析引用后可以发现这个函数调用了aullrem这个函数,经过查询,这是一个64位取模的函数。所以对这个函数下断点,分析其调用方式。
发现8A1020调用了这个取模函数很多次,并且行为非常像快速幂算法。于是手动提取出模数进行验证,可以发现这个函数的功能就是pow(x,y,4288794511)。
到此为止,整个程序的算法就基本还原完成了,大致流程就是取输入的每一个数进行索引,并将索引到的元素求和,最后带入方程1702197298 ^ x mod 4288794511 = 1851878196进行验证。
反解的第一步就是求出1702197298 ^ x mod 4288794511 = 1851878196的解。
直接用sage解出方程
得到通解: x = 4288794510k + 1427250197
最后需要从351个数中选出不超过9个数让他们的和等于通解
所以这里直接用比较经典的背包问题思路去做:
最后就可以得到7个数:2144397255, 2450739720, 2859196340, 3027384360, 3958887240, 553392840, 1171109940, 1559561640, 857758902
按照索引从小到大排序就是17, 27, 60, 97, 133, 161, 243, 292, 309,按照格式输入即可。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!