参赛题目:序列探秘(Sequence Adventure)
题目答案:L3mZ2k9aZ0a36DMM
附件是参赛程序
题目设计:
(如有需要,可以联系我提供源代码)
本题有两个门槛,一个是变形,一个是序列号算法。
先说变形部分。
变形非常的简单,追求的是趣味。还有就是拦截一些直接IDA静态分析,过于草率的认为找到了序列号判断函数的玩家。
判断序列号是否有效的函数为TestKey,这个函数一开始的内容就是一个一般复杂的序列号判断程序(事实上解它的难度高于真实的判断逻辑)。程序运行之后,在初始化对话框的时候,会读取一块内存,解密后覆盖TestKey的代码内存,从而彻底的改变TestKey的逻辑。这就是本题简单的变形过程。
再说序列号算法部分。
只要程序跑起来,玩家就可以得到序列号算法的全部代码了。此处无混淆,除算法本身之外没有别的坑。
算法的难点又有二,一个难点是还原算法的方程,这个认真去逆或者有些“猜”的悟性可以很快得出方程。另一个难点是解方程,这个方程就是求一个二元二次方程符合条件的整数解。
方程为:x*x - 7y*y = 8
(参数是我深思熟虑反复调整的。这个方程的解序列对初始参数即为敏感,不会因为看起来简单而简单)
解的要求是:x长度为正好8字节,满足条件的有两组解,通过首位的判断把较大解排除,正确答案为较小的那个。从而避免多解。本题采用的大数算法排除了负解的可能性。(如果有人发现多解,我穿女装发自拍到群里)
至于方程的解怎么限定为0-9、A-Z、a-z的。我把用户的初始输入和预设字节流亦或了一下,以保证玩家要输入的正确序列号符合题目要求。
通过“输入在亦或后符合比赛要求”来排除部分输入后,蛮力搜索依然在复杂度上不可行。
破解思路:
变形部分的破解太容易,只要程序跑起来,界面初始化完毕,变形就已经结束了。在GetDlgItemTextW下个断点,就能跑到确定长度的代码和判断序列号有效性的代码。喜欢dump出来慢慢分析,还是手动调试,看玩家口味。
然后,玩家发现长度限定为16的字符,通过判断序列号的函数逆出判断方程x*x - 7y*y = 8.
这个方程,凭借蛮力枚举是解不了的。
有两种解法,
第一种是先枚举较小的解,排成一列,分析其规律~
最小的解(x,y)为(6,2).
其它解从小到大为:(90,34), (1434,542)...
有耐心的玩家会觉得,这有个递推公式吧?
试一试,构造一下。
发现递推公式是:
x[k + 1] = 8x[k] + 21y[k]
y[k + 1] = 3x[k] + 8y[k]
是不是很线性~
x[0],y[0]就是6.2.
15次递推之内就能得出本题要求的结果~
第二种解法是,用线性代数方法求X[k+1]=M*X[k],X为解序列的数对,M为2*2待定系数的矩阵,把(6,2)(90,34)(1434,542)代入确定系数,绝大多数递推公式都能这么不浪漫的机械求出。
只要用户想到从序列找规律,就不难了。所以本题叫序列探秘(Sequence Adventure),这本身也起到提示作用。