Solved

Return MD5Hash from .NET DLL - different on Windows 7

Posted on 2011-02-22
14
551 Views
Last Modified: 2012-05-11
I have a VB6 app that calls a .NET dll (written in C#).  The .NET dll has a function that accepts a pathway to an image and returns the MD5 Hash.  

The .NET code works fine on all operating systems and returns the same md5 for the test image.

Calling this function from within vb6 on a XP or Vista machine produces the same md5hash as running the .NET code alone would produce.  However, when the VB6 code is run on a windows 7 box the md5 hash is different.

Do anyone know why this is happening?


VB6 CODE:
  Dim myObject As MD5Hash.cHash
  Set myObject = New MD5Hash.cHash
  MsgBox myObject.MD5Hash("c:\orig_106451_001.jpg")

.NET CODE:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.IO;

namespace MD5Hash
{
    public class cHash
    {
        static MD5 md5 = new MD5CryptoServiceProvider();

        public cHash()
            {
            }

        public string MD5hash(string path)
        {            
            string hexaHash = "";
            foreach (byte b in md5.ComputeHash(BmpToBytes_Unsafe(new Bitmap(path))))
            {
                hexaHash += String.Format("{0:x2}", b);
            }
           
            using (StreamWriter osw = new StreamWriter(@"c:\md5hash.log", true))
            {
                osw.WriteLine(String.Format("{0} - {1}", System.DateTime.Now, hexaHash));
            }
            return hexaHash;
        }

        static private unsafe byte[] BmpToBytes_Unsafe(Bitmap bmp)
        {
            BitmapData bData = bmp.LockBits(new Rectangle(new Point(), bmp.Size),
                ImageLockMode.ReadOnly,
                PixelFormat.Format24bppRgb);
            // number of bytes in the bitmap
            int byteCount = bData.Stride * bmp.Height;
            byte[] bmpBytes = new byte[byteCount];

            // Copy the locked bytes from memory
            Marshal.Copy(bData.Scan0, bmpBytes, 0, byteCount);

            // don't forget to unlock the bitmap!!
            bmp.UnlockBits(bData);

            return bmpBytes;
        }
    }
}
0
Comment
Question by:dlsimic
  • 7
  • 6
14 Comments
 
LVL 23

Expert Comment

by:wdosanjos
ID: 34954525
I suspect it has something to do with 32-bit and 64-bit Windows and your unsafe method.  Check if the C# dll is built with Platform Target = 'Any CPU' or 'x86'.  If it is set to 'Any CPU', try building it with 'x86' and check if the discrepancy is resolved.

 Build Configuration
0
 

Author Comment

by:dlsimic
ID: 34954622
It was set to Any CPU.  I changed it to x86 and it has not made a difference.

Any other suggestions?
0
 
LVL 23

Expert Comment

by:wdosanjos
ID: 34954676
Alternatively you could change your cHash class to avoid the unsafe method as follows:

public class cHash
{
	static MD5 md5 = new MD5CryptoServiceProvider();

	public cHash()
		{
		}

	public string MD5hash(string path)
	{            
		string hexaHash = "";
		using (FileStream image = new FileStream(path, FileMode.Open))
		{
			foreach (byte b in md5.ComputeHash(image))
			{
				hexaHash += String.Format("{0:x2}", b);
			}
		
			using (StreamWriter osw = new StreamWriter(@"c:\temp\md5hash.log", true))
			{
				osw.WriteLine(String.Format("{0} - {1}", System.DateTime.Now, hexaHash));
			}
		}
		return hexaHash;
	}
}

Open in new window

0
 

Author Comment

by:dlsimic
ID: 34954693
I will check if this will make a difference.  The unsafe block of code is to rip out the image portion from the jpeg.  We change exif data and I didn't want that affecting my hash.

I will let you know shortly if this matters.
0
 

Author Comment

by:dlsimic
ID: 34955183
If I remove the unsafe block the md5 is the same whether going through just plain .NEt or with the vb6 wrapper.  So this at least narrows down where the problem is happening.

However, we really need the md5 to only be of the image data itself.

Any ideas why vb6 is changing the data?  Maybe you know of different code for my function bmpToBytes_Unsafe to would work through vb6 also (on a windows 7 machine).
0
 
LVL 23

Expert Comment

by:wdosanjos
ID: 34955343
Please try the following code.  It extracts the image from the file without using the unsafe code.  I successfully tested here with a couple of .jpg's where the only difference was the exif data, and they returned the same MD5 checksum.

public class cHash
{
	static MD5 md5 = new MD5CryptoServiceProvider();

	public cHash()
		{
		}

	public string MD5hash(string path)
	{            
		string hexaHash = "";

		Image image = Image.FromFile(path);
		
		MemoryStream buffer = new MemoryStream();
		
		image.Save(buffer, ImageFormat.Bmp);
		
		buffer.Seek(0, SeekOrigin.Begin);

		foreach (byte b in md5.ComputeHash(buffer))
		{
			hexaHash += String.Format("{0:x2}", b);
		}
	
		using (StreamWriter osw = new StreamWriter(@"c:\temp\md5hash.log", true))
		{
			osw.WriteLine(String.Format("{0} - {1}", System.DateTime.Now, hexaHash));
		}

		return hexaHash;
	}
}

Open in new window

0
 

Author Comment

by:dlsimic
ID: 34955683
No, still get different hash results through VB6.

It is funny it you write the whole file to byte array you get the same results.  But once you start getting just portions of the file it stops working.
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 23

Expert Comment

by:wdosanjos
ID: 34955931
I'll test it on my home Windows 7 machine later today.  I'll post my finds.
0
 
LVL 23

Expert Comment

by:wdosanjos
ID: 34958599
I was able to reproduce the issue on my Windows 7 machine.

The Windows 7 GDI+ library seems to be writing different values on the bitmap header fields (more specifically the vertical & horizontal resolution of the image). Thus causing the discrepancy.  The code below skips the bitmap headers and generates the MD5 only over the Pixel Array.

I tested the code on XP and 7. On both systems the MD5 values are now matching.

public class cHash
{
    static MD5 md5 = new MD5CryptoServiceProvider();

    public cHash()
    {
    }

    public string MD5hash(string path)
    {
        string hexaHash = "";

        Image image = Image.FromFile(path);

        MemoryStream buffer = new MemoryStream();

        image.Save(buffer, ImageFormat.Bmp);

        image.Dispose();

        // offset (ie, starting address of byte where the bitmap image data (Pixel Array) can be found
        buffer.Seek(0x0A, SeekOrigin.Begin);

        byte[] offsetField = new byte[4];
        int offset;
        
        buffer.Read(offsetField, 0, offsetField.Length);

        offset = (offsetField[2] << 24) 
               | (offsetField[3] << 16)
               | (offsetField[0] << 8)
               | offsetField[1];

        buffer.Seek(offset, SeekOrigin.Begin);

        // Compute the md5
        foreach (byte b in md5.ComputeHash(buffer))
        {
            hexaHash += String.Format("{0:x2}", b);
        }

        // Log
        using (StreamWriter osw = new StreamWriter(@"c:\temp\md5hash.log", true))
        {
            osw.WriteLine(String.Format("{0} - {1}", System.DateTime.Now, hexaHash));
        }

        return hexaHash;
    }
}

Open in new window


Please let me know if it works for you.
0
 
LVL 23

Accepted Solution

by:
wdosanjos earned 500 total points
ID: 34958646
To troubleshoot this issue, I generated a bmp from jpeg using the Image class (Image.Save) on XP and 7. I then compared the files for differences.  There are only 4 mismatching bytes on the bmp header vertical and horizontal resolution fields.  All the other bytes on the bmp files match.

 Difference
0
 

Author Comment

by:dlsimic
ID: 34964753
Sorry, we have been playing around with this a little more and here is what we have come up with.  I think your first guess about the x86 compile was on the right track.

We can reproduce this error from within .NET itself if you change the target platform to x86.  Granted I am on a x64 machine, but when I run as any cpu or x64 it works fine.  However, if I change the target platform to x86 is when I have the problem.

This leads me to believe the vb6 is forcing it to run as x86 (which is why I believe you asked to compile it for x86 to start with).

The code you gave will work to get around the bits that are changing, however; our problem is that we have a distributed application that already is using the md5 for years based on the byte range we were previously looking at.  Any changes will cause the program to not work properly because we are expecting the md5 as a certain output.

It seems to me that there is a bug in the x86 framework that is changing the vertical and horizontal resolution when the bytes are read (doing some kind of auto-rotation upon reading the bytes).

We really need to find away around this so that we can get the expected results.

Thanks for your help.
0
 
LVL 23

Expert Comment

by:wdosanjos
ID: 34964956
Can you provide a few sample images and the expected MD5 Checksum?
0
 

Author Comment

by:dlsimic
ID: 34964992
Here is the image we have been playing with.

MD5 should be: 4371f1a86186da2077ee65495c5b1594
orig-106451-001.jpg
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
ID: 34970592
Here is result running your code. The byte size is the same so there must be a change in the bytes when copying as the above posts mentions.
Windows 7 64 bit OS

// Running x64
4371f1a86186da2077ee65495c5b1594

// Running x86
62a46935ff2178bee1db6c477e1dc032

Open in new window

0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you ever wanted to restrict the users input in a textbox to numbers, and while doing that make sure that they can't 'cheat' by pasting in non-numeric text? Of course you can do that with code you write yourself but it's tedious and error-prone …
If you need to start windows update installation remotely or as a scheduled task you will find this very helpful.
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…

920 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now