Skip to content
This repository has been archived by the owner on Feb 13, 2018. It is now read-only.

New update #54

Closed
FastFrench opened this issue Dec 11, 2015 · 198 comments
Closed

New update #54

FastFrench opened this issue Dec 11, 2015 · 198 comments

Comments

@FastFrench
Copy link

As expected, with the new update it doesn't work anymore.

It will probably need some time to find out the new encryption schema.

This update introduced two new messages to support new encryption.
The first one is id=0x2774, it is sent by the client,
The Server answers with message 0x4E84.

LoginMessage only comes after those.

@FICTURE7
Copy link
Owner

The first message is not encrypted actually and thats all I know for now. Will dig into it when I have more time.

@d3death
Copy link

d3death commented Dec 12, 2015

Looking more into it, Protocol structure of most of calls has not changed. e.g i looked at disassembled login subroutine, and it was same structure as in past.

New login

int __fastcall sub_161788(int a1, int a2, int a3)
{
  int v3; // r5@1
  int v4; // r4@1
  int result; // r0@1
  void *v6; // r0@2
  int v7; // r0@2
  char v8; // [sp+4h] [bp-94h]@6
  char v9; // [sp+18h] [bp-80h]@6
  char v10; // [sp+2Ch] [bp-6Ch]@7
  char v11; // [sp+40h] [bp-58h]@10
  char v12; // [sp+54h] [bp-44h]@10
  char v13; // [sp+68h] [bp-30h]@11
  int v14; // [sp+7Ch] [bp-1Ch]@1

  v3 = a1;
  v4 = a1 + 8;
  v14 = _stack_chk_guard;
  nullsub_40(a1, a2, a3);
  *(_DWORD *)(v3 + 48) = sub_1A0176(v4);
  *(_DWORD *)(v3 + 52) = sub_1A0824(v4, 900000);
  *(_DWORD *)(v3 + 56) = sub_1A06E4(v4);
  sub_1A06E4(v4);
  *(_DWORD *)(v3 + 60) = sub_1A06E4(v4);
  *(_DWORD *)(v3 + 84) = sub_1A0824(v4, 900000);
  *(_DWORD *)(v3 + 88) = sub_1A0824(v4, 900000);
  *(_DWORD *)(v3 + 92) = sub_1A0824(v4, 900000);
  *(_DWORD *)(v3 + 96) = sub_1A0824(v4, 900000);
  *(_DWORD *)(v3 + 100) = sub_1A0824(v4, 900000);
  result = sub_1A027C(v4);
  if ( !result )
  {
    *(_DWORD *)(v3 + 124) = sub_14C52A(v4, 1);
    v6 = sub_1A0824(v4, 900000);
    v7 = sub_1A5394(v3 + 104, (int)v6, (const char *)&unk_2BB699);
    sub_175974(v3 + 104, v7);
    result = sub_1A027C(v4);
    if ( !result )
    {
      *(_DWORD *)(v3 + 128) = sub_1A0824(v4, 900000);
      result = sub_1A027C(v4);
      if ( !result )
      {
        *(_DWORD *)(v3 + 132) = sub_1A0824(v4, 900000);
        result = sub_1A027C(v4);
        if ( !result )
        {
          *(_BYTE *)(v3 + 248) = sub_19FFE4(v4);
          result = sub_1A027C(v4);
          if ( !result )
          {
            sub_1A06E8((int)&v8, v4, 900000);
            sub_175974(v3 + 136, (int)&v8);
            sub_17582E((int)&v8);
            sub_1A06E8((int)&v9, v4, 900000);
            sub_175974(v3 + 156, (int)&v9);
            sub_17582E((int)&v9);
            result = sub_1A027C(v4);
            if ( !result )
            {
              sub_1A06E8((int)&v10, v4, 900000);
              sub_175974(v3 + 176, (int)&v10);
              sub_17582E((int)&v10);
              result = sub_1A027C(v4);
              if ( !result )
              {
                *(_BYTE *)(v3 + 249) = sub_19FFE4(v4);
                *(_DWORD *)(v3 + 196) = sub_1A0824(v4, 900000);
                result = sub_1A027C(v4);
                if ( !result )
                {
                  *(_DWORD *)(v3 + 244) = sub_1A06E4(v4);
                  result = sub_1A027C(v4);
                  if ( !result )
                  {
                    *(_DWORD *)(v3 + 200) = sub_1A003A(v4);
                    sub_1A06E8((int)&v11, v4, 900000);
                    sub_175974(v3 + 204, (int)&v11);
                    sub_17582E((int)&v11);
                    sub_1A06E8((int)&v12, v4, 900000);
                    sub_175974(v3 + 224, (int)&v12);
                    sub_17582E((int)&v12);
                    result = sub_1A027C(v4);
                    if ( !result )
                    {
                      sub_1A06E8((int)&v13, v4, 900000);
                      sub_175974(v3 + 64, (int)&v13);
                      result = sub_17582E((int)&v13);
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  if ( v14 != _stack_chk_guard )
    _stack_chk_fail(result);
  return result;
}

Old login

int __fastcall sub_1521F4(int a1, int a2, int a3)
{
  int v3; // r5@1
  int v4; // r4@1
  int result; // r0@1
  void *v6; // r0@2
  int v7; // r0@2
  char v8; // [sp+4h] [bp-94h]@6
  char v9; // [sp+18h] [bp-80h]@6
  char v10; // [sp+2Ch] [bp-6Ch]@7
  char v11; // [sp+40h] [bp-58h]@10
  char v12; // [sp+54h] [bp-44h]@10
  char v13; // [sp+68h] [bp-30h]@11
  int v14; // [sp+7Ch] [bp-1Ch]@1

  v3 = a1;
  v4 = a1 + 8;
  v14 = _stack_chk_guard;
  nullsub_38(a1, a2, a3);
  *(_DWORD *)(v3 + 48) = sub_18FD3A(v4);
  *(_DWORD *)(v3 + 52) = sub_190398(v4, 900000);
  *(_DWORD *)(v3 + 56) = sub_190258(v4);
  sub_190258(v4);
  *(_DWORD *)(v3 + 60) = sub_190258(v4);
  *(_DWORD *)(v3 + 84) = sub_190398(v4, 900000);
  *(_DWORD *)(v3 + 88) = sub_190398(v4, 900000);
  *(_DWORD *)(v3 + 92) = sub_190398(v4, 900000);
  *(_DWORD *)(v3 + 96) = sub_190398(v4, 900000);
  *(_DWORD *)(v3 + 100) = sub_190398(v4, 900000);
  result = sub_18FE3C(v4);
  if ( !result )
  {
    *(_DWORD *)(v3 + 124) = sub_13FC6E(v4, 1);
    v6 = sub_190398(v4, 900000);
    v7 = sub_194D9C(v3 + 104, (int)v6, (const char *)&unk_2971AB);
    sub_16532C(v3 + 104, v7);
    result = sub_18FE3C(v4);
    if ( !result )
    {
      *(_DWORD *)(v3 + 128) = sub_190398(v4, 900000);
      result = sub_18FE3C(v4);
      if ( !result )
      {
        *(_DWORD *)(v3 + 132) = sub_190398(v4, 900000);
        result = sub_18FE3C(v4);
        if ( !result )
        {
          *(_BYTE *)(v3 + 248) = sub_18FBA8(v4);
          result = sub_18FE3C(v4);
          if ( !result )
          {
            sub_19025C((int)&v8, v4, 900000);
            sub_16532C(v3 + 136, (int)&v8);
            sub_1651E6((int)&v8);
            sub_19025C((int)&v9, v4, 900000);
            sub_16532C(v3 + 156, (int)&v9);
            sub_1651E6((int)&v9);
            result = sub_18FE3C(v4);
            if ( !result )
            {
              sub_19025C((int)&v10, v4, 900000);
              sub_16532C(v3 + 176, (int)&v10);
              sub_1651E6((int)&v10);
              result = sub_18FE3C(v4);
              if ( !result )
              {
                *(_BYTE *)(v3 + 249) = sub_18FBA8(v4);
                *(_DWORD *)(v3 + 196) = sub_190398(v4, 900000);
                result = sub_18FE3C(v4);
                if ( !result )
                {
                  *(_DWORD *)(v3 + 244) = sub_190258(v4);
                  result = sub_18FE3C(v4);
                  if ( !result )
                  {
                    *(_DWORD *)(v3 + 200) = sub_18FBFE(v4);
                    sub_19025C((int)&v11, v4, 900000);
                    sub_16532C(v3 + 204, (int)&v11);
                    sub_1651E6((int)&v11);
                    sub_19025C((int)&v12, v4, 900000);
                    sub_16532C(v3 + 224, (int)&v12);
                    sub_1651E6((int)&v12);
                    result = sub_18FE3C(v4);
                    if ( !result )
                    {
                      sub_19025C((int)&v13, v4, 900000);
                      sub_16532C(v3 + 64, (int)&v13);
                      result = sub_1651E6((int)&v13);
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  if ( v14 != _stack_chk_guard )
    _stack_chk_fail(result);
  return result;
}

As you can see its same structure, yet when we try to decode it, it breaks. So I believe that packet structure has not changed, but whole encryption mechanism on packets has changed.

This is the subroutine which deals with new packet (id 10100) that is sent by client

int __fastcall sub_1A55AC(int a1, int a2, int a3)
{
  int v3; // r4@1
  int v4; // r5@1
  int result; // r0@1
  bool v6; // zf@1
  int v7; // [sp+0h] [bp-28h]@1
  int v8; // [sp+14h] [bp-14h]@1

  v3 = a1;
  v4 = a1 + 8;
  v8 = _stack_chk_guard;
  nullsub_40(a1, a2, a3);
  *(_DWORD *)(v3 + 48) = sub_1A06E4(v4);
  *(_DWORD *)(v3 + 52) = sub_1A06E4(v4);
  *(_DWORD *)(v3 + 56) = sub_1A06E4(v4);
  *(_DWORD *)(v3 + 60) = sub_1A06E4(v4);
  *(_DWORD *)(v3 + 64) = sub_1A06E4(v4);
  sub_1A06E8((int)&v7, v4, 900000);
  sub_175974(v3 + 68, (int)&v7);
  sub_17582E((int)&v7);
  *(_DWORD *)(v3 + 88) = sub_1A06E4(v4);
  result = sub_1A06E4(v4);
  v6 = v8 == _stack_chk_guard;
  *(_DWORD *)(v3 + 92) = result;
  if ( !v6 )
    _stack_chk_fail(result);
  return result;
}

so it should be int, int, int, int, int, string, int, int
But when parsing it breaks on string (side not on parsing login packets it also breaks when parsing string) and the initial ints also dont make any sense. I dont think we can just decode new packets without first figuring out new encryption.

@d3death
Copy link

d3death commented Dec 12, 2015

@FICTURE7
Copy link
Owner

I think I have a proper message captured from the network traffic and the message is not encrypted as I've said above. http://bit.ly/1O1GlLX

@FICTURE7
Copy link
Owner

The KeepAliveRequestMessage and KeepAliveResponseMessage has changed also they are 16 bytes long now.

@FastFrench
Copy link
Author

Ouch, not a good news. Looks like it won't be so easy to just keep accounts connected. But maybe it's not as usefull as it was before anyway with this update.
So for the message 0x2774, here is the content of the message to decode it:

        public int Data1;
        public int Data2;
        public byte[] SomeData;
        public string MasterHash;
        public int Data4;
        public int Data5;

        #region BaseMessage

        protected override void Decode(PacketReader reader)
        {
            Data1 = reader.ReadInt32();
            Data2 = reader.ReadInt32();
            SomeData = reader.ReadByteArray();
            MasterHash = reader.ReadString();
            Data4 = reader.ReadInt32();
            Data5 = reader.ReadInt32();
        }

        #endregion BaseMessage

And for the answer from the server, just this will do it:

        public byte[] SessionKey;
        protected override void Decode(PacketReader reader)
        {
            SessionKey = reader.ReadByteArray();
        }

But you first have to make sure that those two messages are not Decrypted, as other messages are.
(most credit due to d3death and thecheater887 for those)

@FICTURE7
Copy link
Owner

I am going to push a commit soon on the branch 'rewrite' with 10100 and some other stuff. I am worried that it would be extremely hard to run clients because they might have added a new checksum to the keep alive messages.

@d3death
Copy link

d3death commented Dec 14, 2015

so this seems to be the flow

  1. client sends 10100
  2. server sends key as byte array in 20100
  3. client uses that key with libsodium.
if ( v19 == 20100 )
    {
      sub_174178(v2, (int)v20);

 and in that sub_174178 it calls libsodium function
crypto_box_curve25519xsalsa20poly1305_tweet_keypair(v3 + 188, v3 + 220); <-- this one

there is also a matter of new key that they put: "77035c098d0a04753b77167c7133cdd4b7052813ed47c461"
see my previous link

@FastFrench
Copy link
Author

As libsodium seems to be part of the new encryption, this may be useful:
https://github.com/adamcaudill/libsodium-net

@FICTURE7
Copy link
Owner

Great! I thought it was only available in C!

@FICTURE7
Copy link
Owner

I found some information about the crypto_box_curve25519xsalsa20poly1305 function on NaCl which I believe is the stuff libsodium is based of off. So we can assume CoC is using a public-key authenticated encryption. Documentations here and here.

@devinvisible
Copy link
Contributor

Didn't realize this would be the most active thread on sharing information (that I've found anyways). :P Sweet! Wish I found this over the weekend instead of when I'm starting work for the day.

That new key is likely just a pepper (https://en.wikipedia.org/wiki/Pepper_(cryptography)). There is a new configuration in clients.csv file that set its use to true.

There is a C# port of libsodium.

I had to hack at ProxyNetworkManager.cs a bit. ReadPacket wants to CoCCrypto.Decrypt(decryptedData) for every packet so I added if (packetId != 10100 && packetId != 20100) around it.

@d3death
Copy link

d3death commented Dec 15, 2015

I will look into KeepAliveRequestMessage later and see what they have added. Here is the functions in which i believe all the pacts are going though.

This is the subroutine which i believe handles all packets. and here you see special casing for 20100

//----- (001745A0) --------------------------------------------------------
_DWORD *__fastcall sub_1745A0(int a1, int *a2)
{
  int v2; // r5@1
  int *v3; // r6@1
  _DWORD *result; // r0@2
  int v5; // r4@3
  int v6; // r2@4
  char *v7; // r0@5
  _DWORD *v8; // r0@6
  int v9; // r3@6
  _DWORD *v10; // r4@13
  void *v11; // r4@14
  int v12; // r0@25
  unsigned int v13; // r6@26
  int v14; // r0@32
  int v15; // r0@35
  _DWORD *v16; // r4@51
  int v17; // [sp+4h] [bp-B4h]@6
  unsigned int v18; // [sp+8h] [bp-B0h]@6
  int v19; // [sp+Ch] [bp-ACh]@6
  _DWORD *v20; // [sp+10h] [bp-A8h]@6
  char v21; // [sp+14h] [bp-A4h]@1
  char v22; // [sp+15h] [bp-A3h]@1
  char v23; // [sp+16h] [bp-A2h]@1
  char v24; // [sp+17h] [bp-A1h]@1
  char v25; // [sp+18h] [bp-A0h]@1
  char v26; // [sp+19h] [bp-9Fh]@1
  char v27; // [sp+1Ah] [bp-9Eh]@1
  char v28; // [sp+1Ch] [bp-9Ch]@3
  char v29; // [sp+30h] [bp-88h]@4
  char v30; // [sp+44h] [bp-74h]@5
  char v31; // [sp+58h] [bp-60h]@16
  char v32; // [sp+6Ch] [bp-4Ch]@30
  char v33; // [sp+80h] [bp-38h]@52
  int v34; // [sp+94h] [bp-24h]@1

  v2 = a1;
  v3 = a2;
  v21 = 0;
  v22 = 0;
  v34 = _stack_chk_guard;
  v23 = 0;
  v24 = 0;
  v25 = 0;
  v26 = 0;
  v27 = 0;
  if ( !sub_1A5AF8(a2, &v21, 7u) )
  {
    result = (_DWORD *)sub_1A5A54((int)v3);
    if ( !result )
      goto LABEL_58;
    v5 = 0;
    sub_175BD0((int)&v28, (const char *)&unk_2BB699);
    do
    {
      v6 = *(&v21 + v5++);
      sub_176138((int)&v29, "%p,", v6);
      sub_175D4C((int)&v28, (int)&v29);
      sub_17582E((int)&v29);
    }
    while ( v5 != 7 );
    sub_175CA0((int)&v30, "Messaging::onReceive Read failed. Header ", (int)&v28);
    sub_1A0C48((int)&v30);
    sub_17582E((int)&v30);
    v7 = &v28;
    goto LABEL_31;
  }
  v19 = 0;
  v17 = 0;
  v18 = 0;
  sub_173BCC((int)&v21, &v19, (int *)&v18, &v17);
  v8 = (_DWORD *)(*(int (**)(void))(**(_DWORD **)(v2 + 4) + 12))();
  v9 = *(_DWORD *)(v2 + 148);
  v20 = v8;
  if ( v9 == 1 && v19 == 20100 )
  {
    do
    {
      if ( v20 )
      {
        (*(void (**)(void))(*v20 + 24))();
        if ( v20 )
          (*(void (**)(void))(*v20 + 4))();
        v20 = 0;
      }
    }
    while ( v20 );
    v10 = operator new(0x38u);
    sub_1A5840(v10);
    v20 = v10;
  }
  v11 = operator new[](v18);
  if ( sub_1A5AF8(v3, v11, v18) )
  {
    v12 = *(_DWORD *)(v2 + 108);
    if ( v12 )
    {
      (*(void (**)(void))(*(_DWORD *)v12 + 8))();
      v13 = v18;
      v18 = v13 - (*(int (**)(void))(**(_DWORD **)(v2 + 108) + 16))();
    }
    if ( !v20 )
    {
      if ( v11 )
        operator delete[](v11);
      sub_176138((int)&v32, "Ignoring message of unknown type %p", v19);
      sub_1A0C48((int)&v32);
      v7 = &v32;
LABEL_31:
      result = (_DWORD *)sub_17582E((int)v7);
      goto LABEL_58;
    }
    sub_1A28CE((int)v20, v17);
    v14 = sub_1A28C0((int)v20);
    sub_1A0256(v14, v11, v18);
    if ( *(_DWORD *)(v2 + 148) == 2 )
      sub_174374(v2, (int)v20);
    (*(void (**)(void))(*v20 + 12))();
    if ( v19 == 20000 )
    {
      v15 = *(_DWORD *)(v2 + 116);
      if ( v15 )
        (*(void (**)(void))(*(_DWORD *)v15 + 8))();
      do
      {
        result = v20;
        if ( v20 )
        {
          (*(void (**)(void))(*v20 + 24))();
          result = v20;
          if ( v20 )
            result = (_DWORD *)(*(int (**)(void))(*v20 + 4))();
          v20 = 0;
        }
      }
      while ( v20 );
    }
    else if ( v19 == 20100 )
    {
      sub_174178(v2, (int)v20);
      do
      {
        result = v20;
        if ( v20 )
        {
          (*(void (**)(void))(*v20 + 24))();
          result = v20;
          if ( v20 )
            result = (_DWORD *)(*(int (**)(void))(*v20 + 4))();
          v20 = 0;
        }
      }
      while ( v20 );
    }
    else
    {
      result = (_DWORD *)sub_174028((pthread_mutex_t *)(v2 + 32), &v20);
      v16 = result;
      if ( !result )
      {
        sub_176138((int)&v33, "Incoming message queue full. Message of type %i discarded.", v19);
        sub_1A0C48((int)&v33);
        sub_17582E((int)&v33);
        do
        {
          result = v20;
          if ( v20 )
          {
            (*(void (**)(void))(*v20 + 24))();
            result = v20;
            if ( v20 )
              result = (_DWORD *)(*(int (**)(void))(*v20 + 4))();
            v20 = v16;
          }
        }
        while ( v20 );
      }
    }
  }
  else
  {
    if ( sub_1A5A54((int)v3) )
    {
      sub_176138((int)&v31, "Messaging::onReceive\tfailed to read message %d", v19);
      sub_1A0C48((int)&v31);
      sub_17582E((int)&v31);
    }
    if ( v11 )
      operator delete[](v11);
    do
    {
      result = v20;
      if ( v20 )
      {
        (*(void (**)(void))(*v20 + 24))();
        result = v20;
        if ( v20 )
          result = (_DWORD *)(*(int (**)(void))(*v20 + 4))();
        v20 = 0;
      }
    }
    while ( v20 );
  }
LABEL_58:
  if ( v34 != _stack_chk_guard )
    _stack_chk_fail(result);
  return result;
}

this is sub routine which calls crypto box

//----- (00174178) --------------------------------------------------------
void *__fastcall sub_174178(int a1, int a2)
{
  const void *v2; // r5@1
  int v3; // r4@1
  int v4; // r6@1
  void *v5; // r3@1
  size_t v6; // r6@1
  void (*v7)(void); // r2@1
  int v8; // r11@1
  int v9; // r0@1
  size_t v10; // r8@1
  char *v11; // r6@5
  char *v12; // r5@5
  int v13; // r10@5
  int v14; // r10@5
  char v15; // t1@6
  int v16; // r5@8
  void *v17; // r6@8
  int v18; // r9@8
  _DWORD *v19; // r8@8
  int v20; // r0@8
  int v21; // r0@8
  int v22; // r0@9
  int v23; // r0@10
  void *result; // r0@14
  char dest[4]; // [sp+10h] [bp+0h]@2
  void *src; // [sp+14h] [bp+4h]@1
  int *v27; // [sp+18h] [bp+8h]@1
  int v28; // [sp+1Ch] [bp+Ch]@1
  int v29; // [sp+20h] [bp+10h]@8
  int v30; // [sp+38h] [bp+28h]@8
  int v31; // [sp+1A4h] [bp+194h]@1

  v2 = (const void *)(a1 + 252);
  v3 = a1;
  v4 = a2;
  v31 = _stack_chk_guard;
  *(_DWORD *)(a1 + 148) = 2;
  sub_174D3C((char *)(a1 + 252), 24);
  *(_BYTE *)(v3 + 252) &= 0xFEu;
  v28 = v3 + 220;
  j_j_j_crypto_box_curve25519xsalsa20poly1305_tweet_keypair(v3 + 188, v3 + 220);
  v5 = *(void **)(v4 + 48);
  v6 = *(_DWORD *)(v4 + 52);
  v7 = *(void (**)(void))(**(_DWORD **)(v3 + 152) + 8);
  src = v5;
  v7();
  v8 = sub_1A28C0(*(_DWORD *)(v3 + 152));
  v9 = sub_1A28C4(*(_DWORD *)(v3 + 152));
  v27 = &_stack_chk_guard;
  v10 = v6 + 24 + v9;
  if ( v6 )
    memmove(dest, src, v6);
  if ( (const void *)(v3 + 276) != v2 )
    memmove(&dest[v6], v2, v3 + 276 - (_DWORD)v2);
  v11 = &dest[v6] + v3 + 276 - (_DWORD)v2;
  v12 = (char *)sub_1A0278(v8);
  v13 = sub_1A0278(v8);
  v14 = v13 + sub_1A28C4(*(_DWORD *)(v3 + 152)) - (_DWORD)v12;
  while ( v14 > 0 )
  {
    v15 = *v12++;
    --v14;
    *v11++ = v15;
  }
  sub_174D0C((int)&v30, 24);
  sub_174D1E((int)&v30, (char *)(v3 + 188), 32);
  sub_174D1E((int)&v30, (char *)(v3 + 156), 32);
  sub_174D2E(&v30, &v29);
  v16 = v10 + 48;
  v17 = operator new[](v10 + 48);
  sub_174AE4(dest, v10, (char *)v17 + 32, v10 + 16, (int)&v29, v3 + 156, v28);
  memcpy(v17, (const void *)(v3 + 188), 0x20u);
  v18 = (*(int (**)(void))(**(_DWORD **)(v3 + 152) + 20))();
  v19 = operator new(0x34u);
  sub_1A2880(v19, 0);
  v19[12] = v18;
  *v19 = &off_36FE10;
  v20 = sub_1A28C0((int)v19);
  sub_1A0256(v20, v17, v16);
  v21 = sub_1A28CA(*(_DWORD *)(v3 + 152));
  sub_1A28CE((int)v19, v21);
  sub_174060(v3, (int)v19);
  do
  {
    v22 = *(_DWORD *)(v3 + 152);
    if ( v22 )
    {
      (*(void (**)(void))(*(_DWORD *)v22 + 24))();
      v23 = *(_DWORD *)(v3 + 152);
      if ( v23 )
        (*(void (**)(void))(*(_DWORD *)v23 + 4))();
      *(_DWORD *)(v3 + 152) = 0;
    }
  }
  while ( *(_DWORD *)(v3 + 152) );
  result = sub_173DE4(&v29);
  if ( v31 != *v27 )
    _stack_chk_fail(result);
  return result;
}

@FastFrench
Copy link
Author

So does it mean that libsodium lib is only used once for the message 20100, and never again at a later time?
This would then confirm that crypting of the messages didn't changed a lot, just the way the key are computed is different?

@d3death
Copy link

d3death commented Dec 15, 2015

would be too early to say that. i found 25 instances of string "crypto_box_curve". analyzing it will take some time

edit: looking a bit more most of it was function declarations. and actual implementation of all the cryto functions. its only used at 3 places. 1 i posted above. other inside sub_174AE4 which is called by sub_174178 (which i posted above; 20100 flow).

call inside sub_174AE4

 v12 = crypto_box_curve25519xsalsa20poly1305_tweet(v11, v11, v7, v7 >> 31, a5, a6, a7) != 0;
  if ( !v12 )
    memcpy(v10, (const void *)(v11 + 16), v9 + 16);

then there is 3rd calling

v12 = crypto_box_curve25519xsalsa20poly1305_tweet_open(v11, v11, v7, 0, a5, a6, a7) != 0;
  if ( !v12 )
    memcpy(v10, (const void *)(v11 + 32), v9 - 16);

which is called inside a function called by routine which parses all packets.

if ( *(_DWORD *)(v2 + 148) == 2 )
      sub_174374(v2, (int)v20);

^ i posted this above. so seems like there is some sort of fallback crypto_box_curve25519xsalsa20poly1305_tweet_open (not sure what open means) for rest of packets

@warclans
Copy link

"crypto_box_curve25519xsalsa20poly1305_tweet_open (not sure what open means)"

this is Decrypt method set by libsodium

Anyways, what bothers me the most is the 32 byte reqired key for this to work. So far we have seen only 24 byte arrays being exchanged, which is the standard length for nonce in libsodium. Even 20100 responds with 24 byte long array.

@FastFrench
Copy link
Author

Note that the message 10100 also has an additional 8 bytes array. It may have something to do with the 8 bytes you're looking for.

@d3death
Copy link

d3death commented Dec 15, 2015

"this is Decrypt method set by libsodium"
okay so this confirms that

  1. libsodium is used to encrypt during 20100
  2. for rest of packets, its used to decrypt

on too those missing bytes. i looked into that crypto_box_curve25519xsalsa20poly1305_tweet_keypair

int __fastcall crypto_box_curve25519xsalsa20poly1305_tweet_keypair(int a1, char *a2)
{
  int v2; // r5@1
  int v3; // r4@1

  v2 = a1;
  v3 = (int)a2;
  randombytes(a2, (int)a2, 32LL);
  return crypto_scalarmult_curve25519_tweet_base(v2, v3);
}

randombytes???

int __fastcall randombytes(char *a1, int a2, __int64 a3)
{
  char *v3; // r6@1
  __int64 v4; // r4@1
  int i; // r7@1
  ssize_t v6; // r0@4

  v3 = a1;
  v4 = a3;
  for ( i = open("/dev/urandom", 0); v4; v4 -= v6 )
  {
    v6 = read(i, v3, v4);
    v3 += v6;
    if ( v6 == -1 )
      break;
  }
  return close(i);
}

as side note guys, i can stop pasting code if its note helpful. just give me heads up

@warclans
Copy link

"Note that the message 10100 also has an additional 8 bytes array. It may have something to do with the 8 bytes you're looking for."

Where do you find this, i only see:
00-00-00-08-00-00-00-00-00-00-00-43- server version
00-00-00-28- client version hash length
65-65-32-38-64-61-63-66-63-38-39-39-30-39-63-37-61-63-66-65-38-36-61-62-39-35-65-33-66-37-30-64-31-33-35-36-65-30-33-36 - client version hash

Note the number of arguments used in that open sub call (7) and take a look at original definition for those subs here:
http://tweetnacl.cr.yp.to/20131229/tweetnacl.h

Unless we are able to attach to process and debug it that way, this is all going to be our best guess only. I have no means to try this out :(

@FastFrench
Copy link
Author

I thought that
00-00-00-08-00-00-00-00-00-00-00-43
was an array of byte (size as an int32 = 8, then 8 bytes). But maybe I was wrong.

@d3death
Copy link

d3death commented Dec 15, 2015

the number of parameter mismatch is not only just in crypto_box_curve25519xsalsa20poly1305_tweet_open.

inside crypto_box_curve25519xsalsa20poly1305_tweet_open_afternm (coc sends 4 parms, while lib takes 5)

there are lots of things off. and as warclans said without live debugger it will be really hard to figure this out. seriously wonder how imod/xmod figured it out (with in 4 hours of update going live). or do they work in memory?

@warclans
Copy link

run-time debugging is the key. I tried once to attach to bluestacks process but wasn't successful :(

just installed the libsodium-net package, so i will play with this tonight

@cadev31
Copy link

cadev31 commented Dec 15, 2015

The structure for 10100 is int, int, int, int, int, string, int, int and published by FICTURE7 above, but here is the summary of it so far

int unknown1 (1, 0x01)
int unknown2 (1, 0x01)
int VersionMajor (8, 0x08)
int VersionRevision (0, always seemed to be 0 in 10101 packet as well)
int VersionMinor (67, 0x43)
string MasterHash (with int length prefix)
int unknown3 (2, 0x02)
int unknown4 (2, 0x02)

We may need to capture more 10100 packets to see if unknown1/2/3/4 ever change.

20100 returns 24bytes, so it's not a full 32byte key. Not sure if unknown1/2/3/4 come in to play there or not, but currently appears unlikely.

As has been pointed out, there are 3 nonce strings now. "nonce" which has been in use for a long while, "4c444a4b4c396876736b6c3b6473766b666c73676a90fbef" which was in use on the stage server for quite some time, and a new one of "77035c098d0a04753b77167c7133cdd4b7052813ed47c461" which is unknown if it is used.

The current server does appear to know to attempt multiple decryption methods, and if an old method is detected, it always sends a 20103 login failure packet with a status of 8 (version is out of date and needs to be updated, which is a slight change from previous functionality).

It does appear only the handshake is changed, and we will set the updated nonce and continue as it was with the current scramble/decryption once we figure out the handshake.

d3death, I'd say those function dumps are actually quite helpful, but posting everything we need would indeed get a bit verbose.

@devinvisible
Copy link
Contributor

Remote debugging bluestacks and ganymotion is tricky because they are running the ARM code in an x86 emulator I believe. I'm not sure IDA supports debugging an ELF binary on x86. If you had a rooted android phone then you can remotely debug ARM. I'd love to be wrong about remote debugging bluestacks/ganymotion so feel free to prove me wrong. 👍

I thought it was pretty clear from d3death's comment above what the structure of the 10100 packet was:

There are 5 dwords:
*(_DWORD *)(v3 + 48) = sub_1A06E4(v4);
*(_DWORD *)(v3 + 52) = sub_1A06E4(v4);
*(_DWORD *)(v3 + 56) = sub_1A06E4(v4);
*(_DWORD *)(v3 + 60) = sub_1A06E4(v4);
*(_DWORD *)(v3 + 64) = sub_1A06E4(v4);

Followed by a string (which is structured as DWORD "length" followed by "length" number of UTF-8 characters):
sub_1A06E8((int)&v7, v4, 900000);
sub_175974(v3 + 68, (int)&v7);
sub_17582E((int)&v7);

Followed by 2 dwords:
*(_DWORD *)(v3 + 88) = sub_1A06E4(v4);
result = sub_1A06E4(v4);
*(_DWORD *)(v3 + 92) = result;

v4 is the reader
sub_1A06E4 reads an Int32
sub_1A06E8 reads an Int32 and returns the max() of that value and the 3rd argument (900000)
sub_175974 reads an array of bytes based on the 2nd argument

@devinvisible
Copy link
Contributor

"4c444a4b4c396876736b6c3b6473766b666c73676a90fbef" is the client salt we've had forever. "77035c098d0a04753b77167c7133cdd4b7052813ed47c461" is newly added and likely just appended (I haven't seen this in code yet - just a guess based on the new option "USE_PEPPER_CRYPTOGRAPHY" and the normal meaning of "pepper" in cryptography).

I suspect 10100 makes sure the client is giving the proper masterhash to the server. The 20100 response is a server generated session key. The client will then use that and the salt+pepper+nonce to respond back with its side of the handshake for full encryption. The point is... network sniffers can see the 20100 contents so there must be an agreed upon hash that both the client and server know (this is the salt+pepper+nonce). Prior to 8.x this was just salt+nonce.

If I could figure out why the heck IDA 6.8 isn't analyzing my file correctly I'd be all over this - I'm chomping at the bits. :) Glad to see others working at this as well.

@cadev31
Copy link

cadev31 commented Dec 15, 2015

Not sure if we need to pay attention to this or not, but the 10101 and 20104 packets have data in the 6th & 7th bytes of the packet, whereas all other packets are 0

10101 is 0x0007
20104 is 0x0001

All other packets appear to be 0x0000, as this project has always generated for all packets.
Just posting this before that thought leaves my head.

@ghost
Copy link

ghost commented Dec 16, 2015

This may or may not help. This suggestion was found here: http://ultrapowa.com/forum/showthread.php?2843-Message-10100-20100

[23:28:28] Ultrapowa: I want to give you a hint about 8.0
[23:28:34] Ultrapowa: So
[23:28:39] Ultrapowa: basically
[23:28:47] Ultrapowa: First CoC packets are not encrypted
[23:29:10] Ultrapowa: They get encrypted after packet 20100
[23:29:20] Ultrapowa: What you should try is
[23:29:55] Ultrapowa: Patch libg.so to avoid entering packet 20100 if condition
[23:30:06] Ultrapowa: If you do that properly
[23:30:38] Ultrapowa: I assume you can avoid packet encryption for any kind of packet
[23:30:58] Ultrapowa: You might have a few other patches to apply to avoid encryption functions
[23:31:41] Ultrapowa: But I estimate this 100x easier than reversing new encryption (I'm not even sure this is possible, especially if Supercell used a public/private key function)
[23:32:00] Ultrapowa: Ofc don't forget to resign apk, but you're used to this

@d3death
Copy link

d3death commented Dec 16, 2015

@devinvisible

i could upload complete ida disassembled file if you want

@FastFrench
Copy link
Author

I think it may be convenient for those who do not have IDA, but it makes quite a big file.

expand32bytes

@cadev31
Copy link

cadev31 commented Dec 16, 2015

@Snargie No, bypassing 10100/20100 will not work, as if you send an unencrypted 10101 packet (or encrypted using the older encryption method) now, it will always return a 20103 login failed packet with a status code of 8 which indicates your client is out of date.

@ghost
Copy link

ghost commented Jan 28, 2016

@zyxwvuts
"-Decrypts encrypted packet with crypto_box_open_easy"

@zyxwvuts
Copy link

@expl0itr I was just trying to translate @clugh 's code as closely as possible to C#, and he uses afternm() (technically he uses PyNaCl's Box.decrypt(), which uses afternm()). Apparently, though, afternm() does return -1 if the key is invalid, so I guess it doesn't matter which method I use if my keys are wrong. Thanks for the info. I'll give your instructions a try, though, since it fits the way my existing classes are structured a little better than clugh's.

@spAnser
Copy link

spAnser commented Jan 28, 2016

The keepalive is 16bytes because of the encryption padding... Or checksun of some sort.

@clugh
Copy link

clugh commented Jan 28, 2016

@zyxwvuts Depending on the implementation you use, the zero padding may or may not be added automatically. The client strips the padding before sending packets, but the encryption also uses a 16 byte Poly1305 authenticator, so the ciphertext (without zero padding) will always be 16 bytes larger than the plaintext (which is why KeepAlive has a 16 byte payload). My suggestion would be to run through some of the examples and/or tests of the library you're using, so you can see the expected input and output.

@ghost
Copy link

ghost commented Jan 28, 2016

@zyxwvuts No problem! Glad to see people working on a C# implementation. Would be great to see more C# geeks here.

@zyxwvuts
Copy link

@expl0itr I modified my code to use your suggestion and I got crypto_box_easy to return a byte[] while decrypting packet 10101. You mentioned above that you call beforenm to generate a shared key; did you ever use the shared key since you're using crypto_box_easy?

Also, can anybody confirm that they dropped the RC4 and scramble function? I didn't see it in clugh's code, but Python might have some magic line of code that does this and I missed that line.

@zyxwvuts
Copy link

Oh yeah, thanks, @clugh, my library didn't want me to pad ciphertext. That suggestion was key to me getting a result from cypto_box_easy.

@ghost
Copy link

ghost commented Jan 28, 2016

@zyxwvuts No, I never used that shared key but stored it just in case it's needed.

@zyxwvuts
Copy link

I finally have my 10101 decrypting with crypto_box_easy, but there are a variable number of bytes (between 60 and 72 so far) at the front of the bytes returned by crypto_box_easy. Have other people seen and handled this, or is it a peculiarity with my implementation?

@deAngelisAlex
Copy link

Between 60 and 72??

... facepalm

@Otikoruk
Copy link

@DevilMental Thank you for that very informative information..

@zyxwvuts Number 1 rule of compiled commercial application programming. Never remove anything. Only modify and add, or your code will break for absolutely no reason, at all, whatsoever.

While ARC4 is still there, because of ^^above, it is not being used. Could it be used for something? I have my theories, but no evidence to prove or disprove them. At this point in time it is safe to say ARC4 is out of the equation for a while.

@FastFrench
Copy link
Author

Do not expect Redmoon team to do or share anything useful. They just hope to convert their amazing superiority complex into cash. I think they already have lost any kind of credibility from the eyes of the community. I just hope they won't hurt the open source spirit too much in the process.

@deAngelisAlex
Copy link

The clash of clans industry does not need biased people like lil' FastFrench. If you wish to have a talk with us, better do it directly instead of mentioning it indirectly. 😉

~ DevilMental

@FICTURE7
Copy link
Owner

Alright we got it bois, no need for drama here.

@clugh
Copy link

clugh commented Jan 29, 2016

@zyxwvuts The first 48 bytes of the decrypted 10101 packet should be the 24 byte binary string from 20100 and a 24 byte nonce. Anything beyond that would seem to indicate some irregularities.

@FICTURE7
Copy link
Owner

I am stuck on step 24 on the protocol page, do we use snonce or rnonce?

@clugh
Copy link

clugh commented Jan 30, 2016

For all packets sent from the client to the server (message ID < 20000), use snonce (from packet 10101). For all packets sent from the server to the client (message ID >= 20000), use rnonce (from packet 20104).

@clugh
Copy link

clugh commented Jan 30, 2016

I made a simple proxy in C#. It is based heavily on Microsoft's asynchronous sockets examples, and has basically no error checking, but it should be a decent reference for those trying to accomplish something similar.

_Note:_ This is my first C# project, so if I've done things that make no sense, try not to judge me too harshly.

@devinvisible
Copy link
Contributor

Gives me a CryptoGraphicException but thank you for the example proxy!

@clugh
Copy link

clugh commented Jan 30, 2016

Post an issue in the repo, so we don't clutter up this thread, but it's working fine for me. The first thing I'd check is that libg.so is patched with the right key.

@zyxwvuts
Copy link

Works for me, too, clugh... You are the man! I want to be like you when I grow up.

Was generating the Blake2 nonce already built into the Sodium library? GenericHash?

@FICTURE7
Copy link
Owner

@clugh you're impressive mate, thanks for proxy in Python and C#! I found my mistake, I was using PublicKeyBox.Open all the time instead of SecretBox.Open after 20104.

@clugh
Copy link

clugh commented Jan 31, 2016

I implemented a packet decoder in C# using Json.NET and the previously mentioned Json packet definitions.

It currently requires .NET 4.0 because of Stream.CopyTo, but it could easily be adapted to the older method. I have no idea how compatible it is with mono.

Json.NET also provides the means to easily map the results to objects.

@grouprcs
Copy link

@clugh Thanks for Proxy. I'm trying to use it but I'm having a problem. In ClientCrypto the state.serverState.nonce is null and when he tries to Utilities.Increment error occurs. Line 35

@clugh
Copy link

clugh commented Jan 31, 2016

It sounds like you're getting a LoginFailed packet. I haven't yet added any handling for it. To avoid cluttering up this thread, please create an issue in the repo, and we'll do any further communication on the topic over there.

@grouprcs
Copy link

Tranks

@FICTURE7
Copy link
Owner

FICTURE7 commented Feb 2, 2016

The proxy is working fine with the latest encryption so I guess we can close this issue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests