498 lines
12 KiB
C
498 lines
12 KiB
C
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
version.h
|
|
|
|
Abstract:
|
|
|
|
Implementation of file version checking.
|
|
|
|
Author:
|
|
|
|
Wesley Witt (wesw) 18-Dec-1998
|
|
|
|
Revision History:
|
|
|
|
Andrew Ritz (andrewr) 6-Jul-1999
|
|
|
|
--*/
|
|
|
|
#include "sfcp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
//
|
|
// Resource type information block
|
|
//
|
|
typedef struct rsrc_typeinfo RSRC_TYPEINFO, *LPRESTYPEINFO;
|
|
|
|
|
|
//
|
|
// Resource name information block
|
|
//
|
|
typedef struct rsrc_nameinfo RSRC_NAMEINFO, *PRSRC_NAMEINFO;
|
|
|
|
|
|
#define RSORDID 0x8000 // if high bit of ID set then integer id
|
|
// otherwise ID is offset of string from
|
|
// the beginning of the resource table
|
|
// Ideally these are the same as the
|
|
// corresponding segment flags
|
|
|
|
typedef struct _RESOURCE_DATAW {
|
|
USHORT TotalSize;
|
|
USHORT DataSize;
|
|
USHORT Type;
|
|
WCHAR Name[16]; // L"VS_VERSION_INFO" + unicode nul
|
|
VS_FIXEDFILEINFO FixedFileInfo;
|
|
} RESOURCE_DATAW, *PRESOURCE_DATAW;
|
|
|
|
typedef struct _RESOURCE_DATAA {
|
|
USHORT TotalSize;
|
|
USHORT DataSize;
|
|
USHORT Type;
|
|
CHAR Name[16]; // L"VS_VERSION_INFO" + unicode nul
|
|
VS_FIXEDFILEINFO FixedFileInfo;
|
|
} RESOURCE_DATAA, *PRESOURCE_DATAA;
|
|
|
|
|
|
|
|
|
|
LPBYTE
|
|
FindResWithIndex(
|
|
LPBYTE lpResTable,
|
|
INT iResIndex,
|
|
LPBYTE lpResType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine searches for a resource in a resource table at the specified index.
|
|
The routine works by walking the resource table until we hit the specified
|
|
resource.
|
|
|
|
Arguments:
|
|
|
|
lpResTable - pointer to the resource table
|
|
iResIndex - integer indicating the index of the resource to be retreived
|
|
lpResType - pointer to data indicating the type of resource we're
|
|
manipulating
|
|
|
|
Return Value:
|
|
|
|
a pointer to the specified resource or NULL on failure.
|
|
|
|
--*/
|
|
{
|
|
LPRESTYPEINFO lpResTypeInfo;
|
|
|
|
ASSERT((lpResTable != NULL) && (iResIndex >= 0));
|
|
|
|
try {
|
|
|
|
lpResTypeInfo = (LPRESTYPEINFO)(lpResTable + sizeof(WORD));
|
|
|
|
while (lpResTypeInfo->rt_id) {
|
|
if ((lpResTypeInfo->rt_id & RSORDID) &&
|
|
(MAKEINTRESOURCE(lpResTypeInfo->rt_id & ~RSORDID) == (LPTSTR)lpResType)) {
|
|
if (lpResTypeInfo->rt_nres > (WORD)iResIndex) {
|
|
return (LPBYTE)(lpResTypeInfo+1) + iResIndex * sizeof(RSRC_NAMEINFO);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
//
|
|
// point to the next resource
|
|
//
|
|
lpResTypeInfo = (LPRESTYPEINFO)((LPBYTE)(lpResTypeInfo+1) + lpResTypeInfo->rt_nres * sizeof(RSRC_NAMEINFO));
|
|
}
|
|
DebugPrint( LVL_VERBOSE, L"FindResWithIndex didn't find resource\n" );
|
|
return(NULL);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
DebugPrint( LVL_VERBOSE, L"FindResWithIndex hit an exception\n" );
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
ULONGLONG
|
|
GetFileVersion16(
|
|
PVOID ImageBase,
|
|
PIMAGE_OS2_HEADER NewImageHeader
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine retreives the version for a downlevel image file.
|
|
|
|
Arguments:
|
|
|
|
ImageBase - base pointer to the image
|
|
NewImageHeader - pointer to the image's new header
|
|
|
|
Return Value:
|
|
|
|
a number indicating the version of the image or 0 if the version is
|
|
unavailable
|
|
|
|
--*/
|
|
{
|
|
PBYTE ResTable;
|
|
PRSRC_NAMEINFO ResPtr;
|
|
PRESOURCE_DATAA ResourceDataA;
|
|
ULONG iShiftCount;
|
|
ULONG Offset;
|
|
ULONGLONG Version = 0;
|
|
|
|
ASSERT(ImageBase != NULL && NewImageHeader != NULL && IMAGE_OS2_SIGNATURE == NewImageHeader->ne_magic);
|
|
|
|
if (NewImageHeader->ne_rsrctab != NewImageHeader->ne_restab) {
|
|
ResTable = (PBYTE) NewImageHeader + NewImageHeader->ne_rsrctab;
|
|
ResPtr = (PRSRC_NAMEINFO) FindResWithIndex( ResTable, 0, (LPBYTE)RT_VERSION );
|
|
if (ResPtr) {
|
|
iShiftCount = *((WORD *)ResTable);
|
|
Offset = MAKELONG(ResPtr->rn_offset << iShiftCount, (ResPtr->rn_offset) >> (16 - iShiftCount));
|
|
ResourceDataA = (PRESOURCE_DATAA)((PBYTE)ImageBase + Offset);
|
|
Version = ((ULONGLONG)ResourceDataA->FixedFileInfo.dwFileVersionMS << 32)
|
|
| (ULONGLONG)ResourceDataA->FixedFileInfo.dwFileVersionLS;
|
|
}
|
|
}
|
|
|
|
return Version;
|
|
}
|
|
|
|
|
|
ULONGLONG
|
|
GetFileVersion32(
|
|
IN PVOID ImageBase
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine retreives the version for a 32 bit image file.
|
|
|
|
Arguments:
|
|
|
|
ImageBase - base pointer to the image resource
|
|
|
|
|
|
Return Value:
|
|
|
|
a number indicating the version of the image or 0 if the version is
|
|
unavailable
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG_PTR IdPath[3];
|
|
ULONG ResourceSize;
|
|
ULONGLONG Version = 0;
|
|
PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
|
|
PRESOURCE_DATAW ResourceDataW;
|
|
|
|
ASSERT(ImageBase != NULL);
|
|
|
|
//
|
|
// Do this to prevent the Ldr routines from faulting.
|
|
//
|
|
ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);
|
|
|
|
IdPath[0] = PtrToUlong(RT_VERSION);
|
|
IdPath[1] = PtrToUlong(MAKEINTRESOURCE(VS_VERSION_INFO));
|
|
IdPath[2] = 0;
|
|
|
|
//
|
|
// find the resource data entry
|
|
//
|
|
try {
|
|
Status = LdrFindResource_U(ImageBase,IdPath,3,&DataEntry);
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
if(!NT_SUCCESS(Status)) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// now get the data out of the entry
|
|
//
|
|
try {
|
|
Status = LdrAccessResource(ImageBase,DataEntry,&ResourceDataW,&ResourceSize);
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
if(!NT_SUCCESS(Status)) {
|
|
return 0;
|
|
}
|
|
|
|
try {
|
|
if((ResourceSize >= sizeof(*ResourceDataW)) && !_wcsicmp(ResourceDataW->Name,L"VS_VERSION_INFO")) {
|
|
|
|
Version = ((ULONGLONG)ResourceDataW->FixedFileInfo.dwFileVersionMS << 32)
|
|
| (ULONGLONG)ResourceDataW->FixedFileInfo.dwFileVersionLS;
|
|
|
|
} else {
|
|
DebugPrint( LVL_MINIMAL, L"GetFileVersion32 warning: invalid version resource" );
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
DebugPrint( LVL_MINIMAL, L"GetFileVersion32 Exception encountered processing bogus version resource" );
|
|
}
|
|
|
|
return Version;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SfcGetVersionFileName(
|
|
IN PVOID ImageBase,
|
|
IN PWSTR FileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine retreives the original filename for an image. can be used to
|
|
determine the actual source name of the hal that is installed on the
|
|
system, for example
|
|
|
|
Arguments:
|
|
|
|
ImageBase - base pointer to the image resource
|
|
FileName - pointer to unicode string buffer which receives the filename.
|
|
There is an assumption that the original file name can never
|
|
exceed 32 characters.
|
|
|
|
Return Value:
|
|
|
|
TRUE indicates no problems retrieving version, FALSE indicates failure.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG_PTR IdPath[3];
|
|
ULONG ResourceSize;
|
|
ULONGLONG Version = 0;
|
|
PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
|
|
PRESOURCE_DATAW ResourceDataW;
|
|
LPVOID lpInfo;
|
|
LPVOID lpvData = NULL;
|
|
DWORD *pdwTranslation;
|
|
UINT uLen;
|
|
UINT cch;
|
|
DWORD dwDefLang = 0x409;
|
|
WCHAR key[80];
|
|
PWSTR s = NULL;
|
|
|
|
ASSERT((ImageBase != NULL) && (FileName != NULL));
|
|
|
|
//
|
|
// Do this to prevent the Ldr routines from faulting.
|
|
//
|
|
ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);
|
|
|
|
IdPath[0] = PtrToUlong(RT_VERSION);
|
|
IdPath[1] = PtrToUlong(MAKEINTRESOURCE(VS_VERSION_INFO));
|
|
IdPath[2] = 0;
|
|
|
|
//
|
|
// find the version resource
|
|
//
|
|
try {
|
|
Status = LdrFindResource_U(ImageBase,IdPath,3,&DataEntry);
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
if(!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// access the version resource
|
|
//
|
|
try {
|
|
Status = LdrAccessResource(ImageBase,DataEntry,&ResourceDataW,&ResourceSize);
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
if(!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
lpvData = ResourceDataW;
|
|
|
|
//
|
|
// get the default language
|
|
//
|
|
if (!VerQueryValue( lpvData, L"\\VarFileInfo\\Translation", &pdwTranslation, &uLen )) {
|
|
pdwTranslation = &dwDefLang;
|
|
uLen = sizeof(DWORD);
|
|
}
|
|
|
|
//
|
|
// get the original file name
|
|
//
|
|
swprintf( key, L"\\StringFileInfo\\%04x%04x\\OriginalFilename", LOWORD(*pdwTranslation), HIWORD(*pdwTranslation) );
|
|
if (VerQueryValue( lpvData, key, &lpInfo, &cch )) {
|
|
ASSERT(UnicodeChars(lpInfo) < 32);
|
|
wcsncpy( FileName, lpInfo, 32 );
|
|
} else {
|
|
DebugPrint( LVL_MINIMAL, L"VerQueryValue for OriginalFileName failed." );
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
SfcGetFileVersion(
|
|
IN HANDLE FileHandle,
|
|
OUT PULONGLONG Version,
|
|
OUT PULONG Checksum,
|
|
OUT PWSTR FileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine retreives the file version for an image, the checksum and the original
|
|
filename resource from the image.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - handle to the file to retrieve an image for
|
|
Version - ULONGLONG that receives the file version (can be NULL)
|
|
Checksum - DWORD which receives the file checksum (can be NULL)
|
|
FileName - pointer to unicode string buffer which receives the original
|
|
filename. There is an assumption that the original file name
|
|
can never exceed 32 characters (can be NULL)
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful: Version receives the major version in the high DWORD and the minor version in the low DWORD.
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE SectionHandle;
|
|
PVOID ImageBase;
|
|
SIZE_T ViewSize;
|
|
DWORD dwFileSize;
|
|
|
|
ASSERT(FileHandle != INVALID_HANDLE_VALUE);
|
|
ASSERT(Version != NULL || Checksum != NULL || FileName != NULL);
|
|
|
|
if(Version != NULL)
|
|
*Version = 0;
|
|
|
|
if(Checksum != NULL)
|
|
*Checksum = 0;
|
|
|
|
if(FileName != NULL)
|
|
*FileName = L'\0';
|
|
|
|
dwFileSize = GetFileSize(FileHandle, NULL);
|
|
|
|
if(-1 == dwFileSize)
|
|
return FALSE;
|
|
|
|
Status = SfcMapEntireFile(FileHandle, &SectionHandle, &ImageBase, &ViewSize);
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
return FALSE;
|
|
|
|
try {
|
|
//
|
|
// There are three sorts of files that can be replaced:
|
|
//
|
|
// 32-bit images. Extract the 32 bit version, checksum and filename
|
|
// 16-bit images. Extract the 16 bit version and checksum
|
|
// other. The version is 1 and we compute the checksum
|
|
//
|
|
if(dwFileSize > sizeof(IMAGE_DOS_HEADER))
|
|
{
|
|
PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER) ImageBase;
|
|
|
|
//
|
|
// this code will break if we ever protect files > 2^32. not to mention
|
|
// being very slow.
|
|
//
|
|
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic && DosHdr->e_lfanew > 0)
|
|
{
|
|
// assume 32bit
|
|
PIMAGE_NT_HEADERS NtHdrs = (PIMAGE_NT_HEADERS) ((PBYTE)ImageBase + DosHdr->e_lfanew);
|
|
|
|
if(dwFileSize > (DWORD) (DosHdr->e_lfanew + sizeof(PIMAGE_NT_HEADERS)) &&
|
|
IMAGE_NT_SIGNATURE == NtHdrs->Signature)
|
|
{
|
|
if(Version !=NULL)
|
|
*Version = GetFileVersion32( ImageBase );
|
|
|
|
if(Checksum != NULL)
|
|
*Checksum = NtHdrs->OptionalHeader.CheckSum;
|
|
|
|
if(FileName != NULL)
|
|
SfcGetVersionFileName( ImageBase, FileName );
|
|
|
|
goto lExit;
|
|
}
|
|
else
|
|
{
|
|
// assume 16bit
|
|
PIMAGE_OS2_HEADER NeHdr = (PIMAGE_OS2_HEADER) NtHdrs;
|
|
|
|
if(dwFileSize > (DWORD) (DosHdr->e_lfanew + sizeof(PIMAGE_OS2_HEADER)) &&
|
|
IMAGE_OS2_SIGNATURE == NeHdr->ne_magic)
|
|
{
|
|
if(Version !=NULL)
|
|
*Version = GetFileVersion16( ImageBase, NeHdr );
|
|
|
|
if(Checksum != NULL)
|
|
*Checksum = NeHdr->ne_crc;
|
|
|
|
goto lExit; // no filename
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
DebugPrint1( LVL_MINIMAL, L"Exception inside SfcGetFileVersion (0x%08X); bad image", GetExceptionCode() );
|
|
// fall through bad image
|
|
}
|
|
|
|
//
|
|
// Not a 16/32bit image. Compute a checksum. In the interest
|
|
// of speed, we'll add up all the ULONGs in the file and
|
|
// ignore any fraction at the end
|
|
//
|
|
if(Version != NULL)
|
|
*Version = 1;
|
|
|
|
if(Checksum != NULL) {
|
|
PULONG Data = (PULONG) ImageBase;
|
|
*Checksum = 0;
|
|
|
|
try {
|
|
while( dwFileSize >= sizeof( ULONG ) ) {
|
|
*Checksum += *Data++;
|
|
dwFileSize -= sizeof( ULONG );
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
DebugPrint1( LVL_MINIMAL, L"Exception inside SfcGetFileVersion while calculating the checksum (0x%08X)", GetExceptionCode() );
|
|
*Checksum = 0;
|
|
}
|
|
}
|
|
|
|
lExit:
|
|
SfcUnmapFile(SectionHandle,ImageBase);
|
|
return TRUE;
|
|
}
|