吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 22611|回复: 63
收起左侧

[原创] 【算法分析】010 Editer

  [复制链接]
currwin 发表于 2014-6-4 12:57
本帖最后由 currwin 于 2014-6-4 12:55 编辑

【文章标题】【算法分析】010 Editer
【文章作者】currwin【F8LEFT】
【软件名称】010Editer v5.0
【软件大小】安装包 12.8 MB
【下载地址】自行搜索下载
【加壳方式】无
【编写语言】Qt (C++)
【操作平台】windows Xp sp3
【操作工具】OD,IDA Pro,VS 2010
【作者声明】在这个010Editor的破解随随便便都可以搜索出来的时候,研究它的算法就真的纯粹是感兴趣,为了学习而已,请各位大大多多指教。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
       说实话,发完这篇帖子后就打算潜水一段时间了,最近比较忙,而且发的东西又没有什么人气,至少算法分析类是不打算再发的了。估计随便破个软件发到发布区里面都能够得到更多的热心与回复,唉,不说了,说多都是泪。直接进入正题吧。     
       010Editor这个软件是使用重启验证的,它的注册信息会直接保存注册表的
      {HKEY_CURRENT_USER\Software\SweetScape\010 Editor}
      这个位置上面,名称是Name与Password,直接就是明码保存了。
      Password的格式有两个,这个我会在正文中说明。对应着Password,核心算法也可以分成两个,虽然他们都在同一个Call里面,但实际上计算部分是分开的。
      另外,由于是使用了Qt进行开发,所以逆向出来的程序与我们平时看到的有点不一样,这给Keygen带来了不少的麻烦。
      核心算法部分非常的长,如果贴ASM代码的话估计也没有几个人会看,就直接贴C源码了。
      IDA还不会使用,所以代码阅读起来可能会有一点吃力,我尽量加上了注释,希望能够使算法流程更加清晰一点。
[C++] 纯文本查看 复制代码
// 参数是固定的,u6 = 0x6,u3E08 = 0x3E08
unsigned int __thiscall sub_63CEB0(int this, unsigned int u6, unsigned int u3E08)
{
  int v3; // esi@1
  int v4; // eax@1
  unsigned int v5; // ebp@1
  int v6; // ebx@1
  signed int v7; // edi@3
  char P5; // bl@6
  __int16 v9; // di@7
  unsigned __int16 v10; // ax@7
  unsigned int v11; // ecx@7
  int v12; // edi@10
  __int16 v13; // dx@14
  unsigned __int8 v14; // cl@14
  unsigned int v15; // eax@14
  int v16; // eax@17
  unsigned int result; // eax@23
  unsigned int v18; // edx@28
  bool bFlag1; // cf@28
  bool bFlag2; // zf@28
  int v21; // eax@30
  signed __int32 *v22; // ecx@30
  unsigned int v23; // edx@30
  void *v24; // [sp-8h] [bp-34h]@21
  int QName; // [sp+Ch] [bp-20h]@17
  int v26; // [sp+10h] [bp-1Ch]@7
  char Pass[10]; // [sp+14h] [bp-18h]@3
  int v28; // [sp+28h] [bp-4h]@17

  v3 = this;
  v4 = *(_DWORD *)(this + 4);
  v5 = 0;
  v6 = this + 4;
  *(_DWORD *)(this + 48) = 0;
  *(_DWORD *)(this + 24) = 0;
  *(_DWORD *)(this + 52) = 0;
  if ( !*(_DWORD *)(v4 + 8) || !*(_DWORD *)(*(_DWORD *)(this + 8) + 8) )
    return 0x93u;
  StringToHex(Pass);                            // 将用户名转换为Hex
  v7 = (signed int)&off_816580;
  do
  {
    if ( (unsigned __int8)QString::operator__(v6, *(_DWORD *)v7) )
      return 0xE7u;
    v7 += 4;
  }
  while ( v7 < (signed int)&unk_816584 );       // P[3] 用于判断注册码类型,只有 9C 和 AC 是接受的,其他的值均会失败
  P5 = Pass[5];
  if ( Pass[3] == 0x9C )                        // 当P[3] = 9C 时V3+28为判断依据
  {
    LOBYTE(v26) = Pass[0] ^ Pass[6];
    v9 = (unsigned __int8)(Pass[2] ^ Pass[5]) + ((unsigned __int8)(Pass[1] ^ Pass[7]) << 8);// v9 = P[1]P[7] P[2]P[5]
    *(_DWORD *)(v3 + 28) = (unsigned __int8)Chg_1(Pass[0] ^ Pass[6]);//  return ((a1 ^ 0x18) + 61) ^ 0xA7;
                                                //  这里返回的值要大于6才会成功
    v10 = Chg_2(v9);                            // 整数:(unsigned __int16)(((a1 ^ 0x7892) + 19760) ^ 0x3421) / 11;
    v11 = *(_DWORD *)(v3 + 28);
    *(_DWORD *)(v3 + 32) = v10;
    if ( !v11 || !v10 || v10 > 0x3E8u )         // v11!=0,0<v10<3E8
      return 0xE7u;
    v12 = v11 < 2 ? v11 : 0;
  }
  else
  {
    if ( Pass[3] == 0xFC )
    {
      v12 = 255;                                // P[3] == FC时会失败,这些都可以忽略
      *(_DWORD *)(v3 + 28) = 255;
      *(_DWORD *)(v3 + 32) = 1;
      *(_DWORD *)(v3 + 48) = 1;
    }
    else
    {
      if ( Pass[3] != -84                       // Pass[3] == AC时,会进入这里
        || (v13 = (unsigned __int8)(Pass[1] ^ Pass[7]) << 8,// v13 = (P[1] ^ P[7]) << 8
            v14 = Pass[2] ^ Pass[5],            // v14 = P[2] ^ P[5]
            *(_DWORD *)(v3                      // 这个参数在这种情况下是无效的
                      + 28) = 2,
            v15 = (unsigned __int16)Chg_2(v14 + v13),// 这里,v15 = Chg_2()的效果与上面的 Chg_2的效果是等价的
            *(_DWORD *)(v3 + 32) = (unsigned __int16)v15,
            !(_WORD)v15)
        || v15 > 0x3E8 )                        // v15 != 0 且 v15 <= 0x3E8
        return 0xE7u;
      v5 = Chg_4(                               // 这里当P[3] = AC时,v5为判断关键
             ((unsigned __int8)Pass[0] ^ (unsigned __int8)Pass[6])// v5 = Chg_4(P[5]P[9] P[8]P[4] P[6]P[0])
           + ((((unsigned __int8)Pass[8] ^ (unsigned __int8)Pass[4])
             + (((unsigned __int8)P5 ^ (unsigned __int8)Pass[9]) << 8)) << 8),
             0x5B8C27u);
      *(_DWORD *)(v3 + 52) = v5;
      v12 = v5;
    }
  }
  QString::toUtf8(v3 + 4, &QName);              // 取名字QString
  v28 = 0;
  QByteArray::detach(&QName);                   // QName + C 就是指向名字的指针
  v16 = Chg_3(*(char **)(QName + 0xC), Pass[3] != -4, v12, *(_DWORD *)(v3 + 32));// Chg_3(UserName, P[3] != FC, v12, v3 + 32)
                                                // 利用加密表对用户名进行变换
  if ( Pass[4] != (_BYTE)v16 )                  // 使P[4] = (_BYTE)v16
    goto LABEL_20;                              // 跳转则失败
  if ( P5 != BYTE1(v16) )                       // 使P[5] = (_BYTE1(v16))
    goto LABEL_50;                              // 跳转则失败
  if ( Pass[6] != (unsigned __int8)((unsigned int)v16 >> 16) )// 使P[6] = BYTE2(V16)
  {
LABEL_20:                                       // 失败
    v28 = -1;
    if ( _InterlockedExchangeAdd((signed __int32 *)QName, 0xFFFFFFFFu) )
      return 0xE7u;
    v24 = (void *)QName;
LABEL_22:
    qFree(v24);
    return 0xE7u;
  }
  if ( Pass[7] != BYTE3(v16) )                  // 使P[7] = BYTE3(v16)
  {
LABEL_50:                                       // 失败
    v28 = -1;
    if ( _InterlockedExchangeAdd((signed __int32 *)QName, 0xFFFFFFFFu) )
      return 0xE7u;
    v24 = (void *)QName;
    goto LABEL_22;
  }
  if ( Pass[3] == 0x9C )
  {
    v18 = -1;
    bFlag1 = u6 < *(_DWORD *)(v3 + 28);
    bFlag2 = u6 == *(_DWORD *)(v3 + 28);
    goto LABEL_37;                              // v3 + 28 >= 6
  }
  if ( Pass[3] != 0xFC )
  {
    if ( Pass[3] == 0xAC && v5 )
    {
      v18 = -1;
      bFlag1 = u3E08 < v5;
      bFlag2 = u3E08 == v5;
LABEL_37:
      v28 = -1;
      if ( bFlag1 | bFlag2 )
      {
        if ( !_InterlockedExchangeAdd((signed __int32 *)QName, v18) )
          qFree((void *)QName);
        result = 0x2Du;                         // 成功
      }
      else
      {
        if ( !_InterlockedExchangeAdd((signed __int32 *)QName, v18) )
          qFree((void *)QName);
        result = 0x4Eu;                         // 早期版本
      }
      return result;
    }
    v22 = (signed __int32 *)QName;
    v28 = -1;
    v23 = -1;
LABEL_45:
    if ( _InterlockedExchangeAdd(v22, v23) )
      return 0xE7u;
    v24 = (void *)QName;
    goto LABEL_22;
  }
  v21 = Chg_4((unsigned __int8)Pass[0] + (((unsigned __int8)Pass[1] + ((unsigned __int8)Pass[2] << 8)) << 8), v16);
  v22 = (signed __int32 *)QName;
  v23 = -1;
  v28 = -1;
  if ( !v21 )
    goto LABEL_45;
  *(_DWORD *)(v3 + 24) = v21;
  if ( !_InterlockedExchangeAdd(v22, 0xFFFFFFFFu) )
    qFree((void *)QName);
  return 0x93u;
}



        上面的就是核心的算法了,不过只需要大致的看看就可以了。
        当程序返回0x2D的时候,便会注册成功,所以跟着这一条线索,
        我们追踪一下它的骨架。
[C++] 纯文本查看 复制代码
// 参数是固定的,u6 = 0x6,u3E08 = 0x3E08
unsigned int __thiscall sub_63CEB0(int this, unsigned int u6, unsigned int u3E08)
{  ...                                                      //省略无用的代码
  StringToHex(Pass);                            // 将用户名转换为Hex
  if ( Pass[3] == 0x9C )                        // 当P[3] = 9C 时V3+28为判断依据
  {
           ...
  }
  else
  {
    if ( Pass[3] == 0xFC )
    {
      ...                                                 // P[3] == FC时会失败,这些都可以忽略
    }
    else
    {
      if ( Pass[3] != 0xAC )                      // Pass[3] == AC时,进入这里才不会失败              
        return 0xE7u;
    }
  }
  if ( Pass[3] == 0x9C )
  {
    v18 = -1;                                                       //P[3] = 0x9C
    bFlag1 = u6 < *(_DWORD *)(v3 + 28);      //这里开始对结果进行验证
    bFlag2 = u6 == *(_DWORD *)(v3 + 28);    //设置标志旗
    goto LABEL_37;                              // v3 + 28 >= 6
  }
  if ( Pass[3] != 0xFC )
  {
    if ( Pass[3] == 0xAC && v5 )
    {
      v18 = -1;                                                  //P[3] = 0xAC
      bFlag1 = u3E08 < v5;                               //设置标志旗
      bFlag2 = u3E08 == v5;
LABEL_37:
      v28 = -1;
      if ( bFlag1 | bFlag2 )
      {
          ...
        result = 0x2Du;                         // 成功
      }
      else
      {
           ...
      }
      return result;
    }
   ...                                                  //下面的都只会返回失败
}

        上面一段便是验证的代码了,当然,估计如果翻译为switch语句的话会更加清晰,不过就算是用if else 的组合,也是足够让我们看到程序在做些什么的。
        我们会看到,返回0x2D的地方只有一个, 并且,只有当 P[3] = 0x9C 或者 0xAC的时候才会进入判定成功的段,其他的值都会失败。所以根据P[3]的具体的值,算法被分成了两个基本上完全不同的两个验证部分.         现在我们来看一看P[3] = 0x9C 的算法
[C++] 纯文本查看 复制代码
// 参数是固定的,u6 = 0x6,u3E08 = 0x3E08
unsigned int __thiscall sub_63CEB0(int this, unsigned int u6, unsigned int u3E08)
{
  v3 = this;
  StringToHex(Pass);                            // 将用户名转换为Hex
  P5 = Pass[5];
  if ( Pass[3] == 0x9C )                        // 当P[3] = 9C 时V3+28为判断依据
  {
    LOBYTE(v26) = Pass[0] ^ Pass[6];
    v9 = (unsigned __int8)(Pass[2] ^ Pass[5]) + ((unsigned __int8)(Pass[1] ^ Pass[7]) << 8);// v9 = P[1]P[7] P[2]P[5]
    *(_DWORD *)(v3 + 28) = (unsigned __int8)Chg_1(Pass[0] ^ Pass[6]);//  return ((a1 ^ 0x18) + 61) ^ 0xA7;
                                                //  这里返回的值要大于6才会成功
    v10 = Chg_2(v9);                            // 整数:(unsigned __int16)(((a1 ^ 0x7892) + 19760) ^ 0x3421) / 11;
    v11 = *(_DWORD *)(v3 + 28);
    *(_DWORD *)(v3 + 32) = v10;
    if ( !v11 || !v10 || v10 > 0x3E8u )         // v11!=0,0<v10<3E8
      return 0xE7u;
    v12 = v11 < 2 ? v11 : 0;
  }
  QString::toUtf8(v3 + 4, &QName);              // 取名字QString
  v28 = 0;
  QByteArray::detach(&QName);                   // QName + C 就是指向名字的指针
  v16 = Chg_3(*(char **)(QName + 0xC), Pass[3] != -4, v12, *(_DWORD *)(v3 + 32));// Chg_3(UserName, P[3] != FC, v12, v3 + 32)
                                                // 利用加密表对用户名进行变换
  if ( Pass[4] != (_BYTE)v16 )                  // 使P[4] = (_BYTE)v16
   // 失败;                              // 跳转则失败
  if ( P5 != BYTE1(v16) )                       // 使P[5] = (_BYTE1(v16))
    //失败;                              // 跳转则失败
  if ( Pass[6] != (unsigned __int8)((unsigned int)v16 >> 16) )// 使P[6] = BYTE2(V16)
     //失败;
  if ( Pass[7] != BYTE3(v16) )                  // 使P[7] = BYTE3(v16)

     //失败;
  if ( Pass[3] == 0x9C )
  {
    v18 = -1;
    bFlag1 = u6 < *(_DWORD *)(v3 + 28);
    bFlag2 = u6 == *(_DWORD *)(v3 + 28);
    goto LABEL_37;                              // v3 + 28 >= 6
  }
LABEL_37:
      v28 = -1;
      if ( bFlag1 | bFlag2 )
      {
        if ( !_InterlockedExchangeAdd((signed __int32 *)QName, v18) )
          qFree((void *)QName);
        result = 0x2Du;                         // 成功
      }
...
}

        这样一来就清晰多了。
        首先,
        result = Chg_1(P[0]^P[6]]), ;这里计算出来的v11便是最终比较的结果了。用result与u6进行对比,只有当result大于6的时候才会成功。
        v9 = 高位 P[1]^P[7],低位 P[2]^P[5];  v9 = (unsigned __int8)(Pass[2] ^ Pass[5]) + ((unsigned __int8)(Pass[1] ^ Pass[7]) << 8)
        紧接着便对v9进行变换
        v10 = Chg_2(v9)                 ; 同样的,这里计算出来的结果要在 1 到 0x3E8之间。
        这样,基本的数据初始化便算完成了。接着是对用户名进行加密。
        Chg_3(UserName, P[3] != FC, v12, v3 + 32);
        因为这里v12 = result < 2 ? v11 : 0;   但result必须是大于6的,故这里v12 恒等于0;
        而v3 + 32 便是用来存储v10的地方。所以整理一下,这个函数应该是这样的
        v16 = Chg_3(UserName, 1, 0, v10);
        最后,这里计算出来的v16的各个字节分别与 P[4],P[5],P[6],P[7]进行比较,只有他们都相等了才会进入判断的地方。
       最后再看一看,这种情况下只使用到了P[0] - P[7],所以密码的长度为 8*2 = 16个。
        那么,P[3] = 9C 的情况到这里便结束了,接下来看 P[3] = 0xAC的情况。   
[C++] 纯文本查看 复制代码
// 参数是固定的,u6 = 0x6,u3E08 = 0x3E08
unsigned int __thiscall sub_63CEB0(int this, unsigned int u6, unsigned int u3E08)
{
  v3 = this;
  v4 = *(_DWORD *)(this + 4);
  v5 = 0;
  v6 = this + 4;
  *(_DWORD *)(this + 48) = 0;
  *(_DWORD *)(this + 24) = 0;
  *(_DWORD *)(this + 52) = 0;
  StringToHex(Pass);                            // 将用户名转换为Hex
  P5 = Pass[5];
if ( Pass[3] != 0xAC                      // Pass[3] == AC时,会进入这里
        || (v13 = (unsigned __int8)(Pass[1] ^ Pass[7]) << 8,// v13 = (P[1] ^ P[7]) << 8
            v14 = Pass[2] ^ Pass[5],            // v14 = P[2] ^ P[5]
            v15 = (unsigned __int16)Chg_2(v14 + v13),// 这里,v15 = Chg_2()的效果与上面的 Chg_2的效果是等价的
            *(_DWORD *)(v3 + 32) = (unsigned __int16)v15,
            !(_WORD)v15)
        || v15 > 0x3E8 )                        // v15 != 0 且 v15 <= 0x3E8
        return 0xE7u;
      v5 = Chg_4(                               // 这里当P[3] = AC时,v5为判断关键
             ((unsigned __int8)Pass[0] ^ (unsigned __int8)Pass[6])// v5 = Chg_4(P[5]P[9] P[8]P[4] P[6]P[0])
           + ((((unsigned __int8)Pass[8] ^ (unsigned __int8)Pass[4])
             + (((unsigned __int8)P5 ^ (unsigned __int8)Pass[9]) << 8)) << 8),
             0x5B8C27u);
      *(_DWORD *)(v3 + 52) = v5;
      v12 = v5;
    }
  }
  QString::toUtf8(v3 + 4, &QName);              // 取名字QString
  v28 = 0;
  QByteArray::detach(&QName);                   // QName + C 就是指向名字的指针
  v16 = Chg_3(*(char **)(QName + 0xC), Pass[3] != -4, v12, *(_DWORD *)(v3 + 32));// Chg_3(UserName, P[3] != FC, v12, v3 + 32)
                                                // 利用加密表对用户名进行变换
 if ( Pass[4] != (_BYTE)v16 )                  // 使P[4] = (_BYTE)v16
   // 失败;                              // 跳转则失败
  if ( P5 != BYTE1(v16) )                       // 使P[5] = (_BYTE1(v16))
    //失败;                              // 跳转则失败
  if ( Pass[6] != (unsigned __int8)((unsigned int)v16 >> 16) )// 使P[6] = BYTE2(V16)
     //失败;
  if ( Pass[7] != BYTE3(v16) )                  // 使P[7] = BYTE3(v16)
     //失败;
    if ( Pass[3] == 0xAC && v5 )
    {
      v18 = -1;
      bFlag1 = u3E08 < v5;
      bFlag2 = u3E08 == v5;
      v28 = -1;
      if ( bFlag1 | bFlag2 )
      {
        result = 0x2Du;                         // 成功
      }
     ...
}
     
       当P[3] == AC时,貌似加密方法又复杂了一点。不过也是一样,是一直线的,没有任何的循环,分析起来也是同样简单的。
      首先是 v13 = (P[1] ^ P[7]) << 8; v14 = P[2] ^ P[5]
      v15 = Chg_2(v13 + v14);     //这里仔细想一想,v13 + v14 不就等于上面的v9么,然后 v15 就等价于上面的v10了,所以这部分是一样的。
      同样的,对v15的要求是 0 < v15 < 0x3E9
      接着是
      v5 = Chg_4(P[0]^P[6] + (P[8]^P[4])<<8 + (P[5]^P[9])<<16); //这里对v5的要求是 v5 > 0x3E08
      这些就是初始化的操作了
      紧接着就是对用户名进行变换了
      Chg_3(UserName, P[3] != FC, v12, v3 + 32);
       还是那个样子,P[3] == AC != FC, v12 = v5, v3 + 32 = v15
      整理一下就是
      v16 = Chg_3(UserName, 1, v5, v15);
      变换完的v16再各位与P[4],P[5],P[6],P[7]进行比较,都相同的话就成功了。
      注意一下,这里用到的密码位为 P[0] - P[9],密码长度为 10 * 2 =20个。
      前面的是16个,呵呵,没想到密码居然是长度可变的。
      好了,主体部分的算法基本上就是这些了,接下来给出一下Chg_1,2,3,4的具体流程吧。
Chg_1:
[C++] 纯文本查看 复制代码
char __cdec Chg_1(char a1)
{
  return ((a1 ^ 0x18) + 61) ^ 0xA7;
}

Chg_2:
[C++] 纯文本查看 复制代码
int __cdec Chg_2(__int16 a1)
{
  int result; // eax@1

  result = (unsigned __int16)(((a1 ^ 0x7892) + 0x4D30) ^ 0x3421) / 11;
  if ( (unsigned __int16)(((a1 ^ 0x7892) + 0x4D30) ^ 0x3421) % 11 )           //这里要求经过一堆变换后的结果是11的倍数才不会返回0
    result = 0;
  return result;
}



Chg_4:
[C++] 纯文本查看 复制代码
int __cdecl Chg_4(int a1, int Key)
{
  int v2; // ecx@1

  v2 = ((Key ^ a1 ^ 0x22C078) - 180597) ^ 0xFFE53167;
  return (v2 & 0xFFFFFFu) % 0x11 == 0 ? (v2 & 0xFFFFFFu) / 0x11 : 0;      //同样的,也是要求经过一堆变换后的结果是0x11的倍数
}


Chg_3:


[C++] 纯文本查看 复制代码
_DWORD __cdecl sub_63B3C0(char *Name, int a2, int a3, int a4)
{
  char *v4; // eax@1
  int i; // ebx@1
  int v6; // ebp@1
  char *v7; // edx@1
  char v8; // cl@2
  int v9; // edi@4
  int v10; // esi@4
  int v11; // eax@5
  int v13; // [sp+8h] [bp-Ch]@4
  int v14; // [sp+Ch] [bp-8h]@4
  int iNameLen; // [sp+10h] [bp-4h]@3

  v4 = Name;
  i = 0;
  v6 = 0;
  v7 = Name + 1;
  do
    v8 = *v4++;
  while ( v8 );
  iNameLen = v4 - v7;
  if ( (signed int)(v4 - v7) > 0 )
  {
    v9 = 15 * a4;
    v14 = 0;
    v13 = 0;
    v10 = 17 * a3;
    do                                          // 对名字的每一个进行转换
    {
      v11 = toupper((unsigned __int8)Name[i]);  // 小写转大写
      if ( a2 )                                 // 这里总是成立,因为P[3] != FC
        v6 = Name_Table[(unsigned __int8)v13]   // 取加密表进行变换
           + Name_Table[(unsigned __int8)v9]
           + Name_Table[(unsigned __int8)v10]
           + Name_Table[(unsigned __int8)(v11 + 47)]
           * (Name_Table[(unsigned __int8)(v11 + 13)] ^ (v6 + Name_Table[v11]));
      else                                      // 下面的可以忽略
        v6 = Name_Table[(unsigned __int8)v14]
           + Name_Table[(unsigned __int8)v9]
           + Name_Table[(unsigned __int8)v10]
           + Name_Table[(unsigned __int8)(v11 + 23)]
           * (Name_Table[(unsigned __int8)(v11 + 63)] ^ (v6 + Name_Table[v11]));
      v13 += 19;
      v14 += 7;
      ++i;
      v10 += 9;
      v9 += 13;
    }
    while ( i < iNameLen );
  }
  return v6;
}
        这几个函数都是没有什么好讲的,因为都是很简单的。
        算法分析完了,要如何去进行Keygen才是关键所在。

         一个最简单的办法就是随机生成P[0]~P[9],然后代入算法里面验证是否满足算法,满足的话就输出结果,不满足的话就继续进行循环,直到找到一组可以使用的Key为止。
         这个方法很好用,不过在这里就难说了。

         只看看P[3] = 0x9C 的情况就可以知道这方法在这里是多么的糟糕了。
               v9 = (unsigned __int8)(Pass[2] ^ Pass[5]) + ((unsigned __int8)(Pass[1] ^ Pass[7]) << 8);   这里,v9由4个密码运算得出
                v10 = Chg_2(v9);     Chg_2的计算中,要求v9经过一堆变换后,必须是11的倍数,这是非常难以满足的。


         看看 Chg_2 与 Chg_4 的代码中,要求对参数进行奇怪的变换,最后居然还要求是11 或者 是 0x11 的倍数。这得多难满足。并且他们的参数都是由多个密码组合而成的,这样想random出正确的结果就更加难了,所以这个方法不行。反正我就试过用计算机random了很久也random不出来一个正确的Key。
       还有另外一个方法。
        我们设 x为注册码,y为判断结果,通常是 y = f(x) 求出结果的。假如,我们可以找到一个函数g,使得 x = g(y),不就可以直接通过结果逆推出注册码么。
         还是同样的例子,在上面中,假如找到了 Chg_2 的 逆函数,Chg_2_1,使得 v9 = Chg_2_1(v10)。那么我们就只需要random一个数据v10,但是却可以确定两个数据P[2]^P[5],P[1]^P[7]。
       把这个想法扩展一下,可以得到keygen的大致思路。
       P[3] == 0x9C 时,
              random出 v10,和 result
         调用 Chg_1_1(result)                               可以确定 P[0]^P[6]的值。
         调用 Chg_2_1(v10)                                 可以确定P[2]^P[5],P[1]^P[7]的值
         调用v16 = Chg_3(UserName, 1, 0, v10);可以确定 P[4],P[5],P[6],P[7]的值。
          最后,P[0] = (P[0] ^ P[6]) ^ P[6]      可以求出P[0]
         这样,密码就出来了。
       P[3] == 0xAC时,
                 random出 v5与 v15
        调用 Chg_2_1(v15)                                 可以确定P[2]^P[5], P[1]^P[7]的值
         调用Chg_4_1(v5)                                   可以确定P[5]^P[9], P[8]^P[4], P[0]^P[6]的值。
         调用v16 = Chg_3(UserName, 1, v5, v15);可以确定P[4], P[5], P[6], P[7]的值。
          最后:
         P[0] = (P[0]^P[6]) ^ P[6];
         P[1] = (P[1]^P[7]) ^ P[7];
         P[2] = (P[2]^P[5]) ^ P[5];
         P[8] = (P[8]^P[4]) ^ P[4];
         P[9] = (P[5]^P[9]) ^ P[5];
          这样又一组密码出来了,那么,只需要保证能够找到Chg_1,Chg_2,Chg_4的逆算法 Chg_1_1, Chg_2_1, Chg_4_1就可以了。
Chg_1_1
[C++] 纯文本查看 复制代码
char Chg_1_1(char a1)                                                                                                                //Chg_1的逆算法
{
        return ((a1 ^ 0xA7) - 61) ^ 0x18;
}


Chg_2_1:
[C++] 纯文本查看 复制代码
unsigned short Chg_2_1(unsigned short a1)                                                                                //Chg_2的逆函数。不求逆估计是算不出来的
{
        return ((((a1 * 11) ^ 0x3421) - 0x4D30) ^ 0x7892);
}


Chg_4_1:
[C++] 纯文本查看 复制代码
int Chg_4_1(int v2, int key)                                                                                        //Chg_4的逆函数
{
        int a1;
        v2 = v2 * 0x11;
        a1 = ((v2 ^ 0xFFE53167) + 0x2C175) ^ 0x22C078 ^ key;
        return (a1 & 0xFFFFFF);
}


         虽然Keygen是出来了,不过这个软件貌似还会不定时去链接网络验证key是否有效,不过,这个也不难去掉,大家搞起吧。
        具体的Keygen代码我就不给了,因为不难啊。附上几张图,玩一下:
7.jpg
【版权声明】: 本文原创于F8LEFT, 转载请注明作者并保持文章的完整, 谢谢!
                                                                                      2014.6.4







010EditerKeygen.zip

30.51 KB, 下载次数: 58, 下载积分: 吾爱币 -1 CB

Keygen

点评

请小心呵护自己曾经的梦想和当初的激情,大家需要你!  发表于 2014-6-12 12:47
师傅好腻害...  发表于 2014-6-11 20:04

免费评分

参与人数 7热心值 +7 收起 理由
冰海之枫 + 1 虽然看不懂,但是感觉说的还是比较详细的。
孤独的红领巾 + 1 膜拜+1
yAYa + 1 算法非常详细,膜拜.
my1229 + 1 楼主分析得很详细,来学习并支持。谢谢@Tha.
beer9989 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
无邪 + 1 赞,不多说
Shark恒 + 1 我很赞同!

查看全部评分

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

cyw 发表于 2014-6-4 13:05
  主要是现在算法分析比较高深的技术  现在论坛的小白太多  看不懂主要是  我也是其中一员。  楼主发的技术贴都比较有质量的, 希望楼主能够坚持,给我们这些小白留下以后学习的资源。
my1229 发表于 2014-6-5 10:18
楼主分析得很详细,来学习并支持。谢谢@Thanks!
loveskyok 发表于 2014-6-4 20:04
star0angel 发表于 2014-6-4 13:15
算法这块还是比较难啊
Passerby 发表于 2014-6-4 13:09
虽然现在回复的人 比较少,楼主不要灰心,因为很多小菜还没接触到算法这一块,

也包括我,不过这个以后拿出来就火了,就跟以前的NET的帖子不火,现在NET这块

直接火爆了。
心断空 发表于 2014-6-5 12:39
我F8大大是大牛,不解释
101 发表于 2014-6-10 08:48
请问学会算法分析该从什么地方入手呢?

点评

首先你得学会爆破掉关键的地方,然后你得看的懂汇编。代码分轻重,看得多了你自然就会懂的了。  发表于 2014-6-10 15:32
九零-鑫鑫 发表于 2014-6-10 09:14
分析的很详细
aishen 发表于 2014-6-10 13:18
不愧是大神,就是厉害
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

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

GMT+8, 2024-4-23 22:24

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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