如何找到加密算法?

逆向工程 C 解密 解压
2021-06-25 07:03:26

我有一个arm编译so文件,找到IDA Pro生成的伪代码的解密函数是这样的:

char __cdecl EncodeUtil::getDecryptStr()
{
  int *v0; // r0
  int *v1; // r7
  unsigned int i; // r5
  char v3; // r6
  int v5; // [sp+4h] [bp-1Ch]

  v1 = v0;
  HttpUtility::URLDecode(&v5);
  for ( i = 0; i < *(_DWORD *)(v5 - 12); ++i )
  {
    sub_3B25D0(&v5);
    v3 = byte_41A7DD[i & 7]; //byte_41A7DD     DCB 0xC, 0x17, 0xDE, 0x22, 0x2C, 0xC9, 0x37, 0x43
    *(_BYTE *)(v5 + i) ^= v3;
    sub_3B25D0(&v5);
    if ( !*(_BYTE *)(v5 + i) )
    {
      sub_3B25D0(&v5);
      *(_BYTE *)(v5 + i) ^= v3;
    }
  }
  sub_3B2E20(v1, &v5);
  sub_3B1CCC(&v5);
  return (char)v1;
}

sub_3B25D0函数为:

int *__fastcall sub_3B25D0(int *result)
{
  if ( *(_DWORD *)(*result - 4) >= 0 )
    result = sub_3B2580(result);
  return result;
}

sub_3B2580函数为:

int *__fastcall sub_3B2580(int *result)
{
  int v1; // r3
  int *v2; // r4

  v1 = *result;
  v2 = result;
  if ( (int *)(*result - 12) != &dword_4C60C0 )
  {
    if ( *(_DWORD *)(v1 - 4) > 0 )
    {
      result = sub_3B1D0C(result, 0, 0, 0);
      v1 = *v2;
    }
    *(_DWORD *)(v1 - 4) = -1;
  }
  return result;
}

接着 sub_3B1D0C

int *__fastcall sub_3B1D0C(int *result, size_t a2, int a3, int a4)
{
  int v4; // r12
  int v5; // r10
  int v6; // r8
  int v7; // r7
  unsigned int v8; // r3
  unsigned int v9; // r8
  int *v10; // r5
  int v11; // r4
  size_t v12; // r6
  size_t v13; // r10
  int v14; // r0
  int v15; // r9
  bool v16; // zf
  int v17; // r7
  int v18; // r4
  const void *v19; // r1
  int v20; // r7
  int v21; // r4
  _BYTE *v22; // r1
  char v23; // [sp+4h] [bp-24h]

  v4 = *result;
  v5 = *(_DWORD *)(*result - 12);
  v6 = a4 - a3;
  v7 = a4;
  v8 = *(_DWORD *)(*result - 8);
  v9 = v6 + v5;
  v10 = result;
  v11 = a3;
  v12 = a2;
  v13 = v5 - a2 - a3;
  if ( v9 > v8 || *(_DWORD *)(v4 - 4) > 0 )
  {
    v14 = sub_3B1B30(v9, v8, &v23);
    if ( v12 )
    {
      v22 = (_BYTE *)*v10;
      if ( v12 == 1 )
      {
        *(_BYTE *)(v14 + 12) = *v22;
        v15 = v14 + 12;
      }
      else
      {
        v15 = v14 + 12;
        memcpy((void *)(v14 + 12), v22, v12);
      }
    }
    else
    {
      v15 = v14 + 12;
    }
    if ( v13 )
    {
      v20 = v7 + v12;
      v21 = v11 + v12;
      if ( v13 == 1 )
        *(_BYTE *)(v15 + v20) = *(_BYTE *)(*v10 + v21);
      else
        memcpy((void *)(v15 + v20), (const void *)(*v10 + v21), v13);
    }
    result = (int *)(*v10 - 12);
    if ( result != &dword_4C60C0 )
      result = (int *)sub_3B1C84();
    v4 = v15;
    *v10 = v15;
  }
  else
  {
    v16 = a3 == v7;
    if ( a3 != v7 )
      v16 = v13 == 0;
    if ( !v16 )
    {
      v17 = v7 + a2;
      v18 = a3 + a2;
      result = (int *)(v4 + v17);
      v19 = (const void *)(v4 + a3 + a2);
      if ( v13 == 1 )
        *(_BYTE *)(v4 + v17) = *(_BYTE *)(v4 + v18);
      else
        result = (int *)memmove(result, v19, v13);
      v4 = *v10;
    }
  }
  if ( (int *)(v4 - 12) != &dword_4C60C0 )
  {
    *(_DWORD *)(v4 - 4) = 0;
    *(_DWORD *)(v4 - 12) = v9;
    *(_BYTE *)(v4 + v9) = 0;
  }
  return result;
}

任何人都可以最好地猜测该函数在做什么之后HttpUtility::URLDecode

编辑

受到@NirIzr 下面的回答和一些评论的启发,我写了一段 Java 代码来尝试这样的 XOR:

 public static void main(String args[])
    {
        byte[] _bytes = null;
        byte[] key = {(byte) 0xC,(byte)0x17,(byte)0xDE,(byte)0x22,(byte)0x2C,(byte)0xC9,(byte)0x37,(byte)0x43};
        String s = "%EB%9Ff%C5%A4q%D0%D9%88%F2M%87%C9Z%92%A6%83%BC%3B%86%8B%2D%8B%EC";
        try {
            String decoded = URLDecoder.decode(s,"UTF-8");
            _bytes = decoded.getBytes();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }


        for(int i=0;i<_bytes.length;i++){
            _bytes[i] = (byte)(_bytes[i] ^ key[i%key.length]);
        }

        System.out.println(new String(_bytes));

    }

但输出似乎并不好:

�cD�mF�����v��c͓tm���1��&���c�v�����͓t��

2个回答

这个答案的所有功劳都应该归功于@NirIzr 和@Avery3R。

这是一个python脚本,它实现了这些好心人提到的异或运算:

import urllib
import binascii
data = "%EB%9Ff%C5%A4q%D0%D9%88%F2M%87%C9Z%92%A6%83%BC%3B%86%8B%2D%8B%EC" #quoted data as is
unquoted = urllib.unquote(data)
key = binascii.unhexlify("0C17DE222CC93743") # the key
idx = 0
res = ""
for c in unquoted:
    res += chr(ord(c) ^ ord(key[idx % len(key)]))
    idx += 1
print res

运行结果:

╭─wireshrink@[cenzored] ~/test  
╰─$ python ./test.py 
爸爸的哥哥叫大伯

我认为您要么在打印前忘记将其编码回 UTF8,要么您的代码或默认环境中存在与编码相关的其他问题。

首先,我不得不说在反编译中有些东西不太好加起来,而且我经常发现反汇编比反编译更容易理解

让我们从完成大部分工作的函数开始 - sub_3B25D0.

由于这个函数有点长,并且里面有一点引用-取消引用的可怕数学,我们的第一印象可能是这个函数完成了所有或大部分的加密繁重工作。然而,经过进一步调查(我不会在这里详述),您可以清楚地看到压缩算法的迹象有多个关键元素揭示了这一点,您可以在此答案中找到有关如何区分压缩与加密的完整说明

但是,我将介绍一些关键指示,因为它们出现在您提供的代码片段中。

  1. 没有按顺序对字符串中的所有字符进行整齐的迭代。几乎所有的加密算法,无论是Block还是Stream,都会 迭代输入字符串的所有字符。块将遍历字符串的块大小的块。
  2. 对输入消息或流/状态的替换不够。
  3. 大量的partial memcpy/ memmoves,表明该函数由输入字符串的不同子序列组成。

因此,让我们重命名sub_3B1D0CNI_BlockDecompression. 您可以使用自己的姓名缩写。我假设它是一个解压缩而不是一个压缩函数,仅仅因为你将流程称为“解密”,暗示输入是无形的。

现在,打算下到上,我们可以简要地回顾sub_3B2580sub_3B25D0看看这些功能只运行一对夫妇上输入简单的完整性检查,但基本不做什么有趣的事。经过简短的一瞥,似乎有某种保护措施可以防止NI_BlockDecompression在同一字符串上多次运行该函数,但不确定。让我们跳过它们并进入第二个有趣的功能 - getDecryptStr

我发现多次调用sub_3B25D0很奇怪,我建议您进一步查看那里发生的事情。EncodeUtil::getDecryptStr函数似乎只是使用硬编码的“异或加密”密钥对字节进行异或byte_41A7DD