390 lines
10 KiB
C++
390 lines
10 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <windows.h>
|
|
#include <assert.h>
|
|
#include <io.h>
|
|
#include <md5.h>
|
|
|
|
#define MD5_CHECKSUM_SIZE 16
|
|
#define RESOURCE_CHECKSUM_LANGID 0x0409
|
|
|
|
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;
|
|
ULONG i;
|
|
|
|
DWORD dwResultLen;
|
|
BOOL bRet = FALSE;
|
|
|
|
//
|
|
// 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 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;
|
|
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;
|
|
|
|
wsprintfW( subBlcok,
|
|
L"\\StringFileInfo\\%04x%04x\\%s",
|
|
pLang->wLanguage,
|
|
pLang->wCodePage,
|
|
pVersionDataType);
|
|
|
|
// Retrieve file description for language and code page "i".
|
|
if (VerQueryValueW(pVerData,
|
|
subBlcok,
|
|
&lpBuffer,
|
|
&dwBytes)) {
|
|
return ((LPWSTR)lpBuffer);
|
|
}
|
|
|
|
return (EmptyString);
|
|
}
|
|
|
|
void PrintFileVersion(LPVOID pVerData)
|
|
{
|
|
UINT cbTranslate;
|
|
|
|
LANGANDCODEPAGE *lpTranslate;
|
|
|
|
|
|
// 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)
|
|
{
|
|
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:\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);
|
|
}
|
|
|