我正在尝试确定某种许可机制使用的解密方案。我花了一些时间才意识到这是加密,这是我的另一个问题的一种跟进:从验证算法创建密钥生成器算法

我做了一些功课,在我看来该解密的关键特征如下:

1-输入首先被base64解码(带有一些非标准密钥表,但是算法是完全相同的)
2-使用与输入长度相同的密钥解密。这可能是一个时间片,或者仅仅是一个非常短的数据(都是512位)的非常长的密钥。
3-最重要的是,让我感到困惑的是解密算法使用的是NO xor。我见过的每个密码在解密的某个时候都使用XOR,但是这里根本没有。

我真的不明白要点3。我看不到如何生成无需XOR即可解密的数据。使用位移和AND / OR掩码似乎会在此过程中丢失很多信息。

是否存在符合这些特征的已知密码?

编辑:

这是对算法的更深入的描述:

有一个函数被多次调用。它首先使用数据和解密密钥创建2个表。第二个基于第一个。

此函数首先使用数据创建一个表,然后使用另一个表中的解密密钥处理该数据。

该表通过对数据中的每个32位块(主块)与整个数据进行加法运算来创建从数据创建的数据,这样对于16个32位块中的每个,它都会产生一个unsigned int。呼叫看起来像这样

hash_block(&result1, &result2, current_master_block, data_array[i]);



void hash_block(int *result1, int *result2, unsigned int pMaster_block, unsigned int pCurrent_block)
{
    int current_block;
    unsigned int current_high_short;
    int master_high_times_master; 
    unsigned int master_high_master_short;
    int master_high_times_current;
    unsigned int sum_of_mixes;

    current_block = (unsigned __int16)pCurrent_block;

    current_high_short = pCurrent_block >> 16;
    master_high_times_master = HIWORD(pCurrent_block) * (unsigned __int16)pMaster_block;
    master_high_master_short = pMaster_block >> 16;
    *(_DWORD *)result1 = current_block * (unsigned __int16)pMaster_block;
    master_high_times_current = current_block * HIWORD(pMaster_block);
    sum_of_mixes = master_high_times_current + master_high_times_master;
    *(_DWORD *)(result2) = (unsigned __int16)current_high_short * (unsigned __int16)master_high_master_short;
    if ( master_high_times_current > sum_of_mixes )
        *(_DWORD *)(result2) += 65536;
    *(_DWORD *)result1 += sum_of_mixes << 16;
    if ( sum_of_mixes << 16 > *(_DWORD *)result1 )
        ++*(_DWORD *)(result2);
    *(_DWORD *)(result2) += sum_of_mixes >> 16;
}


我现在是整理一下代码,我将提供有关算法第二部分工作原理的更多信息

编辑2:

还有一个非常复杂的函数,我无法理解该函数在该函数中运行了248次:

void __fastcall sub_4D5AC0(int* result, int* serial_copy, unsigned int last_keyitem)
{
  unsigned int v3; // edi@1
  unsigned int v4; // edi@2
  int v5; // eax@4
  int v6; // eax@7
  unsigned int v7; // eax@11
  //int return_value; // ecx@25
  int* v10; // [sp+0h] [bp-24h]@1
  unsigned int v11; // [sp+4h] [bp-20h]@4
  int v12; // [sp+4h] [bp-20h]@16
  unsigned __int64 v13; // [sp+4h] [bp-20h]@18
  int v14; // [sp+8h] [bp-1Ch]@1
  unsigned int v15; // [sp+8h] [bp-1Ch]@6
  unsigned int v16; // [sp+10h] [bp-14h]@16

  v3 = *(serial_copy + 1);
  v14 = *(serial_copy + 1);
  if ( HIWORD(last_keyitem) == -1 )
    v4 = v3 >> 16;
  else
    v4 = v3 / ((unsigned int)HIWORD(last_keyitem) + 1);
  v5 = (unsigned __int16)last_keyitem * (unsigned __int16)v4 << 16;
  v11 = *serial_copy - v5;
  //if ( -1 - v5 < v11 )
  if ( ~v5 < v11 )
    --v14;
  v15 = v14 - ((unsigned __int16)last_keyitem * (unsigned int)(unsigned __int16)v4 >> 16) - HIWORD(last_keyitem) * (unsigned __int16)v4;
  while ( 1 )
  {
    if ( HIWORD(last_keyitem) >= v15 )
    {
      v7 = HIWORD(last_keyitem);
      if ( HIWORD(last_keyitem) != v15 )
        break;
      v7 = (unsigned __int16)last_keyitem << 16;
      if ( v7 > v11 )
        break;
    }
    v6 = (unsigned __int16)last_keyitem << 16;
    v11 -= v6;
    if ( -1 - v6 < v11 )
      --v15;
    v15 -= HIWORD(last_keyitem);
    ++v4;
  }
  if ( HIWORD(last_keyitem) == -1 ){
    //LOWORD(v7) = v15;
    v7 &= 0xFFFF0000;
    v7 |= v15 & 0xFFFF;
  }else{
    v7 = ((v11 >> 16) + (v15 << 16)) / ((unsigned int)HIWORD(last_keyitem) + 1);
  }
  v16 = HIWORD(last_keyitem) * (unsigned __int16)v7;
  v12 = v11 - (unsigned __int16)last_keyitem * (unsigned __int16)v7;
  if ( v12 > -1 - (unsigned __int16)last_keyitem * (unsigned int)(unsigned __int16)v7 )
    --v15;
  //LODWORD(v13) = v12 - (v16 << 16);
  v13 &= 0xFFFFFFFF00000000;
  v13 |= (v12 - (v16 << 16)) & 0xFFFFFFFF;
  if ( -1 - (v16 << 16) < (unsigned int)v13 )
    --v15;
  // HIDWORD
  v13 &= 0x00000000FFFFFFFF;
  v13 |= (v15 - (v16 >> 16)) & 0xFFFFFFFF00000000;
  while ( last_keyitem <= v13 )
  {
      v13 &= 0xFFFF0000;
      v13 |= (v13 - last_keyitem) & 0xFFFF;
    //LODWORD(v13) = v13 - a3;
    v13 = (v13 - last_keyitem) & 0xFFFF;
    if ( (unsigned int)v13 > -1 - last_keyitem ){
      //--HIDWORD(v13);
        unsigned __int64 high_word = v13 & 0xFFFFFFFF00000000;
        high_word++;
        v13 &= 0x00000000FFFFFFFF;
        v13 |= high_word;
    }
    ++v7;
  }
  *result = (unsigned __int16)v7 + ((unsigned __int16)v4 << 16);
}


评论

与自制算法相反,RSA就是密码算法领域中的这种密码。但是,您没有给我们太多帮助。举例来说,代码呢?关于假定的信息丢失:XOR是双射的,它本身是逆的,但是任何一对双射函数都可以,从ADD和SUB开始。置换密码根本不对输入元素执行任何算术运算,它们只是对它们进行混洗。一些自酿算法只是简单地将位移位或旋转,甚至跨越字节/字边界。

很好谢谢!是否可以在pastebin上发布这两个功能的反汇编?有些事情在反汇编中更容易看到(减少误导)。一些位似乎类似于自制的大整数数学...

pastebin.com/4yZN2huw

#1 楼

第一个函数(位于.text:004D5A40的函数)计算两个32位无符号操作数的64位乘积,并通过EAX存储结果。它可能是用半固定的32位编译器重新编译的半固定的16位代码...

为了进行快速测试,您可以将反汇编粘贴到__asm块中以使其可重新编译。然后,您可以拨打电话存根以进行实验:

__declspec(naked)
void sub_4D5A40 ()
{
   __asm
   {
/*4D5A40*/                 push    ebx
... BIG SNIP ...
/*4D5ABE*/                 pop     ebx
/*4D5ABF*/                 retn
   }
}

__declspec(noinline)
uint64_t halfassed_mul (uint32_t multiplicand, uint32_t multiplier)
{
   uint64_t result;

   __asm
   {
      mov   ecx, multiplicand
      mov   edx, multiplier
      lea   eax, result

      call sub_4D5A40
   }

   return result;
}


这样,您可以轻松地将结果与参数的64位乘积进行比较/验证。

注意:除非将堆栈变量引用更改为word ptr [esp],否则列表不会编译。如果您忘记了word限定词,则编译器将假定字节大小的访问,这将使事情变得更糟。

用于目标的编译器太差了,以至于Hex-Rays显然无法理解代码...如果使用体面的编译器并充分优化后为选定的函数重新编译反编译器输出,以消除尽可能多的冗余(请不要忘记__declspec(noinline)),则可能会获得更大的成功。如果您对它们进行反编译,那么情况应该会更加清楚。

在Stack Overflow和Code Review上浏览几个与bigint相关的主题,以刷新关于bigint代码看起来像橡胶遇到的地方的记忆。道路,特别是与比赛相关的主题,人们不得不重新发明轮子,而不仅仅是调用一些bigint库。例如,主题“ a ^ b中的数字总和”。这样应该可以更轻松地识别代码中的相关模式。

更新:我解决了sub_4D5AC0,因为我想知道它执行了什么神秘的计算,但是即使删除了很多冗余指令之后,试图理解代码也让我大吃一惊。因此,我决定对其进行黑盒处理,假设它执行一些基本的算术运算。代码的基本操作以相当复杂的方式表示了划分。结合签名(64位op 32位,结果为32位),唯一适合的op是MOD。因此,我将代码粘贴到一个.asm文件中,该文件编译时没有任何障碍(对IDA表示敬意!),创建了一个调用存根,并将一些输入信息输入了黑匣子。事实证明,该函数将* EDX处的64位整数除以ECX,并通过EAX存储商的低32位。

当我编写测试以验证函数输出是否有随机性时像往常一样,我输入了1000000000作为循环计数。 Biiig错误,因为已经有1000秒钟的通话时间了...

评论


编译器是Borland C ++的旧版本。说真的,先生。现在,我会接受您的回答,因为这会让我感到困惑。我并没有真正回答这个问题,但是我可以理解所有这些。谢谢

–埃里克
15年1月3日,21:06

感谢您提出这样一个有趣而有趣的问题(手指在每月的这个时候在工作中做无聊的事情,大脑经常闲着)。如果您发现更多的密码并且想要更多关注,那么我们很高兴再次加入。 bcc32具有很多特性,但是其代码生成器/优化器充其量只能提供服务,并且无法优化消除恒星编码的效果。这说明了目标代码的形状以及奇数寄存器调用约定。

– DarthGizka
2015年1月4日12:49



哇,您真是有见地!我也对您如何重新编译asm感兴趣。这是IDA的功能吗?我尝试将其粘贴到Visual Studio并将其包含在__asm {}指令中,但这会带来很多麻烦。当我们与ebp一起玩时,VS也不会喜欢borland似乎并不在乎的东西。我试图用VS中的-O3重新编译来自IDA的反汇编,然后再次重新汇编它。这通常是可行的吗?您似乎建议这样做。最后,是Borland Delphi而不是C ++,这导致了更多的丑陋=)

–埃里克
2015年1月5日,18:53

据说dcc32和bcc32共享同一个后端,这一事实在广告上非常受人关注...当基于MS的程序员仍然不得不与MFC对抗或购买时,bcc32使其能够将C ++与VCL(真正的双向RAD)结合起来第三方UI套件。更不用说像纯C ++仍然没有的闭包之类的东西。但是我离题了。重新组装/重新编译IDA输出以进行一些动手测试的问题本身就有一个主题,我相信这会引出有趣的答案。为什么不以该主题的清单为例来发布问题呢?我一定会回应

– DarthGizka
2015年1月5日在20:46

请注意:您可以使用IDA的appcall调用应用程序功能。

–rev
15年1月6日,下午4:59