这个请求掩码算法是可逆的吗?

逆向工程 恶意软件
2021-06-12 16:58:50

我试图从我发现的恶意软件中理解算法。该算法将 http 请求编码到 C&C。反编译版本取自 IDA。有一些评论是我写的。

问题是:我没有看到恶意软件将结果从 QueryPerformanceCounter() 发送到它的 C&C。那么如果没有这个值,C&C 如何解码这个请求呢?

int __cdecl sub_A2AA0(int a1, unsigned int a2, int a3, _BYTE *a4, int a5)
{
// a5: length of buffer
// a4: buffer to encode
// a3: always 0
// a2: always 4
// a1: addr contains result from QueryPerformanceCounter()

  signed int v5; // eax@1
  char v6; // si@3
  unsigned int v7; // eax@3
  signed int v8; // edi@3
  char v9; // cl@4
  int v10; // edx@4
  int v11; // edi@7
  int v12; // esi@7
  int result; // eax@7
  char v14; // cl@8
  int v15; // ebp@9
  _BYTE *i; // edi@9
  char v17; // cl@10
  char v18[256]; // [sp+0h] [bp-100h]@2

 // buffer with 0 to 255
  v5 = 0;
  do
  {
    v18[v5] = v5;
    ++v5;
  }
  while ( v5 < 256 );


  // exchange content of a random index starting with index 0
  v6 = 0;
  v7 = 0;
  v8 = 0;
  do
  {
    v9 = v18[v8];
    v10 = (unsigned __int8)(v6 + v18[v8] + *(_BYTE *)(v7++ + a1)); // generate index
    v6 = v10;
    if ( v7 >= a2 )
      v7 = 0;
    v18[v8++] = v18[v10];
    v18[v10] = v9;
  }
  while ( v8 < 256 );

// iterate local buffer again, exchange content of cells, starting with index 0
  v11 = a3; // init with 0
  LOBYTE(v12) = 0;
  for ( result = 0; v11; v18[v12] = v14 )
  {
    result = (unsigned __int8)(result + 1);
    v14 = v18[result];
    --v11;
    v12 = (unsigned __int8)(v12 + v18[result]);
    v18[result] = v18[v12];
  }

// mask the request
  v15 = a5;
  for ( i = a4; v15; --v15 )
  {
    result = (unsigned __int8)(result + 1);
    v17 = v18[result];
    v12 = (unsigned __int8)(v12 + v18[result]);
    v18[result] = v18[v12];
    v18[v12] = v17;
    *i++ ^= v18[(unsigned __int8)(v17 + v18[result])]; // simple xor
  }
  return result;


}
1个回答

在我看来,这就像RC4,一种流密码(这里是一个有点类似的实现这里是另一个)。它不对数据进行编码,而是对其进行加密(使用密钥)。作为一种流密码,它生成从密钥派生的字节序列,然后将数据与这些数据进行异或。这两个步骤通常在两个函数中分开,这就是为什么您在函数中看不到对键的任何引用的原因:因为它是在调用该函数之前与某个已经生成的流进行异或的部分。

a1似乎是一个指向状态结构的指针,所以它不是一个指向 4 字节序列的指针,而是一个保存当前状态的结构。据我所见,大多数二进制文件都做这样的事情:

RC4_Init(state, key, ...)
RC4_Process(...) // this is the function you're looking at now

找到调用函数的地方,再往上看(尝试交叉引用a1),你应该找到初始化函数,它有关键。


进一步说明为什么我认为这是 RC4

您的代码:

 // buffer with 0 to 255
  v5 = 0;
  do
  {
    v18[v5] = v5;
    ++v5;
  }
  while ( v5 < 256 );

rc4_init

void rc4_init(unsigned char *key, unsigned int key_length) {
    for (i = 0; i < 256; i++)
        S[i] = i;

    /* ... more code ... */
}

您的代码:

// exchange content of a random index starting with index 0
  v6 = 0;
  v7 = 0;
  v8 = 0;
  do
  {
    v9 = v18[v8];
    v10 = (unsigned __int8)(v6 + v18[v8] + *(_BYTE *)(v7++ + a1)); // generate index
    v6 = v10;
    if ( v7 >= a2 )
      v7 = 0;
    v18[v8++] = v18[v10];
    v18[v10] = v9;
  }
  while ( v8 < 256 );

大块rc4_init

for (i = j = 0; i < 256; i++) {
        j = (j + key[i % key_length] + S[i]) & 255;
        swap(S, i, j);
    }

    i = j = 0;

内联swap在您的代码中:

    v9 = v18[v8];
    v18[v8++] = v18[v10];
    v18[v10] = v9;

RC4 swap:

void swap(unsigned char *s, unsigned int i, unsigned int j) {
    unsigned char temp = s[i];
    s[i] = s[j];
    s[j] = temp;
}

你看到相似之处了吗?我绝对认为这是 RC4,所以最好的办法是希望恶意软件使用静态/可预测/弱密钥,以便您可以解密通信(如果从外部嗅探)。


TL;DR如果没有正确的密钥,您将无法对其进行解密,这可能是可预测的,也可能是不可预测的。看看它是如何生成的,以确定它是否是。