2

I'm working on this pseudocode trying to find the correct input for the expected calculated value (i'm not sure if i can call it a checksum). I can't focus enough to generate the reverse algorithm, then any kind of help is appreciated.

I may start with the value 2602618273338008543 in v20 and xor back on the random generated input but i think the end result would be too big to be xored to zero with a single byte.

PS: I commented some lines that i suppose have no effect on the computation.

A side question, why is the result checked against 2 values (loword(v20)==-922952045 && HIDWORD(v20) == -902699940 || v20 == 2602618273338008543i64) ? would the result be different when calculated in double words than with hiword/loword(dword)?

Thank you.

    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;
  }

Edit:

Here is a valid C++ code for the same algorithm.

#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;
    */
}

EDIT 2: a bit simplified code

#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
    Did you try angr ( angr.io ) ? Symbolic execution is exactly built for such kind of things if these things are nnot cryptographically strong - and you can see here (vantagepoint.sg/blog/…) example for arm based license validation crackme. – w s Nov 28 '17 at 17:30
  • Thanks. No I didn't, and at first glance I doubt it would apply in my case, but i'll dig into it. – Kheireddine Saidi Dec 1 '17 at 19:11
1

I wouldn't call what you posted in your edit valid C++ code. It may compile, it may run, but although technically C code, it's the output of an automated decompiler and they tend to be a lot more complex and puzzling than genuine, human made code.

When I'm handed a problem of creating the reverse algorithm of a part of a binary, I often follow these three steps:

  1. Re-implement the original algorithm in an easily readable manner.
  2. Make sure all parts of the algorithm are indeed invertible.
  3. implement the inverted algorithm piece by piece.

And in some more details:

re-implement the algorithm itself as the first step. Doesn't really matter what language you use, as long as the code is clear and you have a good understanding of that language.

That way I get a deep understanding on how the algorithm works and have some intuition for it. Additionally, this makes it a lot easier to read and wrap my head around it. Doing it by yourself instead of relying on decompilers is a really good approach, especially when you haven't done that for a long time. My implementation will also come in handy later on when

After I get the original algorithm right (this includes running my version and the original version on different inputs and matching the outputs), the second stage would probably be to look for any single piece that isn't invertible like cryptographic hash functions. If I find one of those and I can't find a way to circumvent the need for it, that's usually wraps that up.

Third and final step would be to place the code side by side with an implementation that tries to invert the output back to the input, starting with only small slices of the entire algorithm. I would start by only inverting a small chunk of the algorithm, run a couple of different values through the end of my implementation of the original algorithm and build up on that as I go.

  • thanks. yes what i meant by valid is that it's accepted by the compiler i just slightly modified the pseudocode. and yes i checked for different inputs while debugging and both programs gave same outputs on all stages of the algorithm. actually i doubt it could be reversed due to the loss of bits while shifting (in func(uint64_t, *uint64_t)) I would appreciate you opinion, if it's the case what would be my options? bruteforcing a 32bytes array may take a while. – Kheireddine Saidi Dec 2 '17 at 19:52
  • Actually, if you take a closer look I think you might be surprised... Although as I said it'll be clearer once you rewrite the code yourself. – NirIzr Dec 3 '17 at 2:19
  • I'm trying, but what I see is (x << y) >> 32 (where x<32) is not reversible as it's a shift rather than a roll, correct me if i'm wrong. and v15 = *((uint64_t *)(v28 + v2)); is overwriten depending on the value of v2 which depends on v20 that depends on the previous value of v15. I maybe missing something – Kheireddine Saidi Dec 3 '17 at 14:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.