反向校验和算法

逆向工程 加密
2021-06-29 03:11:40

我正在处理这个伪代码,试图为预期的计算值找到正确的输入(我不确定是否可以称之为校验和)。我无法专注于生成反向算法,然后任何形式的帮助表示赞赏。

我可能从 v20 中的值 2602618273338008543 开始,然后异或返回随机生成的输入,但我认为最终结果太大而无法用单个字节异或为零。

PS:我评论了一些我认为对计算没有影响的行。

一个附带问题,为什么根据 2 个值检查结果 (loword(v20)==-922952045 && HIDWORD(v20) == -902699940 || v20 == 2602618273338008543i64) ?以双字计算的结果会与使用 hiword/loword(dword) 计算的结果不同吗?

谢谢你。

    v20 = 0i64;
    // salt table
    v28="a#+EJK45fe/efJWDSlesfGe03saHHFddfdq2gr%a3ß0jm2ÜcFEF!JKMÄrAfim+wqe=WD=?f3jDKefDJ§W?)JöSeAEFj_LIeJDF"; // salt table
    input = new byte[32] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    for ( j = 0; j < 200; ++j )
    {
      v24 = (v20 + j) % 60 + 1; // 0x3c
      v15 = input[7 * j % 15];
      if ( v24 == 32 ) // 0x20
        v15 = __PAIR__(v15, HIDWORD(v15));
      if ( v24 == 31 ) // 0x1f
      {
        // v22 = v24 & 31; // 0x1f
        // v11 = HIDWORD(v15);
        // v8 = v15;
        v1 = (unsigned __int64)(v15 << (v24 & 0x1F)) >> 32;
        LODWORD(v15) = __PAIR__((unsigned int)v15, HIDWORD(v15)) << (v24 & 0x1F) >> 32;
        HIDWORD(v15) = v1;
      }
      // v6 = v15;
      v20 += v15;
      v25 = (v20 ^ (unsigned __int64)j) % 62 + 2; // 0x3e + 2
      v2 = (v20 - j) % 91;  // 0x5Bui64
      LODWORD(v16) = *(int *)((char *)&v28 + v2);
      HIDWORD(v16) = *(int *)((char *)&v29 + v2); // &v29=&v28-4
      if ( v25 & 32 ) // 0x20
        v16 = __PAIR__(v16, HIDWORD(v16));
      if ( v25 & 31 ) // 0x1f
      {
        // v21 = v25 & 31; // 0x1f
        // v9 = HIDWORD(v16);
        // v10 = v16;
        v3 = (unsigned __int64)(v16 << (v25 & 0x1F)) >> 32;
        LODWORD(v16) = __PAIR__((unsigned int)v16, HIDWORD(v16)) << (v25 & 0x1F) >> 32;
        HIDWORD(v16) = v3;
      }
      v7 = v16;
      v20 ^= v16;
    }
    if ( (_DWORD)v20 == -922952045 && HIDWORD(v20) == -902699940 || v20 == 2602618273338008543i64 )
      v23 = 1;
  }

编辑:

这是相同算法的有效 C++ 代码。

#define LODWORD(_qw)    ((DWORD)(_qw))
#define HIDWORD(_qw)    ((DWORD)(((_qw) >> 32) & 0xffffffff))

uint64_t v22, v11, v6, v7, v9, v21, v20, v24, v15, v16, v25, v2, v3, vx = 0;
const char _v28[] = "a#+EJK45fe/efJWDSlesfGe03saHHFddfdq2gr%a3ß0jm2ÜcFEF!JKMÄrAfim+wqe=WD=?f3jDKefDJ§W?)JöSeAEFj_LIeJDF"; // salt table
const byte input[] = { 0x3B, 0x8F, 0x80, 0x01, 0x00, 0x00, 0x53, 0x54, 0x4F, 0x4C, 0x4C, 0x4D, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A };

int j, v1, v8, v10;

uint64_t QW_Swap(uint64_t v)
{
    uint64_t temp = v & 0xFFFFFFFF;  // extract the low
    v >>= 32;  // shift right
    v |= (temp << 32); // put the previous low in the high
    return v;
}

void setLODWORD(uint64_t *_var, DWORD _v)
{
    *_var &= 0xFFFFFFFF00000000;
    *_var |= (uint64_t)_v;
}

void setHIDWORD(uint64_t *_var, DWORD _v)
{
    *_var &= 0xFFFFFFFF;
    *_var |= (uint64_t)_v << 32;
}

void atrCRC()
{
    v20 = 0;
    v16 = 0;

    byte * inp;
    inp = (byte*)&input;
    byte * v28;
    v28 = (byte*)&_v28;

    for (j = 0; j < 200; ++j)
    {
        v24 = (v20 + j) % 0x3C + 1; 
        v15 = *(uint64_t*)(inp+(7 * j % 15)); 
        if (v24 & 0x20)
            v15 = QW_Swap(v15);
        if (v24 & 0x1F)
        {
            v22 = v24 & 0x1F; 
            v11 = HIDWORD(v15);
            v8 = v15; 
            v1 = (uint64_t)(v15 << (v24 & 0x1F)) >> 32; 
            setLODWORD(&v15, (QW_Swap(v15) << (v24 & 0x1F) >> 32));
            setHIDWORD(&v15, v1);
        }
        v6 = v15;
        v20 += v15;
        v25 = (v20 ^ (uint64_t)j) % 0x3E + 2;
        v2 = (v20 - j) % 0x5B;
        setLODWORD(&v16, *(int *)((char *)(v28 + v2)));
        setHIDWORD(&v16, *(int *)((char *)((v28 + v2) +4)));
        if (v25 & 0x20)
            v16 = QW_Swap(v16); 
        if (v25 & 0x1F)
        {
            v21 = v25 & 0x1F;
            v9 = HIDWORD(v16); 
            v10 = v16;
            v3 = (uint64_t)(v16 << (v25 & 0x1F)) >> 32;
            setLODWORD(&v16, (QW_Swap(v16) << (v25 & 0x1F) >> 32));
            setHIDWORD(&v16, v3);
        }
        v7 = v16;
        v20 ^= v16;
    }

    /*
    if (v20 == -922952045 && HIDWORD(v20) == -902699940 || v20 == 2602618273338008543i64)
        v23 = 1;
    */
}

编辑 2: 稍微简化的代码

#define LODWORD(_qw)    ((DWORD)(_qw))
#define HIDWORD(_qw)    ((DWORD)(((_qw) >> 32) & 0xffffffff))

uint64_t v22, v20, v24, v15, v2, v3 = 0;
const char _v28[] = "a#+EJK45fe/efJWDSlesfGe03saHHFddfdq2gr%a3ß0jm2ÜcFEF!JKMÄrAfim+wqe=WD=?f3jDKefDJ§W?)JöSeAEFj_LIeJDF"; // salt table
const byte input[] = { 0x3B, 0x8F, 0x80, 0x01, 0x00, 0x00, 0x53, 0x54, 0x4F, 0x4C, 0x4C, 0x4D, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A };

int j;

uint64_t QW_Swap(uint64_t v)
{
    uint64_t temp = v & 0xFFFFFFFF;  // extract the low
    v >>= 32;  // shift right
    v |= (temp << 32); // put the previous low in the high
    return v;
}

void setLODWORD(uint64_t *_var, DWORD _v)
{
    *_var &= 0xFFFFFFFF00000000;
    *_var |= (uint64_t)_v;
}

void setHIDWORD(uint64_t *_var, DWORD _v)
{
    *_var &= 0xFFFFFFFF;
    *_var |= (uint64_t)_v << 32;
}

void func(uint64_t x, uint64_t *var)
{
    byte v22;
    int t;

    if (x & 0x20)
        *var = QW_Swap(*var);
    if (x & 0x1F)
    {
        v22 = x & 0x1F;  
        t = (uint64_t)(*var << v22) >> 32;
        setLODWORD(var, (QW_Swap(*var) << v22 >> 32)); 
        setHIDWORD(var, t);
    }
}

void atrCRC()
{
    v20 = 0;

    byte * inp;
    inp = (byte*)&input;
    byte * v28;
    v28 = (byte*)&_v28;

    for (j = 0; j < 200; ++j)
    {
        v24 = (v20 + j) % 0x3C + 1; // v20 = ((v24-1)*0x3c)-j;
        v15 = *(uint64_t*)(inp+(7 * j % 15)); // *(uint64_t*)(out[j*15%7]) = v15;

        func(v24, &v15);

        v20 += v15;
        v24 = (v20 ^ (uint64_t)j) % 0x3E + 2;

        v2 = (v20 - j) % 0x5B;
        setLODWORD(&v15, *(int *)((char *)(v28 + v2)));
        setHIDWORD(&v15, *(int *)((char *)((v28 + v2) +4))); 

        func(v24, &v15);

        v20 ^= v15;
    }
}
1个回答

我不会称您在编辑有效 C++ 代码中发布的内容。它可以编译,也可以运行,但尽管从技术上讲是 C 代码,但它是自动反编译器的输出,而且它们往往比真正的人工代码复杂和令人费解。

当我遇到创建二进制部分的反向算法的问题时,我通常遵循以下三个步骤:

  1. 以易于阅读的方式重新实现原始算法。
  2. 确保算法的所有部分确实可逆。
  3. 逐个实现逆向算法。

以及更多细节:

作为第一步,重新实现算法本身。你使用什么语言并不重要,只要代码清晰并且你对该语言有很好的理解。

通过这种方式,我对算法的工作原理有了深入的了解,并对它有了一些直觉。此外,这使得阅读和理解它变得更加容易。自己做而不是依赖反编译器是一个非常好的方法,尤其是当你很长时间没有这样做的时候。我的实现也将在以后派上用场

在我得到正确的原始算法之后(这包括在不同的输入上运行我的版本和原始版本并匹配输出),第二阶段可能是寻找任何不可逆的单块,如加密哈希函数。如果我找到了其中一个并且我找不到一种方法来规避它的需要,那通常会结束。

第三步也是最后一步是将代码与尝试将输出反转回输入的实现并排放置,仅从整个算法的一小部分开始。我将首先只反转算法的一小部分,在我实现原始算法的最后运行几个不同的值,并在我进行的过程中建立起来。