|
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <assert.h>
#include <io.h>
#include <md5.h>
#define STRSAFE_LIB
#include <strsafe.h>
#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 <US English file name>\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); }
|