Search code examples
c++md5wincrypt

md5 for an exe does not give the expected digest


I am new here, I hope I am doing this right.

I made a c++ application that computes MD5 hash digest for files following microsoft implementation in this link http://msdn.microsoft.com/en-us/library/windows/desktop/aa382380%28v=vs.85%29.aspx the case is the md5 hash digest is correct for any type file when comparing it with any ready tool that computes the md5 digest. except when the file in hands is an executable like cmd.exe. the hash digest is different and depends on the location of the exe. if you move the cmd.exe to another location then the digest becomes different again. so I went on implementing the same functionality using openssl library to face the same problem. I noticed that the hash digest from microsoft implementation and openssl is the same. so I think there is something missing in reading the file before passing it to compute the digest. but I searched alot to find nothing. I tried reading the file using "createfile" from win API and "fopen" to give the same result... so please help me with this, what am I missing?

here is the source code

#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>

#define BUFSIZE 1024
#define MD5LEN  16

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD dwStatus = 0;
    BOOL bResult = FALSE;
    HCRYPTPROV hProv = 0;
    HCRYPTHASH hHash = 0;
    HANDLE hFile = NULL;
    BYTE rgbFile[BUFSIZE];
    DWORD cbRead = 0;
    BYTE rgbHash[MD5LEN];
    DWORD cbHash = 0;
    CHAR rgbDigits[] = "0123456789abcdef";
    LPCWSTR filename=L"C:\\Windows\\System32\\cmd.exe";
    // Logic to check usage goes here.

    hFile = CreateFile(filename,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL);

    if (INVALID_HANDLE_VALUE == hFile)
    {
        dwStatus = GetLastError();
        printf("Error opening file %s\nError: %d\n", filename, 
            dwStatus); 
        return dwStatus;
    }

    // Get handle to the crypto provider
    if (!CryptAcquireContext(&hProv,
        NULL,
        NULL,
        PROV_RSA_FULL,
        CRYPT_VERIFYCONTEXT))
    {
        dwStatus = GetLastError();
        printf("CryptAcquireContext failed: %d\n", dwStatus); 
        CloseHandle(hFile);
        return dwStatus;
    }

    if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
    {
        dwStatus = GetLastError();
        printf("CryptAcquireContext failed: %d\n", dwStatus); 
        CloseHandle(hFile);
        CryptReleaseContext(hProv, 0);
        return dwStatus;
    }

    while (bResult = ReadFile(hFile, rgbFile, BUFSIZE, 
        &cbRead, NULL))
    {
        if (0 == cbRead)
        {
            break;
        }

        if (!CryptHashData(hHash, rgbFile, cbRead, 0))
        {
            dwStatus = GetLastError();
            printf("CryptHashData failed: %d\n", dwStatus); 
            CryptReleaseContext(hProv, 0);
            CryptDestroyHash(hHash);
            CloseHandle(hFile);
            return dwStatus;
        }
    }

    if (!bResult)
    {
        dwStatus = GetLastError();
        printf("ReadFile failed: %d\n", dwStatus); 
        CryptReleaseContext(hProv, 0);
        CryptDestroyHash(hHash);
        CloseHandle(hFile);
        return dwStatus;
    }

    cbHash = MD5LEN;
    if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
    {
        printf("MD5 hash of file %s is: ", filename);
        for (DWORD i = 0; i < cbHash; i++)
        {
            printf("%c%c", rgbDigits[rgbHash[i] >> 4],
                rgbDigits[rgbHash[i] & 0xf]);
        }
        printf("\n");
    }
    else
    {
        dwStatus = GetLastError();
        printf("CryptGetHashParam failed: %d\n", dwStatus); 
    }

    CryptDestroyHash(hHash);
    CryptReleaseContext(hProv, 0);
    CloseHandle(hFile);

    return dwStatus; 


    return 0;
}

this code computes the hash "59a1d4facd7b333f76c4142cd42d3aba" on the other hand "digital volcano hash tool" and "md5 and sha checksum utility v2.1" computes the hash "fc0b4a626881d7c5980d757214db2d25"


Solution

  • Your code is correct! You've run into an idiosyncrasy of Windows.

    The file you're reading is inside the C:\Windows\System32 directory.

    On 64-bit Windows, if a 32-bit application attempts to access that directory it gets redirected to C:\Windows\SysWow64.

    As there are cmd.exe files in both C:\Windows\System32 and C:\Windows\SysWow64, but they're different builds and so different hashes, programs checking the hash of C:\Windows\System32\cmd.exe will give different results depending on whether they're 32-bit or 64-bit.

    You can see this for yourself by building your example program both 32-bit and 64-bit and observing that they give different answers.