飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 23543|回复: 75

[原创] Internet_Download_Manager 6.36 注册算法与网络验证分析

    [复制链接]
  • TA的每日心情
    开心
    2021-7-27 17:21
  • 签到天数: 9 天

    [LV.3]偶尔看看II

    发表于 2019-12-31 20:51:05 | 显示全部楼层 |阅读模式
    本帖最后由 Rooking 于 2020-1-2 19:08 编辑

    飘云阁全网首发,未经许可,禁止转载
    更新注册表CLSID生成算法

    一直使用的都是破解版的IDM,但版本太低了,有时出现无法截获下载请求的问题。再加上网上只有各种补丁工具,没有找到有人分析IDM注册机制的文章,今天就把最新的IDM6.36做掉,实现无需对程序进行任何patch,就能实现我们想要的注册效果。

    首先通过字符串切入到关键点,没用的图片就不贴了。点击注册按钮,胡乱输入注册信息会提示注册码错误('You have entered incorrect Serial Number.'),对该字符串查找交叉引用得到唯一一处结果如下:
    1.png
    此处将某指针指向了该字符串,继续查找该指针的交叉引用,会得到多组结果,其中一处猜测为验证函数。
    2.png
    此处完全可以静态分析出注册码的格式为“xxxxx-xxxxx-xxxxx-xxxxx”,当然网上也有现成的注册码供我们参考。刚好以‘-’为分隔符,分成了四部分,接下来程序会对这四部分进行验证。
    3.png
    注释写的很清楚,就不过多解释了,四部分全是相同的操作,这里跳过重复的部分直接分析下一环节的验证过程。
    4.png
    验证完毕后就是注册表的操作了,将注册码和身份信息录入注册码,程序会退出。此时本地注册机制已分析完毕,仅给出暴力穷举函数的实现代码,依次暴力穷举四部分,再随机组合就能得到程序全部可用的注册码(给出一组“222YA-2NT7U-2C419-2WKXT”)。
    [Python] 纯文本查看 复制代码
    import itertools
    def getSN(num):
        table = '2YOPB3AQCVUXMNRS97WE0IZD4KLFGHJ8165T'
        for tmp in itertools.product(range(len(table)),repeat=5):
            sum=0
            for i in tmp:
                sum += sum * 36 + i
            if sum % num == 0:
                print "".join(map(lambda x:table[x],tmp))
    但此时我们的工作还没结束,如果我们断开网络,IDM自然也能正常运行。但一旦联网就会弹出虚假注册码的提示,很明显是网络校验,网上绝大多数人的做法都是找到验证点patch掉。即使绕过启动时的一处验证,在运行时程序也会开子线程进行验证,patch思路这里不做探讨。
    今天我们尝试另辟蹊径,我们发现只要程序连过一次网,无论以后网络是否连接都会直接弹出虚假注册码提示,有理由怀疑这个虚假提示信息被存储到了本地。
    后续分析发现,这些弹窗提示字符串都是加密存储的,会在内存中动态解密。因此对MessageBox下断并向上回溯。
    5.png
    定位到此处,分析发现只有该函数返回为0时,程序不会弹出错误提示,并能正常运行出主界面(后续还会有校验,即仅patch此处也行不通),接下来分析该函数。
    6.png
    该函数逻辑有些混乱,感兴趣可以自己分析一下。这里仅关注关键点,直接给出正确的执行路径。首先程序从注册表中读取MData表项,并将其作为sub_452030的参数进行解密操作,最后将结果与我们输入的注册码进行比较,相等则成功。接下来分析sub_452030函数。 7.png
    8.png
    很清晰的加解密函数结构,为了防止FindCrypt插件,程序作者对加密算法的S盒进行了加密,内存中动态解密。通过识别S盒可以得到IDM使用的算法为RC2,而密钥则是传入的字符串“58BE20ast4si5ls2D13”,没有看到IV自然是ECB模式,至此难点都已分析完毕。我们只需要修改注册表中MData表项的值为RC2算法加密后的HEX值即可,给出与我上面提供的注册码(“222YA-2NT7U-2C419-2WKXT”)对应的MData “6806f3ac00e2967a1aaafc9cfd47b60806cd6e7f4828b021”
    9.png
    最后啰嗦一句,在32位系统上注册表的路径是不一样的,如果你无法确定路径是什么,或者以后IDM更新了,请使用注册表监视工具Process Monitor。本文主要分析IDM的注册与网络验证机制,最后给出的无需patch的注册操作仅供参考,测试到目前为止还没有出现问题,如果翻车了,就当学习一下思路吧。
    10.png 11.png

    =================================================================================================

    最终不出所料,果真翻车了,IDM后续还有几处隐藏检测点。只输入上述的内容,会不定时弹出虚假注册码的弹窗,但弹窗不会影响正常使用,即使被呼出注册窗口直接关闭即可。

    但对于强迫症来说依然受不了,本文则刨根问底,一探究竟。注意到当对某链接重新下载时,会立即弹出错误提示。最优的做法是监控注册表,程序可能在某个位置又把注册码拿出来校验了一次。实验发现,在开始下载和下载完成两个时机程序都会主动检测,如下图所示:
    12.png

    这里有重要,如上图所示,在读Serial表项前后程序会读取某表的默认项,如上图高亮处所示,而这里存放的注册码的密文,该密文是什么请继续往下看。
    13.png
    14.png

    上图为一处验证点,sub_452030依然是上文讲到的RC2解密函数,密钥为“lmW02kjM4002FAnzwwsA1”,最后strcmp则是比较解密结果和注册码是否一致,不再赘述。唯一与上文不同的是,这次多了一个sub_593700函数,因此我们对注册码加密后,还必须分析该函数,再进行一层编码。

    [C++] 纯文本查看 复制代码
    int __cdecl sub_593700(_BYTE *a1, int a2, int *a3)
    {
      _BYTE *i; // edi
      char *v4; // eax
      int v5; // edx
      int v6; // ebx
      int v7; // eax
      int v8; // esi
      int result; // eax
      _BYTE *v10; // esi
      _BYTE *v11; // ecx
      unsigned int v12; // ebp
      int v13; // edx
      _BYTE *v14; // esi
      int v15; // eax
      int v16; // [esp+14h] [ebp+4h]
    
      for ( i = a1; *i == ' ' || *i == 9; ++i )
        ;
      v4 = i + 1;
      if ( dword_665B5C[*i] <= 0x3F )
      {
        do
          v5 = *v4++;
        while ( dword_665B5C[v5] <= 0x3F );
      }
      v6 = v4 - i - 1;
      v7 = (v6 + 3) / 4;
      v8 = 3 * v7;
      v16 = 3 * v7;
      result = sub_5806A0(a2, 3 * v7 + 7);
      if ( result )
      {
        if ( a3 )
          *a3 = v8;
        v10 = sub_580690(a2);
        v11 = i;
        if ( v6 > 0 )
        {
          v12 = (v6 + 3) >> 2;
          v6 -= 4 * v12;
          do
          {
            v13 = v11[1];
            v11 += 4;
            *v10 = 4 * LOBYTE(dword_665B5C[*(v11 - 4)]) | (dword_665B5C[v13] >> 4);
            v14 = v10 + 1;
            *v14 = 16 * LOBYTE(dword_665B5C[*(v11 - 3)]) | (dword_665B5C[*(v11 - 2)] >> 2);
            v10 = v14 + 2;
            --v12;
            *(v10 - 1) = LOBYTE(dword_665B5C[*(v11 - 1)]) | (LOBYTE(dword_665B5C[*(v11 - 2)]) << 6);
          }
          while ( v12 );
        }
        if ( v6 & 3 )
        {
          if ( dword_665B5C[*(v11 - 2)] <= 63 )
            v15 = v16 - 1;
          else
            v15 = v16 - 2;
          v16 = v15;
        }
        *(sub_580690(a2) + v16) = 0;
        result = 1;
      }
      return result;
    }


    代码如上,很明显是base64解码算法,此时不能确实是否为标准base64,我们只需要用rc2加密注册码再以标准base64编码,替换到对应默认表项测试程序即可验证(最终符合我们猜测,提供与我上文提供的注册码对应的密文“Ib1wA+FbvWMoIqeSo2c2n003utBSSh0w”)。

    此时,开始下载的检测点就被干掉了,而下载完成的监测点是一模一样的使用相同的方式进行替换。程序依然再测试中,不能保证后续没有其他验证。

    总结一下目前为止发现的验证机制:
    1. 本机注册码校验:非常挫的四则运算最后取余
    2. MData表项校验:来自服务器下发,为RC2加密的HEX值
    3. 默认表项校验:目前发现两处,来自服务器下发,为RC2加密的Base64值


    本文更多是作为分析软件验证机制,不提供傻瓜式一键Hack成品。而注册表项不同电脑可能不一样,需要你再看懂文章的前提下使用ProcMon找到自己电脑的位置(看MData结尾的自然就一定对了,找默认表项则是看读Serial表项的前后)。
    更理想的方案是分析注册时软件与服务器的通信协议,直接伪造服务器下发的MData和默认表项密文,这样就不需要考虑找注册表的问题了,而且不会遗漏。


    =======================================================================================================================================


    和rooking表哥交流了一下,把注册表CLSID的生成算法搞定了,给做通用注册机的表哥填坑。
    [C++] 纯文本查看 复制代码
    if ( sub_4C20F0(&v119) )                      // a22fd67
      {
        sub_624442(&v28, &v120);
        LOBYTE(v123) = 1;
        if ( sscanf(v28, aLx, &sum) == 1 )
        {
          if ( strlen(::a3) > 4 )
          {
            strncpy(v117, ::a3, 5u);
            v118 = 0;
            v1 = 1;
            v2 = 0;
            do
            {
              v3 = v117[v2++];
              v1 = 7 * v1 + 2 * v3;
            }
            while ( v2 < 5 );
            sum += v1;
          }
          v4 = v28;
          v5 = 1.33;
          if ( strlen(v28) < 0x20 )
          {
            while ( 1 )
            {
              v121 = sum;
              v6 = (sum * v5);
              v122 = v5 + 0.27;
              sub_624442(v117, &v28);
              LOBYTE(v123) = 2;
              sub_621421(&v28, aSLx, *v117, v6);
              LOBYTE(v123) = 1;
              sub_6246CD(v117);
              v4 = v28;
              if ( strlen(v28) >= 0x20 )
                break;
              v5 = v122;
            }
          }
        }
        else
        {
          v4 = v28;
        }
        if ( strlen(v4) > 0x1F )
        {
          memset(&a3, 0x2Du, 0x18u);
          v7 = 0;
          v8 = v4 + 31;
          do
            *(&a3 + v7++) = *v8--;
          while ( v7 < 8 );
          v9 = 9;
          if ( v7 < 12 )
          {
            v10 = 12 - v7;
            v11 = &v4[-v7 + 31];
            v7 = 12;
            do
            {
              *(&a3 + v9++) = *v11--;
              --v10;
            }
            while ( v10 );
          }
          v12 = v9 + 1;
          if ( v7 < 16 )
          {
            v13 = 16 - v7;
            v14 = &v4[-v7 + 31];
            v7 = 16;
            do
            {
              *(&a3 + v12++) = *v14--;
              --v13;
            }
            while ( v13 );
          }
          v15 = v12 + 1;
          if ( v7 < 20 )
          {
            v16 = 20 - v7;
            v17 = &v4[-v7 + 31];
            v7 = 20;
            do
            {
              *(&a3 + v15++) = *v17--;
              --v16;
            }
            while ( v16 );
          }
          v18 = v15 + 1;
          if ( v7 < 32 )
          {
            v19 = &v4[-v7 + 31];
            v20 = &a3 + v18;
            v21 = 32 - v7;
            do
            {
              *v20++ = *v19--;
              --v21;
            }
            while ( v21 );
          }

    算法很简单,基本上就是copy就完事,会获取到磁盘驱动器卷标序列号和注册码的前5位进行计算,这里仅贴出以上代码对应的Python算法。
    [Python] 纯文本查看 复制代码
    import math
    VolumeSerialNumber="CA22FD67"
    sn="222YA"
    sum = 1
    for i in sn:
        sum = 7 * sum + 2 * ord(i)
    sum += int(VolumeSerialNumber,16)
    v5 = 1.33
    clsid = VolumeSerialNumber.lower()
    while(len(clsid) < 0x20):
        tmp = sum * v5
        v5 += 0.27
        clsid += "%lx"%(math.trunc(tmp)&0xFFFFFFFF)
    clsid = list(clsid[:32])
    clsid.insert(12,'-')
    clsid.insert(17,'-')
    clsid.insert(22,'-')
    clsid.insert(27,'-')
    print "".join(clsid[::-1])

    15.png

    ===============================================================================================================================



    后续发现两处CLSID生成算法差异有点大,就将两处算法整合到一个脚本中,省去了基础不好的朋友寻找注册表位置的麻烦。只需要将VolumeSerialNumber变量替换为你本机的磁盘序列号,修改对应的注册码sn变量即可。
    直接再cmd窗口输入vol命令即可得到磁盘序列号,请手动去掉中间的‘-’:
    [Bash shell] 纯文本查看 复制代码
    C:\Users\fxb>vol
     驱动器 C 中的卷是 Windows
     卷的序列号是 CA22-FD67

    [Python] 纯文本查看 复制代码
    import math
    VolumeSerialNumber="CA22FD67"
    sn="222YA"
    def getCLSID1(VolumeSerialNumber,sn):
        sum = 1
        for i in sn:
            sum = 7 * sum + 2 * ord(i)
        sum += int(VolumeSerialNumber, 16)
        v5 = 1.33
        clsid = VolumeSerialNumber.lower()
        while (len(clsid) < 0x20):
            tmp = sum * v5
            v5 += 0.27
            clsid += "%lx" % (math.trunc(tmp) & 0xFFFFFFFF)
        clsid = list(clsid[:32])
        clsid.insert(12, '-')
        clsid.insert(17, '-')
        clsid.insert(22, '-')
        clsid.insert(27, '-')
        print "".join(clsid[::-1])
    def getCLSID2(VolumeSerialNumber,sn):
        sum = 1
        for i in sn:
            sum = 5 * sum + 2 * ord(i)
        sum += int(VolumeSerialNumber,16)
        sum /= 3
        v5 = 1.55
        clsid = ''
        while(len(clsid) < 0x20):
            sum = math.trunc(sum * v5)&0xFFFFFFFF
            v5 += 0.25
            clsid += "%lx"%(sum)
        clsid = list(clsid[:32])
        clsid.insert(8,'-')
        clsid.insert(13,'-')
        clsid.insert(18,'-')
        clsid.insert(23,'-')
        print "".join(clsid)
    getCLSID1(VolumeSerialNumber,sn)
    getCLSID2(VolumeSerialNumber,sn)




    程序功能依然在测试中,暂时不能保证后续不会有弹窗提示,但不会影响正常使用。


    评分

    参与人数 25威望 +35 飘云币 +44 收起 理由
    theend + 1 + 1 原创精品 感谢分享!
    zyjsuper + 1 + 1
    萌面侠 + 1 + 1 期待大佬出注册机,提前感谢
    天玄 + 1 + 1 PYG有你更精彩!
    ssack + 1 PYG有你更精彩!
    Superhero + 1 + 1 太厉害了,膜拜。出个keygen吧
    lcleng + 1 + 1 PYG有你更精彩!
    东方优源 + 1 + 1 分享精神,是最值得尊敬的!
    dryzh + 5 + 5 感谢发布原创作品,PYG有你更精彩!
    pentium450 + 1 感谢发布原创作品,PYG有你更精彩!

    查看全部评分

    本帖被以下淘专辑推荐:

    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2021-7-27 17:21
  • 签到天数: 9 天

    [LV.3]偶尔看看II

     楼主| 发表于 2019-12-31 23:24:03 | 显示全部楼层
    zwagon 发表于 2019-12-31 23:17
    最终我们要如何操作呢

    1. 下载最新版IDM
    2.点击注册 序列号输入帖子提供的
    3.修改注册表MData为帖子提供的数据


    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2020-4-21 03:40
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2020-4-21 03:39:28 | 显示全部楼层
    分析的好,6.37build10已用未弹窗,getCLSID2 中 sum /= 3 应改为 sum = math.trunc(sum/3),否则和IDM中的对不上。
    PYG19周年生日快乐!
    回复 支持 1 反对 0

    使用道具 举报

  • TA的每日心情
    开心
    昨天 21:38
  • 签到天数: 1359 天

    [LV.10]以坛为家III

    发表于 2019-12-31 20:54:15 | 显示全部楼层
    沙发膜拜kg牛鞭大师!
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2021-2-14 15:32
  • 签到天数: 9 天

    [LV.3]偶尔看看II

    发表于 2019-12-31 20:59:01 | 显示全部楼层
    大神确实厉害,学习了。
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    难过
    2023-12-1 14:43
  • 签到天数: 77 天

    [LV.6]常住居民II

    发表于 2019-12-31 21:08:02 | 显示全部楼层
    话说这个干啥的
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情

    2021-9-17 10:38
  • 签到天数: 39 天

    [LV.5]常住居民I

    发表于 2019-12-31 21:27:36 | 显示全部楼层
    各位表哥元旦快乐
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    昨天 09:32
  • 签到天数: 2178 天

    [LV.Master]伴坛终老

    发表于 2019-12-31 22:03:16 | 显示全部楼层
    分析很到位i,收藏学习
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    昨天 09:32
  • 签到天数: 2178 天

    [LV.Master]伴坛终老

    发表于 2019-12-31 22:04:00 | 显示全部楼层
    分析很到位,收藏学习
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    难过
    2022-12-2 22:35
  • 签到天数: 142 天

    [LV.7]常住居民III

    发表于 2019-12-31 22:15:46 | 显示全部楼层
    大佬那个注册表信息怎么样查找,我是win10 64位的,应该怎么样找“HKEY_CURRENT_USER\Software\Classes\WOW6432Node\CLSID”找到这个目录后应该找哪个,没有找到跟你文中显示一样的
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2021-7-27 17:21
  • 签到天数: 9 天

    [LV.3]偶尔看看II

     楼主| 发表于 2019-12-31 22:20:03 | 显示全部楼层
    085304229 发表于 2019-12-31 22:15
    大佬那个注册表信息怎么样查找,我是win10 64位的,应该怎么样找“HKEY_CURRENT_USER\Software\Classes\WOW ...

    找不到的话就用帖子里面说的工具,监控一下你电脑的真实路径
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

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