Process Hacker
verify.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * image verification
4  *
5  * Copyright (C) 2009-2013 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <ph.h>
24 #include <verify.h>
25 #include <verifyp.h>
26 
41 static PH_INITONCE PhpVerifyInitOnce = PH_INITONCE_INIT;
42 
43 static GUID WinTrustActionGenericVerifyV2 = WINTRUST_ACTION_GENERIC_VERIFY_V2;
44 static GUID DriverActionVerify = DRIVER_ACTION_VERIFY;
45 
46 static VOID PhpVerifyInitialization(
47  VOID
48  )
49 {
50  HMODULE wintrust;
51  HMODULE crypt32;
52 
53  wintrust = LoadLibrary(L"wintrust.dll");
54  crypt32 = LoadLibrary(L"crypt32.dll");
55 
56  CryptCATAdminCalcHashFromFileHandle = (PVOID)GetProcAddress(wintrust, "CryptCATAdminCalcHashFromFileHandle");
57  CryptCATAdminCalcHashFromFileHandle2 = (PVOID)GetProcAddress(wintrust, "CryptCATAdminCalcHashFromFileHandle2");
58  CryptCATAdminAcquireContext = (PVOID)GetProcAddress(wintrust, "CryptCATAdminAcquireContext");
59  CryptCATAdminAcquireContext2 = (PVOID)GetProcAddress(wintrust, "CryptCATAdminAcquireContext2");
60  CryptCATAdminEnumCatalogFromHash = (PVOID)GetProcAddress(wintrust, "CryptCATAdminEnumCatalogFromHash");
61  CryptCATCatalogInfoFromContext = (PVOID)GetProcAddress(wintrust, "CryptCATCatalogInfoFromContext");
62  CryptCATAdminReleaseCatalogContext = (PVOID)GetProcAddress(wintrust, "CryptCATAdminReleaseCatalogContext");
63  CryptCATAdminReleaseContext = (PVOID)GetProcAddress(wintrust, "CryptCATAdminReleaseContext");
64  WTHelperProvDataFromStateData_I = (PVOID)GetProcAddress(wintrust, "WTHelperProvDataFromStateData");
65  WTHelperGetProvSignerFromChain_I = (PVOID)GetProcAddress(wintrust, "WTHelperGetProvSignerFromChain");
66  WinVerifyTrust_I = (PVOID)GetProcAddress(wintrust, "WinVerifyTrust");
67  CertNameToStr_I = (PVOID)GetProcAddress(crypt32, "CertNameToStrW");
68  CertDuplicateCertificateContext_I = (PVOID)GetProcAddress(crypt32, "CertDuplicateCertificateContext");
69  CertFreeCertificateContext_I = (PVOID)GetProcAddress(crypt32, "CertFreeCertificateContext");
70 }
71 
73  _In_ LONG Status
74  )
75 {
76  switch (Status)
77  {
78  case 0:
79  return VrTrusted;
80  case TRUST_E_NOSIGNATURE:
81  return VrNoSignature;
82  case CERT_E_EXPIRED:
83  return VrExpired;
84  case CERT_E_REVOKED:
85  return VrRevoked;
86  case TRUST_E_EXPLICIT_DISTRUST:
87  return VrDistrust;
88  case CRYPT_E_SECURITY_SETTINGS:
89  return VrSecuritySettings;
90  case TRUST_E_BAD_DIGEST:
91  return VrBadSignature;
92  default:
93  return VrSecuritySettings;
94  }
95 }
96 
98  _In_ HANDLE StateData,
99  _Out_ PCERT_CONTEXT **Signatures,
100  _Out_ PULONG NumberOfSignatures
101  )
102 {
103  PCRYPT_PROVIDER_DATA provData;
104  PCRYPT_PROVIDER_SGNR sgnr;
105  PCERT_CONTEXT *signatures;
106  ULONG i;
107  ULONG numberOfSignatures;
108  ULONG index;
109 
110  provData = WTHelperProvDataFromStateData_I(StateData);
111 
112  if (!provData)
113  {
114  *Signatures = NULL;
115  *NumberOfSignatures = 0;
116  return FALSE;
117  }
118 
119  i = 0;
120  numberOfSignatures = 0;
121 
122  while (sgnr = WTHelperGetProvSignerFromChain_I(provData, i, FALSE, 0))
123  {
124  if (sgnr->csCertChain != 0)
125  numberOfSignatures++;
126 
127  i++;
128  }
129 
130  if (numberOfSignatures != 0)
131  {
132  signatures = PhAllocate(numberOfSignatures * sizeof(PCERT_CONTEXT));
133  i = 0;
134  index = 0;
135 
136  while (sgnr = WTHelperGetProvSignerFromChain_I(provData, i, FALSE, 0))
137  {
138  if (sgnr->csCertChain != 0)
139  signatures[index++] = (PCERT_CONTEXT)CertDuplicateCertificateContext_I(sgnr->pasCertChain[0].pCert);
140 
141  i++;
142  }
143  }
144  else
145  {
146  signatures = NULL;
147  }
148 
149  *Signatures = signatures;
150  *NumberOfSignatures = numberOfSignatures;
151 
152  return TRUE;
153 }
154 
156  _In_ PPH_VERIFY_FILE_INFO Information,
157  _In_ HANDLE StateData
158  )
159 {
160  static PH_INITONCE initOnce = PH_INITONCE_INIT;
161  static _CryptUIDlgViewSignerInfo cryptUIDlgViewSignerInfo;
162 
163  if (PhBeginInitOnce(&initOnce))
164  {
165  HMODULE cryptui = LoadLibrary(L"cryptui.dll");
166 
167  cryptUIDlgViewSignerInfo = (PVOID)GetProcAddress(cryptui, "CryptUIDlgViewSignerInfoW");
168  PhEndInitOnce(&initOnce);
169  }
170 
171  if (cryptUIDlgViewSignerInfo)
172  {
174  PCRYPT_PROVIDER_DATA provData;
175  PCRYPT_PROVIDER_SGNR sgnr;
176 
177  if (!(provData = WTHelperProvDataFromStateData_I(StateData)))
178  return;
179  if (!(sgnr = WTHelperGetProvSignerFromChain_I(provData, 0, FALSE, 0)))
180  return;
181 
182  viewSignerInfo.hwndParent = Information->hWnd;
183  viewSignerInfo.pSignerInfo = sgnr->psSigner;
184  viewSignerInfo.hMsg = provData->hMsg;
185  viewSignerInfo.pszOID = szOID_PKIX_KP_CODE_SIGNING;
186  cryptUIDlgViewSignerInfo(&viewSignerInfo);
187  }
188 }
189 
191  _In_ PPH_VERIFY_FILE_INFO Information,
192  _In_ HANDLE FileHandle,
193  _In_ ULONG UnionChoice,
194  _In_ PVOID UnionData,
195  _In_ PGUID ActionId,
196  _In_opt_ PVOID PolicyCallbackData,
197  _Out_ PCERT_CONTEXT **Signatures,
198  _Out_ PULONG NumberOfSignatures
199  )
200 {
201  LONG status;
202  WINTRUST_DATA trustData = { 0 };
203 
204  trustData.cbStruct = sizeof(WINTRUST_DATA);
205  trustData.pPolicyCallbackData = PolicyCallbackData;
206  trustData.dwUIChoice = WTD_UI_NONE;
207  trustData.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN;
208  trustData.dwUnionChoice = UnionChoice;
209  trustData.dwStateAction = WTD_STATEACTION_VERIFY;
210  trustData.dwProvFlags = WTD_SAFER_FLAG;
211 
212  trustData.pFile = UnionData;
213 
214  if (UnionChoice == WTD_CHOICE_CATALOG)
215  trustData.pCatalog = UnionData;
216 
217  if (Information->Flags & PH_VERIFY_PREVENT_NETWORK_ACCESS)
218  {
219  trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
220 
222  trustData.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
223  else
224  trustData.dwProvFlags |= WTD_REVOCATION_CHECK_NONE;
225  }
226 
227  status = WinVerifyTrust_I(NULL, ActionId, &trustData);
228  PhpGetSignaturesFromStateData(trustData.hWVTStateData, Signatures, NumberOfSignatures);
229 
230  if (status == 0 && (Information->Flags & PH_VERIFY_VIEW_PROPERTIES))
231  PhpViewSignerInfo(Information, trustData.hWVTStateData);
232 
233  // Close the state data.
234  trustData.dwStateAction = WTD_STATEACTION_CLOSE;
235  WinVerifyTrust_I(NULL, ActionId, &trustData);
236 
237  return PhpStatusToVerifyResult(status);
238 }
239 
241  _In_ HANDLE FileHandle,
242  _In_ PWSTR HashAlgorithm,
243  _Out_ PUCHAR *FileHash,
244  _Out_ PULONG FileHashLength,
245  _Out_ HANDLE *CatAdminHandle
246  )
247 {
248  HANDLE catAdminHandle;
249  PUCHAR fileHash;
250  ULONG fileHashLength;
251 
253  {
254  if (!CryptCATAdminAcquireContext2(&catAdminHandle, &DriverActionVerify, HashAlgorithm, NULL, 0))
255  return FALSE;
256  }
257  else
258  {
259  if (!CryptCATAdminAcquireContext(&catAdminHandle, &DriverActionVerify, 0))
260  return FALSE;
261  }
262 
263  fileHashLength = 32;
264  fileHash = PhAllocate(fileHashLength);
265 
267  {
268  if (!CryptCATAdminCalcHashFromFileHandle2(catAdminHandle, FileHandle, &fileHashLength, fileHash, 0))
269  {
270  PhFree(fileHash);
271  fileHash = PhAllocate(fileHashLength);
272 
273  if (!CryptCATAdminCalcHashFromFileHandle2(catAdminHandle, FileHandle, &fileHashLength, fileHash, 0))
274  {
275  CryptCATAdminReleaseContext(catAdminHandle, 0);
276  PhFree(fileHash);
277  return FALSE;
278  }
279  }
280  }
281  else
282  {
283  if (!CryptCATAdminCalcHashFromFileHandle(FileHandle, &fileHashLength, fileHash, 0))
284  {
285  PhFree(fileHash);
286  fileHash = PhAllocate(fileHashLength);
287 
288  if (!CryptCATAdminCalcHashFromFileHandle(FileHandle, &fileHashLength, fileHash, 0))
289  {
290  CryptCATAdminReleaseContext(catAdminHandle, 0);
291  PhFree(fileHash);
292  return FALSE;
293  }
294  }
295  }
296 
297  *FileHash = fileHash;
298  *FileHashLength = fileHashLength;
299  *CatAdminHandle = catAdminHandle;
300 
301  return TRUE;
302 }
303 
305  _In_ PPH_VERIFY_FILE_INFO Information,
306  _In_ HANDLE FileHandle,
307  _In_opt_ PWSTR HashAlgorithm,
308  _Out_ PCERT_CONTEXT **Signatures,
309  _Out_ PULONG NumberOfSignatures
310  )
311 {
312  VERIFY_RESULT verifyResult = VrNoSignature;
313  PCERT_CONTEXT *signatures;
314  ULONG numberOfSignatures;
315  WINTRUST_CATALOG_INFO catalogInfo = { 0 };
316  LARGE_INTEGER fileSize;
317  ULONG fileSizeLimit;
318  PUCHAR fileHash;
319  ULONG fileHashLength;
320  PPH_STRING fileHashTag;
321  HANDLE catAdminHandle;
322  HANDLE catInfoHandle;
323  ULONG i;
324 
325  *Signatures = NULL;
326  *NumberOfSignatures = 0;
327 
328  if (!NT_SUCCESS(PhGetFileSize(FileHandle, &fileSize)))
329  return VrNoSignature;
330 
331  signatures = NULL;
332  numberOfSignatures = 0;
333 
334  if (Information->FileSizeLimitForHash != -1)
335  {
336  fileSizeLimit = PH_VERIFY_DEFAULT_SIZE_LIMIT;
337 
338  if (Information->FileSizeLimitForHash != 0)
339  fileSizeLimit = Information->FileSizeLimitForHash;
340 
341  if (fileSize.QuadPart > fileSizeLimit)
342  return VrNoSignature;
343  }
344 
345  if (PhpCalculateFileHash(FileHandle, HashAlgorithm, &fileHash, &fileHashLength, &catAdminHandle))
346  {
347  fileHashTag = PhBufferToHexStringEx(fileHash, fileHashLength, TRUE);
348 
349  // Search the system catalogs.
350 
351  catInfoHandle = CryptCATAdminEnumCatalogFromHash(
352  catAdminHandle,
353  fileHash,
354  fileHashLength,
355  0,
356  NULL
357  );
358 
359  if (catInfoHandle)
360  {
361  CATALOG_INFO ci = { 0 };
362  DRIVER_VER_INFO verInfo = { 0 };
363 
364  if (CryptCATCatalogInfoFromContext(catInfoHandle, &ci, 0))
365  {
366  // Disable OS version checking by passing in a DRIVER_VER_INFO structure.
367  verInfo.cbStruct = sizeof(DRIVER_VER_INFO);
368 
369  catalogInfo.cbStruct = sizeof(catalogInfo);
370  catalogInfo.pcwszCatalogFilePath = ci.wszCatalogFile;
371  catalogInfo.pcwszMemberFilePath = Information->FileName;
372  catalogInfo.pcwszMemberTag = fileHashTag->Buffer;
373  catalogInfo.pbCalculatedFileHash = fileHash;
374  catalogInfo.cbCalculatedFileHash = fileHashLength;
375  catalogInfo.hCatAdmin = catAdminHandle;
376  verifyResult = PhpVerifyFile(Information, FileHandle, WTD_CHOICE_CATALOG, &catalogInfo, &DriverActionVerify, &verInfo, &signatures, &numberOfSignatures);
377 
378  if (verInfo.pcSignerCertContext)
379  CertFreeCertificateContext_I(verInfo.pcSignerCertContext);
380  }
381 
382  CryptCATAdminReleaseCatalogContext(catAdminHandle, catInfoHandle, 0);
383  }
384  else
385  {
386  // Search any user-supplied catalogs.
387 
388  for (i = 0; i < Information->NumberOfCatalogFileNames; i++)
389  {
390  PhFreeVerifySignatures(signatures, numberOfSignatures);
391 
392  catalogInfo.cbStruct = sizeof(catalogInfo);
393  catalogInfo.pcwszCatalogFilePath = Information->CatalogFileNames[i];
394  catalogInfo.pcwszMemberFilePath = Information->FileName;
395  catalogInfo.pcwszMemberTag = fileHashTag->Buffer;
396  catalogInfo.pbCalculatedFileHash = fileHash;
397  catalogInfo.cbCalculatedFileHash = fileHashLength;
398  catalogInfo.hCatAdmin = catAdminHandle;
399  verifyResult = PhpVerifyFile(Information, FileHandle, WTD_CHOICE_CATALOG, &catalogInfo, &WinTrustActionGenericVerifyV2, NULL, &signatures, &numberOfSignatures);
400 
401  if (verifyResult == VrTrusted)
402  break;
403  }
404  }
405 
406  PhDereferenceObject(fileHashTag);
407  PhFree(fileHash);
408  CryptCATAdminReleaseContext(catAdminHandle, 0);
409  }
410 
411  *Signatures = signatures;
412  *NumberOfSignatures = numberOfSignatures;
413 
414  return verifyResult;
415 }
416 
417 NTSTATUS PhVerifyFileEx(
418  _In_ PPH_VERIFY_FILE_INFO Information,
419  _Out_ VERIFY_RESULT *VerifyResult,
420  _Out_opt_ PCERT_CONTEXT **Signatures,
421  _Out_opt_ PULONG NumberOfSignatures
422  )
423 {
424  NTSTATUS status;
425  HANDLE fileHandle;
426  VERIFY_RESULT verifyResult;
427  PCERT_CONTEXT *signatures;
428  ULONG numberOfSignatures;
429  WINTRUST_FILE_INFO fileInfo = { 0 };
430 
431  if (PhBeginInitOnce(&PhpVerifyInitOnce))
432  {
433  PhpVerifyInitialization();
434  PhEndInitOnce(&PhpVerifyInitOnce);
435  }
436 
437  // Make sure we have successfully imported
438  // the required functions.
439  if (
446  !WinVerifyTrust_I ||
449  !CertNameToStr_I ||
452  )
453  return STATUS_NOT_SUPPORTED;
454 
455  if (!NT_SUCCESS(status = PhCreateFileWin32(
456  &fileHandle,
457  Information->FileName,
458  FILE_GENERIC_READ,
459  0,
460  FILE_SHARE_READ | FILE_SHARE_DELETE,
461  FILE_OPEN,
463  )))
464  return status;
465 
466  fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
467  fileInfo.pcwszFilePath = Information->FileName;
468  fileInfo.hFile = fileHandle;
469 
470  verifyResult = PhpVerifyFile(Information, fileHandle, WTD_CHOICE_FILE, &fileInfo, &WinTrustActionGenericVerifyV2, NULL, &signatures, &numberOfSignatures);
471 
472  if (verifyResult == VrNoSignature)
473  {
475  {
476  PhFreeVerifySignatures(signatures, numberOfSignatures);
477  verifyResult = PhpVerifyFileFromCatalog(Information, fileHandle, BCRYPT_SHA256_ALGORITHM, &signatures, &numberOfSignatures);
478  }
479 
480  if (verifyResult != VrTrusted)
481  {
482  PhFreeVerifySignatures(signatures, numberOfSignatures);
483  verifyResult = PhpVerifyFileFromCatalog(Information, fileHandle, NULL, &signatures, &numberOfSignatures);
484  }
485  }
486 
487  *VerifyResult = verifyResult;
488 
489  if (Signatures)
490  *Signatures = signatures;
491  else
492  PhFreeVerifySignatures(signatures, numberOfSignatures);
493 
494  if (NumberOfSignatures)
495  *NumberOfSignatures = numberOfSignatures;
496 
497  NtClose(fileHandle);
498 
499  return STATUS_SUCCESS;
500 }
501 
503  _In_ PCERT_CONTEXT *Signatures,
504  _In_ ULONG NumberOfSignatures
505  )
506 {
507  ULONG i;
508 
509  if (Signatures)
510  {
511  for (i = 0; i < NumberOfSignatures; i++)
512  CertFreeCertificateContext_I(Signatures[i]);
513 
514  PhFree(Signatures);
515  }
516 }
517 
519  _In_ PCERT_NAME_BLOB Blob
520  )
521 {
522  PPH_STRING string;
523  ULONG bufferSize;
524 
525  // CertNameToStr doesn't give us the correct buffer size unless we
526  // don't provide a buffer at all.
527  bufferSize = CertNameToStr_I(
528  X509_ASN_ENCODING,
529  Blob,
530  CERT_X500_NAME_STR,
531  NULL,
532  0
533  );
534 
535  string = PhCreateStringEx(NULL, bufferSize * sizeof(WCHAR));
537  X509_ASN_ENCODING,
538  Blob,
539  CERT_X500_NAME_STR,
540  string->Buffer,
541  bufferSize
542  );
543 
545 
546  return string;
547 }
548 
550  _In_ PPH_STRINGREF String,
551  _In_ PPH_STRINGREF KeyName
552  )
553 {
554  WCHAR keyNamePlusEqualsBuffer[10];
555  PH_STRINGREF keyNamePlusEquals;
556  SIZE_T keyNameLength;
557  PH_STRINGREF firstPart;
558  PH_STRINGREF remainingPart;
559 
560  keyNameLength = KeyName->Length / sizeof(WCHAR);
561  assert(!(keyNameLength > sizeof(keyNamePlusEquals) / sizeof(WCHAR) - 1));
562  keyNamePlusEquals.Buffer = keyNamePlusEqualsBuffer;
563  keyNamePlusEquals.Length = (keyNameLength + 1) * sizeof(WCHAR);
564 
565  memcpy(keyNamePlusEquals.Buffer, KeyName->Buffer, KeyName->Length);
566  keyNamePlusEquals.Buffer[keyNameLength] = '=';
567 
568  // Find "Key=".
569 
570  if (!PhSplitStringRefAtString(String, &keyNamePlusEquals, FALSE, &firstPart, &remainingPart))
571  return NULL;
572  if (remainingPart.Length == 0)
573  return NULL;
574 
575  // Is the value quoted? If so, return the part inside the quotes.
576  if (remainingPart.Buffer[0] == '"')
577  {
578  PhSkipStringRef(&remainingPart, sizeof(WCHAR));
579 
580  if (!PhSplitStringRefAtChar(&remainingPart, '"', &firstPart, &remainingPart))
581  return NULL;
582 
583  return PhCreateString2(&firstPart);
584  }
585  else
586  {
587  PhSplitStringRefAtChar(&remainingPart, ',', &firstPart, &remainingPart);
588 
589  return PhCreateString2(&firstPart);
590  }
591 }
592 
594  _In_ PCERT_CONTEXT Certificate
595  )
596 {
597  PCERT_INFO certInfo;
598  PH_STRINGREF keyName;
599  PPH_STRING name;
600  PPH_STRING value;
601 
602  // Cert context -> Cert info
603 
604  certInfo = Certificate->pCertInfo;
605 
606  if (!certInfo)
607  return NULL;
608 
609  // Cert info subject -> Subject X.500 string
610 
611  name = PhpGetCertNameString(&certInfo->Subject);
612 
613  // Subject X.500 string -> CN or OU value
614 
615  PhInitializeStringRef(&keyName, L"CN");
616  value = PhpGetX500Value(&name->sr, &keyName);
617 
618  if (!value)
619  {
620  PhInitializeStringRef(&keyName, L"OU");
621  value = PhpGetX500Value(&name->sr, &keyName);
622  }
623 
624  PhDereferenceObject(name);
625 
626  return value;
627 }
628 
642  _In_ PWSTR FileName,
643  _Out_opt_ PPH_STRING *SignerName
644  )
645 {
646  PH_VERIFY_FILE_INFO info = { 0 };
647  VERIFY_RESULT verifyResult;
648  PCERT_CONTEXT *signatures;
649  ULONG numberOfSignatures;
650 
651  info.FileName = FileName;
653 
654  if (NT_SUCCESS(PhVerifyFileEx(&info, &verifyResult, &signatures, &numberOfSignatures)))
655  {
656  if (SignerName)
657  {
658  *SignerName = NULL;
659 
660  if (numberOfSignatures != 0)
661  *SignerName = PhGetSignerNameFromCertificate(signatures[0]);
662  }
663 
664  PhFreeVerifySignatures(signatures, numberOfSignatures);
665  return verifyResult;
666  }
667  else
668  {
669  if (SignerName)
670  *SignerName = NULL;
671 
672  return VrNoSignature;
673  }
674 }