1

I am unable to open PNG files extracted from a certain Android app. I can't open them with the stock Android Gallery app. I can't open them in image browsers after downloading them on my computer. Analyzing the files with pngcheck results in this is neither a PNG or JNG image nor a MNG stream error.

The files in question were extracted with apktool. Extracting them directly from device cache leads to the same problem.

Further investigation with a hex editor shows that the files are missing the PNG signature and the IHDR chunk, which are normally present in a valid PNG file.

Here are the headers and first few lines of both files:

41 6E 74 6D 02 C5 01 DB FB C3 AB 00 00 00 00 00 63 BA A4 AD E7 E0 F0 E0 EA EA EA E7 A3 A2 AE B8 EA EA EA AA EA EA EA AA E2 E9 EA EA EA 77 5D 6B 06 EA EA EA EE 8D AB A7 AB EA EA 5B 65 E1 16 8B EF EA EA EA EB 99 B8 AD A8 EA 44 24 F6 03 EA EA E8 8B BA A6 BE AF EB F3 EA EA E5 EA EA FE EA EB F1 EA 09 13 3F E8 F4 EB EA E0 EA 0A 12 38 EA F8 EA E8 CB EB 34 1D 25 EB FD EA 31 1C 26 05 11 0C 1F 17 07 EA CE EA 32 1F 22 3E 1E 2F EA EF EA EA 07 00 00 0C 00 E9 FB DD 00 6E 00 10 68 07 E4 F9 D7 05 24 02 01 6B 00 0B 43 05 00 09 00 00 30 00 00 28 00 07 34 03 18 9B 0B 0F 62 07 01 73 00 07 39 03 ED FC E3 0D 57 06 E7 FA DB 0C 52 05 13 70 08 F2 FD EA EB FB E0 E6 F9 D9 00 2C 00 08 7C 03 C1 EB B1 06 2C 03 07 30 03 A3 DF 95 01 50 00 0A 3E 04 0F 5C 07 11 78 07 10 8B 07 04 78 01 2D B8 ...

41 6E 74 6D 02 09 01 77 D9 ED AB 00 00 00 00 00 63 BA A4 AD E7 E0 F0 E0 EA EA EA E7 A3 A2 AE B8 EA EA EA 98 EA EA EA 98 E2 E9 EA EA EA 52 E9 9F A5 EA EA EA EE 8D AB A7 AB EA EA 5B 65 E1 16 8B EF EA EA EA EB 99 B8 AD A8 EA 44 24 F6 03 EA EA E9 EA BA A6 BE AF A6 83 9B 6F BF C6 6F BF C1 57 E2 ED 81 6A 22 02 C9 C9 9A D9 F5 68 29 1B 6E BC C1 6F 80 62 6C B9 C1 14 4A 4E 81 68 2D 53 9B D4 14 14 14 34 2D 3D 81 6B 23 B2 81 77 03 32 0B 68 C2 F0 AE DB F3 B7 99 AE CB A7 C3 3E BC BF ED 93 51 02 A3 00 B9 73 39 B0 6D 38 E0 CB DA FF FE FF CD A5 C5 C6 A1 BD 81 BF EE 2B 53 81 2D 56 86 84 0E 0A FE 5D 65 BB 96 B2 98 3C 50 25 4A 7A 79 41 2B E6 D6 E3 D2 15 15 FE 9C A2 83 4D 27 AF 8C A7 5A 41 26 B7 71 3D 32 65 A7 DE C9 D8 D0 11 10 D8 BF D1 CA 0A 09 CE 16 15 62 3B 29 83 68 85 CC A7 ...

By comparison, here's the beginning of an example valid PNG:

89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 50 00 00 00 50 08 06 00 00 00 ...

Interestingly, both files have IDAT and IEND chunks.

I am only interested in viewing the files. How can I "repair" them so they can be opened in normal image viewers?


EDIT: I played with the files over the weekend and I arrived at the similar conclusions. I've decided to focus entirely on IDAT chunks. Here's what I found so far.

I looked for 00 bytes that are evenly spaced. This allowed me to find scanlines of equal size.

Since I know the full size of the IDAT chunk and the number of scanlines (height), I can guess width and they layout of pixels. Using truecolor pixels ended up with overstretched images. Using greyscale pixels was more promising, but well, it's greyscale - and I'm interested in color files.

Could it be that the colors are indexed with a palette?

  • Can you name the app? – TkTech Aug 31 '17 at 16:49
  • Most of the core logic is inside libagame.so and has not been obfuscated. The rest of it is inside encrypted files with an encryption key of "moontonAGame1234". I have not yet found the specific loader for these modified PNGs. – TkTech Sep 3 '17 at 20:45
  • @TkTech That's a fantastic find. Are the encrypted files the ones with the .mt extension? Also, could you explain how did you find the encryption key/encryption method (in case it changes in one of the future updates). – John Q Public Sep 16 '17 at 23:05
3

The first 16 bytes are unknown (likely some kind of private data). After that, a regular PNG file follows, but the first 128 bytes have been XORed with the value 234. The rest of the file is unchanged.

My reason for suspecting a simple XOR was the series of 0xEA bytes near the start; typically there would be zeroes there. Applying the XOR in a hex viewer immediately showed the familiar ‰PNG signature, followed by the missing IHDR and PLTE, and a series of nulls where previously 0xEA appeared. As that series of nulls changed halfway back to 0xEA, this indicated that only part of the file was thus obscured. Counting bytes did the rest.

Unencoding is thus as simple as discarding the first 16 bytes, XORing the next 128 bytes with 234, and copying the entire rest unchanged.

Encoding is theoretically possible, but for that to work, you need to know the meaning of the first 16 bytes. (It could be that these bytes are always the same, or are different in some way.)

Here is an example in its unencoded form, "levelselect/map_1.png":

a randomly selected image

as unencoded by the following quick-and-dirty Python program:

import sys

if len(sys.argv) == 2:
  with open(sys.argv[1], 'rb') as orig:
    # skip first 16 bytes
    orig.seek(16)
    # read in the rest
    data = bytearray(orig.read())
    # xor first 128 bytes with 0xea
    for x in range(0,128):
      data[x] ^= 0xea
    # write to new file
    with open(sys.argv[1]+'.fixed.png', 'wb') as fixed:
      fixed.write(data)

(I did not test this routine on all of the files.)

1

Yeah, this one's kind of funky. I'm not sure if what precedes the IDAT chunk is partially junk data, a compressed/encrypted header of some sort, or what. They both have what appears to be magic of their own (41 6E 74 6D 02), of which I looked around for as bytes and text (Antm, but I settled on Ant). I came across this, but I think it's just coincidental.

I tried a few things like replacing all the bytes preceding the IDAT with bytes from a handful of other PNGs (which included other requisite chunks related to resolution, etc.). I can see results by doing that, but the image dimensions, color, etc. are off.

You might have to rebuild the header yourself doing similar playing around. Are there PNGs from the file that you know are the same size? You could try swapping those bytes like I tried with random PNGs here.

You might also have some luck loading those in GIMP as raw image data. Scroll down to the RAW Image Data section in this post and see how you fair in playing with the width and height sliders. You may need to use a good header from another PNG for those files, which will likely give you the wrong width/height, but you could adjust those with the sliders.

Depending on what you find there, you could try rebuilding the header using other documented chunks from the PNG format--perhaps starting with the critical chunks (2 of which you already have in these files).

  • I played with the files over the weekend and I arrived at the similar conclusions. I've decided to focus entirely on IDAT chunks. I posted my findings in the main question. – John Q Public Aug 30 '17 at 2:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.