windows-nt/Source/XPSP1/NT/base/subsys/sm/sfc/dll/version.c

498 lines
12 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}