#include <new>
#include <windows.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <stdio.h>
#include <tchar.h>
#include <atlconv.h>
#pragma comment(lib, "crypt32.lib")
#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
enum RESULT_FIND_CONTEXT {
RFC_FOUND_CONTEXT,
RFC_NO_CONTEXT,
};
enum RESULT_FIND_CERT_STORE {
RFCS_ERROR = -1,
RFCS_NONE = 0,
RFCS_FOUND_ONE = 1,
};
void RetrieveDigitalSignatureInfo(const WCHAR* pFilePath);
void PrintProgramAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo);
RESULT_FIND_CONTEXT PrintCertificateInformation(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, LPCTSTR pStrCertDescription, BOOL bIsTimeStamp, FILETIME* pftTimeStampUtc = NULL);
void PrintCertContextDetails(PCCERT_CONTEXT pCertContext, DWORD dwNameOutputType, CRYPT_ALGORITHM_IDENTIFIER* pHashAlgo);
void PrintDigestAlgorithmName(CRYPT_ALGORITHM_IDENTIFIER* pSigAlgo);
BOOL PrintSignerDateTime(FILETIME* pftUtc);
int PrintSignerTimeStampDateTime(PCMSG_SIGNER_INFO pSignerInfo);
RESULT_FIND_CERT_STORE FindCertStoreByIndex(int iIndex, HCERTSTORE& hOutStore, CRYPT_DATA_BLOB* p7Data = NULL);
void PrintDualSignatureInformation(PCMSG_SIGNER_INFO pSignerInfo);
void FindAppropriateStoreAndPrintCertificateInformation(PCMSG_SIGNER_INFO pSignerInfo, CRYPT_DATA_BLOB* p7Data, LPCTSTR pStrCertDescription, BOOL bIsTimeStamp, FILETIME* pftTimeStampUtc = NULL);
int _tmain(int argc, WCHAR* argv[])
{
LPCTSTR pExePath;
if (argc <= 1)
{
pExePath = L"C:\\Program Files (x86)\\Tencent\\QQ\\Bin\\QQScLauncher.exe";
//pExePath = L"C:\\Users\\UserName\\Downloads\\putty.exe";
}
else
{
//Otherwise use first argument from command line
pExePath = argv[1];
}
_tprintf(L"File: %s\n", pExePath);
RetrieveDigitalSignatureInfo(pExePath);
return 0;
}
//The following functions were re-written from the following source to be able to
//retrieve dual-signatures from PE binaries:
//
// https://support.microsoft.com/en-us/help/323809/how-to-get-information-from-authenticode-signed-executables
void RetrieveDigitalSignatureInfo(const WCHAR* pFilePath)
{
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
if (CryptQueryObject(CERT_QUERY_OBJECT_FILE,
pFilePath,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
NULL,
NULL,
NULL,
&hStore,
&hMsg,
NULL))
{
//We must have at least one signer
DWORD dwCountSigners = 0;
DWORD dwcbSz = sizeof(dwCountSigners);
if (CryptMsgGetParam(hMsg, CMSG_SIGNER_COUNT_PARAM, 0, &dwCountSigners, &dwcbSz))
{
if (dwCountSigners != 0)
{
//Get Signer Information
dwcbSz = 0;
CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwcbSz);
if (dwcbSz)
{
PCMSG_SIGNER_INFO pSignerInfo = (PCMSG_SIGNER_INFO)new (std::nothrow) BYTE[dwcbSz];
if (pSignerInfo)
{
DWORD dwcbSz2 = dwcbSz;
if (CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, pSignerInfo, &dwcbSz) &&
dwcbSz == dwcbSz2)
{
//Print program publisher info
PrintProgramAndPublisherInfo(pSignerInfo);
_tprintf(L"\n");
//Print signer certificate info
if (PrintCertificateInformation(hStore, pSignerInfo, L"Signer Certificate", FALSE) == RFC_NO_CONTEXT)
{
_tprintf(L"ERROR: (0x%X) CertFindCertificateInStore(CERT_FIND_SUBJECT_CERT) data failed\n", ::GetLastError());
}
//Print dual-signature info
PrintDualSignatureInformation(pSignerInfo);
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) data failed\n", ::GetLastError());
//Free mem
delete[] pSignerInfo;
pSignerInfo = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new(PCMSG_SIGNER_INFO) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: Must have to least one signer\n");
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(CMSG_SIGNER_COUNT_PARAM) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptQueryObject(CERT_QUERY_OBJECT_FILE) failed\n", ::GetLastError());
//Clear up
if (hStore != NULL)
{
CertCloseStore(hStore, 0);
hStore = NULL;
}
if (hMsg != NULL)
{
CryptMsgClose(hMsg);
hMsg = NULL;
}
}
void PrintProgramAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo)
{
// Loop through authenticated attributes and find SPC_SP_OPUS_INFO_OBJID OID.
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (pSignerInfo->AuthAttrs.rgAttr[n].pszObjId)
{
if (lstrcmpA(pSignerInfo->AuthAttrs.rgAttr[n].pszObjId, SPC_SP_OPUS_INFO_OBJID) == 0)
{
// Get Size of SPC_SP_OPUS_INFO structure.
PSPC_SP_OPUS_INFO pInfo = NULL;
DWORD dwcbSz = 0;
if (CryptDecodeObjectEx(ENCODING, SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
CRYPT_DECODE_ALLOC_FLAG,
NULL,
&pInfo, &dwcbSz) &&
pInfo &&
dwcbSz)
{
if (pInfo->pwszProgramName)
{
_tprintf(L"Program Name: %s\n", pInfo->pwszProgramName);
}
if (pInfo->pPublisherInfo)
{
switch (pInfo->pPublisherInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
_tprintf(L"Publisher Link: %s\n", pInfo->pPublisherInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
_tprintf(L"Publisher Link: %s\n", pInfo->pPublisherInfo->pwszFile);
break;
}
}
if (pInfo->pMoreInfo)
{
switch (pInfo->pMoreInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
_tprintf(L"MoreInfo Link: %s\n", pInfo->pMoreInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
_tprintf(L"MoreInfo Link: %s\n", pInfo->pMoreInfo->pwszFile);
break;
}
}
}
else
_tprintf(L"ERROR: (0x%X) CryptDecodeObjectEx(SPC_SP_OPUS_INFO_OBJID) data failed\n", ::GetLastError());
if (pInfo)
{
::LocalFree(pInfo);
pInfo = NULL;
}
}
}
}
}
RESULT_FIND_CONTEXT PrintCertificateInformation(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, LPCTSTR pStrCertDescription, BOOL bIsTimeStamp, FILETIME* pftTimeStampUtc)
{
CERT_INFO ci = { 0 };
ci.Issuer = pSignerInfo->Issuer;
ci.SerialNumber = pSignerInfo->SerialNumber;
PCCERT_CONTEXT pCertContext = NULL;
DWORD dwcbSz;
int c = 0;
for (;; c++)
{
//Enumerate and look for needed cert context
pCertContext = CertFindCertificateInStore(hStore,
ENCODING, 0, CERT_FIND_SUBJECT_CERT,
(PVOID)&ci, pCertContext);
if (!pCertContext)
{
break;
}
if (!c)
{
//Print Signer certificate information.
_tprintf(L"%s:\n", pStrCertDescription);
if (!bIsTimeStamp)
{
_tprintf(L"----------------\n");
}
_tprintf(L"\n");
}
//In case of a timestamp
if (bIsTimeStamp)
{
//Print time stamp
if (!pftTimeStampUtc)
{
//Retrieve and print it
if (PrintSignerTimeStampDateTime(pSignerInfo) == 0)
{
_tprintf(L"ERROR: (0x%X) Failed to retrieve date/time for TimeStamp\n", ::GetLastError());
}
}
else
{
//We have a time-stamp already
if (!PrintSignerDateTime(pftTimeStampUtc))
{
_tprintf(L"ERROR: (0x%X) Time conversion failed from %I64x\n", ::GetLastError(), *(ULONGLONG*)pftTimeStampUtc);
}
}
}
//Print subject name, issuer name, serial, signature algorithm
PrintCertContextDetails(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE, //Or use CERT_NAME_RDN_TYPE for a more detailed output
&pSignerInfo->HashAlgorithm);
volatile static char pmsgDoNotCopyAsIs[] =
"Please read & verify this code before you "
"copy-and-paste it into your production project! "
"https://stackoverflow.com/q/50976612/3170929 "
"{438EE426-7131-4498-8AF7-9DDCB2508F0C}";
srand(rand() ^ pmsgDoNotCopyAsIs[0]);
_tprintf(L"\n");
#ifndef szOID_RFC3161_counterSign
#define szOID_RFC3161_counterSign "1.3.6.1.4.1.311.3.3.1"
#endif
if (!bIsTimeStamp)
{
//Get time stamp certificate(s)
//Loop through unathenticated attributes and look for specific OIDs
for (DWORD n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++)
{
if (pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId)
{
if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, szOID_RSA_counterSign) == 0)
{
//This is a legacy signature standard
PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
dwcbSz = 0;
if (CryptDecodeObjectEx(ENCODING, PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
CRYPT_DECODE_ALLOC_FLAG,
NULL,
&pCounterSignerInfo, &dwcbSz) &&
pCounterSignerInfo &&
dwcbSz)
{
//Got one signature
if (PrintCertificateInformation(hStore, pCounterSignerInfo, L"TimeStamp Certificate", TRUE) == RFC_NO_CONTEXT)
{
_tprintf(L"ERROR: (0x%X) CertFindCertificateInStore(CERT_FIND_SUBJECT_CERT) data failed\n", ::GetLastError());
}
}
else
{
_tprintf(L"ERROR: (0x%X) CryptDecodeObjectEx(PKCS7_SIGNER_INFO) failed\n", ::GetLastError());
}
//Free mem
if (pCounterSignerInfo)
{
::LocalFree(pCounterSignerInfo);
pCounterSignerInfo = NULL;
}
}
else if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, szOID_RFC3161_counterSign) == 0)
{
//Using an RFC3161 time stamp
if (pSignerInfo->UnauthAttrs.rgAttr[n].cValue != 0)
{
HCRYPTMSG hMsg = ::CryptMsgOpenToDecode(ENCODING, 0, 0, NULL, NULL, NULL);
if (hMsg)
{
if (::CryptMsgUpdate(hMsg,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue->pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue->cbData,
TRUE))
{
dwcbSz = 0;
::CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, NULL, &dwcbSz);
if (dwcbSz != 0)
{
BYTE* pCntData = new (std::nothrow) BYTE[dwcbSz];
if (pCntData)
{
if (::CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, pCntData, &dwcbSz))
{
//Retrieve time stamp
FILETIME ftUtc = { 0 };
void* pTmData = NULL;
DWORD dwcbTmDataSz = 0;
struct Microsoft_forgot_to_document_me {
void* something_0[9];
FILETIME ftUtc;
};
#ifndef TIMESTAMP_INFO
#define TIMESTAMP_INFO ((LPCSTR) 80)
#endif
if (CryptDecodeObjectEx(ENCODING, //X509_ASN_ENCODING,
TIMESTAMP_INFO,
pCntData,
dwcbSz,
CRYPT_DECODE_ALLOC_FLAG,
NULL,
&pTmData, &dwcbTmDataSz) &&
pTmData &&
dwcbTmDataSz >= sizeof(Microsoft_forgot_to_document_me))
{
ftUtc = ((Microsoft_forgot_to_document_me*)pTmData)->ftUtc;
}
else
_tprintf(L"ERROR: (0x%X) CryptDecodeObjectEx(RFC3161/TIMESTAMP_INFO) data failed\n", ::GetLastError());
if (pTmData)
{
::LocalFree(pTmData);
pTmData = NULL;
}
//Try to get signer info
dwcbSz = 0;
::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwcbSz);
if (dwcbSz != 0)
{
CMSG_SIGNER_INFO* pTmSignerData = (CMSG_SIGNER_INFO*)new (std::nothrow) BYTE[dwcbSz];
if (pTmSignerData)
{
if (::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, pTmSignerData, &dwcbSz))
{
CRYPT_DATA_BLOB c7Data;
c7Data.pbData = pSignerInfo->UnauthAttrs.rgAttr[n].rgValue->pbData;
c7Data.cbData = pSignerInfo->UnauthAttrs.rgAttr[n].rgValue->cbData;
//Try to locate the appropriate store
FindAppropriateStoreAndPrintCertificateInformation(pTmSignerData, &c7Data, L"TimeStamp Certificate", TRUE, &ftUtc);
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(RFC3161/CMSG_SIGNER_INFO_PARAM) data failed\n", ::GetLastError());
//Free mem
delete[] pTmSignerData;
pTmSignerData = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new(RFC3161) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(RFC3161/CMSG_SIGNER_INFO_PARAM) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(RFC3161/CMSG_CONTENT_PARAM) data failed\n", ::GetLastError());
//Free mem
delete[] pCntData;
pCntData = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new(RFC3161) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(RFC3161/CMSG_CONTENT_PARAM) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgUpdate(RFC3161) failed\n", ::GetLastError());
//Free handle
::CryptMsgClose(hMsg);
hMsg = NULL;
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgOpenToDecode(ENCODING) failed\n", ::GetLastError());
}
}
}
}
}
}
//Free
if (pCertContext)
{
CertFreeCertificateContext(pCertContext);
pCertContext = NULL;
}
return c != 0 ? RFC_FOUND_CONTEXT : RFC_NO_CONTEXT;
}
void PrintCertContextDetails(PCCERT_CONTEXT pCertContext, DWORD dwNameOutputType, CRYPT_ALGORITHM_IDENTIFIER* pHashAlgo)
{
//'dwNameOutputType' = one of: CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_RDN_TYPE, etc. see CertGetNameString()
DWORD dwcbSz;
WCHAR* pBuff;
//Get subject name.
dwcbSz = CertGetNameString(pCertContext, dwNameOutputType, 0, NULL, NULL, 0);
if (dwcbSz != 0)
{
pBuff = new (std::nothrow) WCHAR[dwcbSz];
if (pBuff)
{
if (CertGetNameString(pCertContext, dwNameOutputType, 0, NULL, pBuff, dwcbSz) == dwcbSz)
{
_tprintf(L"Subject: %s\n", pBuff);
}
else
_tprintf(L"ERROR: (0x%X) CertGetNameString(subject) data failed\n", ::GetLastError());
//Free mem
delete[] pBuff;
pBuff = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new CertGetNameString(subject) data failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CertGetNameString(subject) failed\n", ::GetLastError());
//Issuer
dwcbSz = CertGetNameString(pCertContext, dwNameOutputType, CERT_NAME_ISSUER_FLAG, NULL, NULL, 0);
if (dwcbSz != 0)
{
pBuff = new (std::nothrow) WCHAR[dwcbSz];
if (pBuff)
{
if (CertGetNameString(pCertContext, dwNameOutputType, CERT_NAME_ISSUER_FLAG, NULL, pBuff, dwcbSz) == dwcbSz)
{
_tprintf(L"Issuer: %s\n", pBuff);
}
else
_tprintf(L"ERROR: (0x%X) CertGetNameString(issuer) data failed\n", ::GetLastError());
//Free mem
delete[] pBuff;
pBuff = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new CertGetNameString(issuer) data failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CertGetNameString(issuer) failed\n", ::GetLastError());
//Print Serial Number.
_tprintf(_T("Serial Number: "));
dwcbSz = pCertContext->pCertInfo->SerialNumber.cbData;
for (DWORD n = 0; n < dwcbSz; n++)
{
_tprintf(_T("%02x"), pCertContext->pCertInfo->SerialNumber.pbData[dwcbSz - (n + 1)]);
}
_tprintf(_T("\n"));
//Digest algorithm
_tprintf(L"Digest Algorithm: ");
PrintDigestAlgorithmName(pHashAlgo);
_tprintf(_T("\n"));
}
void PrintDigestAlgorithmName(CRYPT_ALGORITHM_IDENTIFIER* pSigAlgo)
{
if (pSigAlgo &&
pSigAlgo->pszObjId)
{
PCCRYPT_OID_INFO pCOI = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, pSigAlgo->pszObjId, 0);
if (pCOI &&
pCOI->pwszName)
{
_tprintf(L"%s", pCOI->pwszName);
}
else
{
USES_CONVERSION;
_tprintf(L"%s", A2W(pSigAlgo->pszObjId));
}
}
}
BOOL PrintSignerDateTime(FILETIME* pftUtc)
{
BOOL bRes = FALSE;
if (pftUtc)
{
//Convert to local time
FILETIME ftLoc = { 0 };
SYSTEMTIME stLoc = { 0 };
if (FileTimeToLocalFileTime(pftUtc, &ftLoc) &&
FileTimeToSystemTime(&ftLoc, &stLoc))
{
_tprintf(L"Date of TimeStamp : %02d/%02d/%04d %02d:%02d:%02d\n",
stLoc.wMonth,
stLoc.wDay,
stLoc.wYear,
stLoc.wHour,
stLoc.wMinute,
stLoc.wSecond);
bRes = TRUE;
}
}
else
::SetLastError(ERROR_INVALID_PARAMETER);
return bRes;
}
int PrintSignerTimeStampDateTime(PCMSG_SIGNER_INFO pSignerInfo)
{
int nCountTimeStamps = 0;
//Loop through authenticated attributes
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (pSignerInfo->AuthAttrs.rgAttr[n].pszObjId &&
lstrcmpA(pSignerInfo->AuthAttrs.rgAttr[n].pszObjId, szOID_RSA_signingTime) == 0)
{
// Decode and get FILETIME structure.
FILETIME ftUtc = { 0 };
DWORD dwData = sizeof(ftUtc);
if (CryptDecodeObject(ENCODING, PKCS_UTC_TIME,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)&ftUtc, &dwData))
{
//Got time stamp
nCountTimeStamps++;
//And print it
if (!PrintSignerDateTime(&ftUtc))
{
_tprintf(L"ERROR: (0x%X) Time conversion failed from %I64x\n", ::GetLastError(), *(ULONGLONG*)&ftUtc);
}
}
else
{
_tprintf(L"ERROR: (0x%X) CryptDecodeObject(PKCS_UTC_TIME) failed\n", ::GetLastError());
}
}
}
return nCountTimeStamps;
}
RESULT_FIND_CERT_STORE FindCertStoreByIndex(int iIndex, HCERTSTORE& hOutStore, CRYPT_DATA_BLOB* p7Data)
{
//'hOutStore' = receives cert store handle. If not NULL, make sure to release it by calling CertCloseStore(hStore, CERT_CLOSE_STORE_FORCE_FLAG);
//'p7Data' = used with index 0 only
hOutStore = NULL;
switch (iIndex)
{
case 0:
hOutStore = CertOpenStore(CERT_STORE_PROV_PKCS7, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, 0, p7Data);
break;
case 1:
hOutStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_READONLY_FLAG | 0x10000, // flags = 0x18001
"ROOT");
break;
case 2:
hOutStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_READONLY_FLAG | 0x10000, // flags = 0x18001
"TRUST");
break;
case 3:
hOutStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_READONLY_FLAG | 0x10000, // flags = 0x18001
"CA");
break;
case 4:
hOutStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_READONLY_FLAG | 0x10000, // flags = 0x18001
"MY");
break;
case 5:
hOutStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, 0,
CERT_STORE_NO_CRYPT_RELEASE_FLAG | CERT_STORE_READONLY_FLAG | 0x20000, // flags = 0x28001
"SPC");
break;
default:
return RFCS_NONE;
}
return hOutStore ? RFCS_FOUND_ONE : RFCS_ERROR;
}
void FindAppropriateStoreAndPrintCertificateInformation(PCMSG_SIGNER_INFO pSignerInfo, CRYPT_DATA_BLOB* p7Data, LPCTSTR pStrCertDescription, BOOL bIsTimeStamp, FILETIME* pftTimeStampUtc)
{
HCERTSTORE hStore = NULL;
//Try to locate the appropriate store
for (int i = 0;; i++)
{
if (hStore)
{
CertCloseStore(hStore, CERT_CLOSE_STORE_FORCE_FLAG);
hStore = NULL;
}
RESULT_FIND_CERT_STORE resFnd = FindCertStoreByIndex(i, hStore, p7Data);
if (resFnd == RFCS_FOUND_ONE)
{
//Try to retrieve info
if (PrintCertificateInformation(hStore, pSignerInfo, pStrCertDescription, bIsTimeStamp, pftTimeStampUtc) == RFC_FOUND_CONTEXT)
{
//All done
break;
}
}
else
{
//Stop the seatch
if (resFnd == RFCS_NONE)
{
//No context
_tprintf(L"ERROR: (0x%X) CertOpenStore(no_context) failed\n", ::GetLastError());
}
else
{
//Error
_tprintf(L"ERROR: (0x%X) CertOpenStore(%i) data failed\n", ::GetLastError(), i);
}
break;
}
}
if (hStore)
{
::CertCloseStore(hStore, CERT_CLOSE_STORE_FORCE_FLAG);
hStore = NULL;
}
}
void PrintDualSignatureInformation(PCMSG_SIGNER_INFO pSignerInfo)
{
//Loop through unauthenticated attributes
for (DWORD a = 0; a < pSignerInfo->UnauthAttrs.cAttr; a++)
{
#ifndef szOID_NESTED_SIGNATURE
#define szOID_NESTED_SIGNATURE "1.3.6.1.4.1.311.2.4.1"
#endif
//We need szOID_NESTED_SIGNATURE att
if (pSignerInfo->UnauthAttrs.rgAttr[a].pszObjId &&
lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[a].pszObjId, szOID_NESTED_SIGNATURE) == 0)
{
HCRYPTMSG hMsg = ::CryptMsgOpenToDecode(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, 0, NULL, NULL, NULL);
if (hMsg)
{
if (::CryptMsgUpdate(hMsg,
pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->pbData,
pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->cbData,
TRUE))
{
DWORD dwSignerInfo = 0;
::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo);
if (dwSignerInfo != 0)
{
PCMSG_SIGNER_INFO pSignerInfo2 = (PCMSG_SIGNER_INFO)new (std::nothrow) BYTE[dwSignerInfo];
if (pSignerInfo2)
{
if (::CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM,
0, (PVOID)pSignerInfo2, &dwSignerInfo))
{
CRYPT_DATA_BLOB c7Data;
c7Data.cbData = pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->cbData;
c7Data.pbData = pSignerInfo->UnauthAttrs.rgAttr[a].rgValue->pbData;
//Try to locate the appropriate store & print from it
FindAppropriateStoreAndPrintCertificateInformation(pSignerInfo2, &c7Data, L"Dual Signer Certificate", FALSE);
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) data failed\n", ::GetLastError());
//Free mem
delete[] pSignerInfo2;
pSignerInfo2 = NULL;
}
else
_tprintf(L"ERROR: (0x%X) new(PCMSG_SIGNER_INFO) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed\n", ::GetLastError());
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgUpdate(dual-sig) failed\n", ::GetLastError());
//Close message
::CryptMsgClose(hMsg);
}
else
_tprintf(L"ERROR: (0x%X) CryptMsgOpenToDecode(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) failed\n", ::GetLastError());
}
}
}
// https://stackoverflow.com/questions/50976612/amended-code-to-retrieve-dual-signature-information-from-pe-executable-in-window
// https://www.sysadmins.lv/blog-en/reading-multiple-signatures-from-signed-file-with-powershell.aspx
// 从技术上讲,Microsoft authenticode signature 一次只支持一个签名。附加签名作为嵌套签名完成。
// 第一张图片显示另一个未经身份验证的属性,OID=1.3.6.1.4.1.311.2.4.1 (szOID_NESTED_SIGNATURE)。这是嵌套的 SHA2 签名。它就在这里。让我们从 SHA1 签名中检索一般信息: