#include #include #include #include #include #include #include #define STRSAFE_LIB #include #define MD5_CHECKSUM_SIZE 16 #define RESOURCE_CHECKSUM_LANGID 0x0409 #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) BOOL g_bVerbose = FALSE; typedef struct { BOOL bContainResource; MD5_CTX ChecksumContext; } CHECKSUM_ENUM_DATA; void PrintError() { LPTSTR lpMsgBuf; if (FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL )) { printf("GetLastError():\n %s", lpMsgBuf); LocalFree( lpMsgBuf ); } return; } //////////////////////////////////////////////////////////////////////////////////// // // ChecksumEnumNamesFunc // // The callback funciton for enumerating the resource names in the specified module and // type. // The side effect is that MD5 checksum context (contained in CHECKSUM_ENUM_DATA // pointed by lParam) will be updated. // // Return: // Always return TRUE so that we can finish all resource enumeration. // //////////////////////////////////////////////////////////////////////////////////// BOOL CALLBACK ChecksumEnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG_PTR lParam){ HRSRC hRsrc; HGLOBAL hRes; const unsigned char* pv; LONG ResSize=0L; WORD IdFlag=0xFFFF; DWORD dwHeaderSize=0L; CHECKSUM_ENUM_DATA* pChecksumEnumData = (CHECKSUM_ENUM_DATA*)lParam; if(!(hRsrc=FindResourceEx(hModule, lpType, lpName, RESOURCE_CHECKSUM_LANGID))) { // // If US English resource is not found for the specified type and name, we // will continue the resource enumeration. // return (TRUE); } pChecksumEnumData->bContainResource = TRUE; if (!(ResSize=SizeofResource(hModule, hRsrc))) { printf("WARNING: Can not get resource size when generating resource checksum.\n"); return (TRUE); } if (!(hRes=LoadResource(hModule, hRsrc))) { printf("WARNING: Can not load resource when generating resource checksum.\n"); return (TRUE); } pv=(unsigned char*)LockResource(hRes); // // Update MD5 context using the binary data of this particular resource. // MD5Update(&(pChecksumEnumData->ChecksumContext), pv, ResSize); return TRUE; } //////////////////////////////////////////////////////////////////////////////////// // // ChecksumEnumTypesFunc // // The callback function for enumerating the resource types in the specified module. // This function will call EnumResourceNames() to enumerate all resource names of // the specified resource type. // // Return: // TRUE if EnumResourceName() succeeds. Otherwise FALSE. // //////////////////////////////////////////////////////////////////////////////////// BOOL CALLBACK ChecksumEnumTypesFunc(HMODULE hModule, LPSTR lpType, LONG_PTR lParam) { CHECKSUM_ENUM_DATA* pChecksumEnumData = (CHECKSUM_ENUM_DATA*)lParam; // // Skip the version resource type, so that version is not included in the resource checksum. // if (lpType == RT_VERSION) { return (TRUE); } if(!EnumResourceNames(hModule, (LPCSTR)lpType, ChecksumEnumNamesFunc, (LONG_PTR)lParam)) { return (FALSE); } return (TRUE); } //////////////////////////////////////////////////////////////////////////////////// // // GenerateResourceChecksum // // Generate the resource checksum for the US English resource in the specified file. // // Parameters: // pszSourceFileName The file used to generate resource checksum. // pResourceChecksum Pointer to a 16 bytes (128 bits) buffer for storing // the calcuated MD5 checksum. // Return: // TURE if resource checksum is generated from the given file. Otherwise FALSE. // // The following situation may return FALSE: // * The specified file does not contain resource. // * If the specified file contains resource, but the resource is not US English. // * The specified file only contains US English version resource. // //////////////////////////////////////////////////////////////////////////////////// BOOL GenerateResourceChecksum(LPCSTR pszSourceFileName, unsigned char* pResourceChecksum) { HMODULE hModule = NULL; ULONG i; DWORD dwResultLen; BOOL bRet = FALSE; if (!pszSourceFileName) { goto GR_EXIT; } // // The stucture to be passed into the resource enumeration functions. // CHECKSUM_ENUM_DATA checksumEnumData; checksumEnumData.bContainResource = FALSE; // // Start MD5 checksum calculation by initializing MD5 context. // MD5Init(&(checksumEnumData.ChecksumContext)); if (g_bVerbose) { printf("Generate resource checksum for [%s]\n", pszSourceFileName); } if(!(hModule = LoadLibraryEx(pszSourceFileName, NULL, DONT_RESOLVE_DLL_REFERENCES|LOAD_LIBRARY_AS_DATAFILE))) { if (g_bVerbose) { printf("\nERROR: Error in opening resource checksum module [%s]\n", pszSourceFileName); } PrintError(); goto GR_EXIT; } if (g_bVerbose) { printf("\nLoad checksum file: %s\n", pszSourceFileName); } // // Enumerate all resources in the specified module. // During the enumeration, the MD5 context will be updated. // if (!EnumResourceTypes(hModule, ChecksumEnumTypesFunc, (LONG_PTR)&checksumEnumData)) { if (g_bVerbose) { printf("\nWARNING: Unable to generate resource checksum from resource checksum module [%s]\n", pszSourceFileName); } goto GR_EXIT; } if (checksumEnumData.bContainResource) { if (!pResourceChecksum) { goto GR_EXIT; } // // If the enumeration succeeds, and the specified file contains US English // resource, get the MD5 checksum from the accumulated MD5 context. // MD5Final(&checksumEnumData.ChecksumContext); memcpy(pResourceChecksum, checksumEnumData.ChecksumContext.digest, 16); if (g_bVerbose) { printf("Generated checksum: ["); for (i = 0; i < MD5_CHECKSUM_SIZE; i++) { printf("%02x ", pResourceChecksum[i]); } printf("]\n"); } bRet = TRUE; } GR_EXIT: if (hModule) { FreeLibrary(hModule); } return (bRet); } void PrintUsage() { printf("muiver - Print out MUI resource checksum\n"); printf("Usage:\n\n"); printf(" muiver \n"); } void PrintChecksum(BYTE* lpChecksum, int nSize) { int i; if (!lpChecksum) { return; } for (i = 0; i < nSize; i++) { printf("%02x ", lpChecksum[i]); } } struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; }; LPWSTR EmptyString = L"N/A"; LPWSTR GetFileVersionStringData(LPVOID pVerData, LANGANDCODEPAGE* pLang, LPWSTR pVersionDataType) { WCHAR subBlcok[256]; LPVOID lpBuffer; UINT dwBytes; HRESULT hresult; if ( (! pVerData) || (!pLang) || (!pVersionDataType)) { goto ERET; } //*STRSAFE* wsprintfW( subBlcok,L"\\StringFileInfo\\%04x%04x\\%s",pLang->wLanguage,pLang->wCodePage,pVersionDataType); hresult = StringCchPrintfW(subBlcok,ARRAYSIZE(subBlcok),L"\\StringFileInfo\\%04x%04x\\%s",pLang->wLanguage,pLang->wCodePage,pVersionDataType); if (!SUCCEEDED(hresult)) { goto ERET; } // Retrieve file description for language and code page "i". if (VerQueryValueW(pVerData, subBlcok, &lpBuffer, &dwBytes)) { return ((LPWSTR)lpBuffer); } ERET: return (EmptyString); } void PrintFileVersion(LPVOID pVerData) { UINT cbTranslate; LANGANDCODEPAGE *lpTranslate; if (!pVerData) { return; } // Read the list of languages and code pages. VerQueryValueW(pVerData, L"\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &cbTranslate); // Read the file description for each language and code page. for(UINT i=0; i < (cbTranslate/sizeof(struct LANGANDCODEPAGE)); i++) { wprintf(L" Locale = 0x%04x, CodePage = %d\n", lpTranslate->wLanguage, lpTranslate->wCodePage); wprintf(L" FileDescriptions: [%s", GetFileVersionStringData(pVerData, lpTranslate+i, L"FileDescription")); wprintf(L"]\n"); wprintf(L" FileVersion : [%s]\n", GetFileVersionStringData(pVerData, lpTranslate+i, L"FileVersion")); wprintf(L" ProductVersion : [%s]\n", GetFileVersionStringData(pVerData, lpTranslate+i, L"ProductVersion")); //wprintf(L" Comments : [%s]\n", GetFileVersionStringData(pVerData, lpTranslate+i, L"Comments")); } BYTE* lpResourceChecksum; UINT cbResourceChecksum; wprintf(L" ResourceChecksum: ["); if (VerQueryValueW(pVerData, L"\\VarFileInfo\\ResourceChecksum", (LPVOID*)&lpResourceChecksum, &cbResourceChecksum)) { for (i = 0; i < cbResourceChecksum; i++) { wprintf(L"%02x ", lpResourceChecksum[i]); } } else { wprintf(L"n/a"); } wprintf(L"]\n"); } void PrintResult(LPSTR fileName, LPVOID pVerData, BYTE* pChecksum) { if (!fileName) { return; } printf("File: [%s]\n", fileName); printf("\nVersion information:\n"); if (pVerData == NULL) { printf(" !!! Not availabe. Failed in GetFileVersionInfo()\n"); } else { PrintFileVersion(pVerData); } printf("\n\n Resource Checksum:%s\n ", fileName); if (pChecksum == NULL) { printf(" n/a. Probably resources for 0x%04x is not available.\n", RESOURCE_CHECKSUM_LANGID); } else { PrintChecksum(pChecksum, MD5_CHECKSUM_SIZE); } printf("\n"); } int __cdecl main(int argc, char *argv[]){ LPSTR pFileName; LPBYTE lpVerData = NULL; DWORD dwVerDataSize; DWORD dwHandle; BYTE MD5Checksum[MD5_CHECKSUM_SIZE]; if (argc == 1) { PrintUsage(); return (1); } pFileName = argv[1]; if (dwVerDataSize = GetFileVersionInfoSizeA(pFileName, &dwHandle)) { lpVerData = new BYTE[dwVerDataSize]; if (!GetFileVersionInfoA(pFileName, 0, dwVerDataSize, (LPVOID)lpVerData)) { lpVerData = NULL; } } if (GenerateResourceChecksum(pFileName, MD5Checksum)) { PrintResult(pFileName, (LPVOID)lpVerData, MD5Checksum); } else { PrintResult(pFileName, (LPVOID)lpVerData, NULL); } if (!lpVerData) { delete [] lpVerData; } return (0); }