欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

新手入门的第一次爆破——010Editor

程序员文章站 2022-07-05 23:14:15
大约一个月以前,准备认真踏实学习加密解密。在这段时间里,汇编语言、看雪经典《加密与解密(第三版)》、一系列相关工具及文档等等弄得我是晕头转向,但是我坚信持之以恒,坚持到底,我一定能突破困难,突...
大约一个月以前,准备认真踏实学习加密解密。在这段时间里,汇编语言、看雪经典《加密与解密(第三版)》、一系列相关工具及文档等等弄得我是晕头转向,但是我坚信持之以恒,坚持到底,我一定能突破困难,突破自己!希望能成功的成为一名Reserver-er.
本文是我的第一个爆破程序,菜鸟水平,但对我是很大的鼓励了,还请大牛们不要见笑。
 
很早之前,下载了一个010Editor,但是基本就没用(到目前为止,下载到电脑里的很多反编译,逆向等很多工具还没动。。。辩解一下呀:不是我懒,一是软件实在是太多,而且很多都不知道是干什么的;二是一直在学习IDA和olldbg和Stud_PE等,这几个已经让我感觉是 博大精深 了!只能说,后面的路还有很长,加油!!!)今天为了找一个功能,运行了010Editor,但是发现他的试用期已经到期了。。。正好,发现是要输入用户名和注册码的,心里顿时一阵窃喜~O(∩_∩)O哈哈~,学了这么久的破解终于有用武之地了!想到这,我就想起了那句哲人的名言:“当你手里拿着锤子的时候,整个世界都成了钉子”!O(∩_∩)O哈哈~,太精辟了!
 
好了,闲话说得太多,别喷我哦。。。言归正传,开工!
首先运行软件,弹出输入注册码的页面,随便填入如下:
 新手入门的第一次爆破——010Editor

很遗憾,弹出错误:
新手入门的第一次爆破——010Editor
不过没关系,关闭,用olldbg重新打开,字符串查找“Invalid password”,很快就找到如下:
 
引用:
超级字串参考+        , 条目1019
 地址=0046CF76
 反汇编=MOV EDX,010Edito.005C9733
 文本字串=invalid password. please enter your name and password exactly as given when registering.
双击进入,发现不仅仅这一句,前后还有很多关于比如password XXX的信息,不过我最感兴趣的是这一句:
 
引用:
password accepted. thank you for purchasing 010 editor!
将局部这段代码显示如下:
 
引用:
0046CE5A  |.  6A 01         PUSH 1                                   ; /Arg2 = 00000001
0046CE5C  |.  FF35 A8375A00 PUSH DWORD PTR DS:[5A37A8]               ; |该地址指向输入的用户名和密码,下面函数要跟入
0046CE62  |.  E8 25E3FFFF   CALL 010Edito.0046B18C                   ; \010Edito.0046B18C
0046CE67  |.  83C4 08       ADD ESP,8
0046CE6A  |.  8945 90       MOV DWORD PTR SS:[EBP-70],EAX
0046CE6D  |.  817D 90 DB000>CMP DWORD PTR SS:[EBP-70],0DB
0046CE74      75 35         JNZ SHORT 010Edito.0046CEAB
0046CE76  |.  66:C745 B0 50>MOV WORD PTR SS:[EBP-50],50
0046CE7C  |.  BA F9955C00   MOV EDX,010Edito.005C95F9                ;  password accepted. thank you for purchasing 010 editor!
可以看出,只有010Edito.0046B18C的函数返回值为0xDB,也就是219时,用户名密码才是通过的。用IDA加载010Editor,还原该函数,代码如下:
代码:
signed int __cdecl sub_46B18C(int a1, unsigned int a2)
{
  int v2; // eax@1
  int v3; // eax@2
  signed int result; // eax@5
  int v5; // eax@8
  int v6; // eax@9
 
  v2 = sub_46B200(a1, a2) - 45;
  if ( v2 )
  {
    v3 = v2 - 33;
    if ( v3 )
    {
      if ( v3 == 153 )
      {
        result = 375;
      }
      else
      {
        v5 = sub_46B4D0(a1) - 23;
        if ( v5 )
        {
          v6 = v5 - 19;
          if ( v6 )
          {
            if ( v6 == 270 )
              result = 47;
            else
              result = 375;
          }
          else
          {
            result = 375;
          }
        }
        else
        {
          result = 113;
        }
      }
    }
    else
    {
      result = 16;
    }
  }
  else
  {
    result = 219;
  }
  return result;
}
很明显看出,还是返回219的条件是v2 = sub_46B200(a1, a2) - 45要等于0,也就是sub_46B200需要返回值为45(0x2d)。双击sub_46B200进入该函数,代码如下:
代码: www.2cto.com
signed int __cdecl sub_46B200(int address, unsigned int a2)
{
  signed int result; // eax@3
  int v3; // eax@5
  char v4; // ST0C_1@5
  __int16 v5; // ST10_2@9
  int v6; // ST0C_4@16
  int v7; // ST08_4@16
  int v8; // ST04_4@16
  const char *v9; // eax@16
  unsigned __int8 v10; // [sp+4h] [bp-3Ch]@4
  unsigned __int8 v11; // [sp+5h] [bp-3Bh]@9
  unsigned __int8 v12; // [sp+6h] [bp-3Ah]@9
  char v13; // [sp+7h] [bp-39h]@8
  char v14; // [sp+8h] [bp-38h]@16
  char v15; // [sp+9h] [bp-37h]@9
  char v16; // [sp+Ah] [bp-36h]@9
  char v17; // [sp+Bh] [bp-35h]@9
  int v18; // [sp+Ch] [bp-34h]@29
  unsigned int v19; // [sp+10h] [bp-30h]@16
  int v20; // [sp+14h] [bp-2Ch]@4
  __int16 v21; // [sp+28h] [bp-18h]@5
  int v22; // [sp+34h] [bp-Ch]@5
  char v23; // [sp+3Ch] [bp-4h]@5
 
  __InitExceptBlockLDTC();
  if ( (unsigned __int8)System::AnsiString::IsEmpty(address + 4)
    || (unsigned __int8)System::AnsiString::IsEmpty(address + 8) )
    return 147;
  sub_46BCD8(address, &v10);
  v20 = 0;
  do
  {
    v21 = 8;
    sub_5A0BA4(&v23, *(&off_5C8D50 + v20));
    ++v22;
    v3 = System::AnsiString::operator__(address + 4, &v23);
    --v22;
    System::AnsiString::_AnsiString(v3);
    if ( v4 )
      return 231;
    ++v20;
  }
  while ( v20 < 1 );
  if ( v13 == -100 )
  {
    v5 = (unsigned __int8)(v15 ^ v12) + ((unsigned __int8)(v17 ^ v11) << 8);
    *(_DWORD *)(address + 24) = (unsigned __int8)sub_46C612(v16 ^ v10);
    *(_DWORD *)(address + 28) = (unsigned __int16)sub_46C628(v5);
    if ( !*(_DWORD *)(address + 24) || !*(_DWORD *)(address + 28) || *(_DWORD *)(address + 28) > 0x3E8u )
      return 231;
  }
  else
  {
    if ( v13 != -4 )
      return 231;
    *(_DWORD *)(address + 24) = 255;
    *(_DWORD *)(address + 28) = 1;
  }
  v6 = *(_DWORD *)(address + 28);
  v7 = *(_DWORD *)(address + 24);
  v8 = v13 == -100;
  v9 = (const char *)System::AnsiString::c_str(address + 4);
  v19 = sub_46C370(v9, v8, v7, v6);
  if ( (_BYTE)v19 == v14 )
  {
    if ( BYTE1(v19) == v15 )
    {
      if ( (unsigned __int8)(v19 >> 16) == v16 )
      {
        if ( BYTE3(v19) == v17 )
        {
          if ( v13 == -100 )
          {
            if ( a2 <= *(_DWORD *)(address + 24) )
              result = 45;                      // ==45 0x2d时才正确
            else
              result = 78;
          }
          else
          {
            if ( v13 == -4 )
            {
              v18 = (v12 << 16) + (v11 << 8) + v10;
              v18 = sub_46C5CD((v12 << 16) + (v11 << 8) + v10, v19);
              if ( v18 )
              {
                *(_DWORD *)(address + 20) = v18;
                result = 147;
              }
              else
              {
                result = 231;
              }
            }
            else
            {
              result = 231;
            }
          }
        }
        else
        {
          result = 231;
        }
      }
      else
      {
        result = 231;
      }
    }
    else
    {
      result = 231;
    }
  }
  else
  {
    result = 231;
  }
  return result;
}
可以看到在我注释的地方,就是返回45的情况。
正确的挑战指令在olldbg中如下:
 
引用:
0046B447   > \B8 2D000000   MOV EAX,2D
0046B44C   .  8B55 D8       MOV EDX,DWORD PTR SS:[EBP-28]
0046B44F   .  64:8915 00000>MOV DWORD PTR FS:[0],EDX
0046B456   .  EB 73         JMP SHORT 010Edito.0046B4CB
所以,爆破就很简单了。直接在验证用户名,密码存在并且密码位数正确的地方通过后的指令改为到此的跳转:
即一些这句
 
引用:
0046B2B6     /0F85 83000000 JNZ 010Edito.0046B33F
,改成:
 
引用:
0046B2B6   . /E9 8C010000   JMP 010Edito.0046B447
0046B2BB     |90            NOP
将修改写到可执行文件,就大功告成了!
 
事实上,我花了更多的时间希望研究出他的验证函数,IDA还原如下:
代码:
int __cdecl sub_46C370(const char *s, int a2, char a3, char a4)
{
  int v4; // eax@2
  size_t v6; // [sp+4h] [bp-10h]@1
  signed int i; // [sp+8h] [bp-Ch]@1
  int v8; // [sp+10h] [bp-4h]@1
 
  v8 = 0;
  v6 = strlen(s);
  for ( i = 0; i < (signed int)v6; ++i )
  {
    v4 = toupper(s[i]);
    if ( a2 )
      v8 = word_5C91A8[(unsigned __int8)(15 * a4 + 13 * i)]
         + word_5C91A8[(unsigned __int8)(17 * a3 + 9 * i)]
         + word_5C91A8[(unsigned __int8)(19 * i)]
         + word_5C91A8[(unsigned __int8)(v4 + 47)] * (word_5C91A8[(unsigned __int8)(v4 + 13)] ^ (v8 + word_5C91A8[v4]));
    else
      v8 = word_5C91A8[(unsigned __int8)(15 * a4 + 13 * i)]
         + word_5C91A8[(unsigned __int8)(17 * a3 + 9 * i)]
         + word_5C91A8[(unsigned __int8)(7 * i)]
         + word_5C91A8[(unsigned __int8)(v4 + 23)] * (word_5C91A8[(unsigned __int8)(v4 + 63)] ^ (v8 + word_5C91A8[v4]));
  }
  return v8;
}
然后返回值需要满足上面sub_46B200中的一系列条件,不过很遗憾,分析还感觉有些困难,等以后功底更深了再分析算法吧~