|
|
/*++
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; }
|