吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 14006|回复: 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++] 纯文本查看 复制代码
_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,就是这么多了,最后附上一个爆破的,这样至少也应该能够得到一点分吧

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, 2024-11-21 16:44

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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