├── .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 |
--------------------------------------------------------------------------------