As you can see, several standard PNG chunk names are present (sRGB, bKGD, IDAT, etc.) however the data between them appears to be scrambledcompressed/encrypted.
Some otherMy observations so far:
- bytes
0x14-17 seem to be a DWORD indicating the custom header size (0x1C or 28 bytes) - bytes
0x1C-1D seem to be a WORD indicating the size ofof the data between themselves and where the actual PNG header begins (0x4B00 or 75 bytes, including the 2 size bytes) - this unknown data could wellmight be the chunkcompressed data table/dictionary - files are aligned to 4 byte blocks with
0x00 - of the 2,196 compressed PNG files, exactly half have the same
IHDR data: F0 E0 08 02 B2 E1 7C 5E 00 01; the other half have 01 90 88 08 02 E0 58 21 6D - similarly, half of the files' compressed data/dictionaries begin with the same 22 bytes:
FC 06 31 46 85 53 68 85 84 F9 95 6E 40 80 55 E8 C5 18 19 DE 4A F8; the other half begin with FC 06 35 54 38 25 56 C8 1F 50 E9 06 04 58 85 5E 8C 91 E1 AD 84 - note how every file begins with FC 03 3x xx
Update: After digging around, I've managed to find a copy of the firmware with the symbols included (available here: https://www.mediafire.com/file/a0u7k2m87aj1jp6/Files.zip/file) and I'm pretty sure I've located the correct function for decompressing the files: cpr_tclDecompressAlgorithm::bDecompressData.
Unfortunately, I'm not able to make a great deal of sense of it, but what I can see from the symbols is that:
- a couple of custom structs are used in the decompression method,
cpr_tclCodeTable and cpr_tclCodeTableEntry - the structs are then used with methods called
cpr_tclCodeTable::iu32SearchIndexOfCode and cpr_tclCodeTable::corfoGetCodeEntry - decompression begins by calling
cpr_tclDecompressAlgorithm::u32GetNextBits to read 32 bits / 4 bytes - the return value from
u32GetNextBits is used with & 3 and right shifted 2 bits, which indicates the first two bits are used as some scrambled formkind of flag
HasI will post IDA's pseudocode of the function below, but I'm not expecting anyone encountered this kindto spend a great deal of PNG before?time on this - I just wondered if anyone can recognise what's happening here, e.g. Huffman, LZSS, etc.
int __fastcall cpr_tclDecompressAlgorithm::bDecompressData(int a1, int a2, int a3, size_t a4, unsigned int a5, int a6) { int v7; // r8 unsigned int v10; // r5 int v11; // r9 void *v12; // r0 unsigned int v13; // r8 unsigned int v14; // r0 const cpr_tclCodeTable *CodeTable; // r11 unsigned int v16; // r6 unsigned int v17; // r7 unsigned int v18; // r0 int v19; // r0 int v20; // r2 int v21; // r3 int v22; // r5 int v23; // r0 int v24; // r7 unsigned int v25; // r1 int v26; // r3 _BYTE *v27; // r0 size_t v28; // r6 __int16 v29; // r12 int v30; // r2 int v31; // r5 int v32; // r0 unsigned int v33; // r10 int v34; // r3 _BYTE *v35; // r1 int v36; // r0 unsigned int v37; // r12 int v38; // r3 void *v39; // r0 size_t v40; // r5 int v41; // r2 int result; // r0 int v43; // r3 int v44; // r2 _BYTE *v45; // r2 int v46; // r0 int v47; // r3 _BOOL4 v48; // r3 int v49; // r0 *(_DWORD *)(a1 + 16) = a4; v7 = a2 & 3; *(_DWORD *)(a1 + 12) = a3; *(_DWORD *)(a1 + 4) = a2; *(_DWORD *)(a1 + 8) = a2; if ( (a2 & 3) != 0 ) { v49 = trc_szGetFileName( "/opt/bosch/DI_SWNAVI_13.2C5P10/di_swnavi/components/dapi/devicemanager/devicehandler/compression/cpr_decompression.cpp"); trc_tclGlobalTrace::vTraceLevel1(21504, 332, v49, "CPR read access to unaligned adress 0x%x", a2); return 0; } cpr_tclDecompressAlgorithm::vInterpreteHeader((cpr_tclDecompressAlgorithm *)a1, a5); v10 = a5 - *(_DWORD *)(a1 + 48); if ( a4 < a5 ) { if ( a4 <= v10 ) { *(_DWORD *)(a1 + 48) = v7; v10 = a4; v11 = 1; goto LABEL_7; } *(_DWORD *)(a1 + 48) = a4 - v10; } v11 = v7; LABEL_7: v12 = *(void **)(a1 + 12); v13 = (unsigned int)v12 + v10; if ( !v10 ) goto LABEL_34; if ( a6 == 3 ) { v14 = cpr_tclDecompressAlgorithm::u32GetNextBits((cpr_tclDecompressAlgorithm *)a1, 0x20u); CodeTable = (const cpr_tclCodeTable *)(*(_DWORD *)a1 + 44 * (v14 & 3)); v16 = v14 >> 2; v17 = 2; cpr_tclDecompressAlgorithm::vInit((cpr_tclDecompressAlgorithm *)a1, CodeTable); while ( 1 ) { while ( 1 ) { if ( *(_DWORD *)(a1 + 12) >= v13 ) goto LABEL_34; v18 = cpr_tclCodeTable::iu32SearchIndexOfCode(CodeTable, v16); v19 = cpr_tclCodeTable::corfoGetCodeEntry(CodeTable, v18); v20 = *(_DWORD *)(v19 + 12); v21 = *(unsigned __int8 *)(v19 + 16); v22 = v19; v17 += v21; v16 >>= v21; if ( v20 != 3 ) break; v23 = cpr_tclDecompressAlgorithm::u32GetNextBits((cpr_tclDecompressAlgorithm *)a1, v17); v24 = *(unsigned __int8 *)(v22 + 17); v25 = v23 | v16; v26 = (v23 | v16) & ~(-1 << v24); v27 = *(_BYTE **)(a1 + 12); v28 = (unsigned __int16)(v26 + *(_WORD *)(v22 + 2)); v29 = *(_WORD *)(v22 + 6); v30 = *(_DWORD *)(a1 + 44); v31 = *(unsigned __int8 *)(v22 + 18); if ( v13 < (unsigned int)&v27[v28] ) { if ( v11 != 1 ) { v32 = trc_szGetFileName( "/opt/bosch/DI_SWNAVI_13.2C5P10/di_swnavi/components/dapi/devicemanager/devicehandler/compression/cpr" "_decompression.cpp"); trc_tclGlobalTrace::vTraceLevel1( 21504, 253, v32, "CPR Overwrite Copy n=%d wpos=0x%x epos=0x%x", v28, *(_DWORD *)(a1 + 12), v13); LABEL_24: result = 0; goto LABEL_35; } v28 = (unsigned __int16)(v13 - (_WORD)v27); } v33 = v25 >> v24; v34 = -(unsigned __int16)(v29 + (((v25 >> v24) & ~(-1 << v31)) << v30)); v35 = &v27[-(unsigned __int16)(v29 + (((v25 >> v24) & ~(-1 << v31)) << v30))]; if ( v28 == 2 ) { *v27 = v27[v34]; *(_BYTE *)(*(_DWORD *)(a1 + 12) + 1) = v35[1]; } else { memcpy(v27, v35, v28); } v17 = v31 + v24; *(_DWORD *)(a1 + 12) += v28; v16 = v33 >> v31; } if ( v20 == 2 ) { v36 = cpr_tclDecompressAlgorithm::u32GetNextBits((cpr_tclDecompressAlgorithm *)a1, v17); v17 = *(unsigned __int8 *)(v22 + 17); v37 = v36 | v16; v38 = (v36 | v16) & ~(-1 << v17); v39 = *(void **)(a1 + 12); v40 = (unsigned __int16)(v38 + *(_WORD *)(v22 + 2)); if ( v13 < (unsigned int)v39 + v40 ) { if ( v11 != 1 ) { v41 = trc_szGetFileName( "/opt/bosch/DI_SWNAVI_13.2C5P10/di_swnavi/components/dapi/devicemanager/devicehandler/compression/cpr" "_decompression.cpp"); trc_tclGlobalTrace::vTraceLevel1( 21504, 291, v41, "CPR Overwrite Lit n=%d wpos=0x%x epos=0x%x", v40, *(_DWORD *)(a1 + 12), v13); goto LABEL_24; } v40 = (unsigned __int16)(v13 - (_WORD)v39); } v16 = v37 >> v17; memcpy(v39, *(const void **)(a1 + 8), v40); v43 = *(_DWORD *)(a1 + 12) + v40; v44 = *(_DWORD *)(a1 + 8) + v40; } else { v45 = *(_BYTE **)(a1 + 12); if ( (_BYTE *)v13 == v45 ) { v46 = trc_szGetFileName( "/opt/bosch/DI_SWNAVI_13.2C5P10/di_swnavi/components/dapi/devicemanager/devicehandler/compression/cpr_d" "ecompression.cpp"); trc_tclGlobalTrace::vTraceLevel1( 21504, 306, v46, "CPR Overwrite Lit1 n=1 wpos=0x%x epos=0x%x", *(_DWORD *)(a1 + 12), v13); goto LABEL_24; } *v45 = **(_BYTE **)(a1 + 8); v43 = *(_DWORD *)(a1 + 12) + 1; v44 = *(_DWORD *)(a1 + 8) + 1; } *(_DWORD *)(a1 + 12) = v43; *(_DWORD *)(a1 + 8) = v44; } } if ( a6 == 1 ) { memcpy(v12, *(const void **)(a1 + 4), v10); result = 1; *(_DWORD *)(a1 + 12) += v10; } else { LABEL_34: result = 1; } LABEL_35: v47 = *(_DWORD *)(a1 + 48); v48 = v47 != 0; if ( result != 1 ) v48 = 0; if ( v48 ) { memset(*(void **)(a1 + 12), 0, *(_DWORD *)(a1 + 48)); result = 1; } return result; }