https://web.archive.org/web/20140217003950/http://forum.sysinternals.com/topic16893_post83634.html
我发现了一个函数,它将调用WinVerifyTrust来检查文件的嵌入式签名,如果失败,则找到适当的系统目录文件,并使用另一个WinVerifyTrust调用进行检查。然而,对C:\Windows\System32\cmd.exe进行测试失败。请注意,测试应用程序是64位的,因此文件重定向不是问题。
将该函数的输出与Microsoft的Sigcheck实用程序进行比较,该函数具有正确的文件哈希值,并找到了正确的目录文件。但是,当使用目录信息调用WinVerifyTrust时,仍会出现以下错误:
TRUST_E_BAD_DIGEST 0x80096010 //对象的数字签名未经验证。
有趣的是,当启用UI时:
dwUIChoice = WTD_UI_ALL
失败代码是不同的:
TRUST_E_SUBJECT_NOT_TRUSTED 0x800B0004 //主题未经信任,无法执行指定操作。
但是,Sigcheck.exe和Signtool.exe都表示它是可信的。
此外,如果设置dwUIChoice = WTD_UI_ALL,则会弹出下面的错误弹出窗口,并提供一个链接,看起来像是一个非常有效的证书。
那么为什么WinVerifyTrust会指示cmd.exe的签名有问题呢?
以下是代码,欢迎任何意见和建议:
BOOL VerifyEmbeddedSignature2(LPCWSTR pwszSourceFile)
{
BOOL bRetVal = FALSE;
LONG lStatus = 0;
GUID WintrustVerifyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
WINTRUST_DATA wd;
WINTRUST_FILE_INFO wfi;
////set up structs to verify files with cert signatures
memset(&wfi, 0, sizeof(wfi));
wfi.cbStruct = sizeof(WINTRUST_FILE_INFO);
wfi.pcwszFilePath = pwszSourceFile;
memset(&wd, 0, sizeof(wd));
wd.cbStruct = sizeof(WINTRUST_DATA);
wd.dwUnionChoice = WTD_CHOICE_FILE;
wd.pFile = &wfi;
wd.dwUIChoice = WTD_UI_NONE;
wd.fdwRevocationChecks = WTD_REVOKE_NONE;
wd.dwStateAction = WTD_STATEACTION_VERIFY;
wd.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL | WTD_USE_DEFAULT_OSVER_CHECK;
lStatus = WinVerifyTrust(NULL, &WintrustVerifyGuid, &wd);
//clean up the state variable
wd.dwStateAction = WTD_STATEACTION_CLOSE;
WinVerifyTrust(NULL, &WintrustVerifyGuid, &wd);
////if failed, try to verify using catalog files
if (lStatus != ERROR_SUCCESS)
{
GUID DriverActionGuid = DRIVER_ACTION_VERIFY;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD dwHash = 0;
BYTE bHash[100] = { 0 };
HCATINFO hCatInfo = NULL;
HCATADMIN hCatAdmin = NULL;
LPWSTR pszMemberTag = NULL;
//open the file
hFile = CreateFileW(pwszSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
goto Cleanup;
if (!CryptCATAdminAcquireContext(&hCatAdmin, &DriverActionGuid, 0))
goto Cleanup;
dwHash = sizeof(bHash);
if (!CryptCATAdminCalcHashFromFileHandle(hFile, &dwHash, bHash, 0))
goto Cleanup;
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
//Create a string form of the hash (used later in pszMemberTag)
pszMemberTag = new WCHAR[dwHash * 2 + 1];
for (DWORD dw = 0; dw < dwHash; ++dw)
{
wsprintfW(&pszMemberTag[dw * 2], L"%02X", bHash[dw]);
}
//find the catalog which contains the hash
hCatInfo = CryptCATAdminEnumCatalogFromHash(hCatAdmin, bHash, dwHash, 0, NULL);
if (hCatInfo)
{
CATALOG_INFO ci = { 0 };
ci.cbStruct = sizeof(ci);
WINTRUST_CATALOG_INFO wci;
CryptCATCatalogInfoFromContext(hCatInfo, &ci, 0);
memset(&wci, 0, sizeof(wci));
wci.cbStruct = sizeof(wci);
wci.pcwszCatalogFilePath = ci.wszCatalogFile;
wci.pcwszMemberFilePath = pwszSourceFile;
wci.pcwszMemberTag = pszMemberTag;
memset(&wd, 0, sizeof(wd));
wd.cbStruct = sizeof(WINTRUST_DATA);
wd.dwUnionChoice = WTD_CHOICE_CATALOG;
wd.pCatalog = &wci;
wd.dwUIChoice = WTD_UI_ALL; //WTD_UI_NONE; //
wd.fdwRevocationChecks = WTD_REVOKE_NONE;
wd.dwStateAction = WTD_STATEACTION_VERIFY;
wd.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL | WTD_USE_DEFAULT_OSVER_CHECK;
lStatus = WinVerifyTrust(NULL, &WintrustVerifyGuid, &wd);
if(ERROR_SUCCESS == lStatus)
bRetVal = TRUE;
//clean up the state variable
wd.dwStateAction = WTD_STATEACTION_CLOSE;
WinVerifyTrust(NULL, &WintrustVerifyGuid, &wd);
CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0);
}
Cleanup:
if(NULL != hCatAdmin)
CryptCATAdminReleaseContext(hCatAdmin, 0);
hCatAdmin = NULL;
if(NULL != pszMemberTag)
delete[] pszMemberTag;
pszMemberTag = NULL;
if(INVALID_HANDLE_VALUE != hFile)
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
else
bRetVal = TRUE;
return bRetVal;
}
请注意,要使用上述函数,您需要:
#include <Softpub.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <mscat.h>
// Link with the Wintrust.lib file.
#pragma comment (lib, "wintrust")
更新:这里提供了一个样例
我刚刚发现使用了\{\{某个IT技术\}\}。CryptCATAdminAcquireContext2(&hCatAdmin, NULL, BCRYPT_SHA256_ALGORITHM, NULL, 0))
在我的Windows Server 2019上,使用CryptCATAdminCalcHashFromFileHandle2代替CryptCATAdminCalcHashFromFileHandle可以正常工作。
现在问题变成了:“为什么?”BCRYPT_SHA256_ALGORITHM是否适用于可能在其他操作系统版本上运行的代码(Win7?Win8?Server 2012 R2?)
更新2:
CryptCATAdminAcquireContext2文档中写道:“此函数使您能够选择或为您选择在需要目录管理员上下文的功能中要使用的哈希算法。虽然您可以设置散列算法的名称,但我们建议您让函数确定算法。这样可以保护您的应用程序免受将来可能不受信任的硬编码算法的影响。”
然而,将NULL(如文档所建议)设置为BCRYPT_SHA256_ALGORITHM会导致先前出现的故障。这非常脆弱,似乎是特定于操作系统的 :(
有没有办法使其可靠地跨操作系统版本工作?
更新3:
现在很明显为什么这不能正确地工作。这里是由sigcheck显示的cmd.exe的哈希列表
当使用NULL参数调用CryptCATAdminAcquireContext2函数时,可以从CryptCATAdminCalcHashFromFileHandle2函数中获取PESHA1哈希值。当使用BCRYPT_SHA256_ALGORITHM参数调用该函数时,可以获取PE256哈希值。
这些都很容易理解。不幸的是,目录文件只包含PE256哈希值。因此,如果您不知道目录文件包含哪种散列算法,我能想到的唯一解决方法就是通过使用不同的算法循环运行所有这些代码,一遍又一遍地对文件进行哈希操作,直到找到一个存在于目录文件中的哈希。
然而,令人困惑的是CryptCATAdminEnumCatalogFromHash如何使用PESHA1哈希值找到相同的目录文件,即使在目录文件中没有找到该哈希值?这肯定是因为有其他额外信息使其能够正常工作。
CryptCATAdminAcquireContext2
函数的参数pwszHashAlgorithm
所述,默认哈希算法可能会在未来的Windows版本中更改。 - YangXiaoPo-MSFT