mirror of
https://github.com/mirror/processhacker
synced 2026-06-08 16:03:24 +00:00
bbe1014194
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@2851 21ef857c-d57f-4fe0-8362-d861dc6d29cd
446 lines
12 KiB
C
446 lines
12 KiB
C
/*
|
|
* Process Hacker -
|
|
* image verification
|
|
*
|
|
* Copyright (C) 2009-2010 wj32
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Process Hacker is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <ph.h>
|
|
#include <verifyp.h>
|
|
|
|
_CryptCATAdminCalcHashFromFileHandle CryptCATAdminCalcHashFromFileHandle;
|
|
_CryptCATAdminAcquireContext CryptCATAdminAcquireContext;
|
|
_CryptCATAdminEnumCatalogFromHash CryptCATAdminEnumCatalogFromHash;
|
|
_CryptCATCatalogInfoFromContext CryptCATCatalogInfoFromContext;
|
|
_CryptCATAdminReleaseCatalogContext CryptCATAdminReleaseCatalogContext;
|
|
_CryptCATAdminReleaseContext CryptCATAdminReleaseContext;
|
|
_WTHelperProvDataFromStateData WTHelperProvDataFromStateData_I;
|
|
_WTHelperGetProvSignerFromChain WTHelperGetProvSignerFromChain_I;
|
|
_WinVerifyTrust WinVerifyTrust_I;
|
|
_CertNameToStr CertNameToStr_I;
|
|
|
|
VOID PhVerifyInitialization()
|
|
{
|
|
LoadLibrary(L"wintrust.dll");
|
|
LoadLibrary(L"crypt32.dll");
|
|
|
|
CryptCATAdminCalcHashFromFileHandle =
|
|
PhGetProcAddress(L"wintrust.dll", "CryptCATAdminCalcHashFromFileHandle");
|
|
CryptCATAdminAcquireContext =
|
|
PhGetProcAddress(L"wintrust.dll", "CryptCATAdminAcquireContext");
|
|
CryptCATAdminEnumCatalogFromHash =
|
|
PhGetProcAddress(L"wintrust.dll", "CryptCATAdminEnumCatalogFromHash");
|
|
CryptCATCatalogInfoFromContext =
|
|
PhGetProcAddress(L"wintrust.dll", "CryptCATCatalogInfoFromContext");
|
|
CryptCATAdminReleaseCatalogContext =
|
|
PhGetProcAddress(L"wintrust.dll", "CryptCATAdminReleaseCatalogContext");
|
|
CryptCATAdminReleaseContext =
|
|
PhGetProcAddress(L"wintrust.dll", "CryptCATAdminReleaseContext");
|
|
WTHelperProvDataFromStateData_I =
|
|
PhGetProcAddress(L"wintrust.dll", "WTHelperProvDataFromStateData");
|
|
WTHelperGetProvSignerFromChain_I =
|
|
PhGetProcAddress(L"wintrust.dll", "WTHelperGetProvSignerFromChain");
|
|
WinVerifyTrust_I =
|
|
PhGetProcAddress(L"wintrust.dll", "WinVerifyTrust");
|
|
CertNameToStr_I =
|
|
PhGetProcAddress(L"crypt32.dll", "CertNameToStrW");
|
|
}
|
|
|
|
VERIFY_RESULT PhpStatusToVerifyResult(
|
|
__in LONG Status
|
|
)
|
|
{
|
|
switch (Status)
|
|
{
|
|
case 0:
|
|
return VrTrusted;
|
|
case TRUST_E_NOSIGNATURE:
|
|
return VrNoSignature;
|
|
case CERT_E_EXPIRED:
|
|
return VrExpired;
|
|
case CERT_E_REVOKED:
|
|
return VrRevoked;
|
|
case TRUST_E_EXPLICIT_DISTRUST:
|
|
return VrDistrust;
|
|
case CRYPT_E_SECURITY_SETTINGS:
|
|
return VrSecuritySettings;
|
|
default:
|
|
return VrSecuritySettings;
|
|
}
|
|
}
|
|
|
|
PPH_STRING PhpGetCertNameString(
|
|
__in PCERT_NAME_BLOB Blob
|
|
)
|
|
{
|
|
PPH_STRING string;
|
|
PVOID buffer;
|
|
ULONG bufferSize = 0x100;
|
|
ULONG returnLength;
|
|
|
|
buffer = PhAllocate(bufferSize * 2);
|
|
|
|
returnLength = CertNameToStr_I(
|
|
X509_ASN_ENCODING,
|
|
Blob,
|
|
CERT_X500_NAME_STR,
|
|
buffer,
|
|
bufferSize
|
|
);
|
|
|
|
if (returnLength > bufferSize)
|
|
{
|
|
PhFree(buffer);
|
|
bufferSize = returnLength;
|
|
buffer = PhAllocate(bufferSize * 2);
|
|
|
|
returnLength = CertNameToStr_I(
|
|
X509_ASN_ENCODING,
|
|
Blob,
|
|
CERT_X500_NAME_STR,
|
|
buffer,
|
|
bufferSize
|
|
);
|
|
}
|
|
|
|
string = PhCreateString((PWSTR)buffer);
|
|
PhFree(buffer);
|
|
|
|
return string;
|
|
}
|
|
|
|
PPH_STRING PhpGetX500Value(
|
|
__in PPH_STRING String,
|
|
__in PWSTR KeyName
|
|
)
|
|
{
|
|
WCHAR keyNamePlusEquals[10];
|
|
ULONG keyNameLength;
|
|
ULONG startIndex;
|
|
ULONG endIndex;
|
|
|
|
keyNameLength = (ULONG)wcslen(KeyName);
|
|
wcsncpy(keyNamePlusEquals, KeyName, 8);
|
|
keyNamePlusEquals[keyNameLength] = '=';
|
|
keyNamePlusEquals[keyNameLength + 1] = 0;
|
|
|
|
// Find "Key=".
|
|
startIndex = PhStringIndexOfString(String, 0, keyNamePlusEquals);
|
|
|
|
if (startIndex == -1)
|
|
return NULL;
|
|
|
|
startIndex += keyNameLength + 1;
|
|
|
|
if (startIndex * sizeof(WCHAR) >= String->Length)
|
|
return NULL;
|
|
|
|
// Is the value quoted?
|
|
if (String->Buffer[startIndex] == '"')
|
|
{
|
|
startIndex++;
|
|
|
|
if (startIndex * sizeof(WCHAR) >= String->Length)
|
|
return NULL;
|
|
|
|
endIndex = PhStringIndexOfChar(String, startIndex, '"');
|
|
|
|
// It's an error if we didn't find the matching quotation mark.
|
|
if (endIndex == -1)
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
endIndex = PhStringIndexOfChar(String, startIndex, ',');
|
|
|
|
// If we didn't find a comma, it means the key/value pair is
|
|
// the last one in the string.
|
|
if (endIndex == -1)
|
|
endIndex = String->Length / sizeof(WCHAR);
|
|
}
|
|
|
|
return PhSubstring(String, startIndex, endIndex - startIndex);
|
|
}
|
|
|
|
PPH_STRING PhpGetSignerNameFromStateData(
|
|
__in HANDLE StateData
|
|
)
|
|
{
|
|
PCRYPT_PROVIDER_DATA provData;
|
|
PCRYPT_PROVIDER_SGNR sgnr;
|
|
PCRYPT_PROVIDER_CERT cert;
|
|
PCCERT_CONTEXT certContext;
|
|
PCERT_INFO certInfo;
|
|
PPH_STRING name;
|
|
PPH_STRING value;
|
|
|
|
// 1. State data -> provider data.
|
|
|
|
provData = WTHelperProvDataFromStateData_I(StateData);
|
|
|
|
if (!provData)
|
|
return NULL;
|
|
|
|
// 2. Provider data -> Provider signer
|
|
|
|
sgnr = WTHelperGetProvSignerFromChain_I(provData, 0, FALSE, 0);
|
|
|
|
if (!sgnr)
|
|
return NULL;
|
|
if (!sgnr->pasCertChain)
|
|
return NULL;
|
|
if (sgnr->csCertChain == 0)
|
|
return NULL;
|
|
|
|
// 3. Provider signer -> Provider cert
|
|
|
|
cert = &sgnr->pasCertChain[0];
|
|
|
|
// 4. Provider cert -> Cert context
|
|
|
|
certContext = cert->pCert;
|
|
|
|
if (!certContext)
|
|
return NULL;
|
|
|
|
// 5. Cert context -> Cert info
|
|
|
|
certInfo = certContext->pCertInfo;
|
|
|
|
if (!certInfo)
|
|
return NULL;
|
|
|
|
// 6. Cert info subject -> Subject X.500 string
|
|
|
|
name = PhpGetCertNameString(&certInfo->Subject);
|
|
|
|
// 7. Subject X.500 string -> CN or OU value
|
|
|
|
value = PhpGetX500Value(name, L"CN");
|
|
|
|
if (!value)
|
|
value = PhpGetX500Value(name, L"OU");
|
|
|
|
PhDereferenceObject(name);
|
|
|
|
return value;
|
|
}
|
|
|
|
VERIFY_RESULT PhpVerifyFileBasic(
|
|
__in PWSTR FileName,
|
|
__out_opt PPH_STRING *SignerName
|
|
)
|
|
{
|
|
LONG status;
|
|
WINTRUST_DATA trustData = { 0 };
|
|
WINTRUST_FILE_INFO fileInfo = { 0 };
|
|
GUID actionGenericVerifyV2 = WINTRUST_ACTION_GENERIC_VERIFY_V2;
|
|
|
|
fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
|
|
fileInfo.pcwszFilePath = FileName;
|
|
|
|
trustData.cbStruct = sizeof(WINTRUST_DATA);
|
|
trustData.dwUIChoice = WTD_UI_NONE;
|
|
trustData.dwProvFlags = WTD_SAFER_FLAG;
|
|
trustData.dwUnionChoice = WTD_CHOICE_FILE;
|
|
trustData.dwStateAction = WTD_STATEACTION_VERIFY;
|
|
trustData.pFile = &fileInfo;
|
|
|
|
if (WindowsVersion >= WINDOWS_VISTA)
|
|
trustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
|
|
|
|
status = WinVerifyTrust_I(NULL, &actionGenericVerifyV2, &trustData);
|
|
|
|
if (SignerName)
|
|
{
|
|
if (status != TRUST_E_NOSIGNATURE)
|
|
*SignerName = PhpGetSignerNameFromStateData(trustData.hWVTStateData);
|
|
else
|
|
*SignerName = NULL;
|
|
}
|
|
|
|
// Close the state data.
|
|
trustData.dwStateAction = WTD_STATEACTION_CLOSE;
|
|
WinVerifyTrust_I(NULL, &actionGenericVerifyV2, &trustData);
|
|
|
|
return PhpStatusToVerifyResult(status);
|
|
}
|
|
|
|
VERIFY_RESULT PhpVerifyFileFromCatalog(
|
|
__in PWSTR FileName,
|
|
__out_opt PPH_STRING *SignerName
|
|
)
|
|
{
|
|
LONG status = TRUST_E_NOSIGNATURE;
|
|
WINTRUST_DATA trustData = { 0 };
|
|
WINTRUST_CATALOG_INFO catalogInfo = { 0 };
|
|
GUID driverActionVerify = DRIVER_ACTION_VERIFY;
|
|
HANDLE fileHandle;
|
|
PBYTE fileHash = NULL;
|
|
ULONG fileHashLength;
|
|
PWSTR fileHashTag = NULL;
|
|
HANDLE catAdminHandle = NULL;
|
|
HANDLE catInfoHandle = NULL;
|
|
ULONG i;
|
|
|
|
fileHandle = CreateFile(
|
|
FileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (fileHandle == INVALID_HANDLE_VALUE)
|
|
return VrNoSignature;
|
|
|
|
// Don't try to hash files over 64 MB in size.
|
|
if (GetFileSize(fileHandle, NULL) > 64 * 1024 * 1024)
|
|
{
|
|
NtClose(fileHandle);
|
|
return VrNoSignature;
|
|
}
|
|
|
|
fileHashLength = 256;
|
|
fileHash = (PBYTE)PhAllocate(fileHashLength);
|
|
|
|
if (!CryptCATAdminCalcHashFromFileHandle(fileHandle, &fileHashLength, fileHash, 0))
|
|
{
|
|
fileHash = (PBYTE)PhReAlloc(fileHash, fileHashLength);
|
|
|
|
if (!CryptCATAdminCalcHashFromFileHandle(fileHandle, &fileHashLength, fileHash, 0))
|
|
{
|
|
NtClose(fileHandle);
|
|
PhFree(fileHash);
|
|
return VrNoSignature;
|
|
}
|
|
}
|
|
|
|
NtClose(fileHandle);
|
|
|
|
if (!CryptCATAdminAcquireContext(&catAdminHandle, &driverActionVerify, 0))
|
|
{
|
|
PhFree(fileHash);
|
|
return VrNoSignature;
|
|
}
|
|
|
|
fileHashTag = (PWSTR)PhAllocate((fileHashLength * 2 + 1) * sizeof(WCHAR));
|
|
|
|
for (i = 0; i < fileHashLength; i++)
|
|
wsprintfW(&fileHashTag[i * 2], L"%02X", fileHash[i]);
|
|
|
|
catInfoHandle = CryptCATAdminEnumCatalogFromHash(
|
|
catAdminHandle,
|
|
fileHash,
|
|
fileHashLength,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
PhFree(fileHash);
|
|
|
|
if (catInfoHandle)
|
|
{
|
|
CATALOG_INFO ci = { 0 };
|
|
|
|
if (CryptCATCatalogInfoFromContext(catInfoHandle, &ci, 0))
|
|
{
|
|
catalogInfo.cbStruct = sizeof(catalogInfo);
|
|
catalogInfo.pcwszCatalogFilePath = ci.wszCatalogFile;
|
|
catalogInfo.pcwszMemberFilePath = FileName;
|
|
catalogInfo.pcwszMemberTag = fileHashTag;
|
|
|
|
trustData.cbStruct = sizeof(trustData);
|
|
trustData.dwUIChoice = WTD_UI_NONE;
|
|
trustData.fdwRevocationChecks = WTD_STATEACTION_VERIFY;
|
|
trustData.dwUnionChoice = WTD_CHOICE_CATALOG;
|
|
trustData.dwStateAction = WTD_STATEACTION_VERIFY;
|
|
trustData.pCatalog = &catalogInfo;
|
|
|
|
if (WindowsVersion >= WINDOWS_VISTA)
|
|
trustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
|
|
|
|
status = WinVerifyTrust_I(NULL, &driverActionVerify, &trustData);
|
|
|
|
if (SignerName)
|
|
{
|
|
if (status != TRUST_E_NOSIGNATURE)
|
|
*SignerName = PhpGetSignerNameFromStateData(trustData.hWVTStateData);
|
|
else
|
|
*SignerName = NULL;
|
|
}
|
|
|
|
// Close the state data.
|
|
trustData.dwStateAction = WTD_STATEACTION_CLOSE;
|
|
WinVerifyTrust_I(NULL, &driverActionVerify, &trustData);
|
|
}
|
|
|
|
CryptCATAdminReleaseCatalogContext(catAdminHandle, catInfoHandle, 0);
|
|
}
|
|
|
|
PhFree(fileHashTag);
|
|
CryptCATAdminReleaseContext(catAdminHandle, 0);
|
|
|
|
return PhpStatusToVerifyResult(status);
|
|
}
|
|
|
|
/**
|
|
* Verifies a file's digital signature.
|
|
*
|
|
* \param FileName A file name.
|
|
* \param SignerName A variable which receives a pointer
|
|
* to a string containing the signer name. You must free
|
|
* the string using PhDereferenceObject() when you no
|
|
* longer need it. Note that the signer name may be NULL
|
|
* if it is not valid.
|
|
*
|
|
* \return A VERIFY_RESULT value.
|
|
*/
|
|
VERIFY_RESULT PhVerifyFile(
|
|
__in PWSTR FileName,
|
|
__out_opt PPH_STRING *SignerName
|
|
)
|
|
{
|
|
VERIFY_RESULT result;
|
|
|
|
// Make sure we have successfully imported
|
|
// the required functions.
|
|
if (
|
|
!CryptCATAdminCalcHashFromFileHandle ||
|
|
!CryptCATAdminAcquireContext ||
|
|
!CryptCATAdminEnumCatalogFromHash ||
|
|
!CryptCATCatalogInfoFromContext ||
|
|
!CryptCATAdminReleaseCatalogContext ||
|
|
!CryptCATAdminReleaseContext ||
|
|
!WinVerifyTrust_I ||
|
|
!WTHelperProvDataFromStateData_I ||
|
|
!WTHelperGetProvSignerFromChain_I ||
|
|
!CertNameToStr_I
|
|
)
|
|
return VrUnknown;
|
|
|
|
result = PhpVerifyFileBasic(FileName, SignerName);
|
|
|
|
if (result == VrNoSignature)
|
|
result = PhpVerifyFileFromCatalog(FileName, SignerName);
|
|
|
|
return result;
|
|
}
|