Solved

Return MD5Hash from .NET DLL - different on Windows 7

Posted on 2011-02-22
14
572 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
SharePoint Admin?

Enable Your Employees To Focus On The Core With Intuitive Onscreen Guidance That is With You At The Moment of Need.

 

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
 
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

[Webinar] How Hackers Steal Your Credentials

Do You Know How Hackers Steal Your Credentials? Join us and Skyport Systems to learn how hackers steal your credentials and why Active Directory must be secure to stop them. Thursday, July 13, 2017 10:00 A.M. PDT

Question has a verified solution.

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

Entity Framework is a powerful tool to help you interact with the DataBase but still doesn't help much when we have a Stored Procedure that returns more than one resultset. The solution takes some of out-of-the-box thinking; read on!
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…

695 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