作为逆向题,题目自然木有神马描述,但看一眼就知道,这题是要算个序列号,但是比较蛋疼的是,这题的大多数对单个参数的限制条件都是不等于什么,而做等于判断的各个条件又都是些涉及到好几个参数的检验,所以很是麻烦。
程序首先读入一个序列号的字符串,然后会转换为5个dword,一个word来进行一些check,然后check的大致流程如下:
__int64 __fastcall check_key(char *a1) { __int64 result; // rax@3 __int16 v2; // [sp+1Ah] [bp-16h]@1 unsigned int v3; // [sp+1Ch] [bp-14h]@1 unsigned int v4; // [sp+20h] [bp-10h]@1 unsigned int v5; // [sp+24h] [bp-Ch]@1 int v6; // [sp+28h] [bp-8h]@1 unsigned int v7; // [sp+2Ch] [bp-4h]@1 v3 = *(_DWORD *)a1; v4 = *((_DWORD *)a1 + 1); v5 = *((_DWORD *)a1 + 2); v6 = *((_DWORD *)a1 + 3); v7 = *((_DWORD *)a1 + 4); v2 = *((_WORD *)a1 + 10); if ( (unsigned int)sub_E10(v2) || (unsigned int)sub_DA2(v2) ) { result = 0LL; } else if ( (sub_F0F(a1, 20) & 0x7FFF) == v2 ) { if ( (unsigned int)sub_E40(v3, v4, v5) == v6 ) { if ( (unsigned int)sub_1566(v7) ) { if ( (unsigned int)no_equal_adj_4(v3) ) { result = 0LL; } else if ( (unsigned int)no_equal_adj_8(v3) ) { result = 0LL; } else if ( (unsigned int)is_prime_larger(v3) ) { if ( (unsigned int)some_bits_mod7_0(v4) ) { if ( (unsigned int)some_bits_saf(v5) ) { if ( (unsigned int)odd_v4_v5_mul_v3(v4, v5, v3) ) result = (unsigned __int16)calc_jiaoyan(v4, v5) == v2; else result = 0LL; } else { result = 0LL; } } else { result = 0LL; } } else { result = 0LL; } } else { result = 0LL; } } else { result = 0LL; } } else { result = 0LL; } return result; }
这里的check很多,为了我们能够高效的找到key,这里我们需要选择一个合适的搜索顺序,尽可能的将强的条件放在外面判断。
-
首先先是some_bits_mod7_0和some_bits_saf分别是对v4、v5的偶数位的判断,我们暴力穷举会发现两者分别有9363和5042种可能;
-
然后在odd_v4_v5_mul_v3这里面,对v4、v5的奇数位做了很弱的限制,但是有个v4_odd * v5_odd = v3 + 1的限制,也就是说,这里我们通过枚举v4、v5的奇数位,我们可以计算出v3,然后就可以将v3带到前面的各个对v3单独限制的地方,很好的做一个筛选;
-
接下来,看calc_jiaoyan,由于v4、v5我们已经枚举过了,根据v4、v5我们可以计算出对应的v2,然后带入前面对v2的单独校验中检查;
-
然后sub_1566是对v7单独的校验,这个校验可以确定v7总共有171955可能;
-
此时,我们仅仅剩下了一个判断(sub_F0F(a1, 20) & 0x7FFF) == v2,这句判断实际是用v3-v7计算出一个校验和,与v2进行比较,当我们进行到这一步之前,我们可以秒出很多的结果,但是,当我们加上了这个判断之后,我跑了很久也没能跑出结果,很是无奈,不过想想也是,本来其实v4、v5枚举完后量还好,但是和v7的这近20W一乘起来,也确实恐怖;
-
最后,仔细再观察一下最后那个校验和的判断,我们会发现,v2其实隐式的被限制必须要小于0x7FFF,于是我们完全可以在v4、v5算出v2后,先进行这个判断,然后再来和v7联合起来校验,这样其实我们搜到一个解的工作量直接就相当于是减少了10W倍这个级别的,尤其是当v2大于0x7FFF的量比较大的时候。因为在这里,其实可行解的量是巨大的,最后一个校验也没有很严,我们现在一次判断就可以排除10W多组不可能的情况。
总之,程序写好之后,很快便可以跑出刷屏的解,随便选个就好,然后再按照规则,用算出来的6个数,反向去计算序列号即可,这一步是队友做的,我这里就没有了……