1

I'm creating a program which reads and prints out metadata from an image, but I'm struggling to get my head around the JFIF and EXIF marker structures.

According to the wikipedia page for JFIF, the JFIF marker should be as follows:

FF E0 s1 s2 4A 46 49 46 00 ii ii jj XX XX YY YY xx yy

Where:

FF E0 is the start of the JFIF marker

s1 and s2 combined give the size of the segment (excluding the APP0 marker)

4A 46 49 46 00 is the identifier (literally JFIF in ascii)

i is the version of JFIF (2 bytes)

j is the density uni for DPI measurement

X is the Horizontal DPI

Y is the Vertical DPI

x is the Horizontal THUMBNAIL DPI

y is the Vertical THUMBNAIL DPI

However, when running an image through my program, I get this:

ff e0 20 10 4a 46 49 46 20 1 1 20 20 48 20 48 20 20 ff e1

The marker start is there, but the segment size looks well off (0x2010??) seeing as the next Marker for EXIF data starts just 15 bytes later! (FF E1) I think that hex values of 0x00 aren't being printed (hence why my image prints the JFIF identifier without the zeros) which may be adding to the confusion, but even then, how is the JFIF version 20 20?

If anyone on here has any experience looking at image metadata, I'd really appreciate your help! There's not a lot of resources that I can find that break down JFIF/EXIF data very clearly.

If you need me to post any code in here then I can, though apart from not printing 0x00 values to the console, it seems to being working as expected, so I think my main issue is actually understanding the meta data

Here is the code for taking the byte stream and then converting it into hex:

fileLocation = Console.ReadLine();
var fileDataAsBytes = File.ReadAllBytes(fileLocation);
var headers = fileDataAsBytes
    .Select((b, i) => (b, i))
    .Where(tuple => tuple.b == 0xFF 
    && fileDataAsBytes[tuple.i + 1] == 0xE1)
    .Select(tuple => $"{tuple.i}: {tuple.b:x} 
    {fileDataAsBytes[tuple.i + 1]:x}");
Console.WriteLine(String.Join(",", headers));
DealWithMarkers(fileDataAsBytes);
DisplayAllConversions(fileDataAsBytes);

public static void DisplayAllConversions(byte[] fileDataAsBytes)
    {
        DisplayBytes(fileDataAsBytes);
        DisplayHex(fileDataAsBytes);
        DisplayString(fileDataAsBytes);
    }

public static void DisplayHex(byte[] fileDataAsBytes)
    {
        Console.WriteLine($"\n\n\n\t*\t*\t*\t(As Hex)\t*\t*\t*\n");
        for (int i = 0; (i < 1000) && (i < fileDataAsBytes.Length); i++)
        {
            Console.Write($"{fileDataAsBytes[i]:x} ");
        }
    }

I tried this with a different image and it actually prints out the 0 value bytes correctly, so there must be something weird with the image file I was analyzing!

Image file with "incorrect" 0 bytes

Image file with "correct" 0 bytes

Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
131U3blue
  • 19
  • 4
  • Are you reading those bytes values as strings? Then printing the strings somewhere? If so, that's not a good star-- After `4A 46 49 46` there must be `00` (null string terminator). After that you always have `01 02` (version 1, revision 2). After, one byte that represents the interpretation of the units expressed by the 4 bytes that follow, must be `0`, `1 `or `2`. -- Don't read or print values as strings here, read bytes, then format, eventually, for presentation. Note that the `FF E0` is not necessarily the first segment, you can have `FF E1` instead, pretty common. – Jimi Dec 09 '20 at 01:04
  • It looks like all the `00` values are replaced by a space, char 32, `20` in hexadecimal. You should really show how you reading those bytes. – Jimi Dec 09 '20 at 01:38
  • Hey @Jimi, thanks for your help. I've edited to include my code as well. Interestingly, without changing the code, another image file actually seems to show the correct bytes, so something is obviously different with the image I was analyzing. Interestingly, the actual byte array that reads from the original file was showing those hex values as 20 when I looked at them in the debugger, so the actual bytes in the file appear to be "wrong" - any idea why this might be? – 131U3blue Dec 09 '20 at 11:53
  • To display the Image bytes in a Cosole. use `Console.WriteLine(string.Join(" ", imageBytes.Select(b => b.ToString("X2")).ToArray()));` in `DisplayHex()`. Add `.Take(1000)` before `ToArray()` if you want to display up to 1000 bytes. Remove the rest. The other methods seem useless and one of these it's probably the one that shows a space instead of a unprintable chars. -- Note that the JFIF / EXIF headers are all optional. The color table is what is required. – Jimi Dec 09 '20 at 13:13
  • But the exact same program will print those "unprintable" chars correctly as 0 when analysing a different image file. The actual byte values - **before being displayed in the console** - are actually 32 (or 0x20) when I view it within the fileDataAsBytes byte array. So, to be clear, it has nothing to do with the DisplayAsX() functions because the byte values are seemingly "incorrect" before they get there. I'm just unsure why, when there is clearly a JFIF header there. And there are a lot of 32 byte values, which do look like they SHOULD be 0s. So why are the byte values themselves wrong? – 131U3blue Dec 09 '20 at 15:44
  • Post the Image that is showing these values. – Jimi Dec 09 '20 at 15:46
  • Hey Jimi, have added a screenshot of the results of analysing both photos. The fileDataAsBytes byte values can be seen at the bottom. Did you want the actual images being analysed as well? – 131U3blue Dec 09 '20 at 22:34
  • You need to post the original Image. – Jimi Dec 09 '20 at 22:43

0 Answers0