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
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):
Resources:
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 CRCI'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.
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):
- Patch the client at runtime (risky due to potential and likely anti-cheat detection)
- Intercept and modify network traffic during login (likely encrypted, requires decoding the packet structure, may be assisted by some private servers' code)
- "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++):
PAZ-Unpacker/Crypt.cpp at a8cd0d8aeeed510afb5198cbac944c6663dcdf54 · kukdh1/PAZ-Unpacker
Blackdesert paz file unpacker. Contribute to kukdh1/PAZ-Unpacker development by creating an account on GitHub.github.comC++ 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
SubstituteEN
for other regions, tested working:RU
,JP
,KR
,SA
,TW
Formatting ismetaVersion: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
Last edited: