From c574f939f18e4d276097b664295dd89b904d573e Mon Sep 17 00:00:00 2001 From: Kai Bolay Date: Fri, 8 May 2020 17:26:34 -0400 Subject: [PATCH 1/4] Fix handling of ADAM7 interlacing: - fix calculation of row filter bytes - fix unpacking check --- .../java/ghidra/file/formats/ios/png/CrushedPNGUtil.java | 6 +++--- .../main/java/ghidra/file/formats/ios/png/IHDRChunk.java | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java index 752853ad96..264a9db7cc 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java @@ -207,8 +207,9 @@ public class CrushedPNGUtil { throws PNGFormatException { int width; int height; - if (ihdrChunk.getInterlaceMethod() == 1) { + if (ihdrChunk.getInterlaceMethod() == CrushedPNGConstants.ADAM7_INTERLACE) { //Msg.debug(this, "Checking Adam7 unpacking"); + int y = 0; for (int pass = 0; pass < CrushedPNGConstants.STARTING_COL.length; pass++) { width = (ihdrChunk.getImgWidth() - CrushedPNGConstants.STARTING_COL[pass] + @@ -220,7 +221,6 @@ public class CrushedPNGUtil { CrushedPNGConstants.ROW_INCREMENT[pass]; int row = 0; - int y = 0; while (row < height) { if (decompressedResult[y] > 4) { throw new PNGFormatException("Unknown row filter type " + @@ -236,7 +236,7 @@ public class CrushedPNGUtil { } } - int y = 0; + y = 0; for (int pass = 0; pass < CrushedPNGConstants.STARTING_COL.length; pass++) { //Formula taken from pngcheck diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/IHDRChunk.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/IHDRChunk.java index 0e53f44fab..013244dd9e 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/IHDRChunk.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/IHDRChunk.java @@ -133,13 +133,14 @@ public class IHDRChunk { for (int pass = 0; pass < CrushedPNGConstants.STARTING_ROW.length; pass++) { int height = - (imgHeight - CrushedPNGConstants.STARTING_ROW[pass] + CrushedPNGConstants.ROW_INCREMENT[pass - 1]) / + (imgHeight - CrushedPNGConstants.STARTING_ROW[pass] + CrushedPNGConstants.ROW_INCREMENT[pass] - 1) / CrushedPNGConstants.ROW_INCREMENT[pass]; rowFilterBytes += height; } + } else { + rowFilterBytes = imgHeight; } - rowFilterBytes = imgHeight; } public int getImgWidth() { From 12a6b23853d1a1044b9f56eef6f5cf66c0da1b31 Mon Sep 17 00:00:00 2001 From: Kai Bolay Date: Mon, 18 May 2020 09:54:28 -0400 Subject: [PATCH 2/4] Fix rewriting of IDAT chunk(s). - check if rewriting is needed, otherwise just copy - don't copy subsequent chunks if first one was rewritten (and therefore contains data from all original chunks) --- .../file/formats/ios/png/CrushedPNGUtil.java | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java index 264a9db7cc..653e18c779 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java @@ -145,31 +145,31 @@ public class CrushedPNGUtil { if (!(Arrays.equals(idBytes, CrushedPNGConstants.INSERTED_IOS_CHUNK))) { //If the chunk is the old IDAT chunk replace with new IDAT data - if (Arrays.equals(idBytes, CrushedPNGConstants.IDAT_CHUNK) && !wroteIDAT) { + if ((repackArray != null) && Arrays.equals(idBytes, CrushedPNGConstants.IDAT_CHUNK)) { + if (!wroteIDAT) { + //Write the chunk data length + int dataLength = repackArray.length; + byte[] lengthBytes = ByteBuffer.allocate(4).putInt(dataLength).array(); + outputStream.write(lengthBytes); - //Write the chunk data length - int dataLength = repackArray.length; - byte[] lengthBytes = ByteBuffer.allocate(4).putInt(dataLength).array(); - outputStream.write(lengthBytes); + //Gather ID and data together to calculate CRC32 + byte[] idat = new byte[CrushedPNGConstants.IDAT_CHUNK.length + dataLength]; + for (int i = 0; i < CrushedPNGConstants.IDAT_CHUNK.length; i++) { + idat[i] = CrushedPNGConstants.IDAT_CHUNK[i]; + } + for (int i = 0; i < dataLength; i++) { + idat[CrushedPNGConstants.IDAT_CHUNK.length + i] = repackArray[i]; + } - //Gather ID and data together to calculate CRC32 - byte[] idat = new byte[CrushedPNGConstants.IDAT_CHUNK.length + dataLength]; - for (int i = 0; i < CrushedPNGConstants.IDAT_CHUNK.length; i++) { - idat[i] = CrushedPNGConstants.IDAT_CHUNK[i]; + //Write the chunk data + outputStream.write(idat); + + //Calculate and write chunk crc32 + byte[] checksum = calculateCRC32(idat); + outputStream.write(checksum); + + wroteIDAT = true; } - for (int i = 0; i < dataLength; i++) { - idat[CrushedPNGConstants.IDAT_CHUNK.length + i] = repackArray[i]; - } - - //Write the chunk data - outputStream.write(idat); - - //Calculate and write chunk crc32 - byte[] checksum = calculateCRC32(idat); - outputStream.write(checksum); - - wroteIDAT = true; - } else { From 096ff3bb371cb175b23e8ea806dd0c0b524a4d65 Mon Sep 17 00:00:00 2001 From: Kai Bolay Date: Mon, 18 May 2020 12:33:42 -0400 Subject: [PATCH 3/4] Fix (de-)compression - detect too much compressed input - correctly write more than 64 kB (INITIAL_REPACK_SIZE) compressed output --- .../file/formats/ios/png/CrushedPNGUtil.java | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java index 653e18c779..08495b43e6 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java @@ -9,8 +9,6 @@ import java.nio.ByteBuffer; import java.util.*; import java.util.zip.*; -import org.apache.commons.compress.utils.IOUtils; - import ghidra.file.formats.zlib.ZLIB; import ghidra.util.task.TaskMonitor; @@ -102,37 +100,31 @@ public class CrushedPNGUtil { int expectedSize = (ihdrChunk.getBytesPerLine() * ihdrChunk.getImgHeight()) + ihdrChunk.getRowFilterBytes(); + byte[] results; + try (ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream(expectedSize); + InflaterOutputStream inflaterStream = new InflaterOutputStream(decompressedOutput)) { - InputStream inputStream = new ByteArrayInputStream(getFixedIdatDataBytes(idatStream)); - byte[] results = new byte[expectedSize]; - Inflater inflater = new Inflater(); - inflater.setInput(IOUtils.toByteArray(inputStream)); - //inflater.setInput(convertInputStreamToByteArray(inputStream)); - int numDecompressed = inflater.inflate(results); - - if (numDecompressed != expectedSize) { + inflaterStream.write(ZLIB_COMPRESSION_DEFAULT); + idatStream.writeTo(inflaterStream); + inflaterStream.finish(); + results = decompressedOutput.toByteArray(); + } + if (results.length != expectedSize) { throw new PNGFormatException("Decompression Error, expected " + expectedSize + - " bytes, but got " + numDecompressed + " bytes"); + " bytes, but got " + results.length + " bytes"); } //Processes the IDAT chunks to 'uncrushify' them processIDATChunks(ihdrChunk, results); - //Conservative sized repack array - byte[] tmp = new byte[CrushedPNGConstants.INITIAL_REPACK_SIZE]; - Deflater deflater = new Deflater(); - deflater.setInput(results); - deflater.finish(); - int numCompressed = deflater.deflate(tmp); + try (ByteArrayOutputStream compressedOutput = new ByteArrayOutputStream(CrushedPNGConstants.INITIAL_REPACK_SIZE); + DeflaterOutputStream deflaterStream = new DeflaterOutputStream(compressedOutput)) { - if (numCompressed <= 0) { - throw new PNGFormatException("Number of compressed bytes <= 0"); + deflaterStream.write(results); + deflaterStream.finish(); + deflaterStream.flush(); + repackArray = compressedOutput.toByteArray(); } - repackArray = new byte[numCompressed]; - for (int i = 0; i < numCompressed; i++) { - repackArray[i] = tmp[i]; - } - } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); From 603f97f5d7780acdb3bfdc8ab1aaf8a2fccd29ad Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Tue, 8 Sep 2020 15:17:59 -0400 Subject: [PATCH 4/4] GP-146 - fix compile error - missing ZLIB class specifier. --- .../main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java index 08495b43e6..a14e5ac296 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/png/CrushedPNGUtil.java @@ -104,7 +104,7 @@ public class CrushedPNGUtil { try (ByteArrayOutputStream decompressedOutput = new ByteArrayOutputStream(expectedSize); InflaterOutputStream inflaterStream = new InflaterOutputStream(decompressedOutput)) { - inflaterStream.write(ZLIB_COMPRESSION_DEFAULT); + inflaterStream.write(ZLIB.ZLIB_COMPRESSION_DEFAULT); idatStream.writeTo(inflaterStream); inflaterStream.finish(); results = decompressedOutput.toByteArray();