Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 596
  • Last Modified:

Return MD5Hash from .NET DLL - different on Windows 7

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
dlsimic
Asked:
dlsimic
  • 7
  • 6
1 Solution
 
wdosanjosCommented:
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
 
dlsimicAuthor Commented:
It was set to Any CPU.  I changed it to x86 and it has not made a difference.

Any other suggestions?
0
 
wdosanjosCommented:
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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
dlsimicAuthor Commented:
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
 
dlsimicAuthor Commented:
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
 
wdosanjosCommented:
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
 
dlsimicAuthor Commented:
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
 
wdosanjosCommented:
I'll test it on my home Windows 7 machine later today.  I'll post my finds.
0
 
wdosanjosCommented:
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
 
wdosanjosCommented:
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
 
dlsimicAuthor Commented:
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
 
wdosanjosCommented:
Can you provide a few sample images and the expected MD5 Checksum?
0
 
dlsimicAuthor Commented:
Here is the image we have been playing with.

MD5 should be: 4371f1a86186da2077ee65495c5b1594
orig-106451-001.jpg
0
 
nffvrxqgrcfqvvcCommented:
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 On Demand] Database Backup and Recovery

Does your company store data on premises, off site, in the cloud, or a combination of these? If you answered “yes”, you need a data backup recovery plan that fits each and every platform. Watch now as as Percona teaches us how to build agile data backup recovery plan.

  • 7
  • 6
Tackle projects and never again get stuck behind a technical roadblock.
Join Now