本帖最后由 L4Nce 于 2014-10-24 22:39 编辑
直接说结论,我认为这个cm是不可能被正常的方法keygen的。因为代码中有几个地方写得不严谨,或许这是作者的有意为之,如果真的是这样的话只能说我实在是太菜了。
这个cm让我想起了我以前玩的一个游戏,打个比方,就是排列9*9的灯泡矩阵,并且通过Name与Key控制灯泡的明灭。如果假设刚开始时灯泡是灭的,那么到最后,如果所有灯泡都是灭的话,就成功了。
问题就出在这里,让我慢慢说明:
首先是初始化灯泡:
每个灯泡的结构如下
struct light {
DWORD statuc; //灯泡的明灭状态
DWORD LineFirst; //灯泡是否处于第一行
DWORD LineLast; //灯泡是否处于最后一行
DWORD ListFirst; //灯泡是否处于第一列
DWORD ListLast; //灯泡是否处于最后一列
}
接下来是初始化了,原程序使用了复杂的数据来混淆视线,弄懂了定义就可以了。
#define true 0xFEFEFEFE
#define false 0xEFEFEFEF //判断灯泡行列的两个常数
#define off 0xFFEEFFEE
#define on 0xEEFFEEFF //判断灯泡状态的两个常数
PS: off ^ 0x11111111 = on. on ^ 0x11111111 = off。 所以,下面的??异或0x11111111实际上是在转换灯泡的状态。
先初始化9*9的灯泡阵列,注意,这里是第一个问题:
代码大致如下:
[C++] 纯文本查看 复制代码 _DWORD *__usercall sub_401EB0<eax>(_DWORD *result<eax>)
{
signed int iLine; // edx@1 初始化棋盘
signed int iList; // ecx@2
iLine = 0; // 9行
do
{
iList = 0; // 9列 1*5 的DWORD
do
{
*result = 0xFFEEFFEEu; // 主元素
if ( !iLine )
{
result[1] = 0xFEFEFEFEu; // 第0行
LABEL_5:
result[2] = 0xEFEFEFEFu;
goto LABEL_6;
}
result[1] = 0xEFEFEFEFu; // 非0行
if ( iLine != 9 )
goto LABEL_5;
result[2] = 0xFEFEFEFEu; // 第9行,不存在?
LABEL_6:
if ( !iList ) // 第0列
{
result[3] = 0xFEFEFEFEu;
LABEL_8:
result[4] = 0xEFEFEFEFu;
goto LABEL_9;
}
result[3] = 0xEFEFEFEFu; // 非0列
if ( iList != 9 )
goto LABEL_8;
result[4] = 0xFEFEFEFEu; // 第9列。。。不存在?
LABEL_9:
++iList;
result += 5;
}
while ( iList < 9 );
++iLine;
}
while ( iLine < 9 );
return result;
不清楚?再弄一下:
[C++] 纯文本查看 复制代码 iLine = 0;
do {
if(iLine == 9)
....//设置灯泡为最后一行
} while(iLine < 9)
你一定是在逗我,这样写的话,无论如何,最后一行的数据是无法被设定的,同理,最后一列也是没有被设置的。也就是说,这灯泡阵列的最后一行,最后一列,能不用就尽量不用,否则不知道会出现什么结果。。。
然后是根据名字设置灯泡,这个挺好懂的:
[C++] 纯文本查看 复制代码 do
{
NameA = (szName[v13] >> 4) & 0xF; // 对Name进行拆分,然后
NameB = szName[v13] & 0xF; // NameA%9行 nameB%9列
if ( (unsigned int)NameA > 9 )
NameA %= 9u;
if ( (unsigned int)NameB > 9 )
NameB %= 9u;
++v13;
v40 = NameA;
v41 = NameB;
(&chess)[5 * (NameA + NameB + 8 * NameA)] = (_DWORD *)0xEEFFEEFF;// 初始化有被名字指向的部分
}
while ( v13 < v12 ); // 使用名字对棋盘进行初始化
拆分每一个Name字符为左右两部分,分别指出了需要修改的灯泡的行与列,然后设置其状态为 on。
自然的,下面就是需要我们去利用Key,把灯泡全弄为off就好了。然后这里又是一个问题。
前面的处理是一样的,拆分每一个key,然后指出需要修改的灯泡的行数与列数。接着,修改它四周的灯泡。
修改的代码如下:
[C++] 纯文本查看 复制代码 v20 = 5 * v19; // 20 = 4*5
if ( (&chess1)[5 * v19] != (_DWORD *)0xFEFEFEFE )// 检查棋盘分配,是否不为第0行
(&chess)[5 * v19 - 45] = (_DWORD *)((unsigned int)(&chess)[5 * v19 - 45] ^ 0x11111111);// 非0行,把上一行的数据 ^ 11111111
if ( (&chess2)[v20] != (_DWORD *)0xFEFEFEFE )// 检查是否不为最后一行(9)?会出现?XXXX
(&chess45)[v20] = (_DWORD *)((unsigned int)(&chess)[5 * v19 - 45] ^ 0x11111111);// 当前行(+9) = 上一行 ^ 11111111
if ( (&chess3)[v20] != (_DWORD *)0xFEFEFEFE )// 检查是否不为第0列
*(int *)((char *)&v38 + v20 * 4) = (unsigned int)(&chess)[5 * v19 - 45] ^ 0x11111111;// 前一个数据 = 当前数据 ^ 11111111
if ( (&chess4)[v20] != (_DWORD *)0xFEFEFEFE )// 检查是否不为第9列,XXXX
(&chess5)[v20] = (_DWORD *)((unsigned int)(&chess)[5 * v19 - 45] ^ 0x11111111);// 后一个数据 = 上一行 ^ 11111111
注释得有点乱,举个例子:
如果灯泡是属于正则内点中,那么可以表示为:
1 2 3 ? off ?
4 5 6 对应的状态为: ? ? ?
7 8 9 ? ? ?
然后这段的作用就是,选中了灯泡5,于是对灯泡2,4,6,8的状态进行修改。
再具体一点就是使用灯泡2的状态来设置灯泡2,4,6,8的状态,最终结果为:
? on ?
off ? off
? off ?
你可能认为我写错了,但是我的确是没有写错。这里4,6,8本身的状态被无条件忽视了,然后2的状态复制给了它们,然后2再改变自己的状态。于是,无论选中哪一个灯泡,都一定会在这灯泡隔壁设置两种状态,所以,通过这种正常的思路是不可能弄出正确的key的。更别提最后一行最后一列的状态都是没有正常设置的了。
并且,这里的判断也不严谨,因为这里都是利用上一行的数据来修改四周的数据的,所以,如果是位于第一行的话,就会造成数据溢出的状况。
然后,第3点就是对Key循环的地方有问题:
就结论来说,它循环的次数为 iKeyLen ^ 4.
对, 你没有看错,就连循环次数也是这种奇怪的情况。
于是,如果这真的是作者有意设置的话,那么只能用这种方法来完成keygen了。
把Name与key的变换都弄掉。具体来说就是:
Name: (空)
Key: 任意4位字符
OK,就是这么多了,最后附上一个爆破的,这样至少也应该能够得到一点分吧
|