Black Desert Online region "unblocking" research (1 Viewer)

Status
Not open for further replies.

UndercoverPervert

Content Creator
Joined
Aug 9, 2016
Since NA/EU is the only region that remains "unblocked" by PA's server-sided check, and I do not have the time to work on this, here's a small dump of information for anyone willing to work on a workaround.

Goal: Alter CRC result (4 bytes usually represented as uint32) of the entire pad00000.meta (modded) file to match the original unaltered file.

Challenge: Proprietary CRC algorithm which also takes content size into account so brute-forcing by appending data gets exponentially harder.

There are 3 options (excluding brute-force):
  1. Patch the client at runtime (risky due to potential and likely anti-cheat detection)
  2. Intercept and modify network traffic during login (likely encrypted, requires decoding the packet structure, may be assisted by some private servers' code)
  3. "Reverse" the CRC function to make any modded meta file match any original meta file (ideal, version independent as the CRC function hasn't changed for over 7 years, but requires big brain math problem solving, which may also be unfeasible).

Resources:
  • PAZ-Unpacker's decompiled CRC function (C++):

    C++ CRC:
      /* CRC calculation codes from Blackdesert_Launcher.exe (KR Client) */  uint32_t calculatePackCRC(uint8_t * data, uint32_t length) {
        int v2; // ST24_4@4
        unsigned int v3; // ST20_4@4
        unsigned int v4; // ST2C_4@4
        int v5; // ST20_4@4
        unsigned int v6; // ST24_4@4
        int v7; // ST2C_4@4
        unsigned int v8; // ST20_4@4
        int v9; // ST24_4@4
        unsigned int v10; // ST2C_4@4
        int v11; // ST20_4@4
        unsigned int v12; // ST24_4@4
        unsigned int result; // eax@18
        int v14; // ST24_4@23
        unsigned int v15; // ST20_4@23
        unsigned int v16; // ST2C_4@23
        int v17; // ST20_4@23
        unsigned int v18; // ST24_4@23
        int v19; // ST2C_4@23
        unsigned int v20; // ST20_4@23
        int v21; // ST24_4@23
        unsigned int v22; // ST2C_4@23
        int v23; // ST20_4@23
        unsigned int v24; // ST24_4@23
        int v25; // ST24_4@41
        unsigned int v26; // ST20_4@41
        unsigned int v27; // ST2C_4@41
        int v28; // ST20_4@41
        unsigned int v29; // ST24_4@41
        int v30; // ST2C_4@41
        unsigned int v31; // ST20_4@41
        int v32; // ST24_4@41
        unsigned int v33; // ST2C_4@41
        int v34; // ST20_4@41
        unsigned int v35; // ST24_4@41
        unsigned int v36; // ST20_4@56
        unsigned int v37; // ST2C_4@56
        unsigned int v38; // ST24_4@56
        unsigned int v39; // ST20_4@56
        unsigned int v40; // ST2C_4@56
        unsigned int v41; // ST24_4@56
        int *pdwData0; // [sp+Ch] [bp-24h]@39
        int nBeginValue0; // [sp+20h] [bp-10h]@1
        unsigned int nBeginValue1; // [sp+24h] [bp-Ch]@1
        int nBeginValue2; // [sp+2Ch] [bp-4h]@1
    
        nBeginValue0 = length - 558228019;
        nBeginValue1 = length - 558228019;
        nBeginValue2 = length - 558228019;
        pdwData0 = (int *)data;
        if (!((uint32_t)data & 3))
        {
          while (length > 0xC)
          {
            v2 = pdwData0[1] + nBeginValue1;
            v3 = pdwData0[2] + nBeginValue0;
            v4 = (*pdwData0 + nBeginValue2 - v3) ^ ((v3 >> 28) | 16 * v3);
            v5 = v2 + v3;
            v6 = (v2 - v4) ^ ((v4 >> 26) | (v4 << 6));
            v7 = v5 + v4;
            v8 = (v5 - v6) ^ ((v6 >> 24) | (v6 << 8));
            v9 = v7 + v6;
            v10 = (v7 - v8) ^ ((v8 >> 16) | (v8 << 16));
            v11 = v9 + v8;
            v12 = (v9 - v10) ^ ((v10 >> 13) | (v10 << 19));
            nBeginValue2 = v11 + v10;
            nBeginValue0 = (v11 - v12) ^ ((v12 >> 28) | 16 * v12);
            nBeginValue1 = nBeginValue2 + v12;
            length -= 12;
            pdwData0 += 3;
          }
          switch (length)
          {
            case 0xCu:
              nBeginValue0 += pdwData0[2];
              nBeginValue1 += pdwData0[1];
              nBeginValue2 += *pdwData0;
              break;
            case 0xBu:
              nBeginValue0 += pdwData0[2] & 0xFFFFFF;
              nBeginValue1 += pdwData0[1];
              nBeginValue2 += *pdwData0;
              break;
            case 0xAu:
              nBeginValue0 += pdwData0[2] & 0xFFFF;
              nBeginValue1 += pdwData0[1];
              nBeginValue2 += *pdwData0;
              break;
            case 9u:
              nBeginValue0 += pdwData0[2] & 0xFF;
              nBeginValue1 += pdwData0[1];
              nBeginValue2 += *pdwData0;
              break;
            case 8u:
              nBeginValue1 += pdwData0[1];
              nBeginValue2 += *pdwData0;
              break;
            case 7u:
              nBeginValue1 += pdwData0[1] & 0xFFFFFF;
              nBeginValue2 += *pdwData0;
              break;
            case 6u:
              nBeginValue1 += pdwData0[1] & 0xFFFF;
              nBeginValue2 += *pdwData0;
              break;
            case 5u:
              nBeginValue1 += pdwData0[1] & 0xFF;
              nBeginValue2 += *pdwData0;
              break;
            case 4u:
              nBeginValue2 += *pdwData0;
              break;
            case 3u:
              nBeginValue2 += *pdwData0 & 0xFFFFFF;
              break;
            case 2u:
              nBeginValue2 += *pdwData0 & 0xFFFF;
              break;
            case 1u:
              nBeginValue2 += *pdwData0 & 0xFF;
              break;
            case 0u:
              return nBeginValue0;
          }
        }
        else if (!((uint32_t)data & 1))
        {
          while (length > 0xC)
          {
            v14 = nBeginValue1 + (pdwData0[3] << 16) + pdwData0[2];
            v15 = nBeginValue0 + (pdwData0[5] << 16) + pdwData0[4];
            v16 = (nBeginValue2 + (pdwData0[1] << 16) + *pdwData0 - v15) ^ ((v15 >> 28) | 16 * v15);
            v17 = v14 + v15;
            v18 = (v14 - v16) ^ ((v16 >> 26) | (v16 << 6));
            v19 = v17 + v16;
            v20 = (v17 - v18) ^ ((v18 >> 24) | (v18 << 8));
            v21 = v19 + v18;
            v22 = (v19 - v20) ^ ((v20 >> 16) | (v20 << 16));
            v23 = v21 + v20;
            v24 = (v21 - v22) ^ ((v22 >> 13) | (v22 << 19));
            nBeginValue2 = v23 + v22;
            nBeginValue0 = (v23 - v24) ^ ((v24 >> 28) | 16 * v24);
            nBeginValue1 = nBeginValue2 + v24;
            length -= 12;
            pdwData0 += 6;
          }
          switch (length)
          {
            case 0xCu:
              nBeginValue0 += (pdwData0[5] << 16) + pdwData0[4];
              nBeginValue1 += (pdwData0[3] << 16) + pdwData0[2];
              nBeginValue2 += (pdwData0[1] << 16) + *pdwData0;
              break;
            case 0xBu:
              nBeginValue0 += *((uint8_t *)pdwData0 + 10) << 16;
            case 0xAu:
              nBeginValue0 += pdwData0[4];
              nBeginValue1 += (pdwData0[3] << 16) + pdwData0[2];
              nBeginValue2 += (pdwData0[1] << 16) + *pdwData0;
              break;
            case 9u:
              nBeginValue0 += *((uint8_t *)pdwData0 + 8);
            case 8u:
              nBeginValue1 += (pdwData0[3] << 16) + pdwData0[2];
              nBeginValue2 += (pdwData0[1] << 16) + *pdwData0;
              break;
            case 7u:
              nBeginValue1 += *((uint8_t *)pdwData0 + 6) << 16;
            case 6u:
              nBeginValue1 += pdwData0[2];
              nBeginValue2 += (pdwData0[1] << 16) + *pdwData0;
              break;
            case 5u:
              nBeginValue1 += *((uint8_t *)pdwData0 + 4);
            case 4u:
              nBeginValue2 += (pdwData0[1] << 16) + *pdwData0;
              break;
            case 3u:
              nBeginValue2 += *((uint8_t *)pdwData0 + 2) << 16;
            case 2u:
              nBeginValue2 += *pdwData0;
              break;
            case 1u:
              nBeginValue2 += *(uint8_t *)pdwData0;
              break;
            case 0u:
              return nBeginValue0;
          }
        }
        else {
          while (length > 0xC)
          {
            v25 = nBeginValue1
              + *((uint8_t *)pdwData0 + 4)
              + (*((uint8_t *)pdwData0 + 5) << 8)
              + (*((uint8_t *)pdwData0 + 6) << 16)
              + (*((uint8_t *)pdwData0 + 7) << 24);
            v26 = nBeginValue0
              + *((uint8_t *)pdwData0 + 8)
              + (*((uint8_t *)pdwData0 + 9) << 8)
              + (*((uint8_t *)pdwData0 + 10) << 16)
              + (*((uint8_t *)pdwData0 + 11) << 24);
            v27 = (nBeginValue2
              + *(uint8_t *)pdwData0
              + (*((uint8_t *)pdwData0 + 1) << 8)
              + (*((uint8_t *)pdwData0 + 2) << 16)
              + (*((uint8_t *)pdwData0 + 3) << 24)
              - v26) ^ ((v26 >> 28) | 16 * v26);
            v28 = v25 + v26;
            v29 = (v25 - v27) ^ ((v27 >> 26) | (v27 << 6));
            v30 = v28 + v27;
            v31 = (v28 - v29) ^ ((v29 >> 24) | (v29 << 8));
            v32 = v30 + v29;
            v33 = (v30 - v31) ^ ((v31 >> 16) | (v31 << 16));
            v34 = v32 + v31;
            v35 = (v32 - v33) ^ ((v33 >> 13) | (v33 << 19));
            nBeginValue2 = v34 + v33;
            nBeginValue0 = (v34 - v35) ^ ((v35 >> 28) | 16 * v35);
            nBeginValue1 = nBeginValue2 + v35;
            length -= 12;
            pdwData0 += 3;
          }
          switch (length)
          {
            case 0u:
              result = nBeginValue0;
              break;
            case 0xCu:
              nBeginValue0 += *((uint8_t *)pdwData0 + 11) << 24;
            case 0xBu:
              nBeginValue0 += *((uint8_t *)pdwData0 + 10) << 16;
            case 0xAu:
              nBeginValue0 += *((uint8_t *)pdwData0 + 9) << 8;
            case 9u:
              nBeginValue0 += *((uint8_t *)pdwData0 + 8);
            case 8u:
              nBeginValue1 += *((uint8_t *)pdwData0 + 7) << 24;
            case 7u:
              nBeginValue1 += *((uint8_t *)pdwData0 + 6) << 16;
            case 6u:
              nBeginValue1 += *((uint8_t *)pdwData0 + 5) << 8;
            case 5u:
              nBeginValue1 += *((uint8_t *)pdwData0 + 4);
            case 4u:
              nBeginValue2 += *((uint8_t *)pdwData0 + 3) << 24;
            case 3u:
              nBeginValue2 += *((uint8_t *)pdwData0 + 2) << 16;
            case 2u:
              nBeginValue2 += *((uint8_t *)pdwData0 + 1) << 8;
            case 1u:
              nBeginValue2 += *(uint8_t *)pdwData0;
          }
        }
    
        v36 = (nBeginValue1 ^ nBeginValue0) - ((nBeginValue1 >> 18) | (nBeginValue1 << 14));
        v37 = (v36 ^ nBeginValue2) - ((v36 >> 21) | (v36 << 11));
        v38 = (v37 ^ nBeginValue1) - ((v37 >> 7) | (v37 << 25));
        v39 = (v38 ^ v36) - ((v38 >> 16) | (v38 << 16));
        v40 = (v39 ^ v37) - ((v39 >> 28) | 16 * v39);
        v41 = (v40 ^ v38) - ((v40 >> 18) | (v40 << 14));
    
        return (v41 ^ v39) - ((v41 >> 8) | (v41 << 24));
      }
  • C# port of the CRC function in BDO Toolkit:
    Update: Simplified the snippet, uses native arrays instead of marshalling and using unsafe pointers, removed redundant code (not really sure what it was for, but in my testing it was never triggered).

    C# CRC (Simplified):
    public static uint CalculatePackCRC(byte[] data)
    {
        int length = data.Length;
    
        const int startingValue = 0x2145E233; // 558228019
    
        int nBeginValue0 = length - startingValue; /// [sp+20h] [bp-10h]@1
        uint nBeginValue1 = (uint)length - startingValue; /// [sp+24h] [bp-Ch]@1
        int nBeginValue2 = length - startingValue; /// [sp+2Ch] [bp-4h]@1
    
        int ToInt(byte[] array, int idx, int size = sizeof(int))
        {
            int value = 0;
    
            //size = System.Math.Min(4, size);
            if (array.Length - idx < size) size = System.Math.Max(0, array.Length - idx);
    
            if (size > 3) value |= array[idx + 3] << 24;
            if (size > 2) value |= array[idx + 2] << 16;
            if (size > 1) value |= array[idx + 1] << 8;
            if (size > 0) value |= array[idx];
    
            return value;
        }
    
        int BitwiseAnd(int value, int len, int offset = 0)
        {
            if (len - offset == 3) return value & 0xFFFFFF;
            if (len - offset == 2) return value & 0xFFFF;
            if (len - offset == 1) return value & 0xFF;
    
            return value;
        }
    
        int index = 0;
    
        const int int3Size = sizeof(int) * 3;
    
        while (length > int3Size)
        {
            int v2 = (int)(ToInt(data, index + sizeof(int)) + nBeginValue1); /// ST24_4@4
            uint v3 = (uint)(ToInt(data, index + sizeof(int) * 2) + nBeginValue0); /// ST20_4@4
            uint v4 = (uint)((ToInt(data, index) + nBeginValue2 - v3) ^ ((v3 >> 28) | 16 * v3)); /// ST2C_4@4
    
            int v5 = v2 + (int)v3; /// ST20_4@4
            uint v6 = ((uint)v2 - v4) ^ ((v4 >> 26) | (v4 << 6)); /// ST24_4@4
            int v7 = v5 + (int)v4; /// ST2C_4@4
            uint v8 = ((uint)v5 - v6) ^ ((v6 >> 24) | (v6 << 8)); /// ST20_4@4
            int v9 = v7 + (int)v6; /// ST24_4@4
            uint v10 = ((uint)v7 - v8) ^ ((v8 >> 16) | (v8 << 16)); /// ST2C_4@4
            int v11 = v9 + (int)v8; /// ST20_4@4
            uint v12 = ((uint)v9 - v10) ^ ((v10 >> 13) | (v10 << 19)); /// ST24_4@4
    
            nBeginValue2 = v11 + (int)v10;
            nBeginValue0 = (int)((v11 - v12) ^ ((v12 >> 28) | 16 * v12));
            nBeginValue1 = (uint)nBeginValue2 + v12;
    
            length -= int3Size;
    
            index += int3Size;
        }
    
        if (length > 0)
        {
            int offset;
            if (length > (offset = sizeof(int) * 2)) nBeginValue0 += BitwiseAnd(ToInt(data, index + offset), length, offset);
            if (length > (offset = sizeof(int))) nBeginValue1 += (uint)BitwiseAnd(ToInt(data, index + offset), length, offset);
            nBeginValue2 += BitwiseAnd(ToInt(data, index), length);
        }
        else
        {
            return (uint)nBeginValue0;
        }
    
        uint v36 = (nBeginValue1 ^ (uint)nBeginValue0) - ((nBeginValue1 >> 18) | (nBeginValue1 << 14)); /// ST20_4@56
        uint v37 = (v36 ^ (uint)nBeginValue2) - ((v36 >> 21) | (v36 << 11)); /// ST2C_4@56
        uint v38 = (v37 ^ nBeginValue1) - ((v37 >> 7) | (v37 << 25)); /// ST24_4@56
    
        uint v39 = (v38 ^ v36) - ((v38 >> 16) | (v38 << 16)); /// ST20_4@56
        uint v40 = (v39 ^ v37) - ((v39 >> 28) | 16 * v39); /// ST2C_4@56
        uint v41 = (v40 ^ v38) - ((v40 >> 18) | (v40 << 14)); /// ST24_4@56
    
        return (v41 ^ v39) - ((v41 >> 8) | (v41 << 24));
    }

    C# CRC:
    //https://github.com/kukdh1/PAZ-Unpacker/blob/master/Crypt.cpp#L321-L584//https://github.com/kukdh1/PAZ-Unpacker/blob/a8cd0d8aeeed510afb5198cbac944c6663dcdf54/Crypt.cpp#L321-L584
    /// <summary>
    /// CRC calculation codes from Blackdesert_Launcher.exe (KR Client)
    /// </summary>
    private static unsafe uint CalculatePackCRC(byte* data, uint length)
    {
        uint result; /// eax@18
    
        int nBeginValue0 = (int)length - 558228019; /// [sp+20h] [bp-10h]@1
        uint nBeginValue1 = length - 558228019; /// [sp+24h] [bp-Ch]@1
        int nBeginValue2 = (int)length - 558228019; /// [sp+2Ch] [bp-4h]@1
    
        int* pdwData0 = (int*)data; /// [sp+Ch] [bp-24h]@39
    
        if (((uint)data & 3) <= 0)
        {
            while (length > 0xC)
            {
                int v2 = (int)(pdwData0[1] + nBeginValue1); /// ST24_4@4
                uint v3 = (uint)(pdwData0[2] + nBeginValue0); /// ST20_4@4
                uint v4 = (uint)((*pdwData0 + nBeginValue2 - v3) ^ ((v3 >> 28) | 16 * v3)); /// ST2C_4@4
    
                int v5 = (int)(v2 + v3); /// ST20_4@4
                uint v6 = (uint)((v2 - v4) ^ ((v4 >> 26) | (v4 << 6))); /// ST24_4@4
                int v7 = (int)(v5 + v4); /// ST2C_4@4
                uint v8 = (uint)((v5 - v6) ^ ((v6 >> 24) | (v6 << 8))); /// ST20_4@4
                int v9 = (int)(v7 + v6); /// ST24_4@4
                uint v10 = (uint)((v7 - v8) ^ ((v8 >> 16) | (v8 << 16))); /// ST2C_4@4
                int v11 = (int)(v9 + v8); /// ST20_4@4
                uint v12 = (uint)((v9 - v10) ^ ((v10 >> 13) | (v10 << 19))); /// ST24_4@4
    
                nBeginValue2 = (int)(v11 + v10);
                nBeginValue0 = (int)((v11 - v12) ^ ((v12 >> 28) | 16 * v12));
                nBeginValue1 = (uint)(nBeginValue2 + v12);
                length -= 12;
                pdwData0 += 3;
            }
            switch (length)
            {
                case 0xCu:
                    nBeginValue0 += pdwData0[2];
                    nBeginValue1 += (uint)pdwData0[1];
                    nBeginValue2 += *pdwData0;
                    break;
                case 0xBu:
                    nBeginValue0 += pdwData0[2] & 0xFFFFFF;
                    nBeginValue1 += (uint)pdwData0[1];
                    nBeginValue2 += *pdwData0;
                    break;
                case 0xAu:
                    nBeginValue0 += pdwData0[2] & 0xFFFF;
                    nBeginValue1 += (uint)pdwData0[1];
                    nBeginValue2 += *pdwData0;
                    break;
                case 9u:
                    nBeginValue0 += pdwData0[2] & 0xFF;
                    nBeginValue1 += (uint)pdwData0[1];
                    nBeginValue2 += *pdwData0;
                    break;
                case 8u:
                    nBeginValue1 += (uint)pdwData0[1];
                    nBeginValue2 += *pdwData0;
                    break;
                case 7u:
                    nBeginValue1 += (uint)pdwData0[1] & 0xFFFFFF;
                    nBeginValue2 += *pdwData0;
                    break;
                case 6u:
                    nBeginValue1 += (uint)pdwData0[1] & 0xFFFF;
                    nBeginValue2 += *pdwData0;
                    break;
                case 5u:
                    nBeginValue1 += (uint)pdwData0[1] & 0xFF;
                    nBeginValue2 += *pdwData0;
                    break;
                case 4u:
                    nBeginValue2 += *pdwData0;
                    break;
                case 3u:
                    nBeginValue2 += *pdwData0 & 0xFFFFFF;
                    break;
                case 2u:
                    nBeginValue2 += *pdwData0 & 0xFFFF;
                    break;
                case 1u:
                    nBeginValue2 += *pdwData0 & 0xFF;
                    break;
                case 0u:
                    return (uint)nBeginValue0;
            }
        }
        else if (((uint)data & 1) <= 0)
        {
            while (length > 0xC)
            {
                int v14 = (int)(nBeginValue1 + (pdwData0[3] << 16) + pdwData0[2]); /// ST24_4@23
                uint v15 = (uint)(nBeginValue0 + (pdwData0[5] << 16) + pdwData0[4]); /// ST20_4@23
                uint v16 = (uint)((nBeginValue2 + (pdwData0[1] << 16) + *pdwData0 - v15) ^ ((v15 >> 28) | 16 * v15)); /// ST2C_4@23
    
                int v17 = (int)(v14 + v15); /// ST20_4@23
                uint v18 = (uint)((v14 - v16) ^ ((v16 >> 26) | (v16 << 6))); /// ST24_4@23
                int v19 = (int)(v17 + v16); /// ST2C_4@23
                uint v20 = (uint)((v17 - v18) ^ ((v18 >> 24) | (v18 << 8))); /// ST20_4@23
                int v21 = (int)(v19 + v18); /// ST24_4@23
                uint v22 = (uint)((v19 - v20) ^ ((v20 >> 16) | (v20 << 16))); /// ST2C_4@23
                int v23 = (int)(v21 + v20); /// ST20_4@23
                uint v24 = (uint)((v21 - v22) ^ ((v22 >> 13) | (v22 << 19))); /// ST24_4@23
    
                nBeginValue2 = (int)(v23 + v22);
                nBeginValue0 = (int)((v23 - v24) ^ ((v24 >> 28) | 16 * v24));
                nBeginValue1 = (uint)(nBeginValue2 + v24);
                length -= 12;
                pdwData0 += 6;
            }
            switch (length)
            {
                case 0xCu:
                    nBeginValue0 += (pdwData0[5] << 16) + pdwData0[4];
                    nBeginValue1 += (uint)((pdwData0[3] << 16) + pdwData0[2]);
                    nBeginValue2 += (pdwData0[1] << 16) + *pdwData0;
                    break;
                case 0xBu:
                    nBeginValue0 += *((byte*)pdwData0 + 10) << 16;
                    break;
                case 0xAu:
                    nBeginValue0 += pdwData0[4];
                    nBeginValue1 += (uint)((pdwData0[3] << 16) + pdwData0[2]);
                    nBeginValue2 += (pdwData0[1] << 16) + *pdwData0;
                    break;
                case 9u:
                    nBeginValue0 += *((byte*)pdwData0 + 8);
                    break;
                case 8u:
                    nBeginValue1 += (uint)((pdwData0[3] << 16) + pdwData0[2]);
                    nBeginValue2 += (pdwData0[1] << 16) + *pdwData0;
                    break;
                case 7u:
                    nBeginValue1 += (uint)(*((byte*)pdwData0 + 6) << 16);
                    break;
                case 6u:
                    nBeginValue1 += (uint)pdwData0[2];
                    nBeginValue2 += (pdwData0[1] << 16) + *pdwData0;
                    break;
                case 5u:
                    nBeginValue1 += *((byte*)pdwData0 + 4);
                    break;
                case 4u:
                    nBeginValue2 += (pdwData0[1] << 16) + *pdwData0;
                    break;
                case 3u:
                    nBeginValue2 += *((byte*)pdwData0 + 2) << 16;
                    break;
                case 2u:
                    nBeginValue2 += *pdwData0;
                    break;
                case 1u:
                    nBeginValue2 += *(byte*)pdwData0;
                    break;
                case 0u:
                    return (uint)nBeginValue0;
            }
        }
        else
        {
            while (length > 0xC)
            {
                int v25 = (int)(nBeginValue1
                    + *((byte*)pdwData0 + 4)
                    + (*((byte*)pdwData0 + 5) << 8)
                    + (*((byte*)pdwData0 + 6) << 16)
                    + (*((byte*)pdwData0 + 7) << 24)); /// ST24_4@41
                uint v26 = (uint)(nBeginValue0
                    + *((byte*)pdwData0 + 8)
                    + (*((byte*)pdwData0 + 9) << 8)
                    + (*((byte*)pdwData0 + 10) << 16)
                    + (*((byte*)pdwData0 + 11) << 24)); /// ST20_4@41
                uint v27 = (uint)((nBeginValue2
                    + *(byte*)pdwData0
                    + (*((byte*)pdwData0 + 1) << 8)
                    + (*((byte*)pdwData0 + 2) << 16)
                    + (*((byte*)pdwData0 + 3) << 24)
                    - v26) ^ ((v26 >> 28) | 16 * v26)); /// ST2C_4@41
    
                int v28 = (int)(v25 + v26); /// ST20_4@41
                uint v29 = (uint)((v25 - v27) ^ ((v27 >> 26) | (v27 << 6))); /// ST24_4@41
                int v30 = (int)(v28 + v27); /// ST2C_4@41
                uint v31 = (uint)((v28 - v29) ^ ((v29 >> 24) | (v29 << 8))); /// ST20_4@41
                int v32 = (int)(v30 + v29); /// ST24_4@41
                uint v33 = (uint)((v30 - v31) ^ ((v31 >> 16) | (v31 << 16))); /// ST2C_4@41
                int v34 = (int)(v32 + v31); /// ST20_4@41
                uint v35 = (uint)((v32 - v33) ^ ((v33 >> 13) | (v33 << 19))); /// ST24_4@41
    
                nBeginValue2 = (int)(v34 + v33);
                nBeginValue0 = (int)((v34 - v35) ^ ((v35 >> 28) | 16 * v35));
                nBeginValue1 = (uint)(nBeginValue2 + v35);
                length -= 12;
                pdwData0 += 3;
            }
            switch (length)
            {
                case 0u:
                    result = (uint)nBeginValue0;
                    break;
                case 0xCu:
                    nBeginValue0 += *((byte*)pdwData0 + 11) << 24;
                    break;
                case 0xBu:
                    nBeginValue0 += *((byte*)pdwData0 + 10) << 16;
                    break;
                case 0xAu:
                    nBeginValue0 += *((byte*)pdwData0 + 9) << 8;
                    break;
                case 9u:
                    nBeginValue0 += *((byte*)pdwData0 + 8);
                    break;
                case 8u:
                    nBeginValue1 += (uint)(*((byte*)pdwData0 + 7) << 24);
                    break;
                case 7u:
                    nBeginValue1 += (uint)(*((byte*)pdwData0 + 6) << 16);
                    break;
                case 6u:
                    nBeginValue1 += (uint)(*((byte*)pdwData0 + 5) << 8);
                    break;
                case 5u:
                    nBeginValue1 += *((byte*)pdwData0 + 4);
                    break;
                case 4u:
                    nBeginValue2 += *((byte*)pdwData0 + 3) << 24;
                    break;
                case 3u:
                    nBeginValue2 += *((byte*)pdwData0 + 2) << 16;
                    break;
                case 2u:
                    nBeginValue2 += *((byte*)pdwData0 + 1) << 8;
                    break;
                case 1u:
                    nBeginValue2 += *(byte*)pdwData0;
                    break;
            }
        }
    
        uint v36 = (uint)(nBeginValue1 ^ nBeginValue0) - ((nBeginValue1 >> 18) | (nBeginValue1 << 14)); /// ST20_4@56
        uint v37 = (uint)(v36 ^ nBeginValue2) - ((v36 >> 21) | (v36 << 11)); /// ST2C_4@56
        uint v38 = (v37 ^ nBeginValue1) - ((v37 >> 7) | (v37 << 25)); /// ST24_4@56
        uint v39 = (v38 ^ v36) - ((v38 >> 16) | (v38 << 16)); /// ST20_4@56
        uint v40 = (v39 ^ v37) - ((v39 >> 28) | 16 * v39); /// ST2C_4@56
        uint v41 = (v40 ^ v38) - ((v40 >> 18) | (v40 << 14)); /// ST24_4@56
    
        return (v41 ^ v39) - ((v41 >> 8) | (v41 << 24));
    }
    
    public static unsafe uint CalculatePackCRC(byte[] data, bool log = false)
    {
        uint hash = 0;
    
        IntPtr dataPtr = IntPtr.Zero;
        try
        {
            dataPtr = Marshal.AllocHGlobal(data.Length);
            Marshal.Copy(data, 0, dataPtr, data.Length);
    
            hash = CalculatePackCRC((byte*)dataPtr.ToPointer(), (uint)data.Length);
    
            if (log) Console.WriteLine($"CRC HASH ({hash})");
        }
        finally
        {
            if (log) Console.WriteLine("FREEING MEMORY");
    
            if (dataPtr != IntPtr.Zero) Marshal.FreeHGlobal(dataPtr);
        }
    
        return hash;
    }

    In-library can be called with:
    C#:
    uint BDOToolkit.Helpers.FileBlockCRC.CalculatePackCRC(byte[] data, bool log = false)
  • Each region's 20 most recent meta version to CRC calculations (supposedly used by the servers, not the clients, so blocking or redirecting these pages is unlikely to work, but that's untested): https://s1.pearlcdn.com/GameInfo/CRC/EN/crc.txt
    Substitute EN for other regions, tested working: RU, JP, KR, SA, TW
    Formatting is metaVersion:uint32CRC.


If you rewrite the CRC function you can use the CRC lists to verify it behaves like the original.

If anyone makes progress on this please do share.
If anyone can figure out reverse CRC I'll be including it in Meta Injector. I've decided to keep it as a separate tool for now until I (finally) release an update to BDO Toolkit and Meta Injector, which may happen for a while.
 
Last edited:

UndercoverPervert

Content Creator
Joined
Aug 9, 2016
Meta Patcher is now available to "unblock" regions, full details available in the thread:

I'm usually not the kind of guy to ask for money, and above all I just want to help people, but donations via the link in my signature would be greatly appreciated to help with increasing living costs.
 

romedon

Potential Patron
Joined
Mar 11, 2020
Meta Patcher is now available to "unblock" regions, full details available in the thread:

I'm usually not the kind of guy to ask for money, and above all I just want to help people, but donations via the link in my signature would be greatly appreciated to help with increasing living costs.

hello there is this toolkid working with mena?
 

UndercoverPervert

Content Creator
Joined
Aug 9, 2016
YES every OFFICIAL region can be modded again, however for the time being it requires an additional tool called Meta Patcher, details about it can be found here.

And once again I'll reiterate that NO, I do not support private servers, most of the private servers that have stopped working with the mod did so intentionally by changing the encryption key and/or algorithm, I offered to include unofficial support (as in, let the tools use the encryption key/algorithm needed to read the files, which may work, may not, I wouldn't go out of my way to support custom file formats or anything of the sort), GamezBD said they were interested but never replied with the information needed to make the tools compatible, that was almost a year ago, you can ask the server owners/developers to provide that information.


I am far more interested in working on new and better tools such as OpenBD, however development has been slow due to reduced amounts of free time and my declining interest in the game, I've explained it in more detail here. I have been looking to spend more time developing these newer and better tools, and if you would like to support that then consider supporting me via the links in my signature.
 
Last edited:
Status
Not open for further replies.

Users who are viewing this thread

Top


Are you 18 or older?

This website requires you to be 18 years of age or older. Please verify your age to view the content, or click Exit to leave.