Get property's value of date - c

I'm currently writing a C program which have to do WMI query.
I can issue a query without trouble, but it begin to be really hard when it come to retrieve property's value from the VARIANT variable.
I have a huge switch case, and for each case I just want to wprintf the value (the final purpose is to retrieve the value in a wchar_t string).
So far, for the integer type, it wasn't so difficult, but for the datetime type, I can't have something at all.
Here's the example i'm working on (there are no function check for the sake of lisibility, but I do it :
typedef struct cpwmi {
IWbemLocator *locator;
IWbemServices *services;
IEnumWbemClassObject *results;
IWbemClassObject *result;
} cpwmi_s;
void CpWmi_Constructor(cpwmi_s *self)
{
self->locator = NULL;
self->services = NULL;
self->results = NULL;
self->result = NULL;
}
void CpWmi_Destructor(cpwmi_s *self)
{
if (self->result) {
self->result->lpVtbl->Release(self->result);
}
if (self->results) {
self->results->lpVtbl->Release(self->results);
}
if (self->services) {
self->services->lpVtbl->Release(self->services);
}
if (self->locator) {
self->locator->lpVtbl->Release(self->locator);
}
}
void CpWmi_MinimalExample(cpwmi_s *self)
{
// Connection
CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, &self->locator);
BSTR ressource = SysAllocString(L"ROOT\\CIMV2");
self->locator->lpVtbl->ConnectServer(self->locator, ressource, NULL, NULL, NULL, 0, NULL, NULL, &self->services);
SysFreeString(ressource);
// Issue WMI query
BSTR query = SysAllocString(L"SELECT ReleaseDate FROM Win32_BIOS");
BSTR language = SysAllocString(L"WQL");
self->services->lpVtbl->ExecQuery(self->services, language, query, WBEM_FLAG_BIDIRECTIONAL, NULL, &self->results);
SysFreeString(query);
SysFreeString(language);
// Go to first result
ULONG count = 0;
self->results->lpVtbl->Next(self->results, WBEM_INFINITE, 1, &self->result, &count);
// Get propperty's value
BSTR propertyName = SysAllocString(L"ReleaseDate");
VARIANT propertyValue;
CIMTYPE propertyType;
self->result->lpVtbl->Get(self->result, propertyName, 0, &propertyValue, &propertyType, 0);
wprintf(L"%s: ", propertyName);
if (propertyValue.vt != VT_NULL && propertyValue.vt != VT_EMPTY) {
switch (propertyType) {
/* Many case here */
case CIM_DATETIME:
wprintf(L"%f", propertyValue.date);
break;
}
}
VariantClear(&propertyValue);
wprintf(L"\n");
}
int main(void)
{
cpwmi_s wmi;
CpWmi_Constructor(&wmi);
// initialize COM
CoInitializeEx(NULL, COINIT_MULTITHREADED);
CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
CpWmi_MinimalExample(&wmi);
CpWmi_Destructor(&wmi);
// unwind everything else we've allocated
CoUninitialize();
printf("Stop\n");
getchar();
return;
}
I expect to have something like
ReleaseDate : 20140401000000.000000+000
but I end up with
ReleaseDate : -92559592126069970772275711289628980735378270670621907468943360.000000
I must misread msdn documentation or don't know how to search/read, because up to this point, I have no idea how to do that in C !
I'm also worried about CIM_REFERENCE_TYPE, CIM_OBJECT_TYPE and the alike.
Is there anyone who have a hint or know to do that ?

propertyValue.date is double, it needs "%g" print format, but it's probably not valid. Try bstrVal value instead:
case CIM_DATETIME:
//wprintf(L"%g\n", propertyValue.date);
wprintf(L"%s\n", propertyValue.bstrVal);
break;
Note, in debug mode you can move the cursor over propertyValue and it should show the values contained in propertyValue

Related

Verify Signed PE File

(Sorry for my bad English.) I have some problems in my code.so I will try to explain my problem. I have a simple exe file with signed(pkcs7). I want to verify this file (without an internet connection). I don't want to use WinVerifyTrust() because I want to know how windows verify PE file. I wrote some code that first calls CryptQueryObject() with BLOB_DATA(mapped pe file), and it works fine. Then I can get certificate information: signer, serial, etc. (with CryptGetMsgParam()) and it also works fine; but, when I go to verify it with CertGetCertificateChain() I get and error. So, can you help me to solve this problem, which I think is that I don't use CertGetCertificateChain() correctly?
BOOL bRet = FALSE;
DWORD dwMsgAndCertEncodingType = 0;
DWORD dwContentType = 0;
DWORD dwFormatType = 0;
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
PCCERT_CONTEXT pCertContext = NULL;
CERT_BLOB blob_data = { 0 };
// CERT_INFO CertInfo = { 0 };
DWORD dwSignerInfo = 0;
PCCERT_CHAIN_CONTEXT pChainContext = NULL;
PCMSG_SIGNER_INFO pSignerInfo = NULL;
CERT_CHAIN_PARA ChainPara = { 0 };
blob_data.pbData = pBuff;
blob_data.cbData = dwSize;
ChainPara.cbSize = sizeof(CERT_CHAIN_PARA);
bRet = CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
&blob_data,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
NULL,
&dwContentType,
NULL,
&hStore,
&hMsg,
&pCertContext);
if (bRet != TRUE)
{
printf("Error CryptQueryObject\n");
return FALSE;
}
if (dwContentType != CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED)
{
printf("Error type\n");
return FALSE;
}
CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo);
pSignerInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSignerInfo);
CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, pSignerInfo, &dwSignerInfo);
bRet = CertGetCertificateChain(NULL, (PCCERT_CONTEXT)pCertContext, NULL, NULL, NULL, CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY,
NULL, &pChainContext);
if (bRet != TRUE)
{
printf("Error there\n");
}
One problem (though maybe not the only one) is the fact that your fifth parameter to CertGetCertificateChain() is NULL? This should point to a CERT_CHAIN_PARA structure. (You have one declared but don't appear to either initialize or use it.)
I don't know exactly what data you should put in this, but here's an example using code taken from a real (working) program (one of my own making) that checks digital certificates:
//...
CERT_CHAIN_PARA ChainPara; memset(&ChainPara, 0, sizeof(CERT_CHAIN_PARA));
ChainPara.cbSize = sizeof(CERT_CHAIN_PARA);
ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
ChainPara.RequestedUsage.Usage.cUsageIdentifier = 0;
//...
bRet = CertGetCertificateChain(NULL, (PCCERT_CONTEXT)pCertContext, NULL, NULL, &ChainPara,
CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY, NULL, &pChainContext);
//...
You can look at the documentation for CERT_CHAIN_PARA to get some hints on what actual data/values you should set.
Hope this helps! (Feel free to ask for further clarification and or suggestions.)

CreateWellKnownSid says Parameter is incorrect with WinAccountAdministratorSid, but works with WinBuiltAdministratorsSid

I am trying to get the well known SID for the builtin administrator account using CreateWellKnownSid so I can use it in other functions, but I am getting The parameter is incorrect error message when using WinAccountAdministratorSid as first parameter; however, if I use WinBuiltinAdministratorsSid or WinBuiltinUsersSid it works. No idea what's going on.
Code:
#include <Windows.h>
#include <wchar.h>
#include <LM.h>
#include <locale.h>
#pragma comment(lib, "Netapi32.lib")
#define MAX_NAME 256
VOID ShowError(DWORD errorCode)
{
//FormatMessageW
DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS;
LPWSTR errorMessage;
DWORD size = 0;
if (!FormatMessageW(flags, NULL, errorCode, 0, (LPWSTR)&errorMessage, size, NULL))
{
fwprintf(stderr, L"Could not get the format message, error code: %u\n", GetLastError());
exit(1);
}
wprintf(L"\n%s", errorMessage);
LocalFree(errorMessage);
}
int wmain(int argc, WCHAR **argv)
{
_wsetlocale(LC_ALL, L"English");
//LocalAlloc
UINT memFlags = LMEM_FIXED; //Allocates fixed memory
DWORD numOfBytes = SECURITY_MAX_SID_SIZE;
PSID builtInAdminSid;
/*Allocating memory to hold the SID for the
built-in administrator user*/
if (!(builtInAdminSid = LocalAlloc(memFlags, numOfBytes)))
{
ShowError(GetLastError());
return 1;
}
//CreateWellKnownSid
WELL_KNOWN_SID_TYPE accountAdminSid = WinAccountAdministratorSid;
PSID domainSid = NULL;
/*We will ask Windows for the well known Admin SID.
If this function fails, we cannot continue*/
if (!CreateWellKnownSid(accountAdminSid, NULL,
builtInAdminSid, &numOfBytes))
{
ShowError(GetLastError());
LocalFree(builtInAdminSid); //Do not forget to free memory!
return 1;
}
return 0;
}
Am I doing something wrong?
EDIT:
Seems like I have to specify the DomainSid parameter, but how do I retrieve it for the local computer?
some time CreateWellKnownSid require DomainSid parameter by very simply reason - it concatenation the DomainSid with well known rid (add one SubAuthority to sid).
for get DomainSid we can use LsaQueryInformationPolicy with PolicyAccountDomainInformation - Retrieves the name and SID of the system's account domain. - this api call return POLICY_ACCOUNT_DOMAIN_INFO structure where exist DomainSid
#include <Ntsecapi.h>
ULONG CreateSid()
{
LSA_HANDLE PolicyHandle;
static LSA_OBJECT_ATTRIBUTES oa = { sizeof(oa) };
NTSTATUS status = LsaOpenPolicy(0, &oa, POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle);
if (0 <= status)
{
PPOLICY_ACCOUNT_DOMAIN_INFO ppadi;
if (0 <= (status = LsaQueryInformationPolicy(PolicyHandle, PolicyAccountDomainInformation, (void**)&ppadi)))
{
PSID sid = alloca(MAX_SID_SIZE);
ULONG cbSid = MAX_SID_SIZE;
if (!CreateWellKnownSid(::WinAccountAdministratorSid, ppadi->DomainSid, sid, &cbSid))
{
status = GetLastError();
}
LsaFreeMemory(ppadi);
}
LsaClose(PolicyHandle);
}
return status;
}
For those who wonder how I set the RbMm's answer to my code, here it is:
// LsaOpenPolicy
NTSTATUS nOpenPolicy;
LSA_OBJECT_ATTRIBUTES objectAttributes;
LSA_HANDLE policyHandle;
// Fills a block of memory with zeros.
ZeroMemory(&objectAttributes, sizeof(objectAttributes));
nOpenPolicy = LsaOpenPolicy(NULL, &objectAttributes,
POLICY_VIEW_LOCAL_INFORMATION, &policyHandle);
if (nOpenPolicy != STATUS_SUCCESS)
{
ShowError(LsaNtStatusToWinError(nOpenPolicy));
LocalFree(builtInAdminSid);
return 1;
}
// LsaQueryInformationPolicy
NTSTATUS nQueryInfo;
POLICY_INFORMATION_CLASS policyInformation = PolicyAccountDomainInformation;
PPOLICY_ACCOUNT_DOMAIN_INFO pDomainInfo;
nQueryInfo = LsaQueryInformationPolicy(policyHandle, policyInformation, (PVOID *)&pDomainInfo);
if (nQueryInfo != STATUS_SUCCESS)
{
ShowError(LsaNtStatusToWinError(nQueryInfo));
LocalFree(builtInAdminSid);
LsaClose(policyHandle);
return 1;
}
// CreateWellKnownSid
WELL_KNOWN_SID_TYPE accountAdminSid = WinAccountAdministratorSid;
/* We will ask Windows for the well known Admin SID.
If this function fails, we cannot continue */
if (!CreateWellKnownSid(accountAdminSid, pDomainInfo->DomainSid,
builtInAdminSid, &numOfBytes))
{
ShowError(GetLastError());
LocalFree(builtInAdminSid); // Do not forget to free memory!
LsaClose(policyHandle);
return 1;
}
LsaClose(policyHandle);
LsaFreeMemory(pDomainInfo);

Public Key bytes from SecKeyRef

I need to get the raw public key bytes (with openssl this is 'der' format) from a public key SecKeyRef to implement public key pinning with. The actual pinning function is already written, cannot change, and takes a unsigned char *pubkey, size_t pubkeylen with which to compare it's pins against.
This is what I have tried so far:
SecTrustRef trust;
OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
SecKeyRef keyRef = SecTrustCopyPublicKey(trust);
CFDataRef publicKeyBits;
OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, &publicKeyBits) == errSecSuccess;
//OSStatus success = SecItemExport(keyRef, kSecFormatPEMSequence, 0, NULL, &publicKeyBits) == errSecSuccess;
size_t pubkeylen = CFDataGetLength(publicKeyBits);
printf("pubkeylen: %d\n", pubkeylen);
unsigned char *pubkey = CFDataGetBytePtr(publicKeyBits);
The problem is this does not seem to be DER format at all, I expect pubkeylen to be 550 for this particular key, but it prints 526, and the data isn't correct either. I also tried kSecFormatPEMSequence and then base64 decoding it with the same results (it does change into the identical 526 length array as before, so at least it's consistent).
edit:
It looks like when exported to kSecFormatPEMSequence the PEM contains:
-----BEGIN RSA PUBLIC KEY-----
instead of the expected
-----BEGIN PUBLIC KEY-----
Which is a different format that does not base64-decode to DER, maybe it could be parsed with more API calls back into a DER format someway?
What am I missing? Thanks for any help!
edit:
I have also tried the following:
CFDataRef d_tag = CFDataCreate(NULL, "com.bob", 7);
const void *cKeys[] = {kSecClass, kSecAttrKeyType,
kSecAttrKeyClass, kSecValueData,
kSecReturnData
};
const void *cValues[] = {kSecClassKey, kSecAttrKeyTypeRSA,
kSecAttrKeyClassPublic, keyRef,
kCFBooleanTrue
};
CFDictionaryRef saveDict = CFDictionaryCreate(NULL, cKeys, cValues,
5L, NULL, NULL);
OSStatus secStatus = SecItemCopyMatching(saveDict, (CFTypeRef)&publicKeyBits);
if (secStatus == errSecDuplicateItem ) {
printf("errSecDuplicateItem\n");
SecItemDelete(saveDict);
secStatus = SecItemAdd(saveDict, (CFTypeRef)&publicKeyBits );
}
if ( secStatus != noErr ) {
printf("return NULL\n");
return NULL;
}
size_t pubkeylen = CFDataGetLength(publicKeyBits);
printf("pubkeylen: %d\n", pubkeylen);
unsigned char *pubkey = CFDataGetBytePtr(publicKeyBits);
And the results are even worse, pubkeylen is consistently 96, but the data in pubkey is always different!
I have also tried converting https://github.com/datatheorem/TrustKit/blob/master/TrustKit/Pinning/public_key_utils.m#L65 from objective-c, I get:
OSStatus resultAdd, resultDel = noErr;
CFDataRef d_tag = CFDataCreate(NULL, "com.bobe", 9);
const void *peerPublicKeyAddcKeys[] = {kSecClass, kSecAttrApplicationTag,
kSecValueRef, kSecAttrAccessible, kSecReturnData
};
const void *peerPublicKeyAddcValues[] = {kSecClassKey, d_tag,
keyRef, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
kCFBooleanTrue
};
CFDictionaryRef peerPublicKeyAdd = CFDictionaryCreate(NULL, peerPublicKeyAddcKeys, peerPublicKeyAddcValues,
5L, NULL, NULL);
const void *publicKeyGetcKeys[] = {kSecClass, kSecAttrApplicationTag,
kSecReturnData
};
const void *publicKeyGetcValues[] = {kSecClassKey, d_tag,
kCFBooleanTrue
};
CFDictionaryRef publicKeyGet = CFDictionaryCreate(NULL, publicKeyGetcKeys, publicKeyGetcValues,
3L, NULL, NULL);
resultAdd = SecItemAdd(peerPublicKeyAdd, &publicKeyBits);
resultDel = SecItemDelete(publicKeyGet);
if ((resultAdd != errSecSuccess) || (resultDel != errSecSuccess))
{
// Something went wrong with the Keychain we won't know if we did get the right key data
printf("return NULL\n");
return NULL;
}
And this time publicKeyBits is always NULL, I'm starting to tear my hair out here!

Should I IUnknown::Release interfaces created with DllGetClassObject

I'm trying to debug some code that uses COM, which I am a beginner at.
The two calls to IUnknown::Release at the end have got me worried.
The interfaces were created with DllGetClassObject and IClassFactory::CreateInstance.
I have seen other similar code that does not call IUnknown::Release on these - which is correct?
int OpenMixer_Win_DirectSound(px_mixer *Px, int index)
{
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA desc;
HMODULE hDsound = INVALID_HANDLE_VALUE;
GCO DllGetClassObject;
IClassFactory *pcf = NULL;
IKsPropertySet *pps = NULL;
HRESULT hr;
ULONG bytes;
LPGUID guidIn;
LPGUID guidOut;
UINT deviceIn = -1;
UINT deviceOut = -1;
int ret = FALSE;
guidIn = PaWinDS_GetStreamInputGUID(Px->pa_stream);
guidOut = PaWinDS_GetStreamOutputGUID(Px->pa_stream);
do {
hDsound = LoadLibraryA("dsound.dll");
if (hDsound == NULL) {
break;
}
DllGetClassObject = (GCO) GetProcAddress(hDsound, "DllGetClassObject");
if (DllGetClassObject == NULL) {
break;
}
hr = DllGetClassObject(&CLSID_DirectSoundPrivate,
&IID_IClassFactory,
(void **)(&pcf));
if (hr || pcf == NULL) {
break;
}
hr = IClassFactory_CreateInstance(pcf,
NULL,
&IID_IKsPropertySet,
(void **)(&pps));
if (hr || pps == NULL) {
break;
}
/* Do stuff with the interfaces */
} while( FALSE );
if (pps) {
IUnknown_Release(pps);
}
if (pcf) {
IUnknown_Release(pcf);
}
// Free the library. Note that portaudio also opens dsound.dll
// so this probably doesn't do anything until Pa_Terminate is called.
if (hDsound != INVALID_HANDLE_VALUE) {
FreeLibrary(hDsound);
}
}
Absolutely. Both functions create a new interface pointer, they will have a reference count of 1, the AddRef() function was already called. When you're done with it then you have to call Release(). You'll leak memory if you don't. Every interface in COM works this way.
Yes. As seen in DllGetClassObject sample, the return ppvObj will have a refcount.
Yes, DllGetClassObject() will create an object and pass ownership of that object to your code. Your code will now own the object and be responsible for releasing it by calling IUnknown::Release().

WinVerifyTrust to check for a specific signature?

I'm implementing a process elevation helper for Windows. It's a program that will run in elevated mode and launch other programs with administrator privileges without displaying additional UAC prompts. For security reasons, I want to make sure only binaries that are digitally signed with my company's Authenticode key can be executed.
The WinVerifyTrust function gets me halfway there, but it only ensures that a binary is signed by some key that is part of Microsoft's chain of trust. Is there a relatively simple way to perform the Authenticode verification AND ensure that it is signed by our private key?
I believe what you're looking for is CryptQueryObject.
With it you should be able to pull the involved certificate out of a PE, and do any additional checks you want.
By way of example, this will get you to a HCRYPTMSG. From there you can use CryptMsgGetParam to pull out whatever you want. I'd hoped to make something more 'robust', but these APIs are pretty hairy insomuch as they require a lot of branching to handle all their return cases.
So, here's a p/invoke-rific c# example (I started in C, but that was basically unreadable):
static class Crypt32
{
//Omitting flag constants; you can look these up in WinCrypt.h
[DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptQueryObject(
int dwObjectType,
IntPtr pvObject,
int dwExpectedContentTypeFlags,
int dwExpectedFormatTypeFlags,
int dwFlags,
out int pdwMsgAndCertEncodingType,
out int pdwContentType,
out int pdwFormatType,
ref IntPtr phCertStore,
ref IntPtr phMsg,
ref IntPtr ppvContext);
}
class Program
{
static void Main(string[] args)
{
//Path to executable here
// I tested with MS-Office .exe's
string path = "";
int contentType;
int formatType;
int ignored;
IntPtr context = IntPtr.Zero;
IntPtr pIgnored = IntPtr.Zero;
IntPtr cryptMsg = IntPtr.Zero;
if (!Crypt32.CryptQueryObject(
Crypt32.CERT_QUERY_OBJECT_FILE,
Marshal.StringToHGlobalUni(path),
Crypt32.CERT_QUERY_CONTENT_FLAG_ALL,
Crypt32.CERT_QUERY_FORMAT_FLAG_ALL,
0,
out ignored,
out contentType,
out formatType,
ref pIgnored,
ref cryptMsg,
ref context))
{
int error = Marshal.GetLastWin32Error();
Console.WriteLine((new Win32Exception(error)).Message);
return;
}
//expecting '10'; CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED
Console.WriteLine("Context Type: " + contentType);
//Which implies this is set
Console.WriteLine("Crypt Msg: " + cryptMsg.ToInt32());
return;
}
To get the certificate information from signed code use this:
using System.Security.Cryptography.X509Certificates;
X509Certificate basicSigner = X509Certificate.CreateFromSignedFile(filename);
X509Certificate2 cert = new X509Certificate2(basicSigner);
Then you can get the cert details like this:
Console.WriteLine(cert.IssuerName.Name);
Console.WriteLine(cert.SubjectName.Name);
// etc
these are some of the nastiest APIs I've ever worked with
A word of warning: it's worse than you already thought.
At least since introducing SHA-256 signing (has this always been the case?), it's possible for Authenticode to have multiple signatures. They're not encoded as multiple signatures in the PKCS-7 signature message; instead, they're unauthenticated message attributes of type OID_NESTED_SIGNATURE, each containing another complete PKCS-7 signature message.
WinVerifyTrust will tell you the file is valid if any of the signatures are valid and come from a trusted certificate chain. However it won't tell you which of the signatures was valid. If you then use CryptQueryObject to read the full PKCS-7 message, and only look at the certificate for the primary signature (as in the code samples here and on MSDN), you're not necessarily looking at a verified certificate. The associated signature might not match the executable, and/or the certificate might not have a trusted CA chain.
If you're using the details of the primary signature to validate that the certificate is one your software trusts, you're vulnerable to a situation where WinVerifyTrust is trusting a secondary signature, but your code is checking the primary signature's certificate is what you expected, and you haven't noticed that the signature from the primary certificate is nonsense. An attacker could use your public certificate without owning its private key, combined with some other code-signing certificate issued to someone else, to bypass a publisher check this way.
From Win8 onwards, WinVerifyTrust can optionally validate specific signatures, so you should be able to iterate the signatures to find one that is valid and one that satisfies your requirements.
If you have to be Win7-compatible, though, as far as I know the best you can manage is MsiGetFileSignatureInformation. From experimentation (as for everything else here, the actual documentation is frustratingly woolly), it seems to return the trusted certificate when WinVerifyTrust trusts one. But if there isn't a trusted signature, it returns the primary signature's certificate anyway, so you still have to use WinVerifyTrust to check that first.
Of course there also plenty of possible time-of-check/time-of-use problems here.
found the solution here:
http://www.ucosoft.com/how-to-program-to-retrieve-the-authenticode-information.html
here it is with indentation:
#define _UNICODE 1
#define UNICODE 1
#include <windows.h>
#include <tchar.h>
#include <wincrypt.h>
#include <Softpub.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment (lib, "Crypt32")
// the Authenticode Signature is encode in PKCS7
#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
// Information structure of authenticode sign
typedef struct
{
LPWSTR lpszProgramName;
LPWSTR lpszPublisherLink;
LPWSTR lpszMoreInfoLink;
DWORD cbSerialSize;
LPBYTE lpSerialNumber;
LPTSTR lpszIssuerName;
LPTSTR lpszSubjectName;
}
SPROG_SIGNATUREINFO, *PSPROG_SIGNATUREINFO;
VOID GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo);
VOID GetCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo);
BOOL GetAuthenticodeInformation(LPCTSTR lpszFileName, PSPROG_SIGNATUREINFO pInfo)
{
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
PCMSG_SIGNER_INFO pSignerInfo = NULL;
DWORD dwSignerInfo;
BOOL bRet = FALSE;
__try
{
// as CryptQueryObject() only accept WCHAR file name, convert first
WCHAR wszFileName[MAX_PATH];
#ifdef UNICODE
if ( !lstrcpynW( wszFileName, lpszFileName, MAX_PATH))
__leave;
#else
if ( mbstowcs( wszFileName, lpszFileName, MAX_PATH) == -1)
__leave;
#endif
//Retrieve the Message Handle and Store Handle
DWORD dwEncoding, dwContentType, dwFormatType;
if ( !CryptQueryObject( CERT_QUERY_OBJECT_FILE, wszFileName,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY, 0, &dwEncoding,
&dwContentType, &dwFormatType, &hStore,
&hMsg, NULL))
__leave;
//Get the length of SignerInfo
if ( !CryptMsgGetParam( hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo))
__leave;
// allocate the memory for SignerInfo
if ( !(pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc( LPTR, dwSignerInfo)))
__leave;
// get the SignerInfo
if ( !CryptMsgGetParam( hMsg, CMSG_SIGNER_INFO_PARAM, 0, (PVOID)pSignerInfo, &dwSignerInfo))
__leave;
//get the Publisher from SignerInfo
GetProgAndPublisherInfo( pSignerInfo, pInfo);
//get the Certificate from SignerInfo
GetCertificateInfo( hStore, pSignerInfo, pInfo);
bRet = TRUE;
}
__finally
{
// release the memory
if (pSignerInfo != NULL) LocalFree(pSignerInfo);
if (hStore != NULL) CertCloseStore(hStore, 0);
if (hMsg != NULL) CryptMsgClose(hMsg);
}
return bRet;
}
LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
{
LPWSTR outputString = NULL;
// allocate the memory
outputString = (LPWSTR)VirtualAlloc(NULL, (wcslen(inputString) + 1) * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE);
// copy
if (outputString != NULL)
{
lstrcpyW(outputString, inputString);
}
return outputString;
}
VOID GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo)
{
PSPC_SP_OPUS_INFO OpusInfo = NULL;
DWORD dwData;
__try
{
// query SPC_SP_OPUS_INFO_OBJID OID in Authenticated Attributes
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID, pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// get the length of SPC_SP_OPUS_INFO
if ( !CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwData))
__leave;
// allocate the memory for SPC_SP_OPUS_INFO
if ( !(OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData)))
__leave;
// get SPC_SP_OPUS_INFO structure
if ( !CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
OpusInfo,
&dwData))
__leave;
// copy the Program Name of SPC_SP_OPUS_INFO to the return variable
if (OpusInfo->pwszProgramName)
{
pInfo->lpszProgramName = AllocateAndCopyWideString(OpusInfo->pwszProgramName);
}
else
pInfo->lpszProgramName = NULL;
// copy the Publisher Info of SPC_SP_OPUS_INFO to the return variable
if (OpusInfo->pPublisherInfo)
{
switch (OpusInfo->pPublisherInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
pInfo->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
pInfo->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
break;
default:
pInfo->lpszPublisherLink = NULL;
break;
}
}
else
{
pInfo->lpszPublisherLink = NULL;
}
// copy the More Info of SPC_SP_OPUS_INFO to the return variable
if (OpusInfo->pMoreInfo)
{
switch (OpusInfo->pMoreInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
pInfo->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
pInfo->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
break;
default:
pInfo->lpszMoreInfoLink = NULL;
break;
}
}
else
{
pInfo->lpszMoreInfoLink = NULL;
}
break; // we have got the information, break
}
}
}
__finally
{
if (OpusInfo != NULL) LocalFree(OpusInfo);
}
}
VOID GetCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo)
{
PCCERT_CONTEXT pCertContext = NULL;
__try
{
CERT_INFO CertInfo;
DWORD dwData;
// query Signer Certificate in Certificate Store
CertInfo.Issuer = pSignerInfo->Issuer;
CertInfo.SerialNumber = pSignerInfo->SerialNumber;
if ( !(pCertContext = CertFindCertificateInStore( hStore,
ENCODING, 0, CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo, NULL)))
__leave;
dwData = pCertContext->pCertInfo->SerialNumber.cbData;
// SPROG_SIGNATUREINFO.cbSerialSize
pInfo->cbSerialSize = dwData;
// SPROG_SIGNATUREINFO.lpSerialNumber
pInfo->lpSerialNumber = (LPBYTE)VirtualAlloc(NULL, dwData, MEM_COMMIT, PAGE_READWRITE);
memcpy( pInfo->lpSerialNumber, pCertContext->pCertInfo->SerialNumber.pbData, dwData);
// SPROG_SIGNATUREINFO.lpszIssuerName
__try
{
// get the length of Issuer Name
if (!(dwData = CertGetNameString( pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG, NULL, NULL, 0)))
__leave;
// allocate the memory
if ( !(pInfo->lpszIssuerName = (LPTSTR)VirtualAlloc(NULL, dwData * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE)))
__leave;
// get Issuer Name
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG, NULL, pInfo->
lpszIssuerName, dwData)))
__leave;
}
__finally
{
}
// SPROG_SIGNATUREINFO.lpszSubjectName
__try
{
//get the length of Subject Name
if (!(dwData = CertGetNameString( pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0)))
__leave;
// allocate the memory
if ( !(pInfo->lpszSubjectName = (LPTSTR)VirtualAlloc(NULL, dwData * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE)))
__leave;
// get Subject Name
if (!(CertGetNameString( pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, pInfo->lpszSubjectName, dwData)))
__leave;
}
__finally
{
}
}
__finally
{
if (pCertContext != NULL)
CertFreeCertificateContext(pCertContext);
}
}
int _tmain(int argc, TCHAR *argv[])
{
if (argc != 2)
{
_tprintf(_T("Usage: SignedFileInfo \n"));
return 0;
}
else
{
SPROG_SIGNATUREINFO SignInfo;
ZeroMemory(&SignInfo, sizeof(SignInfo));
GetAuthenticodeInformation( argv[1], &SignInfo);
wprintf(L"Program Name: %s\n", SignInfo.lpszProgramName);
wprintf(L"Publisher Link: %s\n", SignInfo.lpszPublisherLink);
wprintf(L"More Info Link: %s\n", SignInfo.lpszMoreInfoLink);
{
_tprintf(_T("Serial Number: "));
DWORD dwData = SignInfo.cbSerialSize;
for (DWORD n = 0; n < dwData; n++)
{
_tprintf(_T("%02x "),
SignInfo.lpSerialNumber[dwData - (n + 1)]);
}
_tprintf(_T("\n"));
}
_tprintf(_T("Issuer Name: %s\n"), SignInfo.lpszIssuerName);
_tprintf(_T("Subject Name: %s\n"), SignInfo.lpszSubjectName);
if ( SignInfo.lpszProgramName) VirtualFree(SignInfo.lpszProgramName, 0, MEM_RELEASE);
if ( SignInfo.lpszPublisherLink) VirtualFree(SignInfo.lpszPublisherLink, 0, MEM_RELEASE);
if ( SignInfo.lpszMoreInfoLink) VirtualFree(SignInfo.lpszMoreInfoLink, 0, MEM_RELEASE);
if ( SignInfo.lpSerialNumber) VirtualFree(SignInfo.lpSerialNumber, 0, MEM_RELEASE);
if ( SignInfo.lpszIssuerName) VirtualFree(SignInfo.lpszIssuerName, 0, MEM_RELEASE);
if ( SignInfo.lpszSubjectName) VirtualFree(SignInfo.lpszSubjectName, 0, MEM_RELEASE);
return 0;
}
}

Resources