/*++ Copyright (c) 1989-2000 Microsoft Corporation Module Name: attributes.c Abstract: This file contains complete implementation of attribute retrieval and caching. Author: vadimb created sometime in 2000 Revision History: several people contributed (clupu, dmunsil...) --*/ // // Obtain tag information // #define _WANT_TAG_INFO #include "sdbp.h" #include #include #if defined(KERNEL_MODE) && defined(ALLOC_DATA_PRAGMA) #pragma data_seg() #endif // KERNEL_MODE && ALLOC_DATA_PRAGMA // // Attribute tags // The attributes are checked in the order they are listed below. // TAG g_rgAttributeTags[] = { TAG_SIZE, TAG_CHECKSUM, TAG_BIN_FILE_VERSION, TAG_BIN_PRODUCT_VERSION, TAG_PRODUCT_VERSION, TAG_FILE_DESCRIPTION, TAG_COMPANY_NAME, TAG_PRODUCT_NAME, TAG_FILE_VERSION, TAG_ORIGINAL_FILENAME, TAG_INTERNAL_NAME, TAG_LEGAL_COPYRIGHT, TAG_VERDATEHI, TAG_VERDATELO, TAG_VERFILEOS, TAG_VERFILETYPE, TAG_MODULE_TYPE, TAG_PE_CHECKSUM, TAG_LINKER_VERSION, #ifndef KERNEL_MODE TAG_16BIT_DESCRIPTION, TAG_16BIT_MODULE_NAME, #endif TAG_UPTO_BIN_FILE_VERSION, TAG_UPTO_BIN_PRODUCT_VERSION, TAG_LINK_DATE, TAG_UPTO_LINK_DATE, TAG_VER_LANGUAGE }; #define ATTRIBUTE_COUNT ARRAYSIZE(g_rgAttributeTags) static TAG_INFO gaTagInfo[] = { {TAG_DATABASE ,TEXT("DATABASE")}, {TAG_LIBRARY ,TEXT("LIBRARY")}, {TAG_INEXCLUDE ,TEXT("INEXCLUDE")}, {TAG_SHIM ,TEXT("SHIM")}, {TAG_PATCH ,TEXT("PATCH")}, {TAG_FLAG ,TEXT("FLAG")}, {TAG_APP ,TEXT("APP")}, {TAG_EXE ,TEXT("EXE")}, {TAG_MATCHING_FILE ,TEXT("MATCHING_FILE")}, {TAG_SHIM_REF ,TEXT("SHIM_REF")}, {TAG_PATCH_REF ,TEXT("PATCH_REF")}, {TAG_FLAG_REF ,TEXT("FLAG_REF")}, {TAG_LAYER ,TEXT("LAYER")}, {TAG_FILE ,TEXT("FILE")}, {TAG_APPHELP ,TEXT("APPHELP")}, {TAG_LINK ,TEXT("LINK")}, {TAG_DATA ,TEXT("DATA")}, {TAG_ACTION ,TEXT("ACTION")}, {TAG_MSI_TRANSFORM ,TEXT("MSI TRANSFORM")}, {TAG_MSI_TRANSFORM_REF ,TEXT("MSI TRANSFORM REF")}, {TAG_MSI_PACKAGE ,TEXT("MSI PACKAGE")}, {TAG_MSI_CUSTOM_ACTION ,TEXT("MSI CUSTOM ACTION")}, {TAG_NAME ,TEXT("NAME")}, {TAG_DESCRIPTION ,TEXT("DESCRIPTION")}, {TAG_MODULE ,TEXT("MODULE")}, {TAG_API ,TEXT("API")}, {TAG_VENDOR ,TEXT("VENDOR")}, {TAG_APP_NAME ,TEXT("APP_NAME")}, {TAG_DLLFILE ,TEXT("DLLFILE")}, {TAG_COMMAND_LINE ,TEXT("COMMAND_LINE")}, {TAG_ACTION_TYPE ,TEXT("ACTION_TYPE")}, {TAG_COMPANY_NAME ,TEXT("COMPANY_NAME")}, {TAG_WILDCARD_NAME ,TEXT("WILDCARD_NAME")}, {TAG_PRODUCT_NAME ,TEXT("PRODUCT_NAME")}, {TAG_PRODUCT_VERSION ,TEXT("PRODUCT_VERSION")}, {TAG_FILE_DESCRIPTION ,TEXT("FILE_DESCRIPTION")}, {TAG_FILE_VERSION ,TEXT("FILE_VERSION")}, {TAG_ORIGINAL_FILENAME ,TEXT("ORIGINAL_FILENAME")}, {TAG_INTERNAL_NAME ,TEXT("INTERNAL_NAME")}, {TAG_LEGAL_COPYRIGHT ,TEXT("LEGAL_COPYRIGHT")}, {TAG_16BIT_DESCRIPTION ,TEXT("S16BIT_DESCRIPTION")}, {TAG_APPHELP_DETAILS ,TEXT("PROBLEM_DETAILS")}, {TAG_LINK_URL ,TEXT("LINK_URL")}, {TAG_LINK_TEXT ,TEXT("LINK_TEXT")}, {TAG_APPHELP_TITLE ,TEXT("APPHELP_TITLE")}, {TAG_APPHELP_CONTACT ,TEXT("APPHELP_CONTACT")}, {TAG_SXS_MANIFEST ,TEXT("SXS_MANIFEST")}, {TAG_DATA_STRING ,TEXT("DATA_STRING")}, {TAG_MSI_TRANSFORM_FILE ,TEXT("MSI_TRANSFORM_FILE")}, {TAG_16BIT_MODULE_NAME ,TEXT("S16BIT_MODULE_NAME")}, {TAG_LAYER_DISPLAYNAME ,TEXT("LAYER_DISPLAYNAME")}, {TAG_COMPILER_VERSION ,TEXT("COMPILER_VERSION")}, {TAG_SIZE ,TEXT("SIZE")}, {TAG_OFFSET ,TEXT("OFFSET")}, {TAG_CHECKSUM ,TEXT("CHECKSUM")}, {TAG_SHIM_TAGID ,TEXT("SHIM_TAGID")}, {TAG_PATCH_TAGID ,TEXT("PATCH_TAGID")}, {TAG_LAYER_TAGID ,TEXT("LAYER_TAGID")}, {TAG_FLAG_TAGID ,TEXT("FLAG_TAGID")}, {TAG_MODULE_TYPE ,TEXT("MODULE_TYPE")}, {TAG_VERDATEHI ,TEXT("VERFILEDATEHI")}, {TAG_VERDATELO ,TEXT("VERFILEDATELO")}, {TAG_VERFILEOS ,TEXT("VERFILEOS")}, {TAG_VERFILETYPE ,TEXT("VERFILETYPE")}, {TAG_PE_CHECKSUM ,TEXT("PE_CHECKSUM")}, {TAG_LINKER_VERSION ,TEXT("LINKER_VERSION")}, {TAG_LINK_DATE ,TEXT("LINK_DATE")}, {TAG_UPTO_LINK_DATE ,TEXT("UPTO_LINK_DATE")}, {TAG_OS_SERVICE_PACK ,TEXT("OS_SERVICE_PACK")}, {TAG_VER_LANGUAGE ,TEXT("VER_LANGUAGE")}, {TAG_PREVOSMAJORVER ,TEXT("PREVOSMAJORVERSION")}, {TAG_PREVOSMINORVER ,TEXT("PREVOSMINORVERSION")}, {TAG_PREVOSPLATFORMID ,TEXT("PREVOSPLATFORMID")}, {TAG_PREVOSBUILDNO ,TEXT("PREVOSBUILDNO")}, {TAG_PROBLEMSEVERITY ,TEXT("PROBLEM_SEVERITY")}, {TAG_HTMLHELPID ,TEXT("HTMLHELPID")}, {TAG_INDEX_FLAGS ,TEXT("INDEXFLAGS")}, {TAG_LANGID ,TEXT("APPHELP_LANGID")}, {TAG_ENGINE ,TEXT("ENGINE")}, {TAG_FLAGS ,TEXT("FLAGS") }, {TAG_DATA_VALUETYPE ,TEXT("VALUETYPE")}, {TAG_DATA_DWORD ,TEXT("DATA_DWORD")}, {TAG_MSI_TRANSFORM_TAGID,TEXT("MSI_TRANSFORM_TAGID")}, {TAG_RUNTIME_PLATFORM, TEXT("RUNTIME_PLATFORM")}, {TAG_OS_SKU, TEXT("OS_SKU")}, {TAG_INCLUDE ,TEXT("INCLUDE")}, {TAG_GENERAL ,TEXT("GENERAL")}, {TAG_MATCH_LOGIC_NOT ,TEXT("MATCH_LOGIC_NOT")}, {TAG_APPLY_ALL_SHIMS ,TEXT("APPLY_ALL_SHIMS")}, {TAG_USE_SERVICE_PACK_FILES ,TEXT("USE_SERVICE_PACK_FILES")}, {TAG_TIME ,TEXT("TIME")}, {TAG_BIN_FILE_VERSION ,TEXT("BIN_FILE_VERSION")}, {TAG_BIN_PRODUCT_VERSION,TEXT("BIN_PRODUCT_VERSION")}, {TAG_MODTIME ,TEXT("MODTIME")}, {TAG_FLAG_MASK_KERNEL ,TEXT("FLAG_MASK_KERNEL")}, {TAG_FLAG_MASK_USER ,TEXT("FLAG_MASK_USER")}, {TAG_FLAG_MASK_SHELL ,TEXT("FLAG_MASK_SHELL")}, {TAG_FLAG_MASK_FUSION ,TEXT("FLAG_MASK_FUSION")}, {TAG_UPTO_BIN_PRODUCT_VERSION, TEXT("UPTO_BIN_PRODUCT_VERSION")}, {TAG_UPTO_BIN_FILE_VERSION, TEXT("UPTO_BIN_FILE_VERSION")}, {TAG_DATA_QWORD ,TEXT("DATA_QWORD")}, {TAG_FLAGS_NTVDM1 ,TEXT("FLAGS_NTVDM1")}, {TAG_FLAGS_NTVDM2 ,TEXT("FLAGS_NTVDM2")}, {TAG_FLAGS_NTVDM3 ,TEXT("FLAGS_NTVDM3")}, {TAG_PATCH_BITS ,TEXT("PATCH_BITS")}, {TAG_FILE_BITS ,TEXT("FILE_BITS")}, {TAG_EXE_ID ,TEXT("EXE_ID(GUID)")}, {TAG_DATA_BITS ,TEXT("DATA_BITS")}, {TAG_MSI_PACKAGE_ID ,TEXT("MSI_PACKAGE_ID(GUID)")}, {TAG_DATABASE_ID ,TEXT("DATABASE_ID(GUID)")}, {TAG_MATCH_MODE ,TEXT("MATCH_MODE")}, // // Internal types defined in shimdb.h // {TAG_STRINGTABLE ,TEXT("STRINGTABLE")}, {TAG_INDEXES ,TEXT("INDEXES")}, {TAG_INDEX ,TEXT("INDEX")}, {TAG_INDEX_TAG ,TEXT("INDEX_TAG")}, {TAG_INDEX_KEY ,TEXT("INDEX_KEY")}, {TAG_INDEX_BITS ,TEXT("INDEX_BITS")}, {TAG_STRINGTABLE_ITEM ,TEXT("STRTAB_ITEM")}, {TAG_TAG ,TEXT("TAG")}, {TAG_TAGID ,TEXT("TAGID")}, {TAG_NULL ,TEXT("")} // always needs to be last item }; static MOD_TYPE_STRINGS g_rgModTypeStrings[] = { {MT_UNKNOWN_MODULE, TEXT("NONE")}, {MT_W16_MODULE, TEXT("WIN16")}, {MT_W32_MODULE, TEXT("WIN32")}, {MT_DOS_MODULE, TEXT("DOS")} }; // // Version Strings for stringref attributes // typedef struct _VER_STRINGS { TAG tTag; LPTSTR szName; } VER_STRINGS; static VER_STRINGS g_rgVerStrings[] = { {TAG_PRODUCT_VERSION, TEXT("ProductVersion") }, {TAG_FILE_DESCRIPTION, TEXT("FileDescription") }, {TAG_COMPANY_NAME, TEXT("CompanyName") }, {TAG_PRODUCT_NAME, TEXT("ProductName") }, {TAG_FILE_VERSION, TEXT("FileVersion") }, {TAG_ORIGINAL_FILENAME, TEXT("OriginalFilename") }, {TAG_INTERNAL_NAME, TEXT("InternalName") }, {TAG_LEGAL_COPYRIGHT, TEXT("LegalCopyright") } }; // // Binary version tags (DWORDs and QWORDs) // // static TAG g_rgBinVerTags[] = { TAG_VERDATEHI, TAG_VERDATELO, TAG_VERFILEOS, TAG_VERFILETYPE, TAG_BIN_PRODUCT_VERSION, TAG_BIN_FILE_VERSION, TAG_UPTO_BIN_PRODUCT_VERSION, TAG_UPTO_BIN_FILE_VERSION }; // // Binary header tags (retrieval requires opening a file). // static TAG g_rgHeaderTags[] = { TAG_MODULE_TYPE, TAG_PE_CHECKSUM, TAG_LINKER_VERSION, TAG_CHECKSUM, TAG_16BIT_DESCRIPTION, TAG_16BIT_MODULE_NAME, TAG_LINK_DATE, TAG_UPTO_LINK_DATE }; // // Basic information tags (size). // TAG g_rgDirectoryTags[] = { TAG_SIZE, 0 }; // // Invalid tag token // static TCHAR s_szInvalidTag[] = _T("InvalidTag"); #if defined(KERNEL_MODE) && defined(ALLOC_PRAGMA) #pragma alloc_text(PAGE, TagToIndex) #pragma alloc_text(PAGE, SdbTagToString) #pragma alloc_text(PAGE, SdbpModuleTypeToString) #pragma alloc_text(PAGE, SdbpSetAttribute) #pragma alloc_text(PAGE, SdbpQueryStringVersionInformation) #pragma alloc_text(PAGE, SdbpQueryBinVersionInformation) #pragma alloc_text(PAGE, SdbpGetVersionAttributesNT) #pragma alloc_text(PAGE, SdbpGetHeaderAttributes) #pragma alloc_text(PAGE, SdbpGetAttribute) #pragma alloc_text(PAGE, SdbpCheckAttribute) #pragma alloc_text(PAGE, FindFileInfo) #pragma alloc_text(PAGE, CreateFileInfo) #pragma alloc_text(PAGE, SdbFreeFileInfo) #pragma alloc_text(PAGE, SdbpCleanupAttributeMgr) #pragma alloc_text(PAGE, SdbpCheckAllAttributes) #pragma alloc_text(PAGE, SdbpQueryVersionString) #pragma alloc_text(PAGE, SdbpGetModuleType) #pragma alloc_text(PAGE, SdbpGetModulePECheckSum) #pragma alloc_text(PAGE, SdbpGetImageNTHeader) #pragma alloc_text(PAGE, SdbpGetFileChecksum) #pragma alloc_text(PAGE, SdbpCheckVersion) #pragma alloc_text(PAGE, SdbpCheckUptoVersion) #endif // KERNEL_MODE && ALLOC_PRAGMA int TagToIndex( IN TAG tag // the tag ) /*++ Return: The index in the attribute info array (g_rgAttributeTags). Desc: Self explanatory. --*/ { int i; for (i = 0; i < ATTRIBUTE_COUNT; i++) { if (tag == g_rgAttributeTags[i]) { return i; } } DBGPRINT((sdlError, "TagToIndex", "Invalid attribute 0x%x.\n", tag)); return -1; } LPCTSTR SdbTagToString( TAG tag ) /*++ Return: The pointer to the string name for the specified tag. Desc: Self explanatory. --*/ { int i; for (i = 0; i < ARRAYSIZE(gaTagInfo); ++i) { if (gaTagInfo[i].tWhich == tag) { return gaTagInfo[i].szName; } } return s_szInvalidTag; } LPCTSTR SdbpModuleTypeToString( DWORD dwModuleType ) { int i; for (i = 0; i < ARRAYSIZE(g_rgModTypeStrings); ++i) { if (g_rgModTypeStrings[i].dwModuleType == dwModuleType) { return g_rgModTypeStrings[i].szModuleType; } } // // The first element is the "UNKNOWN" type -- NONE // return g_rgModTypeStrings[0].szModuleType; } BOOL SdbpSetAttribute( OUT PFILEINFO pFileInfo, // pointer to the FILEINFO structure. IN TAG AttrID, // Attribute ID (tag, as in TAG_SIZE IN PVOID pValue // value ) /*++ Return: TRUE on success, FALSE otherwise. Desc: This function sets the value for the specified attribute. If pValue is NULL it means that the specified attribute is not available for the file. --*/ { int nAttrInd; PATTRINFO pAttrInfo; nAttrInd = TagToIndex(AttrID); if (nAttrInd < 0) { DBGPRINT((sdlError, "SdbpSetAttribute", "Invalid attribute %d.\n", nAttrInd)); return FALSE; } pAttrInfo = &pFileInfo->Attributes[nAttrInd]; if (pValue == NULL) { // // No value. Mark and exit. // pAttrInfo->dwFlags = (pAttrInfo->dwFlags & ~ATTRIBUTE_AVAILABLE) | ATTRIBUTE_FAILED; return TRUE; } switch (GETTAGTYPE(AttrID)) { case TAG_TYPE_DWORD: pAttrInfo->dwAttr = *(DWORD*)pValue; break; case TAG_TYPE_QWORD: pAttrInfo->ullAttr = *(ULONGLONG*)pValue; break; case TAG_TYPE_STRINGREF: pAttrInfo->lpAttr = (LPTSTR)pValue; break; } pAttrInfo->tAttrID = AttrID; pAttrInfo->dwFlags |= ATTRIBUTE_AVAILABLE; return TRUE; } // // This is a guard against bad code in version.dll that stomps over the // buffer size for Unicode apis on 16-bit exes. // #define VERSIONINFO_BUFFER_PAD 16 void SdbpQueryStringVersionInformation( IN PSDBCONTEXT pContext, IN PFILEINFO pFileInfo, OUT LPVOID pVersionInfo ) /*++ Return: void. Desc: Sets all the version string info available for the specified file. --*/ { int i; LPTSTR szVerString; PLANGANDCODEPAGE pLangCodePage = NULL; UINT cbLangCP = 0; int nTranslations = 0; if (!pContext->pfnVerQueryValue(pVersionInfo, TEXT("\\VarFileInfo\\Translation"), (LPVOID)&pLangCodePage, &cbLangCP)) { DBGPRINT((sdlError, "SdbpQueryStringVersionInformation", "VerQueryValue failed for translation\n")); pLangCodePage = NULL; } nTranslations = cbLangCP / sizeof(*pLangCodePage); for (i = 0; i < ARRAYSIZE(g_rgVerStrings); ++i) { szVerString = SdbpQueryVersionString(pContext, pVersionInfo, pLangCodePage, nTranslations, g_rgVerStrings[i].szName); SdbpSetAttribute(pFileInfo, g_rgVerStrings[i].tTag, szVerString); } #ifndef KERNEL_MODE // // Set the attribute for Language // if (pLangCodePage != NULL && nTranslations == 1) { DWORD dwLanguage = (DWORD)pLangCodePage->wLanguage; SdbpSetAttribute(pFileInfo, TAG_VER_LANGUAGE, &dwLanguage); } else { SdbpSetAttribute(pFileInfo, TAG_VER_LANGUAGE, NULL); } #endif // KERNEL_MODE } VOID SdbpQueryBinVersionInformation( IN PSDBCONTEXT pContext, IN PFILEINFO pFileInfo, OUT VS_FIXEDFILEINFO* pFixedInfo ) /*++ Return: void. Desc: Sets all the version string info available for the specified file from the fixed size resources. --*/ { LARGE_INTEGER liVerData; SdbpSetAttribute(pFileInfo, TAG_VERDATEHI, &pFixedInfo->dwFileDateMS); SdbpSetAttribute(pFileInfo, TAG_VERDATELO, &pFixedInfo->dwFileDateLS); SdbpSetAttribute(pFileInfo, TAG_VERFILEOS, &pFixedInfo->dwFileOS); SdbpSetAttribute(pFileInfo, TAG_VERFILETYPE, &pFixedInfo->dwFileType); liVerData.LowPart = pFixedInfo->dwProductVersionLS; liVerData.HighPart = pFixedInfo->dwProductVersionMS; SdbpSetAttribute(pFileInfo, TAG_BIN_PRODUCT_VERSION, &liVerData.QuadPart); SdbpSetAttribute(pFileInfo, TAG_UPTO_BIN_PRODUCT_VERSION, &liVerData.QuadPart); liVerData.LowPart = pFixedInfo->dwFileVersionLS; liVerData.HighPart = pFixedInfo->dwFileVersionMS; SdbpSetAttribute(pFileInfo, TAG_BIN_FILE_VERSION, &liVerData.QuadPart); SdbpSetAttribute(pFileInfo, TAG_UPTO_BIN_FILE_VERSION, &liVerData.QuadPart); UNREFERENCED_PARAMETER(pContext); } #if defined(NT_MODE) || defined(KERNEL_MODE) BOOL SdbpGetVersionAttributesNT( IN PSDBCONTEXT pContext, OUT PFILEINFO pFileInfo, IN PIMAGEFILEDATA pImageData ) /*++ Return: TRUE on success, FALSE otherwise. Desc: This function retrieves all of the Version-related attributes Imports apis from version.dll if called for the first time --*/ { BOOL bSuccess; LPVOID pVersionInfo = NULL; VS_FIXEDFILEINFO* pFixedInfo = NULL; int i; // // First retrieve the version info. // bSuccess = SdbpGetFileVersionInformation(pImageData, &pVersionInfo, &pFixedInfo); if (!bSuccess) { DBGPRINT((sdlInfo, "SdbpGetVersionAttributesNT", "No version info.\n")); goto ErrHandle; } // // Version information available. // // // Set the pointer to our internal function. // pContext->pfnVerQueryValue = SdbpVerQueryValue; for (i = 0; i < ARRAYSIZE(g_rgVerStrings); ++i) { SdbpSetAttribute(pFileInfo, g_rgVerStrings[i].tTag, NULL); } SdbpSetAttribute(pFileInfo, TAG_VER_LANGUAGE, NULL); // // Query binary stuff // SdbpQueryBinVersionInformation(pContext, pFileInfo, pFixedInfo); pFileInfo->pVersionInfo = pVersionInfo; return TRUE; ErrHandle: // // Reset all the string info. // for (i = 0; i < ARRAYSIZE(g_rgBinVerTags); ++i) { SdbpSetAttribute(pFileInfo, g_rgBinVerTags[i], NULL); } for (i = 0; i < ARRAYSIZE(g_rgVerStrings); ++i) { SdbpSetAttribute(pFileInfo, g_rgVerStrings[i].tTag, NULL); } return FALSE; } #endif // NT_MODE || KERNEL_MODE BOOL SdbpGetHeaderAttributes( IN PSDBCONTEXT pContext, OUT PFILEINFO pFileInfo ) /*++ Return: TRUE on success, FALSE otherwise. Desc: This function retrieves the header attributes for the specified file. --*/ { IMAGEFILEDATA ImageData; ULONG ulPEChecksum = 0; ULONG ulChecksum = 0; DWORD dwModuleType = 0; DWORD dwLinkerVer; DWORD dwLinkDate; BOOL bSuccess; int i; ImageData.dwFlags = 0; if (pFileInfo->hFile != INVALID_HANDLE_VALUE) { ImageData.hFile = pFileInfo->hFile; ImageData.dwFlags |= IMAGEFILEDATA_HANDLEVALID; } if (pFileInfo->pImageBase != NULL) { ImageData.pBase = pFileInfo->pImageBase; ImageData.ViewSize = (SIZE_T) pFileInfo->dwImageSize; ImageData.FileSize = (ULONGLONG)pFileInfo->dwImageSize; ImageData.dwFlags |= IMAGEFILEDATA_PBASEVALID; } // // SdbpOpenAndMapFile uses DOS_PATH type as an argument // In kernel mode this parameter is ignored. // if (SdbpOpenAndMapFile(pFileInfo->FilePath, &ImageData, DOS_PATH)) { bSuccess = SdbpGetModuleType(&dwModuleType, &ImageData); SdbpSetAttribute(pFileInfo, TAG_MODULE_TYPE, bSuccess ? (PVOID)&dwModuleType : NULL); bSuccess = SdbpGetModulePECheckSum(&ulPEChecksum, &dwLinkerVer, &dwLinkDate, &ImageData); SdbpSetAttribute(pFileInfo, TAG_PE_CHECKSUM, bSuccess ? (PVOID)&ulPEChecksum : NULL); SdbpSetAttribute(pFileInfo, TAG_LINKER_VERSION, bSuccess ? (PVOID)&dwLinkerVer : NULL); SdbpSetAttribute(pFileInfo, TAG_LINK_DATE, bSuccess ? (PVOID)&dwLinkDate : NULL); SdbpSetAttribute(pFileInfo, TAG_UPTO_LINK_DATE, bSuccess ? (PVOID)&dwLinkDate : NULL); bSuccess = SdbpGetFileChecksum(&ulChecksum, &ImageData); SdbpSetAttribute(pFileInfo, TAG_CHECKSUM, bSuccess ? (PVOID)&ulChecksum : NULL); #ifndef KERNEL_MODE // // Now retrieve 16-bit description string, it's max size is 256 bytes. // // This attribute is not available in kernel mode. // bSuccess = SdbpGet16BitDescription(&pFileInfo->pDescription16, &ImageData); SdbpSetAttribute(pFileInfo, TAG_16BIT_DESCRIPTION, pFileInfo->pDescription16); bSuccess = SdbpGet16BitModuleName(&pFileInfo->pModuleName16, &ImageData); SdbpSetAttribute(pFileInfo, TAG_16BIT_MODULE_NAME, pFileInfo->pModuleName16); #if defined(NT_MODE) // // Hit this case only on current platform // if (pFileInfo->hFile != INVALID_HANDLE_VALUE || pFileInfo->pImageBase != NULL) { SdbpGetVersionAttributesNT(pContext, pFileInfo, &ImageData); } #endif // NT_MODE #else // KERNEL_MODE // // When we are running in kernel mode retrieve version-related // data now as well. // SdbpGetVersionAttributesNT(pContext, pFileInfo, &ImageData); // // Retrieve file directory attributes. // SdbpGetFileDirectoryAttributesNT(pFileInfo, &ImageData); #endif // KERNEL_MODE SdbpUnmapAndCloseFile(&ImageData); return TRUE; } for (i = 0; i < ARRAYSIZE(g_rgHeaderTags); ++i) { SdbpSetAttribute(pFileInfo, g_rgHeaderTags[i], NULL); } #ifdef KERNEL_MODE // // Reset all the version attributes here as well. // for (i = 0; i < ARRAYSIZE(g_rgBinVerTags); ++i) { SdbpSetAttribute(pFileInfo, g_rgBinVerTags[i], NULL); } for (i = 0; i < ARRAYSIZE(g_rgVerStrings); ++i) { SdbpSetAttribute(pFileInfo, g_rgVerStrings[i].tTag, NULL); } #endif // KERNEL_MODE return FALSE; UNREFERENCED_PARAMETER(pContext); } BOOL SdbpGetAttribute( IN PSDBCONTEXT pContext, OUT PFILEINFO pFileInfo, IN TAG AttrID ) /*++ Return: TRUE on success, FALSE otherwise. Desc: Retrieve an attribute for a given file. We retrieve all the attributes of the same class. --*/ { BOOL bReturn = FALSE; switch (AttrID) { // // The tags below require checking the file and making a directory query. // case TAG_SIZE: #ifndef KERNEL_MODE // in kernel mode we fall through to header attributes bReturn = SdbpGetFileDirectoryAttributes(pFileInfo); break; #endif // KERNEL_MODE // // The tags below require retrieving version resources. // case TAG_VERDATEHI: case TAG_VERDATELO: case TAG_VERFILEOS: case TAG_VERFILETYPE: case TAG_UPTO_BIN_PRODUCT_VERSION: case TAG_UPTO_BIN_FILE_VERSION: case TAG_BIN_FILE_VERSION: case TAG_BIN_PRODUCT_VERSION: case TAG_PRODUCT_VERSION: case TAG_FILE_DESCRIPTION: case TAG_COMPANY_NAME: case TAG_PRODUCT_NAME: case TAG_FILE_VERSION: case TAG_ORIGINAL_FILENAME: case TAG_INTERNAL_NAME: case TAG_LEGAL_COPYRIGHT: case TAG_VER_LANGUAGE: // // In KERNEL_MODE we fall through and do the attributes using the // header attributes. // #ifndef KERNEL_MODE // // Version attributes are retrieved through the header attributes if // caller provided a handle/image base // if (pFileInfo->hFile == INVALID_HANDLE_VALUE && pFileInfo->pImageBase == NULL) { bReturn = SdbpGetVersionAttributes(pContext, pFileInfo); break; } #endif // KERNEL_MODE // // The tags below require opening a file and mapping it into memory. // case TAG_CHECKSUM: case TAG_PE_CHECKSUM: case TAG_LINKER_VERSION: case TAG_16BIT_DESCRIPTION: case TAG_16BIT_MODULE_NAME: case TAG_MODULE_TYPE: case TAG_UPTO_LINK_DATE: case TAG_LINK_DATE: bReturn = SdbpGetHeaderAttributes(pContext, pFileInfo); break; } return bReturn; } BOOL SdbpCheckAttribute( IN PSDBCONTEXT pContext, // Database Context pointer IN PVOID pFileData, // pointer returned from CheckFile IN TAG AttrID, // Attribute ID IN PVOID pAttribute // attribute value ptr (see above for description) ) /*++ Return: TRUE if the value for given attribute matches the file's attribute, FALSE otherwise. Desc: Check an attribute against a given value. This function retrieves attributes as necessary. --*/ { int nAttrIndex; PATTRINFO pAttrInfo; BOOL bReturn = FALSE; PFILEINFO pFileInfo = (PFILEINFO)pFileData; if (pAttribute == NULL) { DBGPRINT((sdlError, "SdbpCheckAttribute", "Invalid parameter.\n")); return FALSE; } nAttrIndex = TagToIndex(AttrID); if (nAttrIndex < 0) { DBGPRINT((sdlError, "SdbpCheckAttribute", "Bad Attribute ID 0x%x\n", AttrID)); return FALSE; } // // Now see if this attribute is any good. // pAttrInfo = &pFileInfo->Attributes[nAttrIndex]; if (!(pAttrInfo->dwFlags & ATTRIBUTE_AVAILABLE)) { // // See if we have tried already // if (pAttrInfo->dwFlags & ATTRIBUTE_FAILED) { DBGPRINT((sdlInfo, "SdbpCheckAttribute", "Already tried to get attr ID 0x%x.\n", AttrID)); return FALSE; } // // The attribute has not been retrieved yet, do it now then. // // Try to obtain this attribute from the file. // if (!SdbpGetAttribute(pContext, pFileInfo, AttrID)) { DBGPRINT((sdlWarning, "SdbpCheckAttribute", "Failed to get attribute \"%s\" for \"%s\"\n", SdbTagToString(AttrID), pFileInfo->FilePath)); // // ATTRIBUTE_FAILED is set by the SdbpGetAttribute // return FALSE; } } // // Check again here in case we had to retrieve the attribute. // if (!(pAttrInfo->dwFlags & ATTRIBUTE_AVAILABLE)) { return FALSE; } switch (AttrID) { case TAG_BIN_PRODUCT_VERSION: case TAG_BIN_FILE_VERSION: bReturn = SdbpCheckVersion(*(ULONGLONG*)pAttribute, pAttrInfo->ullAttr); if (!bReturn) { #ifdef _DEBUG_SPEW ULONGLONG qwDBFileVer = *(ULONGLONG*)pAttribute; ULONGLONG qwBinFileVer = pAttrInfo->ullAttr; DBGPRINT((sdlInfo, "SdbpCheckAttribute", "\"%s\" mismatch file: \"%s\". Expected %d.%d.%d.%d, Found %d.%d.%d.%d\n", SdbTagToString(AttrID), pFileInfo->FilePath, (WORD)(qwDBFileVer >> 48), (WORD)(qwDBFileVer >> 32), (WORD)(qwDBFileVer >> 16), (WORD)(qwDBFileVer), (WORD)(qwBinFileVer >> 48), (WORD)(qwBinFileVer >> 32), (WORD)(qwBinFileVer >> 16), (WORD)(qwBinFileVer))); #endif } break; case TAG_UPTO_BIN_PRODUCT_VERSION: case TAG_UPTO_BIN_FILE_VERSION: bReturn = SdbpCheckUptoVersion(*(ULONGLONG*)pAttribute, pAttrInfo->ullAttr); if (!bReturn) { #ifdef _DEBUG_SPEW ULONGLONG qwDBFileVer = *(ULONGLONG*)pAttribute; ULONGLONG qwBinFileVer = pAttrInfo->ullAttr; DBGPRINT((sdlInfo, "SdbpCheckAttribute", "\"%s\" mismatch file: \"%s\". Expected %d.%d.%d.%d, Found %d.%d.%d.%d\n", SdbTagToString(AttrID), pFileInfo->FilePath, (WORD)(qwDBFileVer >> 48), (WORD)(qwDBFileVer >> 32), (WORD)(qwDBFileVer >> 16), (WORD)(qwDBFileVer), (WORD)(qwBinFileVer >> 48), (WORD)(qwBinFileVer >> 32), (WORD)(qwBinFileVer >> 16), (WORD)(qwBinFileVer))); #endif } break; case TAG_UPTO_LINK_DATE: bReturn = (*(DWORD*)pAttribute >= pAttrInfo->dwAttr); if (!bReturn) { DBGPRINT((sdlInfo, "SdbpCheckAttribute", "\"%s\" mismatch file \"%s\". Expected less than 0x%x Found 0x%x\n", SdbTagToString(AttrID), pFileInfo->FilePath, *(DWORD*)pAttribute, pAttrInfo->dwAttr)); } break; default: switch (GETTAGTYPE(AttrID)) { case TAG_TYPE_DWORD: // // This is likely to be hit first. // bReturn = (*(DWORD*)pAttribute == pAttrInfo->dwAttr); if (!bReturn) { DBGPRINT((sdlInfo, "SdbpCheckAttribute", "\"%s\" mismatch file \"%s\". Expected 0x%x Found 0x%x\n", SdbTagToString(AttrID), pFileInfo->FilePath, *(DWORD*)pAttribute, pAttrInfo->dwAttr)); } break; case TAG_TYPE_STRINGREF: bReturn = SdbpPatternMatch((LPCTSTR)pAttribute, (LPCTSTR)pAttrInfo->lpAttr); if (!bReturn) { DBGPRINT((sdlInfo, "SdbpCheckAttribute", "\"%s\" mismatch file \"%s\". Expected \"%s\" Found \"%s\"\n", SdbTagToString(AttrID), pFileInfo->FilePath, pAttribute, pAttrInfo->lpAttr)); } break; case TAG_TYPE_QWORD: bReturn = (*(ULONGLONG*)pAttribute == pAttrInfo->ullAttr); if (!bReturn) { DBGPRINT((sdlInfo, "SdbpCheckAttribute", "\"%s\" mismatch file \"%s\". Expected 0x%I64x Found 0x%I64x\n", SdbTagToString(AttrID), pFileInfo->FilePath, *(ULONGLONG*)pAttribute, pAttrInfo->ullAttr)); } break; } break; } return bReturn; } PFILEINFO FindFileInfo( IN PSDBCONTEXT pContext, IN LPCTSTR FilePath ) /*++ Return: A pointer to the cached FILEINFO structure if one is found or NULL otherwise. Desc: This function performs a search in the file cache to determine whether a given file has already been touched. --*/ { PFILEINFO pFileInfo = (PFILEINFO)pContext->pFileAttributeCache; // global cache while (pFileInfo != NULL) { if (ISEQUALSTRING(pFileInfo->FilePath, FilePath)) { DBGPRINT((sdlInfo, "FindFileInfo", "FILEINFO for \"%s\" found in the cache.\n", FilePath)); return pFileInfo; } pFileInfo = pFileInfo->pNext; } return NULL; } PFILEINFO CreateFileInfo( IN PSDBCONTEXT pContext, IN LPCTSTR FullPath, IN DWORD dwLength OPTIONAL, // length (in characters) of FullPath string IN HANDLE hFile OPTIONAL, // file handle IN LPVOID pImageBase OPTIONAL, IN DWORD dwImageSize OPTIONAL, IN BOOL bNoCache ) /*++ Return: A pointer to the allocated FILEINFO structure. Desc: Allocates the FILEINFO structure for the specified file. --*/ { PFILEINFO pFileInfo; SIZE_T sizeBase; SIZE_T size; DWORD nPathLen; nPathLen = dwLength ? dwLength : (DWORD)_tcslen(FullPath); sizeBase = sizeof(*pFileInfo) + (ATTRIBUTE_COUNT - 1) * sizeof(ATTRINFO); size = sizeBase + (nPathLen + 1) * sizeof(*FullPath); pFileInfo = (PFILEINFO)SdbAlloc(size); if (pFileInfo == NULL) { DBGPRINT((sdlError, "CreateFileInfo", "Failed to allocate %d bytes for FILEINFO structure.\n", size)); return NULL; } RtlZeroMemory(pFileInfo, size); pFileInfo->FilePath = (LPTSTR)((PBYTE)pFileInfo + sizeBase); RtlCopyMemory(pFileInfo->FilePath, FullPath, nPathLen * sizeof(*FullPath)); pFileInfo->FilePath[nPathLen] = TEXT('\0'); pFileInfo->hFile = hFile; pFileInfo->pImageBase = pImageBase; pFileInfo->dwImageSize = dwImageSize; // // Now link it in if we use the cache. // if (!bNoCache) { pFileInfo->pNext = (PFILEINFO)pContext->pFileAttributeCache; pContext->pFileAttributeCache = (PVOID)pFileInfo; } return pFileInfo; } void SdbFreeFileInfo( IN PVOID pFileData // pointer returned from SdbpGetFileAttributes ) /*++ Return: void. Desc: Self explanatory. Use this only after calling GetFileInfo with bNoCache set to TRUE. --*/ { PFILEINFO pFileInfo = (PFILEINFO)pFileData; if (pFileInfo == NULL) { DBGPRINT((sdlError, "SdbFreeFileInfo", "Invalid parameter.\n")); return; } if (pFileInfo->pVersionInfo != NULL) { SdbFree(pFileInfo->pVersionInfo); } if (pFileInfo->pDescription16 != NULL) { SdbFree(pFileInfo->pDescription16); } if (pFileInfo->pModuleName16 != NULL) { SdbFree(pFileInfo->pModuleName16); } SdbFree(pFileInfo); } void SdbpCleanupAttributeMgr( IN PSDBCONTEXT pContext // database context ) /*++ Return: void. Desc: This function should be called afer we are done checking a given exe it performs cleanup tasks, such as: . unload dynamically linked dll (version.dll) . cleanup file cache --*/ { PFILEINFO pFileInfo = (PFILEINFO)pContext->pFileAttributeCache; PFILEINFO pNext; while (pFileInfo != NULL) { pNext = pFileInfo->pNext; SdbFreeFileInfo(pFileInfo); pFileInfo = pNext; } // // Reset the cache pointer. // pContext->pFileAttributeCache = NULL; } BOOL SdbpCheckAllAttributes( IN PSDBCONTEXT pContext, // pointer to the database channel IN PDB pdb, // pointer to the Shim Database that we're checking against IN TAGID tiMatch, // TAGID for a given file(exe) to be checked IN PVOID pFileData // pointer returned from CheckFile ) /*++ Return: TRUE if all the file's attributes match the ones described in the database for this file, FALSE otherwise. Desc: TBD --*/ { int i; TAG tAttrID; PVOID pAttribute; TAGID tiTemp; DWORD dwAttribute; ULONGLONG ullAttribute; BOOL bReturn = TRUE; // match by default assert(tiMatch != TAGID_NULL); if (pFileData == NULL) { // // No file was passed in. This can happen if LOGIC="NOT" is used. // return FALSE; } for (i = 0; i < ATTRIBUTE_COUNT && bReturn; ++i) { tAttrID = g_rgAttributeTags[i]; tiTemp = SdbFindFirstTag(pdb, tiMatch, tAttrID); if (tiTemp != TAGID_NULL) { pAttribute = NULL; switch (GETTAGTYPE(tAttrID)) { case TAG_TYPE_DWORD: dwAttribute = SdbReadDWORDTag(pdb, tiTemp, 0); pAttribute = &dwAttribute; break; case TAG_TYPE_QWORD: ullAttribute = SdbReadQWORDTag(pdb, tiTemp, 0); pAttribute = &ullAttribute; break; case TAG_TYPE_STRINGREF: pAttribute = SdbGetStringTagPtr(pdb, tiTemp); break; } // // Now check the attribute. // bReturn = SdbpCheckAttribute(pContext, pFileData, tAttrID, pAttribute); // // we bail out if !bReturn via the condition in FOR loop above // } } return bReturn; } // // VERSION DATA // /*-- Search order is: - Language neutral, Unicode (0x000004B0) - Language neutral, Windows-multilingual (0x000004e4) - US English, Unicode (0x040904B0) - US English, Windows-multilingual (0x040904E4) If none of those exist, it's not likely we're going to get good matching info from what does exist. --*/ LPTSTR SdbpQueryVersionString( IN PSDBCONTEXT pContext, // the database channel IN PVOID pVersionData, // Version data buffer IN PLANGANDCODEPAGE pTranslations, IN DWORD TranslationCount, IN LPCTSTR szString // String to search for; see VerQueryValue in MSDN ) /*++ Return: The pointer to the string if found, NULL if not. Desc: Gets a pointer to a particular string in the StringFileInfo section of a version resource. Lookup is performed for known english-language resources followed up by a lookup in the available translations section (if such was found) --*/ { TCHAR szTemp[128]; LPTSTR szReturn = NULL; int i; static DWORD adwLangs[] = {0x000004B0, 0x000004E4, 0x040904B0, 0x040904E4}; assert(pVersionData && szString); for (i = 0; i < ARRAYSIZE(adwLangs); ++i) { UINT unLen; StringCchPrintf(szTemp, CHARCOUNT(szTemp), _T("\\StringFileInfo\\%08X\\%s"), adwLangs[i], szString); if (pContext->pfnVerQueryValue(pVersionData, szTemp, (PVOID*)&szReturn, &unLen)) { return szReturn; } } if (pTranslations != NULL) { for (i = 0; i < (int)TranslationCount; ++i, ++pTranslations) { UINT unLen; StringCchPrintf(szTemp, CHARCOUNT(szTemp), _T("\\StringFileInfo\\%04X%04X\\%s"), (DWORD)pTranslations->wLanguage, (DWORD)pTranslations->wCodePage, szString); if (pContext->pfnVerQueryValue(pVersionData, szTemp, (PVOID*)&szReturn, &unLen)) { return szReturn; } } } return NULL; // none found } BOOL SdbpGetModuleType( OUT LPDWORD lpdwModuleType, IN PIMAGEFILEDATA pImageData ) /*++ Return: TRUE on success, FALSE otherwise. Desc: Gets a pointer to a particular string in the StringFileInfo section of a version resource. --*/ { PIMAGE_DOS_HEADER pDosHeader; DWORD dwModuleType = MT_UNKNOWN_MODULE; LPBYTE lpSignature; DWORD OffsetNew; pDosHeader = (PIMAGE_DOS_HEADER)pImageData->pBase; if (pDosHeader == NULL || pDosHeader == (PIMAGE_DOS_HEADER)-1) { return FALSE; } // // Check size and read signature. // if (pImageData->ViewSize < sizeof(*pDosHeader) || pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { return FALSE; } // // Assume DOS module. // dwModuleType = MT_DOS_MODULE; OffsetNew = (DWORD)pDosHeader->e_lfanew; // // New header signature. Check offset. // if (pImageData->ViewSize < OffsetNew + sizeof(DWORD)) { return FALSE; } lpSignature = ((LPBYTE)pImageData->pBase + OffsetNew); if (IMAGE_NT_SIGNATURE == *(LPDWORD)lpSignature) { dwModuleType = MT_W32_MODULE; } else if (IMAGE_OS2_SIGNATURE == *(PWORD)lpSignature) { dwModuleType = MT_W16_MODULE; } if (lpdwModuleType != NULL) { *lpdwModuleType = dwModuleType; } return TRUE; } BOOL SdbpGetImageNTHeader( OUT PIMAGE_NT_HEADERS* ppHeader, IN PIMAGEFILEDATA pImageData ) /*++ Return: TRUE on success, FALSE otherwise. Desc: Gets a pointer to the IMAGE_NT_HEADERS. --*/ { PIMAGE_DOS_HEADER pDosHeader; PIMAGE_NT_HEADERS pNtHeaders = NULL; DWORD ModuleType; if (!SdbpGetModuleType(&ModuleType, pImageData)) { return FALSE; } if (ModuleType != MT_W32_MODULE) { return FALSE; } // // Header is valid. // pDosHeader = (PIMAGE_DOS_HEADER)pImageData->pBase; pNtHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)pImageData->pBase + pDosHeader->e_lfanew); if (pImageData->ViewSize >= pDosHeader->e_lfanew + sizeof(*pNtHeaders)) { // not too short? *ppHeader = pNtHeaders; return TRUE; } return FALSE; } BOOL SdbpGetModulePECheckSum( OUT PULONG pChecksum, OUT LPDWORD pdwLinkerVersion, OUT LPDWORD pdwLinkDate, IN PIMAGEFILEDATA pImageData ) /*++ Return: TRUE on success, FALSE otherwise. Desc: Gets the checksum from the PE headers. --*/ { PIMAGE_NT_HEADERS pNtHeader; PIMAGE_DOS_HEADER pDosHeader; ULONG ulChecksum = 0; if (!SdbpGetImageNTHeader(&pNtHeader, pImageData)) { DBGPRINT((sdlError, "SdbpGetModulePECheckSum", "Failed to get Image NT header.\n")); return FALSE; } pDosHeader = (PIMAGE_DOS_HEADER)pImageData->pBase; // // Fill in the linker version (as it used to calculated in ntuser). // *pdwLinkerVersion = (pNtHeader->OptionalHeader.MinorImageVersion & 0xFF) + ((pNtHeader->OptionalHeader.MajorImageVersion & 0xFF) << 16); *pdwLinkDate = pNtHeader->FileHeader.TimeDateStamp; switch (pNtHeader->OptionalHeader.Magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: if (pImageData->ViewSize >= pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32)) { ulChecksum = ((PIMAGE_NT_HEADERS32)pNtHeader)->OptionalHeader.CheckSum; *pChecksum = ulChecksum; return TRUE; } break; case IMAGE_NT_OPTIONAL_HDR64_MAGIC: // // Do an additional check. // if (pImageData->ViewSize >= pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS64)) { ulChecksum = ((PIMAGE_NT_HEADERS64)pNtHeader)->OptionalHeader.CheckSum; *pChecksum = ulChecksum; return TRUE; } break; default: // // Unknown image type ? // DBGPRINT((sdlError, "SdbpGetModulePECheckSum", "Bad image type 0x%x\n", pNtHeader->OptionalHeader.Magic)); *pChecksum = 0; break; } return FALSE; } #define CHECKSUM_SIZE 4096 #define CHECKSUM_START 512 BOOL SdbpGetFileChecksum( OUT PULONG pChecksum, IN PIMAGEFILEDATA pImageData ) /*++ Return: TRUE on success, FALSE otherwise. Desc: Calculates a checksum for the file. --*/ { ULONG size = CHECKSUM_SIZE; ULONG StartAddress = CHECKSUM_START; ULONG ulChecksum = 0; LPDWORD lpdw; int i; if ((SIZE_T)pImageData->FileSize < (SIZE_T)size) { StartAddress = 0; size = (ULONG)pImageData->FileSize; // this is safe (size is rather small) } else if ((SIZE_T)(size + StartAddress) > (SIZE_T)pImageData->FileSize) { // // The cast here is safe (FileSize is small) // StartAddress = (ULONG)(pImageData->FileSize - size); } if (size >= sizeof(DWORD)) { ULONG ulCarry; lpdw = (LPDWORD)((LPBYTE)pImageData->pBase + StartAddress); for (i = 0; i < (INT)(size/sizeof(DWORD)); ++i) { if (PtrToUlong(lpdw) & 0x3) { // alignment fault fixup ulChecksum += *((DWORD UNALIGNED*)lpdw); lpdw++; } else { ulChecksum += *lpdw++; } ulCarry = ulChecksum & 1; ulChecksum >>= 1; if (ulCarry) { ulChecksum |= 0x80000000; } } } *pChecksum = ulChecksum; return TRUE; } BOOL SdbpCheckVersion( IN ULONGLONG qwDBFileVer, IN ULONGLONG qwBinFileVer ) /*++ Return: TRUE if the versions match, FALSE if they don't. Desc: Checks a binary version from the db against the version from the file, including allowing for wildcards, which are represented in the DB by using FFFF for that word-sized portion of the version. --*/ { WORD wDBSegment, wFileSegment; int i; for (i = 3; i >= 0; --i) { // // Get the appropriate word out of the QWORD // wDBSegment = (WORD)(qwDBFileVer >> (16 * i)); wFileSegment = (WORD)(qwBinFileVer >> (16 * i)); // // The DB segment may be 0xFFFF, in which case it matches on // everything. // if (wDBSegment != wFileSegment && wDBSegment != 0xFFFF) { return FALSE; } } return TRUE; } BOOL SdbpCheckUptoVersion( IN ULONGLONG qwDBFileVer, IN ULONGLONG qwBinFileVer ) /*++ Return: TRUE if the versions match, FALSE if they don't. Desc: Checks a binary version from the db against the version from the file, including allowing for wildcards, which are represented in the DB by using FFFF for that word-sized portion of the version. --*/ { WORD wDBSegment, wFileSegment; BOOL bReturn = TRUE; int i; for (i = 3; i >= 0; --i) { // // Get the appropriate word out of the QWORD // wDBSegment = (WORD)(qwDBFileVer >> (16 * i)); wFileSegment = (WORD)(qwBinFileVer >> (16 * i)); if (wDBSegment == wFileSegment || wDBSegment == 0xFFFF) { continue; } // // At this point we know that the two values don't match // the wFileSegment has to be less than wDBSegment to satisfy this // test - so set bReturn and exit // bReturn = (wDBSegment > wFileSegment); break; } return bReturn; } #ifndef KERNEL_MODE BOOL SdbFormatAttribute( IN PATTRINFO pAttrInfo, // pointer to the attribute information OUT LPTSTR pchBuffer, // receives XML corresponding to the given attribute IN DWORD dwBufferSize // size in wide characters of the buffer pchBuffer ) /*++ Return: FALSE if the buffer is too small or attribute not available. Desc: TBD. --*/ { size_t cchRemaining; TCHAR* pszEnd = NULL; HRESULT hr; #if defined(WIN32A_MODE) || defined(WIN32U_MODE) struct tm* ptm; time_t tt; #else LARGE_INTEGER liTime; TIME_FIELDS TimeFields; #endif if (!(pAttrInfo->dwFlags & ATTRIBUTE_AVAILABLE)) { return FALSE; } hr = StringCchPrintfEx(pchBuffer, (int)dwBufferSize, &pszEnd, &cchRemaining, 0, TEXT("%s="), SdbTagToString(pAttrInfo->tAttrID)); if (FAILED(hr)) { DBGPRINT((sdlError, "SdbFormatAttribute", "Buffer is too small to accomodate \"%s\"\n", SdbTagToString(pAttrInfo->tAttrID))); return FALSE; } switch (pAttrInfo->tAttrID) { case TAG_BIN_PRODUCT_VERSION: case TAG_BIN_FILE_VERSION: case TAG_UPTO_BIN_PRODUCT_VERSION: case TAG_UPTO_BIN_FILE_VERSION: hr = StringCchPrintf(pszEnd, cchRemaining, TEXT("\"%d.%d.%d.%d\""), (WORD)(pAttrInfo->ullAttr >> 48), (WORD)(pAttrInfo->ullAttr >> 32), (WORD)(pAttrInfo->ullAttr >> 16), (WORD)(pAttrInfo->ullAttr)); break; case TAG_MODULE_TYPE: hr = StringCchPrintf(pszEnd, cchRemaining, TEXT("\"%s\""), SdbpModuleTypeToString(pAttrInfo->dwAttr)); break; case TAG_VER_LANGUAGE: // // language is a dword attribute that we shall make a string out of // { TCHAR szLanguageName[MAX_PATH]; DWORD dwLength; szLanguageName[0] = TEXT('\0'); dwLength = VerLanguageName((LANGID)pAttrInfo->dwAttr, szLanguageName, CHARCOUNT(szLanguageName)); if (dwLength) { hr = StringCchPrintf(pszEnd, cchRemaining, TEXT("\"%s [0x%x]\""), szLanguageName, pAttrInfo->dwAttr); } else { hr = StringCchPrintf(pszEnd, cchRemaining, TEXT("\"0x%x\""), pAttrInfo->dwAttr); } } break; case TAG_LINK_DATE: case TAG_UPTO_LINK_DATE: #if defined(WIN32A_MODE) || defined(WIN32U_MODE) tt = (time_t) pAttrInfo->dwAttr; ptm = gmtime(&tt); if (ptm) { hr = StringCchPrintf(pszEnd, cchRemaining, TEXT("\"%02d/%02d/%02d %02d:%02d:%02d\""), ptm->tm_mon+1, ptm->tm_mday, ptm->tm_year+1900, ptm->tm_hour, ptm->tm_min, ptm->tm_sec); } #else RtlSecondsSince1970ToTime((ULONG)pAttrInfo->dwAttr, &liTime); RtlTimeToTimeFields(&liTime, &TimeFields); hr = StringCchPrintf(pszEnd, cchRemaining, TEXT("\"%02d/%02d/%02d %02d:%02d:%02d\""), TimeFields.Month, TimeFields.Day, TimeFields.Year, TimeFields.Hour, TimeFields.Minute, TimeFields.Second); #endif break; case TAG_SIZE: hr = StringCchPrintf(pszEnd, cchRemaining, TEXT("\"%ld\""), pAttrInfo->dwAttr); break; default: switch (GETTAGTYPE(pAttrInfo->tAttrID)) { case TAG_TYPE_DWORD: hr = StringCchPrintf(pszEnd, cchRemaining, TEXT("\"0x%lX\""), pAttrInfo->dwAttr); break; case TAG_TYPE_QWORD: // // This is an unidentified QWORD attribute // DBGPRINT((sdlError, "SdbFormatAttribute", "Unexpected qword attribute found\n")); hr = StringCchPrintf(pszEnd, cchRemaining, TEXT("\"0x%I64X\""), pAttrInfo->ullAttr); break; case TAG_TYPE_STRINGREF: if (cchRemaining < 3) { return FALSE; // not enough room even for " ? } *pszEnd++ = TEXT('\"'); cchRemaining--; if (!SdbpSanitizeXML(pszEnd, (int)cchRemaining, pAttrInfo->lpAttr)) { // handle error please return FALSE; } // // Once done with this, sanitize further // if (!SafeNCat(pszEnd, (int)cchRemaining, TEXT("\""), -1)) { return FALSE; } hr = S_OK; break; } } return (hr == S_OK); // evaluates to TRUE when we successfully printed the value into the buffer } BOOL SdbpGetVersionAttributes( IN PSDBCONTEXT pContext, OUT PFILEINFO pFileInfo ) /*++ Return: TRUE on success, FALSE otherwise. Desc: This function retrieves all of the Version-related attributes Imports apis from version.dll if called for the first time --*/ { DWORD dwNull = 0; VS_FIXEDFILEINFO* pFixedInfo = NULL; // fixed info ptr UINT FixedInfoSize = 0; PVOID pBuffer = NULL; // version data buffer DWORD dwBufferSize; // version data buffer size int i; #ifdef NT_MODE // // check to see whether we need to run NT routine // if (pFileInfo->hFile != INVALID_HANDLE_VALUE || pFileInfo->pImageBase != NULL) { // // not an error -- this case is handled in header attributes // goto err; } #endif // NT_MODE if (pContext == NULL) { // // Special case when it's called with null context. // In this case we use an internal structure allocated from the stack. // STACK_ALLOC(pContext, sizeof(SDBCONTEXT)); if (pContext == NULL) { DBGPRINT((sdlError, "SdbpGetVersionAttributes", "Failed to allocate %d bytes from stack\n", sizeof(SDBCONTEXT))); goto err; } RtlZeroMemory(pContext, sizeof(SDBCONTEXT)); } #ifdef WIN32A_MODE pContext->pfnGetFileVersionInfoSize = GetFileVersionInfoSizeA; pContext->pfnGetFileVersionInfo = GetFileVersionInfoA; pContext->pfnVerQueryValue = VerQueryValueA; #else pContext->pfnGetFileVersionInfoSize = GetFileVersionInfoSizeW; pContext->pfnGetFileVersionInfo = GetFileVersionInfoW; pContext->pfnVerQueryValue = VerQueryValueW; #endif dwBufferSize = pContext->pfnGetFileVersionInfoSize(pFileInfo->FilePath, &dwNull); if (dwBufferSize == 0) { DBGPRINT((sdlInfo, "SdbpGetVersionAttributes", "No version info.\n")); // // We have failed to obtain version attributes // goto err; } pBuffer = SdbAlloc(dwBufferSize + VERSIONINFO_BUFFER_PAD); if (pBuffer == NULL) { DBGPRINT((sdlError, "SdbpGetVersionAttributes", "Failed to allocate %d bytes for version info buffer.\n", dwBufferSize + VERSIONINFO_BUFFER_PAD)); goto err; } if (!pContext->pfnGetFileVersionInfo(pFileInfo->FilePath, 0, dwBufferSize, pBuffer)) { DBGPRINT((sdlError, "SdbpGetVersionAttributes", "Failed to retrieve version info for file \"%s\"", pFileInfo->FilePath)); goto err; } if (!pContext->pfnVerQueryValue(pBuffer, TEXT("\\"), (PVOID*)&pFixedInfo, &FixedInfoSize)) { DBGPRINT((sdlError, "SdbpGetVersionAttributes", "Failed to query for fixed version info size for \"%s\"\n", pFileInfo->FilePath)); goto err; } // // Retrieve string attributes. // SdbpQueryStringVersionInformation(pContext, pFileInfo, pBuffer); // // Now retrieve other attributes. // if (FixedInfoSize >= sizeof(VS_FIXEDFILEINFO)) { SdbpQueryBinVersionInformation(pContext, pFileInfo, pFixedInfo); } else { // // No other version attributes are available. Set the rest of the // attributes as being not available. // for (i = 0; i < ARRAYSIZE(g_rgBinVerTags); ++i) { SdbpSetAttribute(pFileInfo, g_rgBinVerTags[i], NULL); } } // // Store the pointer to the version info buffer. // pFileInfo->pVersionInfo = pBuffer; return TRUE; err: // // We are here ONLY when we failed to obtain version info // through apis -- regardless of the state of other value we might have // obtained if (pBuffer != NULL) { SdbFree(pBuffer); } for (i = 0; i < ARRAYSIZE(g_rgBinVerTags); ++i) { SdbpSetAttribute(pFileInfo, g_rgBinVerTags[i], NULL); } for (i = 0; i < ARRAYSIZE(g_rgVerStrings); ++i) { SdbpSetAttribute(pFileInfo, g_rgVerStrings[i].tTag, NULL); } return FALSE; } BOOL SdbpGetFileDirectoryAttributes( OUT PFILEINFO pFileInfo ) /*++ Return: TRUE on success, FALSE otherwise. Desc: This function retrieves the file directory attributes for the specified file. --*/ { BOOL bSuccess = FALSE; FILEDIRECTORYATTRIBUTES fda; int i; bSuccess = SdbpQueryFileDirectoryAttributes(pFileInfo->FilePath, &fda); if (!bSuccess) { DBGPRINT((sdlInfo, "SdbpGetFileDirectoryAttributes", "No file directory attributes available.\n")); goto Done; } if (fda.dwFlags & FDA_FILESIZE) { assert(fda.dwFileSizeHigh == 0); SdbpSetAttribute(pFileInfo, TAG_SIZE, &fda.dwFileSizeLow); } Done: if (!bSuccess) { for (i = 0; g_rgDirectoryTags[i] != 0; ++i) { SdbpSetAttribute(pFileInfo, g_rgDirectoryTags[i], NULL); } } return bSuccess; } BOOL SdbGetFileAttributes( IN LPCTSTR lpwszFileName, // the file for which attributes are requested OUT PATTRINFO* ppAttrInfo, // receives allocated pointer to the attribute array OUT LPDWORD lpdwAttrCount // receives the number of entries in an attributes table ) /*++ Return: FALSE if the file does not exist or some other severe error had occured. Note that each attribute has it's own flag ATTRIBUTE_AVAILABLE that allows for checking whether an attribute has been retrieved successfully Not all attributes might be present for all files. Desc: TBD --*/ { PFILEINFO pFileInfo; BOOL bReturn; // // The call below allocates the structure, context is not used // pFileInfo = SdbGetFileInfo(NULL, lpwszFileName, INVALID_HANDLE_VALUE, NULL, 0, TRUE); if (pFileInfo == NULL) { DBGPRINT((sdlError, "SdbGetFileAttributes", "Error retrieving FILEINFO structure\n")); return FALSE; } // // The three calls below, even when fail do not produce a fatal condition // as the exe may not have all the attributes available. // bReturn = SdbpGetFileDirectoryAttributes(pFileInfo); if (!bReturn) { DBGPRINT((sdlInfo, "SdbGetFileAttributes", "Error retrieving directory attributes\n")); } bReturn = SdbpGetVersionAttributes(NULL, pFileInfo); if (!bReturn) { DBGPRINT((sdlInfo, "SdbGetFileAttributes", "Error retrieving version attributes\n")); } bReturn = SdbpGetHeaderAttributes(NULL, pFileInfo); if (!bReturn) { DBGPRINT((sdlInfo, "SdbGetFileAttributes", "Error retrieving header attributes\n")); } pFileInfo->dwMagic = FILEINFO_MAGIC; // // Now that we are done, put the return pointer. // if (lpdwAttrCount != NULL) { *lpdwAttrCount = ATTRIBUTE_COUNT; } if (ppAttrInfo != NULL) { // // Return the pointer to the attribute info itself. // It is the same pointer we expect to get in a complimentary // call to SdbFreeFileInfo. // *ppAttrInfo = &pFileInfo->Attributes[0]; } else { // // Pointer is not needed. Release the memory. // SdbFreeFileInfo(pFileInfo); } return TRUE; } BOOL SdbFreeFileAttributes( IN PATTRINFO pFileAttributes // pointer returned by SdbGetFileAttributes ) /*++ Return: FALSE if a wrong pointer was passed in (not the one from SdbGetFileAttributes). Desc: Self explanatory. --*/ { PFILEINFO pFileInfo; // // We are assuming the pointer that was passed in points inside of a // larger structure FILEINFO. To verify that we step back a pre-determined number // of bytes (calculated below as an offset) and check the "magic" signature. // pFileInfo = (PFILEINFO)((PBYTE)pFileAttributes - OFFSETOF(FILEINFO, Attributes)); if (pFileInfo->dwMagic != FILEINFO_MAGIC) { DBGPRINT((sdlError, "SdbFreeFileAttributes", "Bad pointer to attributes.\n")); return FALSE; } SdbFreeFileInfo(pFileInfo); return TRUE; } BOOL SdbpQuery16BitDescription( OUT LPSTR szBuffer, // min length 256 chars ! IN PIMAGEFILEDATA pImageData ) /*++ Return: TRUE on success, FALSE otherwise. Desc: Gets the 16 bit description for a DOS executable. --*/ { PIMAGE_DOS_HEADER pDosHeader; PIMAGE_OS2_HEADER pNEHeader; PBYTE pSize; DWORD ModuleType; if (!SdbpGetModuleType(&ModuleType, pImageData)) { return FALSE; } if (ModuleType != MT_W16_MODULE) { return FALSE; } pDosHeader = (PIMAGE_DOS_HEADER)pImageData->pBase; pNEHeader = (PIMAGE_OS2_HEADER)((PBYTE)pImageData->pBase + pDosHeader->e_lfanew); // // Now we know that pNEHeader is valid, just have to make sure that // the next offset is valid as well, make a check against file size. // if (pImageData->ViewSize < pDosHeader->e_lfanew + sizeof(*pNEHeader)) { return FALSE; } if (pImageData->ViewSize < pNEHeader->ne_nrestab + sizeof(*pSize)) { return FALSE; } pSize = (PBYTE)((PBYTE)pImageData->pBase + pNEHeader->ne_nrestab); if (*pSize == 0) { return FALSE; } // // Now check for the string size. // if (pImageData->ViewSize < pNEHeader->ne_nrestab + sizeof(*pSize) + *pSize) { return FALSE; } RtlCopyMemory(szBuffer, pSize + 1, *pSize); szBuffer[*pSize] = '\0'; return TRUE; } BOOL SdbpQuery16BitModuleName( OUT LPSTR szBuffer, IN PIMAGEFILEDATA pImageData ) { PIMAGE_DOS_HEADER pDosHeader; PIMAGE_OS2_HEADER pNEHeader; PBYTE pSize; DWORD ModuleType; if (!SdbpGetModuleType(&ModuleType, pImageData)) { return FALSE; } if (ModuleType != MT_W16_MODULE) { return FALSE; } pDosHeader = (PIMAGE_DOS_HEADER)pImageData->pBase; pNEHeader = (PIMAGE_OS2_HEADER)((PBYTE)pImageData->pBase + pDosHeader->e_lfanew); // // Now we know that pNEHeader is valid, just have to make sure that // the next offset is valid as well, make a check against file size. // if (pImageData->ViewSize < pDosHeader->e_lfanew + sizeof(*pNEHeader)) { return FALSE; } if (pImageData->ViewSize < pNEHeader->ne_restab + sizeof(*pSize)) { return FALSE; } pSize = (PBYTE)((PBYTE)pImageData->pBase + pDosHeader->e_lfanew + pNEHeader->ne_restab); if (*pSize == 0) { return FALSE; } // // Now check for the string size. // if (pImageData->ViewSize < pDosHeader->e_lfanew + pNEHeader->ne_restab + sizeof(*pSize) + *pSize) { return FALSE; } RtlCopyMemory(szBuffer, pSize + 1, *pSize); szBuffer[*pSize] = '\0'; return TRUE; } #endif // KERNEL_MODE