├── .gitignore ├── tests ├── src │ ├── Main.cpp │ └── src │ │ ├── native_api_wreppers │ │ ├── CryptoApiWrapperTests.cpp │ │ ├── TrustVerifyWrapperTests.cpp │ │ └── HashApiWrapperTests.cpp │ │ └── PeSignatureVerifierTests.cpp ├── tests.vcxproj.user ├── tests.vcxproj.filters └── tests.vcxproj ├── command-line-tool ├── src │ ├── Main.cpp │ ├── cliUtils.h │ └── cliUtils.cpp ├── command-line-tool.vcxproj.filters ├── command-line-tool.vcxproj.user └── command-line-tool.vcxproj ├── pe-signature-utils ├── pe-signature-utils.vcxproj.user ├── src │ ├── certificate_info │ │ ├── SignerInfo.h │ │ ├── TimestampCertificateInfo.h │ │ ├── CertificateInfoBase.h │ │ └── TimestampCertificateInfo.cpp │ ├── native_api_wrappers │ │ ├── HashApiWrapper.h │ │ ├── TrustVerifyWrapper.h │ │ ├── CryptoApiWrapper.h │ │ ├── HashApiWrapper.cpp │ │ ├── TrustVerifyWrapper.cpp │ │ └── CryptoApiWrapper.cpp │ ├── PeSignatureVerifier.cpp │ └── PeSignatureVerifier.h ├── pe-signature-utils.vcxproj.filters └── pe-signature-utils.vcxproj ├── LICENSE ├── README.md └── windows-pe-signature-verifying.sln /.gitignore: -------------------------------------------------------------------------------- 1 | x64 2 | Debug 3 | Release 4 | .vs 5 | bin 6 | -------------------------------------------------------------------------------- /tests/src/Main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" -------------------------------------------------------------------------------- /tests/src/src/native_api_wreppers/CryptoApiWrapperTests.cpp: -------------------------------------------------------------------------------- 1 | #include "./../../catch.hpp" 2 | -------------------------------------------------------------------------------- /tests/src/src/native_api_wreppers/TrustVerifyWrapperTests.cpp: -------------------------------------------------------------------------------- 1 | #include "./../../catch.hpp" 2 | -------------------------------------------------------------------------------- /command-line-tool/src/Main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konstantin89/windows-pe-signature-verifying/HEAD/command-line-tool/src/Main.cpp -------------------------------------------------------------------------------- /command-line-tool/src/cliUtils.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konstantin89/windows-pe-signature-verifying/HEAD/command-line-tool/src/cliUtils.h -------------------------------------------------------------------------------- /command-line-tool/src/cliUtils.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/konstantin89/windows-pe-signature-verifying/HEAD/command-line-tool/src/cliUtils.cpp -------------------------------------------------------------------------------- /tests/tests.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pe-signature-utils/pe-signature-utils.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pe-signature-utils/src/certificate_info/SignerInfo.h: -------------------------------------------------------------------------------- 1 | #ifndef __SIGNER_INFO_H 2 | #define __SIGNER_INFO_H 3 | 4 | #include 5 | #include "CertificateInfoBase.h" 6 | 7 | class SignerInfo : public CertificateInfoBase 8 | { 9 | 10 | public: 11 | 12 | using SignerInfoPtr = std::shared_ptr; 13 | 14 | SignerInfo() { /* EMPTY*/ }; 15 | virtual ~SignerInfo() { /* EMPTY*/ }; 16 | }; 17 | 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /pe-signature-utils/src/native_api_wrappers/HashApiWrapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class HashApiWrapper 7 | { 8 | public: 9 | 10 | /** 11 | * 12 | */ 13 | static DWORD CalculateFileHash( 14 | std::wstring aFileName, 15 | std::wstring aHashType, 16 | std::wstring& aHashWstr); 17 | 18 | /** 19 | * 20 | */ 21 | static std::wstring ByteHashIntoWstring( 22 | BYTE* aHash, 23 | size_t aHashLen); 24 | 25 | }; -------------------------------------------------------------------------------- /pe-signature-utils/src/certificate_info/TimestampCertificateInfo.h: -------------------------------------------------------------------------------- 1 | #ifndef __TIMESTAMP_CERTIFICATE_INFO_H 2 | #define __TIMESTAMP_CERTIFICATE_INFO_H 3 | 4 | #include "CertificateInfoBase.h" 5 | #include 6 | #include 7 | 8 | class TimestampCertificateInfo : public CertificateInfoBase 9 | { 10 | 11 | public: 12 | 13 | using TimmeStampCertPtr = std::shared_ptr; 14 | 15 | TimestampCertificateInfo(); 16 | virtual ~TimestampCertificateInfo(); 17 | virtual void PrintCertificateInfo() override; 18 | std::wstring GetDateAsWstr(); 19 | 20 | public: 21 | std::shared_ptr dateOfTimeStamp; 22 | }; 23 | 24 | #endif -------------------------------------------------------------------------------- /pe-signature-utils/src/certificate_info/CertificateInfoBase.h: -------------------------------------------------------------------------------- 1 | #ifndef __CERTIFICATE_INFO_BASE_H 2 | #define __CERTIFICATE_INFO_BASE_H 3 | 4 | #include 5 | #include 6 | 7 | class CertificateInfoBase 8 | { 9 | 10 | public: 11 | CertificateInfoBase() {}; 12 | virtual ~CertificateInfoBase() {}; 13 | 14 | virtual void PrintCertificateInfo() 15 | { 16 | std::wcout << "Serial number: " << serialNumber.c_str() << std::endl; 17 | std::wcout << "Issuer name: " << issuerName.c_str() << std::endl; 18 | std::wcout << "Subject name: " << subjectName.c_str() << std::endl; 19 | std::wcout << "Signing algorithm: " << signAlgorithm.c_str() << std::endl; 20 | }; 21 | 22 | public: 23 | std::wstring serialNumber; 24 | std::wstring subjectName; 25 | std::wstring issuerName; 26 | std::wstring signAlgorithm; 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /pe-signature-utils/src/PeSignatureVerifier.cpp: -------------------------------------------------------------------------------- 1 | #include "PeSignatureVerifier.h" 2 | 3 | DWORD PeSignatureVerifier::CheckFileSignature(std::wstring aPePath) 4 | { 5 | return TrustVerifyWrapper::CheckFileSignature(aPePath); 6 | } 7 | 8 | DWORD PeSignatureVerifier::GetCertificateInfo( 9 | std::wstring aFileName, 10 | SignerInfoPtr &aCertInfo) 11 | { 12 | return CryptoApiWrapper::GetCertificateInfo(aFileName, aCertInfo); 13 | } 14 | 15 | DWORD PeSignatureVerifier::GetTimestampCertificateInfo( 16 | std::wstring aFileName, 17 | TimeStampCertInfoPtr &aCertInfo) 18 | { 19 | return CryptoApiWrapper::GetTimestampCertificateInfo(aFileName, aCertInfo); 20 | } 21 | 22 | DWORD PeSignatureVerifier::CalculateFileHash( 23 | std::wstring aFileName, 24 | std::wstring aHashType, 25 | std::wstring& aHashWstr) 26 | { 27 | return HashApiWrapper::CalculateFileHash(aFileName, aHashType, aHashWstr); 28 | } -------------------------------------------------------------------------------- /command-line-tool/command-line-tool.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 6 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 7 | 8 | 9 | {d29329ef-ebe0-41ce-bab6-6d0ac1a77708} 10 | 11 | 12 | 13 | 14 | src 15 | 16 | 17 | src 18 | 19 | 20 | 21 | 22 | src 23 | 24 | 25 | -------------------------------------------------------------------------------- /pe-signature-utils/src/certificate_info/TimestampCertificateInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "TimestampCertificateInfo.h" 2 | #include 3 | 4 | TimestampCertificateInfo::TimestampCertificateInfo() 5 | { 6 | /* EMPTY */ 7 | } 8 | 9 | TimestampCertificateInfo::~TimestampCertificateInfo() 10 | { 11 | /* EMPTY */ 12 | } 13 | 14 | void TimestampCertificateInfo::PrintCertificateInfo() 15 | { 16 | auto lDateWstr = GetDateAsWstr(); 17 | 18 | CertificateInfoBase::PrintCertificateInfo(); 19 | std::wcout << L"Date of timestamp: " << lDateWstr.c_str() << std::endl; 20 | } 21 | 22 | std::wstring TimestampCertificateInfo::GetDateAsWstr() 23 | { 24 | if (dateOfTimeStamp == NULL) { 25 | return L""; 26 | } 27 | 28 | const int lBufSize = 100; 29 | wchar_t lStrBuf[lBufSize]; 30 | 31 | int lDateStrLen = swprintf( 32 | lStrBuf, 33 | lBufSize, 34 | L"%02d/%02d/%04d %02d:%02d", 35 | dateOfTimeStamp->wDay, 36 | dateOfTimeStamp->wMonth, 37 | dateOfTimeStamp->wYear, 38 | dateOfTimeStamp->wHour, 39 | dateOfTimeStamp->wMinute); 40 | 41 | return std::wstring(lStrBuf, lDateStrLen); 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Kosta Bulgakov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /command-line-tool/command-line-tool.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | C:\WIndows\explorer.exe 5 | WindowsLocalDebugger 6 | 7 | 8 | C:\WIndows\explorer.exe 9 | WindowsLocalDebugger 10 | 11 | 12 | C:\WIndows\explorer.exe 13 | WindowsLocalDebugger 14 | 15 | 16 | C:\WIndows\explorer.exe 17 | WindowsLocalDebugger 18 | 19 | -------------------------------------------------------------------------------- /pe-signature-utils/src/native_api_wrappers/TrustVerifyWrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef __TRUST_VERIFY_WRAPPER_H 2 | #define __TRUST_VERIFY_WRAPPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class TrustVerifyWrapper 12 | { 13 | public: 14 | 15 | /** 16 | * @brief: Check if the given file is signed with valid certificate. 17 | * @returns: ERROR_SUCCESS iff the PE's signature is verified, othervise returns error code. 18 | */ 19 | static DWORD CheckFileSignature(std::wstring aPePath); 20 | 21 | private: 22 | 23 | /** 24 | * @brief: Try to verify PE from signature embeeded in it. 25 | * @param: aPePath - Path of the PE to verify. 26 | */ 27 | static DWORD verifyFromFile(std::wstring aPePath); 28 | 29 | /** 30 | * @param: aPePath - Path of the PE to verify. 31 | * @param: aCatalogHashAlgo - Hash algorithm used by Windows signature catalogue system. 32 | * Note that Windows 8 and 10 use SHA256 in theie catalogues, 33 | * while older versions may use SHA1. 34 | * For additional information please visit the attached link. 35 | * 36 | *@link: https://stackoverflow.com/questions/26216789/getting-digital-signature-from-mmc-exe-at-windows-8 37 | * 38 | */ 39 | static DWORD verifyFromCatalog( 40 | std::wstring aPePath, 41 | std::wstring aCatalogHashAlgo); 42 | 43 | static DWORD verifyTrustFromCatObject( 44 | HCATINFO aCatInfo, 45 | std::wstring aFileName, 46 | std::wstring aHash); 47 | 48 | }; 49 | 50 | #endif -------------------------------------------------------------------------------- /tests/tests.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 6 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 7 | 8 | 9 | {664da5ef-56d7-4ca3-89d8-6a013fb9a777} 10 | 11 | 12 | {82dff4ba-cacb-4c0f-80b6-3810ecdcc7cd} 13 | 14 | 15 | {baeea470-e6aa-49e8-8052-e515baddb988} 16 | 17 | 18 | 19 | 20 | src 21 | 22 | 23 | 24 | 25 | src 26 | 27 | 28 | src\src 29 | 30 | 31 | src\src\native_api_wrappers 32 | 33 | 34 | src\src\native_api_wrappers 35 | 36 | 37 | src\src\native_api_wrappers 38 | 39 | 40 | -------------------------------------------------------------------------------- /pe-signature-utils/src/PeSignatureVerifier.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include ".\certificate_info\SignerInfo.h" 4 | #include ".\native_api_wrappers\CryptoApiWrapper.h" 5 | #include ".\native_api_wrappers\HashApiWrapper.h" 6 | #include ".\native_api_wrappers\TrustVerifyWrapper.h" 7 | 8 | class PeSignatureVerifier 9 | { 10 | public: 11 | 12 | using SignerInfoPtr = CryptoApiWrapper::SignerInfoPtr; 13 | using TimeStampCertInfoPtr = CryptoApiWrapper::TimeStampCertInfoPtr; 14 | 15 | /** 16 | * @brief: Check if the given file is signed with valid certificate. 17 | * @returns: ERROR_SUCCESS iff the PE's signature is verified, otherwise returns error code. 18 | */ 19 | static DWORD CheckFileSignature( 20 | __in std::wstring aPePath); 21 | 22 | /** 23 | * @brief: Calculate hash for a given file. 24 | * 25 | * @param: aFileName - Full path of file for hash calculation. 26 | * @param: aHashType - Hash algorithm to use. For example L"SHA256". 27 | * @param: aHashWstr - Wstring containing the calculated hash. 28 | * 29 | * @returns: ERROR_SUCCESS iff call is successful, otherwise returns error code. 30 | * 31 | */ 32 | static DWORD CalculateFileHash( 33 | __in std::wstring aFileName, 34 | __in std::wstring aHashType, 35 | __out std::wstring& aHashWstr); 36 | 37 | /** 38 | * @brief: Get info about the certificate used to sign the file. 39 | * 40 | * @returns: ERROR_SUCCESS iff call is successful, otherwise returns error code. 41 | * 42 | */ 43 | static DWORD GetCertificateInfo( 44 | __in std::wstring aFileName, 45 | __out SignerInfoPtr &aCertInfo); 46 | 47 | /** 48 | * @brief: Get info about the time stamp certificate used to sign the file. 49 | * 50 | * @returns: ERROR_SUCCESS iff call is successful, otherwise returns error code. 51 | * 52 | */ 53 | static DWORD GetTimestampCertificateInfo( 54 | std::wstring aFileName, 55 | TimeStampCertInfoPtr &aCertInfo); 56 | }; 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # windows-pe-signature-verifying 2 | 3 | ## Brief 4 | Library that used to verify PE files signatures and get certificate info 5 | 6 | ## Projects 7 | **command-line-tool**
8 | **pe-signature-utils**
9 | **tests**
10 | 11 | ## Links 12 | [WinVerifyTrust](https://docs.microsoft.com/en-us/windows/desktop/api/wintrust/nf-wintrust-winverifytrust)
13 | [Certificate and Trust return values](https://docs.microsoft.com/en-us/windows/desktop/seccrypto/certificate-and-trust-return-values)
14 | [Windows 8 and 10 signature hash algorithm](https://stackoverflow.com/questions/26216789/getting-digital-signature-from-mmc-exe-at-windows-8)
15 | [Forum discussion about PE signature info](http://qaru.site/questions/7338503/amended-code-to-retrieve-dual-signature-information-from-pe-executable-in-windows)
16 | 17 | 18 | ## Usage 19 | 20 | ### command-line-tool 21 | 22 | Example usage:
23 |   `command-line-tool.exe "C:\\Program Files\\Mozilla Firefox\\firefox.exe"` 24 | 25 | Example output:
26 |   `File name: C:\Program Files\Mozilla Firefox\firefox.exe`
27 |   `Verified: Signed`
28 |   `SHA256: 7AF330A6446D56457BA9E90FFF0418A589E26385566BD7AF8F28578E3210C553`
29 |   `Serial number: 0c5396dcb2949c70fac48ab08a07338e`
30 |   `Issuer name: DigiCert SHA2 Assured ID Code Signing CA`
31 |   `Subject name: Mozilla Corporation`
32 |   `Signing algorithm: sha256RSA`
33 |   `Signing date: 08/01/2019 10:01`
34 | 35 | ### pe-signature-utils 36 |   Generates static library.
37 |   The public API is specified in **src/PeSignatureVerifier.h** header.
38 | 39 | ### tests 40 |   Generates binary of tests for pe-signature-utils.
41 | 42 | ## Todos 43 | Dual signatures 44 | 45 | 46 | -------------------------------------------------------------------------------- /pe-signature-utils/src/native_api_wrappers/CryptoApiWrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef __CRYPTO_API_WRAPPER_H 2 | #define __CRYPTO_API_WRAPPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include ".\..\certificate_info\SignerInfo.h" 10 | #include ".\..\certificate_info\\TimestampCertificateInfo.h" 11 | 12 | #pragma comment(lib, "crypt32.lib") 13 | 14 | class CryptoApiWrapper 15 | { 16 | public: 17 | 18 | using SignerInfoPtr = SignerInfo::SignerInfoPtr; 19 | using TimeStampCertInfoPtr = TimestampCertificateInfo::TimmeStampCertPtr; 20 | 21 | /** 22 | * 23 | */ 24 | static DWORD GetCertificateInfo( 25 | std::wstring aFileName, 26 | SignerInfoPtr &aCertInfo); 27 | 28 | /** 29 | * 30 | */ 31 | static DWORD GetTimestampCertificateInfo( 32 | std::wstring aFileName, 33 | TimeStampCertInfoPtr &aCertInfo); 34 | 35 | private: 36 | 37 | static DWORD queryCertificateInfo( 38 | PCCERT_CONTEXT aCertContext, 39 | DWORD aType, 40 | std::wstring &aOutputName); 41 | 42 | static DWORD getSignatureAlgoWstring( 43 | CRYPT_ALGORITHM_IDENTIFIER* pSigAlgo, 44 | std::wstring &signatureAlgo); 45 | 46 | static DWORD getCertificateContext( 47 | std::shared_ptr aSignerInfo, 48 | HCERTSTORE aCertStore, 49 | PCCERT_CONTEXT &aCertContextPtr); 50 | 51 | static DWORD getTimeStampSignerInfo( 52 | std::shared_ptr &aSignerInfo, 53 | std::shared_ptr &aCounterSignerInfo); 54 | 55 | static DWORD getCertificateSerialNumber( 56 | PCCERT_CONTEXT aCertContext, 57 | std::wstring &aSerialNumberWstr); 58 | 59 | static DWORD getSignerInfo( 60 | std::wstring aFileName, 61 | std::shared_ptr &aSignerInfo, 62 | HCERTSTORE &aCertStore); 63 | 64 | static bool getDateOfTimeStamp( 65 | std::shared_ptr &aSignerInfo, 66 | std::shared_ptr &aSysTime); 67 | 68 | }; 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /pe-signature-utils/src/native_api_wrappers/HashApiWrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "HashApiWrapper.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | DWORD HashApiWrapper::CalculateFileHash( 8 | std::wstring aFileName, 9 | std::wstring aHashType, 10 | std::wstring& aHashWstr) 11 | { 12 | GUID WintrustVerifyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2; 13 | GUID DriverActionGuid = DRIVER_ACTION_VERIFY; 14 | HANDLE hFile; 15 | DWORD dwHash; 16 | BYTE bHash[100]; 17 | HCATINFO hCatInfo = NULL; 18 | HCATADMIN hCatAdmin; 19 | 20 | hFile = CreateFileW(aFileName.c_str(), 21 | GENERIC_READ, 22 | FILE_SHARE_READ, 23 | NULL, 24 | OPEN_EXISTING, 25 | FILE_ATTRIBUTE_NORMAL, 26 | NULL); 27 | 28 | if (hFile == INVALID_HANDLE_VALUE) 29 | { 30 | return GetLastError(); 31 | } 32 | 33 | if (!CryptCATAdminAcquireContext2( 34 | &hCatAdmin, 35 | &DriverActionGuid, 36 | aHashType.c_str(), 37 | NULL, 38 | 0)) 39 | { 40 | CloseHandle(hFile); 41 | return GetLastError(); 42 | } 43 | 44 | dwHash = sizeof(bHash); 45 | if (!CryptCATAdminCalcHashFromFileHandle2( 46 | hCatAdmin, 47 | hFile, 48 | &dwHash, 49 | bHash, 50 | 0)) 51 | { 52 | CloseHandle(hFile); 53 | return GetLastError(); 54 | } 55 | 56 | CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0); 57 | CryptCATAdminReleaseContext(hCatAdmin, 0); 58 | CloseHandle(hFile); 59 | 60 | aHashWstr = ByteHashIntoWstring(bHash, dwHash); 61 | 62 | return ERROR_SUCCESS; 63 | } 64 | 65 | std::wstring HashApiWrapper::ByteHashIntoWstring(BYTE* aHash, size_t aHashLen) 66 | { 67 | if (!aHash || !aHashLen) 68 | { 69 | return L""; 70 | } 71 | 72 | auto lHashString = new WCHAR[aHashLen * 2 + 1]; 73 | 74 | for (DWORD dw = 0; dw < aHashLen; ++dw) 75 | { 76 | wsprintfW(&lHashString[dw * 2], L"%02X", aHash[dw]); 77 | } 78 | 79 | std::wstring lHashWstr(lHashString); 80 | 81 | delete[] lHashString; 82 | 83 | return lHashWstr; 84 | } -------------------------------------------------------------------------------- /tests/src/src/PeSignatureVerifierTests.cpp: -------------------------------------------------------------------------------- 1 | #include "./../catch.hpp" 2 | #include "./../src/PeSignatureVerifier.h" 3 | 4 | #define WINDOWS_EXPLORER_PE_WSTR L"C:\\Windows\\explorer.exe" 5 | 6 | TEST_CASE("Ensure that explorer.exe is trusted PE", "[windows]") 7 | { 8 | auto lResult = PeSignatureVerifier::CheckFileSignature( 9 | WINDOWS_EXPLORER_PE_WSTR); 10 | 11 | REQUIRE(lResult == ERROR_SUCCESS); 12 | } 13 | 14 | TEST_CASE("Try to check if current running exe is signed", "[GetModuleFileName]") 15 | { 16 | wchar_t lCurrentExePath[MAX_PATH]; 17 | GetModuleFileName(NULL, lCurrentExePath, MAX_PATH); 18 | 19 | bool lResult = PeSignatureVerifier::CheckFileSignature(lCurrentExePath); 20 | 21 | REQUIRE(lResult != ERROR_SUCCESS); 22 | } 23 | 24 | TEST_CASE("Try to scan file with invalid name", "[invalid]") 25 | { 26 | auto lResult = PeSignatureVerifier::CheckFileSignature(L"INVALID_FILE_NAME"); 27 | 28 | REQUIRE(lResult != ERROR_SUCCESS); 29 | } 30 | 31 | TEST_CASE("Try to get certificate info for signed explorer.exe", "[exproler]") 32 | { 33 | 34 | PeSignatureVerifier::SignerInfoPtr lSignerInfo; 35 | 36 | auto lResult = PeSignatureVerifier::GetCertificateInfo(WINDOWS_EXPLORER_PE_WSTR, lSignerInfo); 37 | 38 | REQUIRE(lResult == ERROR_SUCCESS); 39 | 40 | REQUIRE(lSignerInfo->issuerName.empty() == false); 41 | REQUIRE(lSignerInfo->serialNumber.empty() == false); 42 | REQUIRE(lSignerInfo->signAlgorithm.empty() == false); 43 | REQUIRE(lSignerInfo->subjectName.empty() == false); 44 | } 45 | 46 | TEST_CASE("Try to get time stamp certificate info for signed explorer.exe", "[exproler]") 47 | { 48 | /* 49 | 50 | PeSignatureVerifier::TimeStampCertInfoPtr lSignerInfo; 51 | 52 | auto lResult = PeSignatureVerifier::GetTimestampCertificateInfo(WINDOWS_EXPLORER_PE_WSTR, lSignerInfo); 53 | 54 | REQUIRE(lResult == ERROR_SUCCESS); 55 | 56 | REQUIRE(lSignerInfo->issuerName.empty() == false); 57 | REQUIRE(lSignerInfo->serialNumber.empty() == false); 58 | REQUIRE(lSignerInfo->signAlgorithm.empty() == false); 59 | REQUIRE(lSignerInfo->subjectName.empty() == false); 60 | REQUIRE(lSignerInfo->GetDateAsWstr().empty() == false); 61 | 62 | */ 63 | } -------------------------------------------------------------------------------- /tests/src/src/native_api_wreppers/HashApiWrapperTests.cpp: -------------------------------------------------------------------------------- 1 | #include "./../../catch.hpp" 2 | #include "native_api_wrappers/HashApiWrapper.h" 3 | 4 | #define CORRECT_SHA256_HASH_LENGTH 64 5 | 6 | #include 7 | 8 | TEST_CASE("Calculate sha256 for explorer", "[windows, explorer]") 9 | { 10 | std::wstring lSha256Wstr; 11 | std::wstring lExplorerPath = L"c:\\windows\\explorer.exe"; 12 | 13 | auto lRetVal = HashApiWrapper::CalculateFileHash( 14 | lExplorerPath, 15 | L"SHA256", 16 | lSha256Wstr); 17 | 18 | REQUIRE(lRetVal == ERROR_SUCCESS); 19 | REQUIRE(lSha256Wstr.length() == CORRECT_SHA256_HASH_LENGTH); 20 | } 21 | 22 | TEST_CASE("Error case - invalid file name", "[error]") 23 | { 24 | std::wstring lSha256Wstr; 25 | std::wstring lExplorerPath = L"f:\\invalid.sys"; 26 | 27 | auto lRetVal = HashApiWrapper::CalculateFileHash( 28 | lExplorerPath, 29 | L"SHA256", 30 | lSha256Wstr); 31 | 32 | REQUIRE(lRetVal == ERROR_PATH_NOT_FOUND); 33 | REQUIRE(lSha256Wstr.empty() == true); 34 | } 35 | 36 | TEST_CASE("Error case - invalid hash algorithm", "[error]") 37 | { 38 | std::wstring lSha256Wstr; 39 | std::wstring lExplorerPath = L"c:\\windows\\explorer.exe"; 40 | 41 | auto lRetVal = HashApiWrapper::CalculateFileHash( 42 | lExplorerPath, 43 | L"SHA666", 44 | lSha256Wstr); 45 | 46 | REQUIRE(lRetVal == NTE_BAD_ALGID); 47 | REQUIRE(lSha256Wstr.empty() == true); 48 | } 49 | 50 | TEST_CASE("Test ByteHashIntoWstring - One byte hash", "[ByteHashIntoWstring]") 51 | { 52 | const size_t lBytesCount = 1; 53 | BYTE lByte[lBytesCount] = {0xab}; 54 | 55 | std::wstring lWstr = HashApiWrapper::ByteHashIntoWstring(lByte, lBytesCount); 56 | 57 | REQUIRE(lWstr == L"AB"); 58 | } 59 | 60 | TEST_CASE("Test ByteHashIntoWstring - NULL hash", "[ByteHashIntoWstring]") 61 | { 62 | std::wstring lWstr = HashApiWrapper::ByteHashIntoWstring(NULL, 0); 63 | 64 | REQUIRE(lWstr == L""); 65 | } 66 | 67 | TEST_CASE("Test ByteHashIntoWstring - hash of all hex characters", "[ByteHashIntoWstring]") 68 | { 69 | const size_t lBytesCount = 8; 70 | BYTE lByte[lBytesCount] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; 71 | 72 | std::wstring lWstr = HashApiWrapper::ByteHashIntoWstring(lByte, lBytesCount); 73 | 74 | REQUIRE(lWstr == L"0123456789ABCDEF"); 75 | } 76 | -------------------------------------------------------------------------------- /pe-signature-utils/pe-signature-utils.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 6 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 7 | 8 | 9 | {7895877e-5f96-40c0-b7a4-c2258765e152} 10 | 11 | 12 | {df01525a-ce2d-4055-b962-74b15e79ee62} 13 | 14 | 15 | {bc4b7df8-40f4-4cf4-b697-e067967d60f3} 16 | 17 | 18 | 19 | 20 | src 21 | 22 | 23 | src\certificate_info 24 | 25 | 26 | src\certificate_info 27 | 28 | 29 | src\certificate_info 30 | 31 | 32 | src\native_api_wrappers 33 | 34 | 35 | src\native_api_wrappers 36 | 37 | 38 | src\native_api_wrappers 39 | 40 | 41 | 42 | 43 | src 44 | 45 | 46 | src\certificate_info 47 | 48 | 49 | src\native_api_wrappers 50 | 51 | 52 | src\native_api_wrappers 53 | 54 | 55 | src\native_api_wrappers 56 | 57 | 58 | -------------------------------------------------------------------------------- /windows-pe-signature-verifying.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2003 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "command-line-tool", "command-line-tool\command-line-tool.vcxproj", "{C008CD30-0AA2-441C-BD7A-3C3EE76FA537}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {F845ACF5-073A-4D6C-978B-F32D9C3D304D} = {F845ACF5-073A-4D6C-978B-F32D9C3D304D} 9 | EndProjectSection 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pe-signature-utils", "pe-signature-utils\pe-signature-utils.vcxproj", "{F845ACF5-073A-4D6C-978B-F32D9C3D304D}" 12 | EndProject 13 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests\tests.vcxproj", "{D31131D2-E4B6-4C40-8F6D-673B561AA323}" 14 | ProjectSection(ProjectDependencies) = postProject 15 | {F845ACF5-073A-4D6C-978B-F32D9C3D304D} = {F845ACF5-073A-4D6C-978B-F32D9C3D304D} 16 | EndProjectSection 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F3FBB888-6B86-489E-9FF5-586CA4CF3830}" 19 | ProjectSection(SolutionItems) = preProject 20 | README.md = README.md 21 | EndProjectSection 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Debug|x64 = Debug|x64 26 | Debug|x86 = Debug|x86 27 | Release|x64 = Release|x64 28 | Release|x86 = Release|x86 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {C008CD30-0AA2-441C-BD7A-3C3EE76FA537}.Debug|x64.ActiveCfg = Debug|x64 32 | {C008CD30-0AA2-441C-BD7A-3C3EE76FA537}.Debug|x64.Build.0 = Debug|x64 33 | {C008CD30-0AA2-441C-BD7A-3C3EE76FA537}.Debug|x86.ActiveCfg = Debug|Win32 34 | {C008CD30-0AA2-441C-BD7A-3C3EE76FA537}.Debug|x86.Build.0 = Debug|Win32 35 | {C008CD30-0AA2-441C-BD7A-3C3EE76FA537}.Release|x64.ActiveCfg = Release|x64 36 | {C008CD30-0AA2-441C-BD7A-3C3EE76FA537}.Release|x64.Build.0 = Release|x64 37 | {C008CD30-0AA2-441C-BD7A-3C3EE76FA537}.Release|x86.ActiveCfg = Release|Win32 38 | {C008CD30-0AA2-441C-BD7A-3C3EE76FA537}.Release|x86.Build.0 = Release|Win32 39 | {F845ACF5-073A-4D6C-978B-F32D9C3D304D}.Debug|x64.ActiveCfg = Debug|x64 40 | {F845ACF5-073A-4D6C-978B-F32D9C3D304D}.Debug|x64.Build.0 = Debug|x64 41 | {F845ACF5-073A-4D6C-978B-F32D9C3D304D}.Debug|x86.ActiveCfg = Debug|Win32 42 | {F845ACF5-073A-4D6C-978B-F32D9C3D304D}.Debug|x86.Build.0 = Debug|Win32 43 | {F845ACF5-073A-4D6C-978B-F32D9C3D304D}.Release|x64.ActiveCfg = Release|x64 44 | {F845ACF5-073A-4D6C-978B-F32D9C3D304D}.Release|x64.Build.0 = Release|x64 45 | {F845ACF5-073A-4D6C-978B-F32D9C3D304D}.Release|x86.ActiveCfg = Release|Win32 46 | {F845ACF5-073A-4D6C-978B-F32D9C3D304D}.Release|x86.Build.0 = Release|Win32 47 | {D31131D2-E4B6-4C40-8F6D-673B561AA323}.Debug|x64.ActiveCfg = Debug|x64 48 | {D31131D2-E4B6-4C40-8F6D-673B561AA323}.Debug|x64.Build.0 = Debug|x64 49 | {D31131D2-E4B6-4C40-8F6D-673B561AA323}.Debug|x86.ActiveCfg = Debug|Win32 50 | {D31131D2-E4B6-4C40-8F6D-673B561AA323}.Debug|x86.Build.0 = Debug|Win32 51 | {D31131D2-E4B6-4C40-8F6D-673B561AA323}.Release|x64.ActiveCfg = Release|x64 52 | {D31131D2-E4B6-4C40-8F6D-673B561AA323}.Release|x64.Build.0 = Release|x64 53 | {D31131D2-E4B6-4C40-8F6D-673B561AA323}.Release|x86.ActiveCfg = Release|Win32 54 | {D31131D2-E4B6-4C40-8F6D-673B561AA323}.Release|x86.Build.0 = Release|Win32 55 | EndGlobalSection 56 | GlobalSection(SolutionProperties) = preSolution 57 | HideSolutionNode = FALSE 58 | EndGlobalSection 59 | GlobalSection(ExtensibilityGlobals) = postSolution 60 | SolutionGuid = {93B46A5B-A029-467F-A7C5-4F91A8853A85} 61 | EndGlobalSection 62 | EndGlobal 63 | -------------------------------------------------------------------------------- /pe-signature-utils/src/native_api_wrappers/TrustVerifyWrapper.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "TrustVerifyWrapper.h" 3 | #include "HashApiWrapper.h" 4 | 5 | #pragma comment(lib, "wintrust") 6 | 7 | #define SHA256 L"SHA256" 8 | 9 | DWORD TrustVerifyWrapper::CheckFileSignature(std::wstring aPePath) 10 | { 11 | // Try to find embeeded signature in the given PE. 12 | if (verifyFromFile(aPePath) == ERROR_SUCCESS) 13 | { 14 | return ERROR_SUCCESS; 15 | } 16 | 17 | // Calculate the hash for the given PE and look for in Windows catalogs. 18 | return verifyFromCatalog(aPePath, SHA256); 19 | } 20 | 21 | DWORD TrustVerifyWrapper::verifyFromFile(std::wstring aPePath) 22 | { 23 | GUID WintrustVerifyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2; 24 | GUID DriverActionGuid = DRIVER_ACTION_VERIFY; 25 | 26 | WINTRUST_DATA wd = { 0 }; 27 | WINTRUST_FILE_INFO wfi = { 0 }; 28 | WINTRUST_CATALOG_INFO wci = { 0 }; 29 | 30 | ////set up structs to verify files with cert signatures 31 | memset(&wfi, 0, sizeof(wfi)); 32 | wfi.cbStruct = sizeof(WINTRUST_FILE_INFO); 33 | wfi.pcwszFilePath = aPePath.c_str(); 34 | wfi.hFile = NULL; 35 | wfi.pgKnownSubject = NULL; 36 | 37 | memset(&wd, 0, sizeof(wd)); 38 | wd.cbStruct = sizeof(WINTRUST_DATA); 39 | wd.dwUnionChoice = WTD_CHOICE_FILE; 40 | wd.pFile = &wfi; 41 | wd.dwUIChoice = WTD_UI_NONE; 42 | wd.fdwRevocationChecks = WTD_REVOKE_NONE; 43 | wd.dwStateAction = 0; 44 | wd.dwProvFlags = WTD_SAFER_FLAG; 45 | wd.hWVTStateData = NULL; 46 | wd.pwszURLReference = NULL; 47 | wd.pPolicyCallbackData = NULL; 48 | wd.pSIPClientData = NULL; 49 | wd.dwUIContext = 0; 50 | 51 | return WinVerifyTrust(NULL, &WintrustVerifyGuid, &wd); 52 | } 53 | 54 | DWORD TrustVerifyWrapper::verifyFromCatalog( 55 | std::wstring aPePath, 56 | std::wstring aCatalogHashAlgo) 57 | { 58 | LONG lStatus = TRUST_E_NOSIGNATURE; 59 | GUID WintrustVerifyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2; 60 | GUID DriverActionGuid = DRIVER_ACTION_VERIFY; 61 | HANDLE hFile; 62 | DWORD dwHash; 63 | BYTE bHash[100]; 64 | HCATINFO hCatInfo = NULL; 65 | HCATADMIN hCatAdmin; 66 | 67 | hFile = CreateFileW(aPePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 68 | if (hFile == INVALID_HANDLE_VALUE) 69 | { 70 | return GetLastError(); 71 | } 72 | 73 | if (!CryptCATAdminAcquireContext2( 74 | &hCatAdmin, 75 | &DriverActionGuid, 76 | aCatalogHashAlgo.c_str(), 77 | NULL, 78 | 0)) 79 | { 80 | CloseHandle(hFile); 81 | return GetLastError(); 82 | } 83 | 84 | dwHash = sizeof(bHash); 85 | if (!CryptCATAdminCalcHashFromFileHandle2( 86 | hCatAdmin, 87 | hFile, 88 | &dwHash, 89 | bHash, 90 | 0)) 91 | { 92 | CloseHandle(hFile); 93 | return GetLastError(); 94 | } 95 | 96 | auto lHashWstr = HashApiWrapper::ByteHashIntoWstring(bHash, dwHash); 97 | 98 | /* 99 | * Find the calalogue that contains hash of our file. 100 | * Note that CryptCATAdminEnumCatalogFromHash gives you 101 | * the ability to iterate over all the catalogues that are 102 | * containing your hash. 103 | */ 104 | hCatInfo = CryptCATAdminEnumCatalogFromHash(hCatAdmin, bHash, dwHash, 0, NULL); 105 | 106 | if (!hCatInfo) 107 | { 108 | CryptCATAdminReleaseContext(hCatAdmin, 0); 109 | CloseHandle(hFile); 110 | return GetLastError(); 111 | } 112 | 113 | lStatus = verifyTrustFromCatObject(hCatInfo, aPePath, lHashWstr); 114 | 115 | CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0); 116 | CryptCATAdminReleaseContext(hCatAdmin, 0); 117 | CloseHandle(hFile); 118 | 119 | return lStatus; 120 | 121 | } 122 | 123 | 124 | DWORD TrustVerifyWrapper::verifyTrustFromCatObject( 125 | HCATINFO aCatInfo, 126 | std::wstring aFileName, 127 | std::wstring aHash) 128 | { 129 | GUID WintrustVerifyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2; 130 | 131 | WINTRUST_DATA wd = { 0 }; 132 | WINTRUST_CATALOG_INFO wci = { 0 }; 133 | 134 | CATALOG_INFO ci = { 0 }; 135 | CryptCATCatalogInfoFromContext(aCatInfo, &ci, 0); 136 | 137 | memset(&wci, 0, sizeof(wci)); 138 | wci.cbStruct = sizeof(WINTRUST_CATALOG_INFO); 139 | wci.pcwszCatalogFilePath = ci.wszCatalogFile; 140 | wci.pcwszMemberFilePath = aFileName.c_str(); 141 | wci.pcwszMemberTag = aHash.c_str(); 142 | 143 | memset(&wd, 0, sizeof(wd)); 144 | wd.cbStruct = sizeof(WINTRUST_DATA); 145 | wd.fdwRevocationChecks = WTD_REVOKE_NONE; 146 | wd.dwUnionChoice = WTD_CHOICE_CATALOG; 147 | wd.pCatalog = &wci; 148 | wd.dwUIChoice = WTD_UI_NONE; 149 | wd.dwUIContext = WTD_UICONTEXT_EXECUTE; 150 | wd.fdwRevocationChecks = WTD_STATEACTION_VERIFY; 151 | wd.dwStateAction = WTD_STATEACTION_VERIFY; 152 | wd.dwProvFlags = 0; 153 | wd.hWVTStateData = NULL; 154 | wd.pwszURLReference = NULL; 155 | wd.pPolicyCallbackData = NULL; 156 | wd.pSIPClientData = NULL; 157 | wd.dwUIContext = 0; 158 | 159 | return WinVerifyTrust(NULL, &WintrustVerifyGuid, &wd); 160 | } 161 | -------------------------------------------------------------------------------- /pe-signature-utils/pe-signature-utils.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 15.0 39 | {F845ACF5-073A-4D6C-978B-F32D9C3D304D} 40 | Win32Proj 41 | pesignatureutils 42 | 10.0.17134.0 43 | 44 | 45 | 46 | StaticLibrary 47 | true 48 | v141 49 | Unicode 50 | 51 | 52 | StaticLibrary 53 | false 54 | v141 55 | true 56 | Unicode 57 | 58 | 59 | StaticLibrary 60 | true 61 | v141 62 | Unicode 63 | 64 | 65 | StaticLibrary 66 | false 67 | v141 68 | true 69 | Unicode 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | true 91 | $(SolutionDir)\bin\ 92 | 93 | 94 | true 95 | $(SolutionDir)\bin\ 96 | 97 | 98 | false 99 | $(SolutionDir)\bin\ 100 | 101 | 102 | false 103 | $(SolutionDir)\bin\ 104 | 105 | 106 | 107 | NotUsing 108 | Level4 109 | Disabled 110 | true 111 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 112 | true 113 | pch.h 114 | true 115 | 116 | 117 | Console 118 | true 119 | 120 | 121 | 122 | 123 | NotUsing 124 | Level4 125 | Disabled 126 | true 127 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 128 | true 129 | pch.h 130 | true 131 | 132 | 133 | Console 134 | true 135 | 136 | 137 | 138 | 139 | NotUsing 140 | Level4 141 | MaxSpeed 142 | true 143 | true 144 | true 145 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 146 | true 147 | pch.h 148 | true 149 | 150 | 151 | Console 152 | true 153 | true 154 | true 155 | 156 | 157 | 158 | 159 | NotUsing 160 | Level4 161 | MaxSpeed 162 | true 163 | true 164 | true 165 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 166 | true 167 | pch.h 168 | true 169 | 170 | 171 | Console 172 | true 173 | true 174 | true 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /tests/tests.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 15.0 33 | {D31131D2-E4B6-4C40-8F6D-673B561AA323} 34 | Win32Proj 35 | tests 36 | 10.0.17134.0 37 | 38 | 39 | 40 | Application 41 | true 42 | v141 43 | Unicode 44 | 45 | 46 | Application 47 | false 48 | v141 49 | true 50 | Unicode 51 | 52 | 53 | Application 54 | true 55 | v141 56 | Unicode 57 | 58 | 59 | Application 60 | false 61 | v141 62 | true 63 | Unicode 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | true 85 | $(SolutionDir)\bin\ 86 | 87 | 88 | true 89 | $(SolutionDir)\bin\ 90 | 91 | 92 | false 93 | $(SolutionDir)\bin\ 94 | 95 | 96 | false 97 | $(SolutionDir)\bin\ 98 | 99 | 100 | 101 | NotUsing 102 | Level3 103 | Disabled 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | pch.h 108 | $(SolutionDir)/pe-signature-utils/src; 109 | 110 | 111 | Console 112 | true 113 | $(SolutionDir)\bin\pe-signature-utils.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 114 | 115 | 116 | 117 | 118 | NotUsing 119 | Level3 120 | Disabled 121 | true 122 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 123 | true 124 | pch.h 125 | $(SolutionDir)/pe-signature-utils/src; 126 | 127 | 128 | Console 129 | true 130 | $(SolutionDir)\bin\pe-signature-utils.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 131 | 132 | 133 | 134 | 135 | NotUsing 136 | Level3 137 | MaxSpeed 138 | true 139 | true 140 | true 141 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 142 | true 143 | pch.h 144 | $(SolutionDir)/pe-signature-utils/src; 145 | 146 | 147 | Console 148 | true 149 | true 150 | true 151 | $(SolutionDir)\bin\pe-signature-utils.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 152 | 153 | 154 | 155 | 156 | NotUsing 157 | Level3 158 | MaxSpeed 159 | true 160 | true 161 | true 162 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 163 | true 164 | pch.h 165 | $(SolutionDir)/pe-signature-utils/src; 166 | 167 | 168 | Console 169 | true 170 | true 171 | true 172 | $(SolutionDir)\bin\pe-signature-utils.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /command-line-tool/command-line-tool.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 15.0 30 | {C008CD30-0AA2-441C-BD7A-3C3EE76FA537} 31 | Win32Proj 32 | commandlinetool 33 | 10.0.17134.0 34 | 35 | 36 | 37 | Application 38 | true 39 | v141 40 | Unicode 41 | 42 | 43 | Application 44 | false 45 | v141 46 | true 47 | Unicode 48 | 49 | 50 | Application 51 | true 52 | v141 53 | Unicode 54 | 55 | 56 | Application 57 | false 58 | v141 59 | true 60 | Unicode 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | true 82 | $(VC_SourcePath); 83 | $(SolutionDir)\bin\ 84 | 85 | 86 | true 87 | $(VC_SourcePath); 88 | $(SolutionDir)\bin\ 89 | 90 | 91 | false 92 | $(VC_SourcePath); 93 | $(SolutionDir)\bin\ 94 | 95 | 96 | false 97 | $(VC_SourcePath); 98 | $(SolutionDir)\bin\ 99 | 100 | 101 | 102 | NotUsing 103 | Level4 104 | Disabled 105 | true 106 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 107 | true 108 | pch.h 109 | $(SolutionDir)/pe-signature-utils/src; 110 | true 111 | 112 | 113 | Console 114 | true 115 | $(SolutionDir)\bin\pe-signature-utils.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 116 | 117 | 118 | 119 | 120 | NotUsing 121 | Level4 122 | Disabled 123 | true 124 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 125 | true 126 | pch.h 127 | $(SolutionDir)/pe-signature-utils/src; 128 | true 129 | 130 | 131 | Console 132 | true 133 | $(SolutionDir)\bin\pe-signature-utils.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 134 | 135 | 136 | 137 | 138 | NotUsing 139 | Level4 140 | MaxSpeed 141 | true 142 | true 143 | true 144 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 145 | true 146 | pch.h 147 | $(SolutionDir)/pe-signature-utils/src; 148 | true 149 | 150 | 151 | Console 152 | true 153 | true 154 | true 155 | $(SolutionDir)\bin\pe-signature-utils.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 156 | 157 | 158 | 159 | 160 | NotUsing 161 | Level4 162 | MaxSpeed 163 | true 164 | true 165 | true 166 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 167 | true 168 | pch.h 169 | $(SolutionDir)/pe-signature-utils/src; 170 | true 171 | 172 | 173 | Console 174 | true 175 | true 176 | true 177 | $(SolutionDir)\bin\pe-signature-utils.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 178 | 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /pe-signature-utils/src/native_api_wrappers/CryptoApiWrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "CryptoApiWrapper.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) 8 | 9 | 10 | DWORD CryptoApiWrapper::GetCertificateInfo( 11 | std::wstring aFileName, 12 | CryptoApiWrapper::SignerInfoPtr &aCertInfo) 13 | { 14 | HCERTSTORE lCertStore; 15 | std::shared_ptr lSignerInfo; 16 | DWORD lRetVal = ERROR_SUCCESS; 17 | PCCERT_CONTEXT lCertContexPtr = NULL; 18 | 19 | lRetVal = getSignerInfo(aFileName, lSignerInfo, lCertStore); 20 | if (lRetVal != ERROR_SUCCESS) 21 | { 22 | return lRetVal; 23 | } 24 | 25 | lRetVal = getCertificateContext(lSignerInfo, lCertStore, lCertContexPtr); 26 | if (lRetVal != ERROR_SUCCESS) 27 | { 28 | return lRetVal; 29 | } 30 | 31 | aCertInfo = std::make_shared(); 32 | 33 | std::wstring lSerialNumber; 34 | lRetVal = getCertificateSerialNumber(lCertContexPtr, lSerialNumber); 35 | if (lRetVal == ERROR_SUCCESS) 36 | { 37 | aCertInfo->serialNumber = lSerialNumber; 38 | } 39 | 40 | std::wstring lIssuerName; 41 | lRetVal = queryCertificateInfo(lCertContexPtr, CERT_NAME_ISSUER_FLAG, lIssuerName); 42 | if (lRetVal == ERROR_SUCCESS) 43 | { 44 | aCertInfo->issuerName = lIssuerName; 45 | } 46 | 47 | std::wstring lSubjectName; 48 | lRetVal = queryCertificateInfo(lCertContexPtr, 0, lSubjectName); 49 | if (lRetVal == ERROR_SUCCESS) 50 | { 51 | aCertInfo->subjectName = lSubjectName; 52 | } 53 | 54 | std::wstring lSignAlgorithm; 55 | lRetVal = getSignatureAlgoWstring(&lCertContexPtr->pCertInfo->SignatureAlgorithm, lSignAlgorithm); 56 | if (lRetVal == ERROR_SUCCESS) 57 | { 58 | aCertInfo->signAlgorithm = lSignAlgorithm; 59 | } 60 | 61 | if (lCertContexPtr) 62 | { 63 | CertFreeCertificateContext(lCertContexPtr); 64 | } 65 | 66 | return ERROR_SUCCESS; 67 | } 68 | 69 | DWORD CryptoApiWrapper::GetTimestampCertificateInfo( 70 | std::wstring aFileName, 71 | CryptoApiWrapper::TimeStampCertInfoPtr &aCertInfo) 72 | { 73 | HCERTSTORE lCertStore; 74 | std::shared_ptr lSignerInfo; 75 | std::shared_ptr lTimeStammpSignerInfo; 76 | DWORD lRetVal = ERROR_SUCCESS; 77 | PCCERT_CONTEXT lCertContexPtr = NULL; 78 | 79 | lRetVal = getSignerInfo(aFileName, lSignerInfo, lCertStore); 80 | if (lRetVal != ERROR_SUCCESS) 81 | { 82 | return lRetVal; 83 | } 84 | 85 | lRetVal = getCertificateContext(lSignerInfo, lCertStore, lCertContexPtr); 86 | if (lRetVal != ERROR_SUCCESS) 87 | { 88 | return lRetVal; 89 | } 90 | 91 | lRetVal = getTimeStampSignerInfo(lSignerInfo, lTimeStammpSignerInfo); 92 | if (lRetVal != ERROR_SUCCESS) 93 | { 94 | return lRetVal; 95 | } 96 | 97 | lRetVal = getCertificateContext(lTimeStammpSignerInfo, lCertStore, lCertContexPtr); 98 | if (lRetVal != ERROR_SUCCESS) 99 | { 100 | return lRetVal; 101 | } 102 | 103 | aCertInfo = std::make_shared(); 104 | 105 | std::wstring lSerialNumber; 106 | lRetVal = getCertificateSerialNumber(lCertContexPtr, lSerialNumber); 107 | if (lRetVal == ERROR_SUCCESS) 108 | { 109 | aCertInfo->serialNumber = lSerialNumber; 110 | } 111 | 112 | std::wstring lIssuerName; 113 | lRetVal = queryCertificateInfo(lCertContexPtr, CERT_NAME_ISSUER_FLAG, lIssuerName); 114 | if (lRetVal == ERROR_SUCCESS) 115 | { 116 | aCertInfo->issuerName = lIssuerName; 117 | } 118 | 119 | std::wstring lSubjectName; 120 | lRetVal = queryCertificateInfo(lCertContexPtr, 0, lSubjectName); 121 | if (lRetVal == ERROR_SUCCESS) 122 | { 123 | aCertInfo->subjectName = lSubjectName; 124 | } 125 | 126 | std::wstring lSignAlgorithm; 127 | lRetVal = getSignatureAlgoWstring(&lCertContexPtr->pCertInfo->SignatureAlgorithm, lSignAlgorithm); 128 | if (lRetVal == ERROR_SUCCESS) 129 | { 130 | aCertInfo->signAlgorithm = lSignAlgorithm; 131 | } 132 | 133 | std::shared_ptr lSysTime; 134 | bool lBoolRetVal = getDateOfTimeStamp(lTimeStammpSignerInfo, lSysTime); 135 | if (lBoolRetVal == true) 136 | { 137 | aCertInfo->dateOfTimeStamp = lSysTime; 138 | } 139 | 140 | if (lCertContexPtr) 141 | { 142 | CertFreeCertificateContext(lCertContexPtr); 143 | } 144 | 145 | return ERROR_SUCCESS; 146 | } 147 | 148 | DWORD CryptoApiWrapper::getSignerInfo( 149 | std::wstring aFileName, 150 | std::shared_ptr &aSignerInfo, 151 | HCERTSTORE &aCertStore) 152 | { 153 | BOOL lRetVal = TRUE; 154 | DWORD lEncoding = 0; 155 | DWORD lContentType = 0; 156 | DWORD lFormatType = 0; 157 | HCERTSTORE lStoreHandle = NULL; 158 | HCRYPTMSG lCryptMsgHandle = NULL; 159 | 160 | CERT_INFO CertInfo = { 0 }; 161 | 162 | DWORD lSignerInfoSize = 0; 163 | 164 | lRetVal = CryptQueryObject(CERT_QUERY_OBJECT_FILE, 165 | aFileName.data(), 166 | CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, 167 | CERT_QUERY_FORMAT_FLAG_BINARY, 168 | 0, 169 | &lEncoding, 170 | &lContentType, 171 | &lFormatType, 172 | &lStoreHandle, 173 | &lCryptMsgHandle, 174 | NULL); 175 | 176 | if (!lRetVal) 177 | { 178 | return GetLastError(); 179 | } 180 | 181 | lRetVal = CryptMsgGetParam(lCryptMsgHandle, 182 | CMSG_SIGNER_INFO_PARAM, 183 | 0, 184 | NULL, 185 | &lSignerInfoSize); 186 | 187 | if (!lRetVal) 188 | { 189 | return GetLastError(); 190 | } 191 | 192 | PCMSG_SIGNER_INFO lSignerInfoPtr = (PCMSG_SIGNER_INFO) new BYTE[lSignerInfoSize]; 193 | 194 | // Get Signer Information. 195 | lRetVal = CryptMsgGetParam(lCryptMsgHandle, 196 | CMSG_SIGNER_INFO_PARAM, 197 | 0, 198 | (PVOID)lSignerInfoPtr, 199 | &lSignerInfoSize); 200 | 201 | if (!lRetVal) 202 | { 203 | delete lSignerInfoPtr; 204 | return GetLastError(); 205 | } 206 | 207 | aSignerInfo = std::shared_ptr(lSignerInfoPtr); 208 | aCertStore = lStoreHandle; 209 | 210 | return ERROR_SUCCESS; 211 | } 212 | 213 | DWORD CryptoApiWrapper::getCertificateContext( 214 | std::shared_ptr aSignerInfo, 215 | HCERTSTORE aCertStore, 216 | PCCERT_CONTEXT &aCertContextPtr) 217 | { 218 | 219 | PCCERT_CONTEXT pCertContext = NULL; 220 | CERT_INFO CertInfo = { 0 }; 221 | 222 | CertInfo.Issuer = aSignerInfo->Issuer; 223 | CertInfo.SerialNumber = aSignerInfo->SerialNumber; 224 | 225 | pCertContext = CertFindCertificateInStore( 226 | aCertStore, 227 | ENCODING, 228 | 0, 229 | CERT_FIND_SUBJECT_CERT, 230 | (PVOID)&CertInfo, 231 | NULL); 232 | 233 | if (!pCertContext) 234 | { 235 | return GetLastError(); 236 | } 237 | 238 | aCertContextPtr = pCertContext; 239 | 240 | return ERROR_SUCCESS; 241 | } 242 | 243 | DWORD CryptoApiWrapper::queryCertificateInfo( 244 | PCCERT_CONTEXT aCertContext, 245 | DWORD aType, 246 | std::wstring &aOutputName) 247 | { 248 | 249 | DWORD lNameLength; 250 | 251 | lNameLength = CertGetNameString(aCertContext, 252 | CERT_NAME_SIMPLE_DISPLAY_TYPE, 253 | aType, 254 | NULL, 255 | NULL, 256 | 0); 257 | 258 | if (!lNameLength) 259 | { 260 | return GetLastError(); 261 | } 262 | 263 | std::vector lNameVector; 264 | lNameVector.reserve(lNameLength); 265 | 266 | // Get Issuer name. 267 | lNameLength = CertGetNameString(aCertContext, 268 | CERT_NAME_SIMPLE_DISPLAY_TYPE, 269 | aType, 270 | NULL, 271 | lNameVector.data(), 272 | lNameLength); 273 | 274 | if (!lNameLength) 275 | { 276 | return GetLastError(); 277 | } 278 | 279 | aOutputName.assign(lNameVector.data(), lNameLength); 280 | 281 | return ERROR_SUCCESS; 282 | } 283 | 284 | DWORD CryptoApiWrapper::getSignatureAlgoWstring( 285 | CRYPT_ALGORITHM_IDENTIFIER* pSigAlgo, 286 | std::wstring &signatureAlgo) 287 | { 288 | if (!pSigAlgo || !pSigAlgo->pszObjId) 289 | { 290 | return ERROR_INVALID_PARAMETER; 291 | } 292 | 293 | PCCRYPT_OID_INFO pCOI = ::CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, pSigAlgo->pszObjId, 0); 294 | if (!pCOI) 295 | { 296 | return GetLastError(); 297 | } 298 | if (pCOI && pCOI->pwszName) 299 | { 300 | signatureAlgo.assign(pCOI->pwszName); 301 | } 302 | else 303 | { 304 | USES_CONVERSION; 305 | signatureAlgo.assign(A2W(pSigAlgo->pszObjId)); 306 | } 307 | 308 | return ERROR_SUCCESS; 309 | } 310 | 311 | 312 | DWORD CryptoApiWrapper::getTimeStampSignerInfo( 313 | std::shared_ptr &aSignerInfo, 314 | std::shared_ptr &aCounterSignerInfo) 315 | { 316 | BOOL lRetValBool; 317 | DWORD dwSize; 318 | bool lFoundCounterSign = false; 319 | 320 | PCMSG_SIGNER_INFO pCounterSignerInfo = NULL; 321 | aCounterSignerInfo = NULL; 322 | 323 | // Loop through unathenticated attributes for 324 | // szOID_RSA_counterSign OID. 325 | for (DWORD n = 0; n < aSignerInfo->UnauthAttrs.cAttr; n++) 326 | { 327 | if (lstrcmpA(aSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, 328 | szOID_RSA_counterSign) == 0) 329 | { 330 | // Get size of CMSG_SIGNER_INFO structure. 331 | lRetValBool = CryptDecodeObject(ENCODING, 332 | PKCS7_SIGNER_INFO, 333 | aSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData, 334 | aSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData, 335 | 0, 336 | NULL, 337 | &dwSize); 338 | if (!lRetValBool) 339 | { 340 | return GetLastError(); 341 | } 342 | 343 | // Allocate memory for CMSG_SIGNER_INFO. 344 | pCounterSignerInfo = (PCMSG_SIGNER_INFO) new BYTE[dwSize]; 345 | if (!pCounterSignerInfo) 346 | { 347 | return GetLastError(); 348 | } 349 | 350 | // Decode and get CMSG_SIGNER_INFO structure 351 | // for timestamp certificate. 352 | lRetValBool = CryptDecodeObject(ENCODING, 353 | PKCS7_SIGNER_INFO, 354 | aSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData, 355 | aSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData, 356 | 0, 357 | (PVOID)pCounterSignerInfo, 358 | &dwSize); 359 | 360 | if (!lRetValBool) 361 | { 362 | return GetLastError(); 363 | } 364 | 365 | lFoundCounterSign = true; 366 | 367 | break; 368 | } 369 | } 370 | 371 | if (lFoundCounterSign) 372 | { 373 | aCounterSignerInfo = std::shared_ptr(pCounterSignerInfo); 374 | return ERROR_SUCCESS; 375 | } 376 | else 377 | { 378 | return ERROR_GEN_FAILURE; 379 | } 380 | } 381 | 382 | DWORD CryptoApiWrapper::getCertificateSerialNumber( 383 | PCCERT_CONTEXT aCertContext, 384 | std::wstring &aSerialNumberWstr) 385 | { 386 | if (!aCertContext) 387 | { 388 | return ERROR_INVALID_PARAMETER; 389 | } 390 | 391 | const int lBufferSize = 3; 392 | 393 | wchar_t lTempBuffer[lBufferSize] = { 0 }; 394 | 395 | aSerialNumberWstr = L""; 396 | 397 | auto lDataBytesCount = aCertContext->pCertInfo->SerialNumber.cbData; 398 | for (DWORD n = 0; n < lDataBytesCount; n++) 399 | { 400 | 401 | auto lSerialByte = aCertContext->pCertInfo->SerialNumber.pbData[lDataBytesCount - (n + 1)]; 402 | 403 | swprintf(lTempBuffer, lBufferSize*2, L"%02x", lSerialByte); 404 | 405 | aSerialNumberWstr += std::wstring(lTempBuffer, 2); 406 | 407 | } 408 | 409 | return ERROR_SUCCESS; 410 | } 411 | 412 | 413 | bool CryptoApiWrapper::getDateOfTimeStamp( 414 | std::shared_ptr &aSignerInfo, 415 | std::shared_ptr &aSysTime) 416 | { 417 | FILETIME lft, ft; 418 | DWORD dwData; 419 | aSysTime = std::make_shared(); 420 | 421 | DWORD lRetVal = false; 422 | 423 | // Loop through authenticated attributes and find 424 | // szOID_RSA_signingTime OID. 425 | for (DWORD n = 0; n < aSignerInfo->AuthAttrs.cAttr; n++) 426 | { 427 | if (lstrcmpA(szOID_RSA_signingTime, 428 | aSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0) 429 | { 430 | // Decode and get FILETIME structure. 431 | dwData = sizeof(ft); 432 | lRetVal = CryptDecodeObject(ENCODING, 433 | szOID_RSA_signingTime, 434 | aSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData, 435 | aSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData, 436 | 0, 437 | (PVOID)&ft, 438 | &dwData); 439 | 440 | if (!lRetVal) 441 | { 442 | break; 443 | } 444 | 445 | // Convert to local time. 446 | FileTimeToLocalFileTime(&ft, &lft); 447 | FileTimeToSystemTime(&lft, aSysTime.get()); 448 | 449 | lRetVal = true; 450 | 451 | break; // Break from for loop. 452 | 453 | } //lstrcmp szOID_RSA_signingTime 454 | } // for 455 | 456 | return lRetVal; 457 | } 458 | --------------------------------------------------------------------------------