吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 14170|回复: 14
收起左侧

[第二题] 【吾爱破解2014CrackMe大赛】【第二组】

[复制链接]
currwin 发表于 2014-10-23 09:27
本帖最后由 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++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
_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++] 纯文本查看 复制代码
1
2
3
4
5
iLine = 0;
do {
if(iLine == 9)
....//设置灯泡为最后一行
} while(iLine < 9)

        你一定是在逗我,这样写的话,无论如何,最后一行的数据是无法被设定的,同理,最后一列也是没有被设置的。也就是说,这灯泡阵列的最后一行,最后一列,能不用就尽量不用,否则不知道会出现什么结果。。。

        然后是根据名字设置灯泡,这个挺好懂的:
[C++] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
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++] 纯文本查看 复制代码
1
2
3
4
5
6
7
8
9
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,就是这么多了,最后附上一个爆破的,这样至少也应该能够得到一点分吧

Crack.rar

681.54 KB, 下载次数: 43

免费评分

参与人数 2热心值 +2 收起 理由
yypE + 1 谢谢,厉害
Ylca + 1 谢谢@Thanks! 很厉害

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| currwin 发表于 2014-10-23 12:50
标题弄错了,应该是第二组的
ximo 发表于 2014-10-24 17:55
最后成绩:80*2=160分
评委评价:利用bug,顺利kg,攻击有效。
谢谢参与,请继续加油。
ximo 发表于 2014-10-24 17:56
此cm得确存在逻辑bug,楼主顺利找到了bug,并且在此情况下,依然找到了通解,要赞一下。
a070458 发表于 2014-10-24 23:52
因为最后一个想来想去没办法设置
我还傻乎乎去解方程,去穷举
 楼主| currwin 发表于 2014-10-25 09:05
ximo 发表于 2014-10-24 17:56
此cm得确存在逻辑bug,楼主顺利找到了bug,并且在此情况下,依然找到了通解,要赞一下。

      谢谢评委,O(∩_∩)O哈哈~,瞬间找回自信了
 楼主| currwin 发表于 2014-10-25 09:07
a070458 发表于 2014-10-24 23:52
因为最后一个想来想去没办法设置
我还傻乎乎去解方程,去穷举

cm是人写出来的,出现逻辑上的错误也是非常正常的。就像你看那些教科书一样,不可能全是正确的,正确率有%75以上就应该庆祝了
Avenshy 发表于 2014-10-25 11:50
看到排行榜默默地进来....你那么会找bug你家里人知道吗……{:1_931:}
Bigtang 发表于 2014-10-30 20:56
膜拜了。yd的思维。这个游戏叫 点灯游戏
bess 发表于 2014-11-3 00:06
真的很厉害   觉得自己差好远
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-12 11:21

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表