Obtaining handle to key with NtCreateKey/NtOpenKey - c

PURPOSE
I'm trying to make a function which will create a given sub key in the HKCU registry hive, or open the sub key if it already exists, then return TRUE.
NOTES
Let RegSidPath represent a fully qualified HKCU registry path with an user SID appended to it such as \\Registry\\User\\S-1-5-20-xxxxxx-xxxxxx-xxxxxxxx-1050
Let KeyToCreate represent a specific registry path such as \\Software\\MyCompany\\MySoftware\\MySubKey
CODE
I have the following function:
BOOL CreateHKCUKey(PWCHAR RegSidPath, PWCHAR KeyToCreate) {
UNICODE_STRING uString;
RtlInitUnicodeString(&uString, RegSidPath);
OBJECT_ATTRIBUTES ObjAttributes;
InitializeObjectAttributes(&ObjAttributes, &uString, OBJ_CASE_INSENSITIVE, 0, 0);
HANDLE BaseKeyHandle = NULL;
NTSTATUS Status = NtOpenKey(&BaseKeyHandle, KEY_CREATE_SUB_KEY, &ObjAttributes);
if (NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND) {
UNICODE_STRING KeyString = { 0 };
do {
PWCHAR NextSubKey = StrStrW((KeyString.Length == 0 ? KeyToCreate : KeyString.Buffer) + 1, L"\\");
DWORD CurrentKeyLength = lstrlenW(KeyToCreate) - lstrlenW(NextSubKey);
PWCHAR CurrentSubKey = PWCHAR(GlobalAlloc(GPTR, CurrentKeyLength + sizeof(WCHAR)));
if (CurrentSubKey != ERROR) {
memcpy(CurrentSubKey, KeyToCreate, CurrentKeyLength * sizeof(WCHAR));
CurrentSubKey[CurrentKeyLength] = UNICODE_NULL;
RtlInitUnicodeString(&KeyString, CurrentSubKey);
OBJECT_ATTRIBUTES KeyAttributes;
InitializeObjectAttributes(&KeyAttributes, &KeyString, OBJ_CASE_INSENSITIVE, &BaseKeyHandle, 0);
HANDLE CurrentHiveEntry = NULL;
Status = NtOpenKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes);
if (RtlNtStatusToDosError(Status) == ERROR_BAD_PATHNAME) {
InitializeObjectAttributes(&KeyAttributes, &KeyString, OBJ_CASE_INSENSITIVE, &CurrentHiveEntry, 0);
DWORD DefaultDisposition;
Status = NtCreateKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &DefaultDisposition);
if (NT_SUCCESS(Status)) {
if (StrCmpNW(KeyString.Buffer + uString.Length, KeyString.Buffer, lstrlenW(KeyToCreate) == 0))
return TRUE;
else continue;
} else break;
} else break;
BaseKeyHandle = CurrentHiveEntry;
}
} while (TRUE);
}
NtClose(BaseKeyHandle);
return FALSE;
}
PROBLEM
Whenever the code gets to this part of the function
Status = NtOpenKey(&CurrentHiveEntry, KEY_CREATE_SUB_KEY, &KeyAttributes);
if (RtlNtStatusToDosError(Status) == ERROR_BAD_PATHNAME) {
The return value is always ERROR_BAD_PATHNAME (161) even if the current sub key already exists.
QUESTION
What is the reason, and what am I doing wrong? Is there anything that I've done which is not correct, and how can I fix it?

NTSTATUS CreateKey(PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, PWCHAR RegSidPath, PWCHAR KeyToCreate, PULONG Disposition)
{
UNICODE_STRING ObjectName;
RtlInitUnicodeString(&ObjectName, RegSidPath);
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName ,OBJ_CASE_INSENSITIVE };
NTSTATUS status = ZwOpenKey(&oa.RootDirectory, KEY_CREATE_SUB_KEY, &oa);
if (0 <= status)
{
ObjectName.Buffer = KeyToCreate;
do
{
ACCESS_MASK Access;
if (KeyToCreate = wcschr(++ObjectName.Buffer, '\\'))
{
ObjectName.Length = (USHORT)RtlPointerToOffset(ObjectName.Buffer, KeyToCreate);
Access = KEY_CREATE_SUB_KEY;
}
else
{
ObjectName.Length = (USHORT)wcslen(ObjectName.Buffer) * sizeof(WCHAR);
Access = DesiredAccess;
}
ObjectName.MaximumLength = ObjectName.Length;
status = ZwCreateKey(KeyHandle, Access, &oa, 0, 0, 0, Disposition);
NtClose(oa.RootDirectory);
oa.RootDirectory = *KeyHandle;
} while (0 <= status && (ObjectName.Buffer = KeyToCreate));
}
return status;
}
and use as
HANDLE hKey;
NTSTATUS status = CreateKey(&hKey, KEY_ALL_ACCESS,
L"\\REGISTRY\\USER\\S-***",
L"\\Software\\MyCompany\\MySoftware\\MySubKey", 0);

Unless you are writing a driver, use RegCreateKeyEx() instead. It handles all the logic of creating intermediate keys for you if they don't already exist. 1 function call, no looping needed.
HKEY hKey;
DWORD dwDisposition;
LONG lRet = RegCreateKeyExW(HKEY_USERS, L"S-1-5-20-xxxxxx-xxxxxx-xxxxxxxx-1050\\Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
if (lRet == 0)
{
...
RegCloseKey(hKey);
}
However, to access the HKEY_CURRENT_USER hive of a specific user, the preferred solution is to use RegOpenCurrentUser() or LoadUserProfile() instead of accessing HKEY_USERS directly:
// impersonate the desired user first, then...
HKEY hRootKey;
LONG lRet = RegOpenCurrentUser(samDesired, &hRootKey);
if (lRet == 0)
{
HKEY hKey;
DWORD dwDisposition;
lRet = RegCreateKeyExW(hRootKey, L"Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
if (lRet == 0)
{
...
RegCloseKey(hKey);
}
RegCloseKey(hRootKey);
}
// stop impersonating...
// obtain token to desired user first, then...
PROFILEINFO profile = {0};
profile.dwSize = sizeof(profile);
profile.dwFlags = PI_NOUI;
if (LoadUserProfile(hToken, &profile))
{
HKEY hKey;
DWORD dwDisposition;
LONG lRet = RegCreateKeyExW((HKEY)profile.hProfile, L"Software\\MyCompany\\MySoftware\\MySubKey", 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, &hKey, &dwDisposition);
if (lRet == 0)
{
...
RegCloseKey(hKey);
}
UnloadUserProfile(hToken, profile.hProfile);
}
// release token ...

Related

Serial number enumeration failed when we cast Descriptor+Descriptor->SerialNumberOffset

I have a kernel driver which is used to find the serial number of storage devices, but there is an issue with the driver.
Descriptor->SerialNumberOffset is 103
but (LPCSTR)(UINT_PTR)Descriptor+(DWORD32)Descriptor->SerialNumberOffset is NULL
here is my code
NTSTATUS GetDeviceTypeAndUniqueID(IN PDEVICE_OBJECT StorageStackDeviceObject, cwDevices *lDeviceTypeArg, char *pszUidArg)
{
DWORRD lDeviceType=0;
STORAGE_PROPERTY_QUERY Query;
STORAGE_DEVICE_DESCRIPTOR Buffer[4];
NTSTATUS Status = STATUS_SUCCESS;
ULONG uBusType=BusTypeUnknown;
PSTORAGE_DEVICE_DESCRIPTOR Descriptor=NULL;
PIRP NewIrp2=NULL;
PIO_STACK_LOCATION NextIrpStack=NULL;
IO_STATUS_BLOCK IoStatus;
char szSptr[2]={'_','\0'};
Query.PropertyId = StorageDeviceProperty;// first set the query properties
Query.QueryType = PropertyStandardQuery;
lDeviceType=0;
if (KeGetCurrentIrql() > PASSIVE_LEVEL)
{
return STATUS_SUCCESS;
}
if(StorageStackDeviceObject == NULL)
{
return STATUS_SUCCESS;
}
if((StorageStackDeviceObject->DeviceType != FILE_DEVICE_DISK) &&
(StorageStackDeviceObject->DeviceType != FILE_DEVICE_CD_ROM)&&
(StorageStackDeviceObject->DeviceType != FILE_DEVICE_DVD)&&
(StorageStackDeviceObject->DeviceType !=FILE_DEVICE_TAPE) )
{
return STATUS_SUCCESS;
}
KeInitializeEvent(&WaitEvent_newIrp, NotificationEvent, TRUE);// initialize the waitable event
__try
{
NewIrp2=IoBuildDeviceIoControlRequest(IOCTL_STORAGE_QUERY_PROPERTY, StorageStackDeviceObject,
(PVOID)&Query,sizeof(STORAGE_PROPERTY_QUERY),
(PVOID)Buffer,sizeof(STORAGE_DEVICE_DESCRIPTOR)*4,
FALSE,&WaitEvent_newIrp,&IoStatus);
if(NewIrp2==NULL)
{
return STATUS_SUCCESS;
}
Status = IoCallDriver(StorageStackDeviceObject, NewIrp2);// send this irp to the storage device
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&WaitEvent_newIrp, Executive, KernelMode, FALSE, NULL);
Status =IoStatus.Status;
}
}
__finally
{
if(NT_SUCCESS(Status))
{
if (NT_SUCCESS(Status))
{
if(Buffer!=NULL)
{
char szStart[256];
Descriptor = (PSTORAGE_DEVICE_DESCRIPTOR)Buffer;
uBusType = Descriptor->BusType; //Get the bus type.
if(Descriptor->SerialNumberOffset!=0)//Is Valid SerialNumberOffset, returns 103
{
strcpy(szStart,(char*)(UINT_PTR)Descriptor+(DWORD32)Descriptor->SerialNumberOffset);
//szStart is null
}
}
NewIrp2 = NULL;
}
}
}
}
please share how to solve this
you mistake in OutputBufferLength - why you decide that sizeof(STORAGE_DEVICE_DESCRIPTOR)*4 is enough ? why not 5* sizeof(STORAGE_DEVICE_DESCRIPTOR) for example ? really what OutputBufferLength must be is unknown - you need call this query in loop and compare your current OutputBufferLength with STORAGE_DEVICE_DESCRIPTOR.Size - if OutputBufferLength < STORAGE_DEVICE_DESCRIPTOR.Size - you must again send IOCTL_STORAGE_QUERY_PROPERTY with OutputBufferLength = STORAGE_DEVICE_DESCRIPTOR.Size. the code can be look like.
STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery };
union {
PVOID buf;
PSTR psz;
PSTORAGE_DEVICE_DESCRIPTOR psdd;
};
ULONG size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x100;
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
do
{
status = STATUS_INSUFFICIENT_RESOURCES;
if (buf = ExAllocatePool(PagedPool, size))
{
switch (status = (NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb,
IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, size)))
{
case STATUS_SUCCESS:
case STATUS_BUFFER_OVERFLOW:
if (psdd->Version == sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
if (psdd->Size > size)
{
size = psdd->Size;
status = STATUS_BUFFER_OVERFLOW;
}
else
{
if (psdd->SerialNumberOffset)
{
DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
}
}
}
else
{
status = STATUS_INVALID_PARAMETER;
}
break;
}
ExFreePool(buf);
}
} while (status == STATUS_BUFFER_OVERFLOW);
in place NtDeviceIoControlFile we of course can use IoBuildDeviceIoControlRequest + IoCallDriver - this nothing change and unrelated to problem

X509 Certificate verification with Windows Crypto API

I need to write a C program for Windows that receives a certificate from network (in PEM format) and validates its signature with a certificate chain file (which is already presented in the application's folder).
Writing such an application is pretty easy and strait forward with openssl library but seems a little complicated with the Windows Crypto API.
Here is what I've tried so far:
First I thought I can create a HCERTSTORE using the certificate-chain file:
HCERTSTORE hFileStoreHandle = CertOpenStore(
CERT_STORE_PROV_FILENAME,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL,
(CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG),
L"certificate-chain.pem.cert");
Then I figured I can iterate through the store and get PCCERT_CONTEXT struct of certificates:
PCCERT_CONTEXT CAfileContext = NULL;
while(CAfileContext = CertEnumCertificatesInStore(
hCertStore,
CAfileContext)) {
//start verification here
}
I don't know if I am on the right track or not but I'm facing two major problems here.
First is I don't know how to get the received certificate from buffer and convert it to a proper struct in order to validate its signature with certificate-chain file.
Second is I don't know how to verify a certificate signature using the CA chain file.
I'll appreciate all the suggestions and helps.
Here is the sample code for you, hopefully it can help you.
HRESULT hr = E_FAIL;
DWORD dwError = 0;
PCCERT_CONTEXT pCert = NULL;
HCERTSTORE hCertStore = CertOpenStore(
CERT_STORE_PROV_FILENAME,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
NULL,
(CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG),
L"certificate-chain.pem.cert");
do
{
if ((pCert = CertEnumCertificatesInStore(hCertStore, pCert)) == NULL)
break;
PCCERT_CONTEXT pcIssuer = NULL;
PCCERT_CONTEXT pcSubject = CertDuplicateCertificateContext(pCert);
for (; NULL != pcSubject;)
{
DWORD dwFlags = 0;
BOOL bret = TRUE;
hr = S_OK;
pcIssuer = CertGetIssuerCertificateFromStore(
hCertStore, pcSubject, NULL, &dwFlags);
if (pcIssuer == NULL)
{
dwError = GetLastError();
if (CRYPT_E_SELF_SIGNED != dwError)
{
hr = E_FAIL;
break;
}
else
{
if ((bret = CertCompareCertificateName(
X509_ASN_ENCODING,
&pcSubject->pCertInfo->Subject,
&pcSubject->pCertInfo->Issuer)) == FALSE)
{
hr = E_FAIL;
break;
}
}
}
HCRYPTPROV hprov = NULL;
bret = CryptAcquireContext(
&hprov, nullptr, nullptr, PROV_RSA_FULL,
CRYPT_SILENT | CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET);
if (FALSE == bret)
{
dwError = GetLastError();
if (NTE_EXISTS == dwError)
bret = CryptAcquireContext(
&hprov, nullptr, nullptr, PROV_RSA_FULL,
CRYPT_SILENT | CRYPT_VERIFYCONTEXT);
if (FALSE == bret)
{
hr = E_FAIL;
break;
}
}
if ((bret = CryptVerifyCertificateSignatureEx(
hprov, X509_ASN_ENCODING,
CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
(void*)pcSubject,
CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
(void*)(pcIssuer == nullptr ? pcSubject : pcIssuer),
0, nullptr)) == FALSE)
{
hr = E_FAIL;
break;
}
if (nullptr == pcIssuer)
{
CERT_PUBLIC_KEY_INFO msCert;
msCert.Algorithm =
pcSubject->pCertInfo->SubjectPublicKeyInfo.Algorithm;
msCert.PublicKey.cbData =
sizeof(PETRUSTED_ROOTCERT_PUBKEY);
msCert.PublicKey.pbData =
const_cast<LPBYTE>(PETRUSTED_ROOTCERT_PUBKEY);
msCert.PublicKey.cUnusedBits =
pcSubject->pCertInfo->SubjectPublicKeyInfo.PublicKey.cUnusedBits;
if (FALSE == CertComparePublicKeyInfo(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&pcSubject->pCertInfo->SubjectPublicKeyInfo, &msCert))
{
hr = E_FAIL;
break;
}
}
bret = CryptReleaseContext(hprov, 0);
hprov = NULL;
CertFreeCertificateContext(pcSubject);
pcSubject = pcIssuer;
pcIssuer = NULL;
}
if (pcIssuer != NULL)
CertFreeCertificateContext(pcIssuer);
if (pcSubject != NULL)
CertFreeCertificateContext(pcSubject);
if (FAILED(hr))
{
CertFreeCertificateContext(pCert);
break;
}
} while (pCert != NULL);
CertCloseStore(hCertStore, 0);
if (FAILED(hr))
wprintf(L"Failed to verify X509 certification.\n");
else
wprintf(L"Successfully verify X509 certification.\n");

How do I configure WinHTTP Tracing programmatically?

I have a C application (not C++ or C#) that tests connectivity to our API from a client's perspective using WinHTTP. Here's code that downloads a file.
DWORD Measure(FILE* fout, LPCTSTR lpszUrl) {
int i;
DWORD dwResult = 0;
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
DWORD dwRequestSize = 0;
LPSTR pszOutBuffer;
BOOL bResults = FALSE;
HINTERNET hSession = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
WINHTTP_STATUS_CALLBACK status = NULL;
if (0 == dwResult) {
hSession = WinHttpOpen(_T("Test/1.0"), WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (hSession == NULL) {
dwResult = GetLastError();
}
}
if (0 == dwResult) {
status = WinHttpSetStatusCallback(hSession, (WINHTTP_STATUS_CALLBACK)HttpCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, NULL);
if (WINHTTP_INVALID_STATUS_CALLBACK == status) {
dwResult = GetLastError();
}
}
if (0 == dwResult) {
hConnect = WinHttpConnect(hSession, lpszUrl, INTERNET_DEFAULT_HTTPS_PORT, 0);
if (hConnect == NULL) {
dwResult = GetLastError();
}
}
if (0 == dwResult) {
hRequest = WinHttpOpenRequest(hConnect, L"GET", NULL, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE | WINHTTP_FLAG_REFRESH);
if (hRequest == NULL) {
dwResult = GetLastError();
}
}
if (0 == dwResult) {
bResults = WinHttpSendRequest(hRequest,WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
}
if (0 == dwResult) {
if(!QueryPerformanceCounter(&liDownloadStart)){
dwResult = GetLastError();
}
}
if (bResults) {
bResults = WinHttpReceiveResponse(hRequest, NULL);
if (!bResults) {
dwResult = GetLastError();
}
}
if (dwResult == 0 && bResults) {
do {
dwSize = 0;
if( !WinHttpQueryDataAvailable( hRequest, &dwSize ) ){
dwResult = GetLastError();
}
if (0 == dwResult) {
dwRequestSize += dwSize;
}
pszOutBuffer = (LPSTR)malloc(dwSize+1);
if (!pszOutBuffer) {
dwResult = ERROR_OUTOFMEMORY;
}
if (dwResult == 0) {
ZeroMemory(pszOutBuffer, dwSize + 1);
if( !WinHttpReadData( hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded ) ){
dwResult = GetLastError();
}
free(pszOutBuffer);
}
} while (dwSize > 0 && dwResult == 0);
}
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
return dwResult;
}
I can enable tracing using the following module:
BOOL enable = TRUE;
WinHttpSetOption(NULL, WINHTTP_OPTION_ENABLETRACING, &enable, sizeof(enable));
Following the instructions in the article Capturing WinHTTP Logs, I could ask the user to enable tracing, including the path where files should be stored.
netsh winhttp set tracing trace-file-prefix="C:\Temp\dpws" level=verbose format=ansi state=enabled max-trace-file-size=1073741824
However, most folks running my application are technically challenged. They may forget to disable it when they are done.
How do I programmatically enable WinHTTP tracing for my process?

Windows 7 DLL Injection

I am trying to inject a dll into an existing process. I am trying to use the CreateRemoteThread LoadLibrary way. I understand how it works, but I cannot figure out why CreateRemoteThread is returning null (failing)... I am on Windows 7 so this may be the reason, but I don't know for sure if it is.. Perhaps I need to set privaleges? My code is below:
#define DLL_NAME "message.dll"
void main()
{
InjectDLL(1288, DLL_NAME);
}
BOOL InjectDLL(DWORD dwProcessId, LPCSTR lpszDLLPath)
{
HANDLE hProcess, hThread;
LPVOID lpBaseAddr, lpFuncAddr;
DWORD dwMemSize, dwExitCode;
BOOL bSuccess = FALSE;
HMODULE hUserDLL;
if((hProcess = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION
|PROCESS_VM_WRITE|PROCESS_VM_READ, FALSE, dwProcessId)))
{
dwMemSize = lstrlen(lpszDLLPath) + 1;
if(lpBaseAddr = VirtualAllocEx(hProcess, NULL, dwMemSize, MEM_COMMIT, PAGE_READWRITE))
{
if(WriteProcessMemory(hProcess, lpBaseAddr, lpszDLLPath, dwMemSize, NULL))
{
if(hUserDLL = LoadLibrary(TEXT("kernel32.dll")))
{
if(lpFuncAddr = GetProcAddress(hUserDLL, TEXT("LoadLibraryA")))
{
if(hThread = CreateRemoteThread(hProcess, NULL, 0, lpFuncAddr, lpBaseAddr, 0, NULL))
{
WaitForSingleObject(hThread, INFINITE);
if(GetExitCodeThread(hThread, &dwExitCode)) {
bSuccess = (dwExitCode != 0) ? TRUE : FALSE;
}
CloseHandle(hThread);
}
}
FreeLibrary(hUserDLL);
}
}
VirtualFreeEx(hProcess, lpBaseAddr, 0, MEM_RELEASE);
}
CloseHandle(hProcess);
}
return bSuccess;
}
yes you need privileges before you open the precess, here's the code:
int GimmePrivileges(){
HANDLE Token;
TOKEN_PRIVILEGES tp;
if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &Token)
{
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid); tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(Token, 0, &tp, sizeof(tp), NULL, NULL);
}
}
An other thing... this code is confusing!!! you need to synthesize!

C - Verify code signatures - Windows API

I need to verify code signatures of binaries. Microsoft Authenticode I think is the term. Is there a sane way to do this using the Windows API?
Have you looked at WinVerifyTrust ? Since it's not immediately obvious how to use it to verify the signature of a binary, you probably want to look at the sample code specifically for that.
How to find authenticode for drivers:
Disclaimer: I did not write this code.
BOOL VerifyEmbeddedSignature(LPCWSTR pwszSourceFile)
{
LONG lStatus;
GUID WintrustVerifyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
GUID DriverActionGuid = DRIVER_ACTION_VERIFY;
HANDLE hFile;
DWORD dwHash;
BYTE bHash[100];
HCATINFO hCatInfo;
HCATADMIN hCatAdmin;
WINTRUST_DATA wd = { 0 };
WINTRUST_FILE_INFO wfi = { 0 };
WINTRUST_CATALOG_INFO wci = { 0 };
////set up structs to verify files with cert signatures
memset(&wfi, 0, sizeof(wfi));
wfi.cbStruct = sizeof( WINTRUST_FILE_INFO );
wfi.pcwszFilePath = pwszSourceFile;
wfi.hFile = NULL;
wfi.pgKnownSubject = NULL;
memset(&wd, 0, sizeof(wd));
wd.cbStruct = sizeof( WINTRUST_DATA );
wd.dwUnionChoice = WTD_CHOICE_FILE;
wd.pFile = &wfi;
wd.dwUIChoice = WTD_UI_NONE;
wd.fdwRevocationChecks = WTD_REVOKE_NONE;
wd.dwStateAction = 0;
wd.dwProvFlags = WTD_SAFER_FLAG;
wd.hWVTStateData = NULL;
wd.pwszURLReference = NULL;
wd.pPolicyCallbackData = NULL;
wd.pSIPClientData = NULL;
wd.dwUIContext = 0;
lStatus = WinVerifyTrust( NULL, &WintrustVerifyGuid, &wd );
////if failed, try to verify using catalog files
if (lStatus != ERROR_SUCCESS)
{
//open the file
hFile = CreateFileW(pwszSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
dwHash = sizeof(bHash);
if (!CryptCATAdminCalcHashFromFileHandle(hFile, &dwHash, bHash, 0))
{
CloseHandle(hFile);
return FALSE;
}
//Create a string form of the hash (used later in pszMemberTag)
LPWSTR pszMemberTag = new WCHAR[dwHash * 2 + 1];
for ( DWORD dw = 0; dw < dwHash; ++dw )
{
wsprintfW( &pszMemberTag[dw * 2], L"%02X", bHash[dw] );
}
if (!CryptCATAdminAcquireContext(&hCatAdmin, &DriverActionGuid, 0))
{
CloseHandle(hFile);
return FALSE;
}
//find the catalog which contains the hash
hCatInfo = CryptCATAdminEnumCatalogFromHash(hCatAdmin, bHash, dwHash, 0, NULL);
if ( hCatInfo )
{
CATALOG_INFO ci = { 0 };
CryptCATCatalogInfoFromContext( hCatInfo, &ci, 0 );
memset(&wci, 0, sizeof(wci));
wci.cbStruct = sizeof( WINTRUST_CATALOG_INFO );
wci.pcwszCatalogFilePath = ci.wszCatalogFile;
wci.pcwszMemberFilePath = pwszSourceFile;
wci.pcwszMemberTag = pszMemberTag;
memset(&wd, 0, sizeof(wd));
wd.cbStruct = sizeof( WINTRUST_DATA );
wd.dwUnionChoice = WTD_CHOICE_CATALOG;
wd.pCatalog = &wci;
wd.dwUIChoice = WTD_UI_NONE;
wd.fdwRevocationChecks = WTD_STATEACTION_VERIFY;
wd.dwProvFlags = 0;
wd.hWVTStateData = NULL;
wd.pwszURLReference = NULL;
wd.pPolicyCallbackData = NULL;
wd.pSIPClientData = NULL;
wd.dwUIContext = 0;
lStatus = WinVerifyTrust( NULL, &WintrustVerifyGuid, &wd );
CryptCATAdminReleaseCatalogContext( hCatAdmin, hCatInfo, 0 );
}
CryptCATAdminReleaseContext( hCatAdmin, 0 );
delete[] pszMemberTag;
CloseHandle(hFile);
}
if (lStatus != ERROR_SUCCESS)
return false;
else
return true;
}
Here is the working code to verify a file (technically any file type).
#include <stdio.h>
#include <windows.h>
#include <Softpub.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <mscat.h>
#include <atlbase.h>
// Link with the Wintrust.lib file.
#pragma comment (lib, "wintrust")
BOOL VerifySignature(LPCSTR path) //We will receive the char* filepath not wchar*
{
USES_CONVERSION;
LPCWSTR pwszSourceFile = A2W(path); //We convert the char* to wchar*
LONG lStatus;
GUID WintrustVerifyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
GUID DriverActionGuid = DRIVER_ACTION_VERIFY;
HANDLE hFile;
DWORD dwHash;
BYTE bHash[100];
HCATINFO hCatInfo;
HCATADMIN hCatAdmin;
WINTRUST_DATA wd = { 0 };
WINTRUST_FILE_INFO wfi = { 0 };
WINTRUST_CATALOG_INFO wci = { 0 };
////set up structs to verify files with cert signatures
wfi.cbStruct = sizeof(WINTRUST_FILE_INFO);
wfi.pcwszFilePath = pwszSourceFile;
wfi.hFile = NULL;
wfi.pgKnownSubject = NULL;
wd.cbStruct = sizeof(WINTRUST_DATA);
wd.pPolicyCallbackData = NULL;
wd.pSIPClientData = NULL;
wd.dwUIChoice = WTD_UI_NONE;
wd.fdwRevocationChecks = WTD_REVOKE_NONE;
wd.dwUnionChoice = WTD_CHOICE_FILE;
wd.pFile = &wfi;
wd.dwStateAction = WTD_STATEACTION_VERIFY;
wd.hWVTStateData = NULL;
wd.pwszURLReference = NULL;
wd.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
wd.dwUIContext = 0;
wd.pSignatureSettings = 0;
lStatus = WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &WintrustVerifyGuid, &wd);
wd.dwStateAction = WTD_STATEACTION_CLOSE;
WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &WintrustVerifyGuid, &wd); //close hWVTStateData
////if failed, try to verify using catalog files
if (lStatus != ERROR_SUCCESS)
{
//open the file
hFile = CreateFileW(pwszSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
dwHash = sizeof(bHash);
if (!CryptCATAdminCalcHashFromFileHandle(hFile, &dwHash, bHash, 0))
{
CloseHandle(hFile);
return FALSE;
}
//Create a string form of the hash (used later in pszMemberTag)
LPWSTR pszMemberTag = new WCHAR[dwHash * 2 + 1];
for (DWORD dw = 0; dw < dwHash; ++dw)
{
wsprintfW(&pszMemberTag[dw * 2], L"%02X", bHash[dw]);
}
if (!CryptCATAdminAcquireContext(&hCatAdmin, &DriverActionGuid, 0))
{
CloseHandle(hFile);
return FALSE;
}
//find the catalog which contains the hash
hCatInfo = CryptCATAdminEnumCatalogFromHash(hCatAdmin, bHash, dwHash, 0, NULL);
if (hCatInfo)
{
CATALOG_INFO ci = { 0 };
CryptCATCatalogInfoFromContext(hCatInfo, &ci, 0);
memset(&wci, 0, sizeof(wci));
wci.cbStruct = sizeof(WINTRUST_CATALOG_INFO);
wci.pcwszCatalogFilePath = ci.wszCatalogFile;
wci.pcwszMemberFilePath = pwszSourceFile;
wci.hMemberFile = hFile;
wci.pcwszMemberTag = pszMemberTag;
wci.pbCalculatedFileHash = bHash;
wci.cbCalculatedFileHash = dwHash;
wci.hCatAdmin = hCatAdmin;
memset(&wd, 0, sizeof(wd));
wd.cbStruct = sizeof(WINTRUST_DATA);
wd.pPolicyCallbackData = NULL;
wd.pSIPClientData = NULL;
wd.dwUIChoice = WTD_UI_NONE;
wd.fdwRevocationChecks = WTD_REVOKE_NONE;
wd.dwUnionChoice = WTD_CHOICE_CATALOG;
wd.pCatalog = &wci;
wd.dwStateAction = WTD_STATEACTION_VERIFY;
wd.hWVTStateData = NULL;
wd.pwszURLReference = NULL;
wd.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
wd.dwUIContext = 0;
wd.pSignatureSettings = 0;
lStatus = WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &WintrustVerifyGuid, &wd);
wd.dwStateAction = WTD_STATEACTION_CLOSE;
WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &WintrustVerifyGuid, &wd); //close hWVTStateData
CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0);
}
CryptCATAdminReleaseContext(hCatAdmin, 0);
delete[] pszMemberTag;
CloseHandle(hFile);
}
return (lStatus == ERROR_SUCCESS);
}
int main(int argc, char *argv[])
{
if (VerifySignature(argv[1]))
printf("Verified file signature\n");
else
printf("Could not verify file signature\n");
return 0;
}

Resources