NtQueryInformationProcess keep to fail - c++

I try to get command line parameters of another process in Windows 7. I'm using this article as reference.
But the return error gives me always (NTSTATUS dwStatus) negative number like:
dwStatus = -1073741820
the process ID is valid, and the apa = GetLastError(); is S_OK
here is my code:
header file
class NtDll
{
...
typedef DWORD (WINAPI *PNtQueryInformationProcess)(HANDLE, DWORD, PVOID, DWORD, PVOID );
static PNtQueryInformationProcess NtQueryInformationProcess;
....
....
}
c++ file
i do this to enable access :
//== only to have better chance to read processes =====
if(!sm_EnableTokenPrivilege(SE_DEBUG_NAME))
{
return 0;
}
// Attempt to access process
BOOL bReturnStatus = TRUE;
DWORD dwSize = 0;
DWORD dwSizeNeeded = 0;
DWORD dwBytesRead = 0;
DWORD dwBufferSize = 0;
HANDLE hHeap = 0;
WCHAR *pwszBuffer = NULL;
smPROCESSINFO spi = {0};
smPPROCESS_BASIC_INFORMATION pbi = NULL;
smPEB peb = {0};
smPEB_LDR_DATA peb_ldr = {0};
smRTL_USER_PROCESS_PARAMETERS peb_upp = {0};
ZeroMemory(&spi, sizeof(spi));
ZeroMemory(&peb, sizeof(peb));
ZeroMemory(&peb_ldr, sizeof(peb_ldr));
ZeroMemory(&peb_upp, sizeof(peb_upp));
// pSysProcess->dUniqueProcessId is the process number
HRESULT apa = 0;
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ, FALSE,pSysProcess->dUniqueProcessId);
if(hProcess == INVALID_HANDLE_VALUE) {
return FALSE;
}
else if(hProcess == NULL)
{
apa = GetLastError();
}
// Try to allocate buffer
hHeap = GetProcessHeap();
dwSize = sizeof(smPPROCESS_BASIC_INFORMATION);
pbi = (smPPROCESS_BASIC_INFORMATION)HeapAlloc(hHeap,
HEAP_ZERO_MEMORY,
dwSize);
// Did we successfully allocate memory
if(!pbi) {
CloseHandle(hProcess);
return FALSE;
}
// Attempt to get basic info on process
NTSTATUS dwStatus = NtDll::NtQueryInformationProcess(hProcess,
ProcessBasicInformation,
pbi,
dwSize,
&dwSizeNeeded);
HRESULT dwStatus_err = 0;
dwStatus_err = GetLastError(); // This gives me always access denied
all seems right but still I'm getting the wrong results any idea why ?
UPDATE:
i added the indication that i do sm_EnableTokenPrivilege
and :
dwStatus_err = GetLastError(); // This gives me always access denied

Simple way to get the command line parameters for processes:
WMIC path win32_process get Caption,Processid,Commandline
Another way via code (see this article):
VOID GetCommandLines()
{
HRESULT hr = 0;
IWbemLocator *WbemLocator = NULL;
IWbemServices *WbemServices = NULL;
IEnumWbemClassObject *EnumWbem = NULL;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
// Note: If you are using Windows 2000, you need to specify -
// the default authentication credentials for a user by using
// a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
// parameter of CoInitializeSecurity ------------------------
hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &WbemLocator);
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
hr = WbemLocator->ConnectServer(L"ROOT\\CIMV2", NULL, NULL, NULL, 0, NULL, NULL, &WbemServices);
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hr = CoSetProxyBlanket(WbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
hr = WbemServices->ExecQuery(L"WQL", L"SELECT ProcessId,CommandLine FROM Win32_Process", WBEM_FLAG_FORWARD_ONLY, NULL, &EnumWbem);
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
if (EnumWbem != NULL) {
IWbemClassObject *result = NULL;
ULONG returnedCount = 0;
while((hr = EnumWbem->Next(WBEM_INFINITE, 1, &result, &returnedCount)) == S_OK) {
VARIANT ProcessId;
VARIANT CommandLine;
// access the properties
hr = result->Get(L"ProcessId", 0, &ProcessId, 0, 0);
hr = result->Get(L"CommandLine", 0, &CommandLine, 0, 0);
if (!(CommandLine.vt==VT_NULL))
wprintf(L"%u %s \r\n", ProcessId.uintVal, CommandLine.bstrVal);
result->Release();
}
}
// Cleanup
// ========
EnumWbem->Release();
WbemServices->Release();
WbemLocator->Release();
CoUninitialize();
getchar();
}
If you really want to get NtQueryInformationProcess working, see this blog entry, or this file.
DWORD GetRemoteCommandLineW(HANDLE hProcess, LPWSTR pszBuffer, UINT bufferLength)
{
struct RTL_USER_PROCESS_PARAMETERS_I
{
BYTE Reserved1[16];
PVOID Reserved2[10];
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
};
struct PEB_INTERNAL
{
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
struct PEB_LDR_DATA* Ldr;
RTL_USER_PROCESS_PARAMETERS_I* ProcessParameters;
BYTE Reserved4[104];
PVOID Reserved5[52];
struct PS_POST_PROCESS_INIT_ROUTINE* PostProcessInitRoutine;
BYTE Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
};
typedef NTSTATUS (NTAPI* NtQueryInformationProcessPtr)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL);
typedef ULONG (NTAPI* RtlNtStatusToDosErrorPtr)(NTSTATUS Status);
// Locating functions
HINSTANCE hNtDll = GetModuleHandleW(L"ntdll.dll");
NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hNtDll, "NtQueryInformationProcess");
RtlNtStatusToDosErrorPtr RtlNtStatusToDosError = (RtlNtStatusToDosErrorPtr)GetProcAddress(hNtDll, "RtlNtStatusToDosError");
if(!NtQueryInformationProcess || !RtlNtStatusToDosError)
{
printf("Functions cannot be located.\n");
return 0;
}
// Get PROCESS_BASIC_INFORMATION
PROCESS_BASIC_INFORMATION pbi;
ULONG len;
NTSTATUS status = NtQueryInformationProcess(
hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &len);
SetLastError(RtlNtStatusToDosError(status));
if(NT_ERROR(status) || !pbi.PebBaseAddress)
{
printf("NtQueryInformationProcess(ProcessBasicInformation) failed.\n");
return 0;
}
// Read PEB memory block
SIZE_T bytesRead = 0;
PEB_INTERNAL peb;
if(!ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), &bytesRead))
{
printf("Reading PEB failed.\n");
return 0;
}
// Obtain size of commandline string
RTL_USER_PROCESS_PARAMETERS_I upp;
if(!ReadProcessMemory(hProcess, peb.ProcessParameters, &upp, sizeof(upp), &bytesRead))
{
printf("Reading USER_PROCESS_PARAMETERS failed.\n");
return 0;
}
if(!upp.CommandLine.Length)
{
printf("Command line length is 0.\n");
return 0;
}
// Check the buffer size
DWORD dwNeedLength = (upp.CommandLine.Length+1) / sizeof(wchar_t) +1;
if(bufferLength < dwNeedLength)
{
printf("Not enough buffer.\n");
return dwNeedLength;
}
// Get the actual command line
pszBuffer[dwNeedLength - 1] = L'\0';
if(!ReadProcessMemory(hProcess, upp.CommandLine.Buffer, pszBuffer, upp.CommandLine.Length, &bytesRead))
{
printf("Reading command line failed.\n");
return 0;
}
return bytesRead / sizeof(wchar_t);
}

Related

Is there a Win32 function to get the architecture of a DLL?

There is GetBinaryType() for determining if an .exe file is 32-bit or 64-bit, but how can I do this for a .dll file? I want to ensure that a DLL is the right architecture before trying to load it with LoadLibrary().
if you want use dll for call functions or load resource from it - just try load it. if it was wrong architecture - you got error ERROR_BAD_EXE_FORMAT and dll wil be not loaded. check before this nothing more give. the try load is check already.
if you need check for some other reasons, exist several ways. most correct is next - open file, create image section from it and check section (last operation is undocumented)
HRESULT CheckImage( _In_ PCWSTR lpLibFileName, _Out_ PUSHORT Machine)
{
HANDLE hFile = CreateFileW(lpLibFileName, FILE_EXECUTE|FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
HANDLE hSection = CreateFileMappingW(hFile, 0, PAGE_EXECUTE_READ|SEC_IMAGE, 0, 0, 0);
NtClose(hFile);
if (hSection)
{
SECTION_IMAGE_INFORMATION sii;
NTSTATUS status = ZwQuerySection(hSection, SectionImageInformation, &sii, sizeof(sii), 0);
NtClose(hSection);
*Machine = sii.Machine;
return status ? HRESULT_FROM_NT(status) : S_OK;
}
}
return HRESULT_FROM_WIN32(GetLastError());
}
you got or some error from this func (file not found, bad image, etc) or some IMAGE_FILE_MACHINE_*. usually IMAGE_FILE_MACHINE_AMD64 or IMAGE_FILE_MACHINE_I386
another way - use LoadLibraryExW with LOAD_LIBRARY_AS_DATAFILE and check IMAGE_NT_HEADERS of mapped image - really this way do all what first (including ZwQuerySection call internally) + mapped dll to memory - last is not need. so this is less efficient way.
HRESULT CheckImage2( _In_ PCWSTR lpLibFileName, _Out_ PUSHORT Machine)
{
if (HMODULE hmod = LoadLibraryExW(lpLibFileName, 0, LOAD_LIBRARY_AS_DATAFILE))
{
HRESULT hr = S_OK;
if (PIMAGE_NT_HEADERS pinth = RtlImageNtHeader(PAGE_ALIGN(hmod)))
{
*Machine = pinth->FileHeader.Machine;
}
else
{
hr = HRESULT_FROM_NT(STATUS_INVALID_IMAGE_NOT_MZ);
}
FreeLibrary(hmod);
return hr;
}
return HRESULT_FROM_WIN32(GetLastError());
}
else one way - direct read file and check it headers. this from one side is fastest, from another side - even if headers is correct - no guarantee that whole file is ok and not corrupted
HRESULT CheckImage3( _In_ PCWSTR lpLibFileName, _Out_ PUSHORT Machine, _Out_ PBOOL Is64Bit)
{
HANDLE hFile = CreateFileW(lpLibFileName, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
HRESULT hr = HRESULT_FROM_NT(STATUS_INVALID_IMAGE_NOT_MZ);
union {
IMAGE_DOS_HEADER idh;
IMAGE_NT_HEADERS inth;
};
OVERLAPPED ov {};
ULONG dwBytesRead;
if (ReadFile(hFile, &idh, sizeof(idh), &dwBytesRead, &ov))
{
if (dwBytesRead == sizeof(idh) && idh.e_magic == IMAGE_DOS_SIGNATURE)
{
hr = HRESULT_FROM_NT(STATUS_INVALID_IMAGE_FORMAT);
ov.Offset = idh.e_lfanew;
if (ReadFile(hFile, &inth, sizeof(inth), &dwBytesRead, &ov))
{
if (dwBytesRead == sizeof(inth) && inth.Signature == IMAGE_NT_SIGNATURE)
{
switch (inth.OptionalHeader.Magic)
{
case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
*Is64Bit = FALSE;
hr = S_OK;
break;
case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
*Is64Bit = TRUE;
hr = S_OK;
break;
}
*Machine = inth.FileHeader.Machine;
}
}
}
}
CloseHandle(hFile);
return hr;
}
return HRESULT_FROM_WIN32(GetLastError());
}

AcquireCredentialsHandleA() returns 0x8009030e (No credentials are available in the security package) for PFX file

I'm trying to setup server-side encryption using SSPI. I'm successfully (as far as I can tell) loading a certificate stored as a PFX file, but the call to m_pSSPI->AcquireCredentialsHandleA() returns 0x8009030e.
This method seems to successfully load the file and return a CERT_CONTEXT object.
HRESULT CTLSPackage::LoadCertContextFromFilePFX (PCWSTR pcwzFile, PCWSTR pcwzPassword, __deref_out PCCERT_CONTEXT* ppctxCert)
{
HRESULT hr;
HANDLE hFile, hSection = NULL;
BOOL (WINAPI* pfnPFXIsPFXBlob)(CRYPT_DATA_BLOB*);
HCERTSTORE (WINAPI* pfnPFXImportCertStore)(CRYPT_DATA_BLOB*, LPCWSTR, DWORD);
PCCERT_CONTEXT (WINAPI* pfnCertEnumCertificatesInStore)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext);
CRYPT_DATA_BLOB blob; blob.pbData = NULL;
HCERTSTORE pfxStore = NULL;
hFile = CreateFile(pcwzFile, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
CheckIfGetLastError(INVALID_HANDLE_VALUE == hFile);
blob.cbData = GetFileSize(hFile, NULL);
hSection = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
CheckIfGetLastError(NULL == hSection);
blob.pbData = reinterpret_cast<PBYTE>(MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, 0));
CheckIfGetLastError(NULL == blob.pbData);
Check(TGetFunction(m_hCrypt32, "PFXIsPFXBlob", &pfnPFXIsPFXBlob));
Check(TGetFunction(m_hCrypt32, "PFXImportCertStore", &pfnPFXImportCertStore));
Check(TGetFunction(m_hCrypt32, "CertEnumCertificatesInStore", &pfnCertEnumCertificatesInStore));
CheckIf(!pfnPFXIsPFXBlob(&blob), E_FAIL);
pfxStore = pfnPFXImportCertStore(&blob, pcwzPassword, CRYPT_MACHINE_KEYSET | CRYPT_EXPORTABLE);
CheckIf(NULL == pfxStore, SEC_E_NO_CREDENTIALS);
*ppctxCert = pfnCertEnumCertificatesInStore(pfxStore, NULL);
CheckIf(NULL == *ppctxCert, SEC_E_NO_CREDENTIALS);
Cleanup:
if(pfxStore)
{
BOOL (WINAPI* pfnCertCloseStore)(HCERTSTORE, DWORD);
if(SUCCEEDED(TGetFunction(m_hCrypt32, "CertCloseStore", &pfnCertCloseStore)))
pfnCertCloseStore(pfxStore, 0);
}
if(blob.pbData)
UnmapViewOfFile(blob.pbData);
SafeCloseHandle(hSection);
SafeCloseFileHandle(hFile);
return hr;
}
The result is immediately passed to another class method, which makes the failing AcquireCredentialsHandleA() call.
HRESULT CTLSPackage::AcquireCredentials (__in_opt PCCERT_CONTEXT pCertContext, PCredHandle phCreds)
{
SCHANNEL_CRED SchannelCred;
TimeStamp tsExpiry;
ZeroMemory(&SchannelCred, sizeof(SchannelCred));
SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
if(pCertContext)
{
SchannelCred.cCreds = 1;
SchannelCred.paCred = &pCertContext;
}
SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2;
if(!m_fServer)
SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS | SCH_USE_STRONG_CRYPTO;
//
// Create an SSPI credential.
//
return m_pSSPI->AcquireCredentialsHandleA(
NULL, // Name of principal
m_fServer ? NEGOSSP_NAME_A : UNISP_NAME_A, // Name of package
m_fServer ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND,
NULL, // Pointer to logon ID
&SchannelCred, // Package specific data
NULL, // Pointer to GetKey() func
NULL, // Value to pass to GetKey()
phCreds, // (out) Cred Handle
&tsExpiry); // (out) Lifetime (optional)
}
My CTLSPackage::AcquireCredentials() code path is also used for setting up client-side encryption, and that works. For the server-side path, m_fServer is TRUE. The m_hCrypt32 member was loaded from Crypt32.dll.
I've cobbled together this code from samples, but I must be missing something for the server case. I only need to setup SSL/TLS-style encryption, so the "No credentials are available in the security package" error is weird because I have no need for credential authentication.
Does anyone know what might be missing? Thanks!
With a hint from RbMm, I then found this article:
https://www.codeproject.com/articles/125124/how-to-use-certificate-from-disk-with-microsoft-cr
The short answer is that CryptAcquireCertificatePrivateKey() needed to be used when loading a PFX from a file, and UNISP_NAME_A needed to be passed to AcquireCredentialsHandleA().
For reference, here is the revised code:
HRESULT CTLSPackage::LoadCertContextFromFilePFX (PCWSTR pcwzFile, PCWSTR pcwzPassword, __deref_out PCCERT_CONTEXT* ppctxCert)
{
HRESULT hr;
HANDLE hFile, hSection = NULL;
BOOL (WINAPI* pfnPFXIsPFXBlob)(CRYPT_DATA_BLOB*);
HCERTSTORE (WINAPI* pfnPFXImportCertStore)(CRYPT_DATA_BLOB*, LPCWSTR, DWORD);
PCCERT_CONTEXT (WINAPI* pfnCertFindCertificateInStore)(HCERTSTORE hCertStore, DWORD dwCertEncodingType, DWORD dwFindFlags, DWORD dwFindType, const void* pvFindPara, PCCERT_CONTEXT pPrevCertContext);
BOOL (WINAPI* pfnCryptAcquireCertificatePrivateKey)(PCCERT_CONTEXT pCert, DWORD dwFlags, void* pvReserved, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE *phCryptProvOrNCryptKey, DWORD* pdwKeySpec, BOOL* pfCallerFreeProvOrNCryptKey);
HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hProv;
DWORD dwKeySpec;
BOOL fFreeProv = FALSE;
CRYPT_DATA_BLOB blob; blob.pbData = NULL;
HCERTSTORE hpfxStore = NULL;
hFile = CreateFile(pcwzFile, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
CheckIfGetLastError(INVALID_HANDLE_VALUE == hFile);
blob.cbData = GetFileSize(hFile, NULL);
hSection = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, 0);
CheckIfGetLastError(NULL == hSection);
blob.pbData = reinterpret_cast<PBYTE>(MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, 0));
CheckIfGetLastError(NULL == blob.pbData);
Check(TGetFunction(m_hCrypt32, "PFXIsPFXBlob", &pfnPFXIsPFXBlob));
Check(TGetFunction(m_hCrypt32, "PFXImportCertStore", &pfnPFXImportCertStore));
Check(TGetFunction(m_hCrypt32, "CertFindCertificateInStore", &pfnCertFindCertificateInStore));
Check(TGetFunction(m_hCrypt32, "CryptAcquireCertificatePrivateKey", &pfnCryptAcquireCertificatePrivateKey));
CheckIf(!pfnPFXIsPFXBlob(&blob), HRESULT_FROM_WIN32(ERROR_BAD_FORMAT));
hpfxStore = pfnPFXImportCertStore(&blob, pcwzPassword, 0);
if(NULL == hpfxStore && pcwzPassword && L'\0' == *pcwzPassword)
{
hpfxStore = pfnPFXImportCertStore(&blob, NULL, 0);
CheckIf(NULL == hpfxStore, SEC_E_NO_CREDENTIALS);
}
*ppctxCert = pfnCertFindCertificateInStore(hpfxStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL);
CheckIfGetLastError(NULL == *ppctxCert);
// Acquire the private key and make it available for the later AcquireCredentalsHandle() call.
if(!pfnCryptAcquireCertificatePrivateKey(*ppctxCert, 0, NULL, &hProv, &dwKeySpec, &fFreeProv))
{
DWORD dwError = GetLastError();
FreeCertificateContext(*ppctxCert);
*ppctxCert = NULL;
CheckWin32Error(dwError);
}
Cleanup:
if(fFreeProv)
FreeProvOrNCryptKey(hProv, dwKeySpec);
if(hpfxStore)
{
BOOL (WINAPI* pfnCertCloseStore)(HCERTSTORE, DWORD);
if(SUCCEEDED(TGetFunction(m_hCrypt32, "CertCloseStore", &pfnCertCloseStore)))
pfnCertCloseStore(hpfxStore, 0);
}
if(blob.pbData)
UnmapViewOfFile(blob.pbData);
SafeCloseHandle(hSection);
SafeCloseFileHandle(hFile);
return hr;
}
HRESULT CTLSPackage::AcquireCredentials (__in_opt PCCERT_CONTEXT pCertContext, PCredHandle phCreds)
{
SCHANNEL_CRED SchannelCred;
TimeStamp tsExpiry;
ZeroMemory(&SchannelCred, sizeof(SchannelCred));
SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
if(pCertContext)
{
SchannelCred.cCreds = 1;
SchannelCred.paCred = &pCertContext;
}
SchannelCred.grbitEnabledProtocols = SP_PROT_SSL3 | SP_PROT_TLS1 | SP_PROT_TLS1_1 | SP_PROT_TLS1_2;
SchannelCred.dwFlags = SCH_USE_STRONG_CRYPTO;
if(!m_fServer)
SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
//
// Create an SSPI credential.
//
return m_pSSPI->AcquireCredentialsHandleA(
NULL, // Name of principal
UNISP_NAME_A, // Name of package
m_fServer ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND,
NULL, // Pointer to logon ID
&SchannelCred, // Package specific data
NULL, // Pointer to GetKey() func
NULL, // Value to pass to GetKey()
phCreds, // (out) Cred Handle
&tsExpiry); // (out) Lifetime (optional)
}

How to calculate Subject Key Identifier in CNG?

My goal is to populate the Subject Key Identifier Extension (2.5.29.14) for a certificate using Microsoft CNG. I did it previously with Microsoft CAPI but the function I used:
CryptHashPublicKeyInfo
https://msdn.microsoft.com/en-us/library/windows/desktop/aa380204(v=vs.85).aspx
Is now depreciated. CNG has no such method. However, the descripion for CryptHashPublicKeyInfo in the link above says that they do a SHA1 hash of the public key information. So I did a SHA1 hash of the public key bytes in CNG on the same data in CryptHashPublicKeyInfo (CAPI) and the two hashes are different. I need to resolve this difference. To be clear, my logic is running on the same public key from the same CSR.
Details in RFC 5280 seem to confirm what Microsoft says:
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.2
(1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
value of the BIT STRING subjectPublicKey (excluding the tag,
length, and number of unused bits).
Cooper, et al. Standards Track [Page
28] RFC 5280 PKIX Certificate and CRL Profile
May 2008
(2) The keyIdentifier is composed of a four-bit type field with
the value 0100 followed by the least significant 60 bits of
the SHA-1 hash of the value of the BIT STRING
subjectPublicKey (excluding the tag, length, and number of
unused bits).
^I'm guessing Microsoft is doing case #1.
Here is my CAPI code:
//depreciated (CAPI)
//get data for subject key identifier
//get length
HCRYPTPROV hHashProv = NULL;
if (!CryptHashPublicKeyInfo(
hHashProv,
CALG_SHA1, //sha1
0,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&serverCertInfo.SubjectPublicKeyInfo,
NULL,
&dwSubjectKeyIdentifier
))
{
throw std::runtime_error("Unable to get length of public key info hash");
}
//alocate data buffer
pbSubjectKeyIdentifier = (LPBYTE)LocalAlloc(0, dwSubjectKeyIdentifier);
//fill data buffer with subject key identifier
if (!CryptHashPublicKeyInfo(
hHashProv,
CALG_SHA1, //sha1
0,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&serverCertInfo.SubjectPublicKeyInfo,
pbSubjectKeyIdentifier,
&dwSubjectKeyIdentifier
))
{
throw std::runtime_error("Unable to fill public key info hash");
}
CRYPT_DATA_BLOB skiBlob;
skiBlob.cbData = dwSubjectKeyIdentifier;
skiBlob.pbData = pbSubjectKeyIdentifier;
//encode subject key identifier extension
LPBYTE pbEncodeSKI = NULL;
DWORD dwEncodedSKI;
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
NULL,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to get length to encode extension: subject key identifier");
}
pbEncodeSKI = (LPBYTE)LocalAlloc(0, dwEncodedSKI);
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
pbEncodeSKI,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to encode extension: subject key identifier");
}
This produces this value in the extension (same public key in certificate request): 9d77f29e4fa15e46237d59a7c00efde9d286b9dc
This is my CNG code:
NTSTATUS statusBCryptOpenAlgorithmProvider;
NTSTATUS statusBCryptHash;
BCRYPT_ALG_HANDLE hHashAlg;
LPBYTE pbHash;
DWORD dwHash = 20;
LPSTR lpstrPublicKeyEncoded;
DWORD dwPublicKeyEncoded;
CRYPT_DATA_BLOB skiBlob;
if (!CryptBinaryToStringA(
infoPublicKey.PublicKey.pbData,
infoPublicKey.PublicKey.cbData,
CRYPT_STRING_BINARY,
NULL,
&dwPublicKeyEncoded
))
{
throw std::runtime_error("Error getting length of encoded binary string (CryptBinaryToString)");
}
lpstrPublicKeyEncoded = (LPSTR)LocalAlloc(0, dwPublicKeyEncoded);
if (!CryptBinaryToStringA(
infoPublicKey.PublicKey.pbData,
infoPublicKey.PublicKey.cbData,
CRYPT_STRING_BINARY,
lpstrPublicKeyEncoded,
&dwPublicKeyEncoded
))
{
LocalFree(lpstrPublicKeyEncoded);
throw std::runtime_error("Error encoding binary string (CryptBinaryToString)");
}
statusBCryptOpenAlgorithmProvider = BCryptOpenAlgorithmProvider(
&hHashAlg,
BCRYPT_SHA1_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
0
);
if (0 != statusBCryptOpenAlgorithmProvider)
{
LocalFree(lpstrPublicKeyEncoded);
throw std::runtime_error("Error opening SHA1 algorithm provider (BCryptOpenAlgorithmProvider)");
}
pbHash = (LPBYTE)LocalAlloc(0, dwHash);
statusBCryptHash = BCryptHash(
hHashAlg,
NULL,
0,
(BYTE*)lpstrPublicKeyEncoded,
dwPublicKeyEncoded,
pbHash,
dwHash
);
if (0 != statusBCryptHash)
{
LocalFree(lpstrPublicKeyEncoded);
BCryptCloseAlgorithmProvider(hHashAlg, 0);
LocalFree(pbHash);
throw std::runtime_error("Error hashing public key (BCryptHash)");
}
skiBlob.pbData = pbHash;
skiBlob.cbData = dwHash;
BCryptCloseAlgorithmProvider(hHashAlg, 0);
LocalFree(pbHash);
LocalFree(lpstrPublicKeyEncoded);
//encode subject key identifier extension
LPBYTE pbEncodeSKI = NULL;
DWORD dwEncodedSKI;
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
NULL,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to get length to encode extension: subject key identifier");
}
pbEncodeSKI = (LPBYTE)LocalAlloc(0, dwEncodedSKI);
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
pbEncodeSKI,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to encode extension: subject key identifier");
}
This produces this SKI value (different): 210816297e8e76879f99ec4762452b5d38967b5b
Any clue what I am doing wrong in the CNG code sample? There is apparently a magic sequence of calls but I don't know what it is.
Here you go: both CNG and CAPI variants.
HRESULT capiCreateKeyIdentifierFromPublicKey(NCRYPT_KEY_HANDLE hCngKey, CRYPT_DATA_BLOB* outHash)
{
HRESULT hr = S_OK;
BOOL bResult = FALSE;
PCERT_PUBLIC_KEY_INFO pCertInfo = NULL;
DWORD cbCertInfo = 0;
outHash->pbData = NULL;
outHash->cbData = 0;
/* STEP1: Extract public key. */
bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
pCertInfo = (PCERT_PUBLIC_KEY_INFO)HeapAlloc(GetProcessHeap(), 0, cbCertInfo);
if (NULL == pCertInfo) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}
bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
/* STEP2: Make hash. */
bResult = CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, NULL, &outHash->cbData);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
outHash->pbData = (BYTE*)HeapAlloc(GetProcessHeap(), 0, outHash->cbData);
bResult = CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, outHash->pbData, &outHash->cbData);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
Cleanup:
if (!SUCCEEDED(hr) && NULL != outHash->pbData) {
HeapFree(GetProcessHeap(), 0, outHash->pbData);
outHash->pbData = NULL;
outHash->cbData = 0;
}
if (NULL != pCertInfo) {
HeapFree(GetProcessHeap(), 0, pCertInfo);
pCertInfo = 0;
}
return hr;
}
HRESULT cngCreateKeyIdentifierFromPublicKey(NCRYPT_KEY_HANDLE hCngKey, CRYPT_DATA_BLOB* outHash)
{
// #see https://learn.microsoft.com/en-us/windows/desktop/seccng/creating-a-hash-with-cng
HRESULT hr = S_OK;
BOOL bResult = FALSE;
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
NTSTATUS status = 0;
DWORD cbData = 0;
DWORD cbHashObject = 0;
PBYTE pbHashObject = NULL;
PCERT_PUBLIC_KEY_INFO pCertInfo = NULL;
DWORD cbCertInfo = 0;
BYTE* pbEncodedCertInfo = NULL;
ULONG cbEncodedCertInfo = 0;
outHash->pbData = NULL;
outHash->cbData = 0;
/* STEP1: Extract public key. */
bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
pCertInfo = (PCERT_PUBLIC_KEY_INFO)HeapAlloc(GetProcessHeap(), 0, cbCertInfo);
if (NULL == pCertInfo) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}
bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
/* STEP2: Encode the public key. */
bResult = CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pCertInfo, pbEncodedCertInfo, &cbEncodedCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
pbEncodedCertInfo = (BYTE*)HeapAlloc(GetProcessHeap(), 0, cbEncodedCertInfo);
bResult = CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pCertInfo, pbEncodedCertInfo, &cbEncodedCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
/* STEP3: Open an algorithm handle. */
status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_SHA1_ALGORITHM,
NULL,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
/* STEP4: Calculate the size of the buffer to hold the hash object. */
status = BCryptGetProperty(
hAlg,
BCRYPT_OBJECT_LENGTH,
(PBYTE)&cbHashObject,
sizeof(DWORD),
&cbData,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
/* STEP5: Allocate the buffer for hash object on the heap. */
pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject);
if (NULL == pbHashObject) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}
/* STEP6: Create a hash object (get handle to CNG hash object). */
status = BCryptCreateHash(
hAlg,
&hHash,
pbHashObject,
cbHashObject,
NULL,
0,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
/* STEP7: Calculate the length of buffer for result hash. */
status = BCryptGetProperty(
hAlg,
BCRYPT_HASH_LENGTH,
(PBYTE)&outHash->cbData,
sizeof(DWORD),
&cbData,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
/* STEP8: Allocate buffer for result hash on the heap. */
outHash->pbData = (PBYTE)HeapAlloc(GetProcessHeap(), 0, outHash->cbData);
if (NULL == outHash->pbData) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}
/* STEP9: Hash data. */
status = BCryptHashData(
hHash,
(PBYTE)pbEncodedCertInfo,
cbEncodedCertInfo,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
/* STEP10: Close hash object and get result value. */
status = BCryptFinishHash(
hHash,
outHash->pbData,
outHash->cbData,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
Cleanup:
if (!SUCCEEDED(hr) && NULL != outHash->pbData) {
HeapFree(GetProcessHeap(), 0, outHash->pbData);
outHash->pbData = NULL;
outHash->cbData = 0;
}
if (NULL != hHash) {
BCryptDestroyHash(hHash);
hHash = NULL;
}
if (NULL != pbHashObject) {
HeapFree(GetProcessHeap(), 0, pbHashObject);
pbHashObject = NULL;
}
if (NULL != hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
hAlg = NULL;
}
if (NULL != pbEncodedCertInfo) {
HeapFree(GetProcessHeap(), 0, pbEncodedCertInfo);
pCertInfo = 0;
}
if (NULL != pCertInfo) {
HeapFree(GetProcessHeap(), 0, pCertInfo);
pCertInfo = 0;
}
return hr;
}
Usage:
CRYPT_DATA_BLOB subjectKeyIdentifier = { 0 };
NCRYPT_KEY_HANDLE hCngKey = NULL;
HRESULT hr = NCryptOpenStorageProvider(&hProvider, MS_KEY_STORAGE_PROVIDER, 0);
if (hr) {
hr = NCryptOpenKey(hProvider, &hCngKey, wszKeyName, 0, 0);
if (ERROR_SUCCESS == hr) {
hr = cngCreateKeyIdentifierFromPublicKey(hCngKey, &subjectKeyIdentifier);
if (hr) {
// do smth with data
// clear the memory
HeapFree(GetProcessHeap(), 0, subjectKeyIdentifier.pbData);
subjectKeyIdentifier.pbData = NULL;
subjectKeyIdentifier.cbData = 0;
}
}
}
......

Enumerating process handles, weird issue

I scan for opened handles to my process and print them in the console.
I start my process
I attach cheat engine
I run the enumeration of opened handles
I see which process has a handle to my process
The weird issue at this point is as follows, check the code:
array<Accessor^>^ AntiCheat::ScanHandles()
{
List<Accessor^>^ accessorList = gcnew List<Accessor^>();
if (!EnableDebugPrivilege(true))
printf("EnableDebugPrivilege failed: %d\n", GetLastError());
tNtQuerySystemInformation oNtQuerySystemInformation = (tNtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
PSYSTEM_HANDLE_INFORMATION handleInfo = new SYSTEM_HANDLE_INFORMATION;
SYSTEM_INFORMATION_CLASS infoClass = (SYSTEM_INFORMATION_CLASS)16; // SystemHandleInformation
DWORD size = sizeof(SYSTEM_HANDLE_INFORMATION);
DWORD needed = 0;
NTSTATUS status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
while (!NT_SUCCESS(status))
{
if (needed == 0)
return nullptr;
// The previously supplied buffer wasn't enough.
delete handleInfo;
size = needed + 1024;
handleInfo = (PSYSTEM_HANDLE_INFORMATION)new BYTE[size];
status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
}
HANDLE currentProcess = GetCurrentProcess();
DWORD currentProcessId = GetProcessId(currentProcess);
for (DWORD i = 0; i < handleInfo->dwCount; i++)
{
//printf(".");
SYSTEM_HANDLE handle = handleInfo->Handles[i];
HANDLE procHandle = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, handle.dwProcessId);
if (GetLastError() == ERROR_ACCESS_DENIED)
continue;
HANDLE dupl = 0;
if (!DuplicateHandle(procHandle, (HANDLE)handle.wValue, currentProcess, &dupl, 0, false, DUPLICATE_SAME_ACCESS))
continue;
DWORD procId = GetProcessId(dupl);
if (procId == currentProcessId)
{
printf("accessing us\n");
char processName[MAX_PATH];
GetModuleFileNameEx((HMODULE)procHandle, NULL, processName, MAX_PATH);
accessorList->Add(gcnew Accessor(gcnew String(processName), handle.GrantedAccess));
}
CloseHandle(dupl);
}
return accessorList->ToArray();
}
If I uncomment the line with printf(".");, I see 3 opened handles to my process (cheatengine). If it's commented (runs way faster), there is no opened handle. However I don't know why this affects my code. Im surprised, does anyone know why this happens? Or how to find out how to find the handles without my printf("."); line?
Another issue is: each time I call the function, the number of allocated bytes duplicates. And I don't know why.
I see logic problems with your code.
You are not ignoring array items where handle.dwProcessId equals currentProcessId, so you end up opening handles to your own process. Since you are only interested in looking for other processes, you should be ignoring items where handle.dwProcessId is equal to currentProcessId.
You are not checking if OpenProcess() fails for any reason other than ERROR_ACCESS_DENIED. Do not call GetLastError() unless OpenProcess() actually returns NULL first.
You are not closing an opened handle if DuplicateHandle() fails. And why are you duplicating each source handle just to call GetProcessId() on it? You already have their process IDs from the array, so the whole DuplicateHandle()+GetProcessId() is completely unnecessary.
You are taking the wrong approach anyway. Have a look at this discussion:
Enumerating the processes referencing an object
Use NtQuerySystemInformation with SystemInformationClass set to SystemHandleInformation. This fills in an array of SYSTEM_HANDLE_INFORMATION structures, which are defined as:
typedef struct _SYSTEM_HANDLE_INFORMATION {
ULONG ProcessId;
UCHAR ObjectTypeNumber;
UCHAR Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION;
Search for the entry corresponding to the handle you opened with ProcessID equal to GetCurrentProcessId(), then find all entries with the same Object pointer.
Although the discussion shows the wrong declaration for SYSTEM_HANDLE_INFORMATION. The following article shows the correct one:
HOWTO: Enumerate handles
#define SystemHandleInformation 16
typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
/* The following structure is actually called SYSTEM_HANDLE_TABLE_ENTRY_INFO, but SYSTEM_HANDLE is shorter. */
typedef struct _SYSTEM_HANDLE
{
ULONG ProcessId;
BYTE ObjectTypeNumber;
BYTE Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG HandleCount; /* Or NumberOfHandles if you prefer. */
SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
With that said, try something more like this:
array<Accessor^>^ AntiCheat::ScanHandles()
{
List<Accessor^>^ accessorList = gcnew List<Accessor^>();
if (!EnableDebugPrivilege(true))
printf("EnableDebugPrivilege failed: %d\n", GetLastError());
tNtQuerySystemInformation oNtQuerySystemInformation = (tNtQuerySystemInformation) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
DWORD currentProcessId = GetCurrentProcessId();
HANDLE currentProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, currentProcessId);
PVOID currentProcessAddr = nullptr;
DWORD size = sizeof(SYSTEM_HANDLE_INFORMATION);
DWORD needed = 0;
PSYSTEM_HANDLE_INFORMATION handleInfo = (PSYSTEM_HANDLE_INFORMATION) new BYTE[size];
SYSTEM_INFORMATION_CLASS infoClass = (SYSTEM_INFORMATION_CLASS) 16; // SystemHandleInformation
NTSTATUS status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
while (status == STATUS_INFO_LENGTH_MISMATCH)
{
// The previously supplied buffer wasn't enough.
delete[] handleInfo;
size += 1024;
handleInfo = (PSYSTEM_HANDLE_INFORMATION) new BYTE[size];
status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
}
if (status != 0)
{
delete[] handleInfo;
return nullptr;
}
for (DWORD i = 0; i < handleInfo->dwCount; i++)
{
SYSTEM_HANDLE &handle = handleInfo->Handles[i];
if ((handle.dwProcessId == currentProcessId) &&
(currentProcess == (HANDLE)handle.wValue))
{
currentProcessAddr = handle.pAddress;
break;
}
}
for (DWORD i = 0; i < handleInfo->dwCount; i++)
{
SYSTEM_HANDLE &handle = handleInfo->Handles[i];
if ((handle.dwProcessId != currentProcessId) &&
(handle.pAddress == currentProcessAddr))
{
printf("accessing us\n");
HANDLE procHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, handle.dwProcessId);
if (procHandle != 0)
{
char processName[MAX_PATH+1];
DWORD len = GetModuleFileNameEx((HMODULE)procHandle, NULL, processName, MAX_PATH);
CloseHandle(procHandle);
processName[len] = '\0';
accessorList->Add(gcnew Accessor(gcnew String(processName), handle.GrantedAccess));
}
else
accessorList->Add(gcnew Accessor(gcnew String("unknown"), handle.GrantedAccess));
}
}
CloseHandle(currentProcess);
delete[] handleInfo;
return accessorList->ToArray();
}

C++ Get Username From Process

I have a process handle with
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, THE_PROCESS_ID);
How can I get the username of the user that is running the process?
I am using unmanaged code (no .NET).
Use OpenProcessToken to get the token (obviously), then GetTokenInformation with the TokenOwner flag to get the SID of the owner. Then you can use LookupAccountSid to get the username.
if WMI is not an option, then use GetUserFromProcess below that takes the process ID as an input parameter and returns the user name and domain:
#include <comdef.h>
#define MAX_NAME 256
BOOL GetLogonFromToken (HANDLE hToken, _bstr_t& strUser, _bstr_t& strdomain)
{
DWORD dwSize = MAX_NAME;
BOOL bSuccess = FALSE;
DWORD dwLength = 0;
strUser = "";
strdomain = "";
PTOKEN_USER ptu = NULL;
//Verify the parameter passed in is not NULL.
if (NULL == hToken)
goto Cleanup;
if (!GetTokenInformation(
hToken, // handle to the access token
TokenUser, // get information about the token's groups
(LPVOID) ptu, // pointer to PTOKEN_USER buffer
0, // size of buffer
&dwLength // receives required buffer size
))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
goto Cleanup;
ptu = (PTOKEN_USER)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, dwLength);
if (ptu == NULL)
goto Cleanup;
}
if (!GetTokenInformation(
hToken, // handle to the access token
TokenUser, // get information about the token's groups
(LPVOID) ptu, // pointer to PTOKEN_USER buffer
dwLength, // size of buffer
&dwLength // receives required buffer size
))
{
goto Cleanup;
}
SID_NAME_USE SidType;
char lpName[MAX_NAME];
char lpDomain[MAX_NAME];
if( !LookupAccountSid( NULL , ptu->User.Sid, lpName, &dwSize, lpDomain, &dwSize, &SidType ) )
{
DWORD dwResult = GetLastError();
if( dwResult == ERROR_NONE_MAPPED )
strcpy (lpName, "NONE_MAPPED" );
else
{
printf("LookupAccountSid Error %u\n", GetLastError());
}
}
else
{
printf( "Current user is %s\\%s\n",
lpDomain, lpName );
strUser = lpName;
strdomain = lpDomain;
bSuccess = TRUE;
}
Cleanup:
if (ptu != NULL)
HeapFree(GetProcessHeap(), 0, (LPVOID)ptu);
return bSuccess;
}
HRESULT GetUserFromProcess(const DWORD procId, _bstr_t& strUser, _bstr_t& strdomain)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,FALSE,procId);
if(hProcess == NULL)
return E_FAIL;
HANDLE hToken = NULL;
if( !OpenProcessToken( hProcess, TOKEN_QUERY, &hToken ) )
{
CloseHandle( hProcess );
return E_FAIL;
}
BOOL bres = GetLogonFromToken (hToken, strUser, strdomain);
CloseHandle( hToken );
CloseHandle( hProcess );
return bres?S_OK:E_FAIL;
}
WMI is probably the path of least resistance. You should also be able to get the token using OpenProcessToken, then GetTokenInformation to get the SID of the owner. You can then turn the SID into a user name.
WMI should be able to tell you that information. Otherwise you need to rely on undocumented fun in ntdll.dll. It appears others have found solutions that don't use ntdll.dll -- use them rather than undocumented stuff.
Here a solution knowing the process id.
std::optional<std::wstring> GetUserNameFromProcess(DWORD id)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id); // 1- OpenProcess
std::wstring endUser = L"";
std::wstring endDomain = L"";
if (hProcess != NULL)
{
HANDLE hToken = NULL;
if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) // 2- OpenProcessToken
{
DWORD tokenSize = 0;
GetTokenInformation(hToken, TokenUser, NULL, 0, &tokenSize);
if (tokenSize > 0)
{
BYTE* data = new BYTE[tokenSize];
GetTokenInformation(hToken, TokenUser, data, tokenSize, &tokenSize); // 3- GetTokenInformation
TOKEN_USER* pUser = (TOKEN_USER*)data;
PSID pSID = pUser->User.Sid;
DWORD userSize = 0;
DWORD domainSize = 0;
SID_NAME_USE sidName;
LookupAccountSid(NULL, pSID, NULL, &userSize, NULL, &domainSize, &sidName);
wchar_t* user = new wchar_t[userSize + 1];
wchar_t* domain = new wchar_t[domainSize + 1];
LookupAccountSid(NULL, pSID, user, &userSize, domain, &domainSize, &sidName); // 4- LookupAccountSid
user[userSize] = L'\0';
domain[domainSize] = L'\0';
endUser = user;
endDomain = domain;
delete[] domain;
delete[] user;
delete[] data;
}
CloseHandle(hToken);
}
CloseHandle(hProcess);
if (endUser != L"")
return endUser;
}
return {};
}