I am working on a certificate-based authentication project for domain users against Active Directory without the use of smartcards. I am trying to access the issued domain user certificate container and pass the value in a serialized format to LsaLogonUser for authentication.
First, I am enrolling the domain user certificate by creating a new certificate template described below:-
Duplicate a new certificate template from "User" certificate template.
In the Properties of new certificate template window, go to the General tab and change the following settings:
Check the "Publish certificate in Active Directory" box and enter a template name.
Go to the Request Handling tab and change the following settings:
Change "Purpose" to "Signature and encryption".
Check the box "Allow private key to be exported".
Select "Prompt the user during enrollment".
Go to the Subject Name tab and change the following settings:
Select "Build from this Active Directory information".
Change "Subject name format" to "None".
Check the "User principal name (UPN)" box.
Go to the Extensions tab and edit "Application Policies" so that the only listed policies are "Client Authentication" and "Smart Card Logon". (Remove any default policies as necessary.)
Go to the Cryptography tab and change the following settings:
Change "Minimum key size" to 2048.
Select "Requests must use one of the following providers: "
Select "Microsoft Enhanced Cryptographic Provider v1.0" as "Providers".
After making the above changes, issue/publish the new certificate template in Active Directory Certificate Authority.
The domain user enrolls using the new certificate template and the user certificate is stored in the current user's Certificate Store.
After the enrollment process is complete, I am trying to access the CSP container handle of the user certificate and using it for KERB_CERTIFICATE_LOGON structure for packing and serializing the authentication data specified by LsaLogonUser. Here is the code:
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <Ntsecapi.h>
#include <iostream>
#include <intsafe.h>
#include <WinCred.h>
#include <winbase.h>
#pragma comment(lib, "Crypt32")
#define NEGOSSP_NAME_A "Negotiate"
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif
#include <vcruntime_exception.h>
#pragma comment(lib, "Secur32.lib")
using namespace std;
// 1-Byte packing for this structure
#pragma pack(push, KerbCspInfo, 1)
typedef struct _KERB_SMARTCARD_CSP_INFO {
DWORD dwCspInfoLen;
DWORD MessageType;
union {
PVOID ContextInformation;
ULONG64 SpaceHolderForWow64;
};
DWORD flags;
DWORD KeySpec;
ULONG nCardNameOffset;
ULONG nReaderNameOffset;
ULONG nContainerNameOffset;
ULONG nCSPNameOffset;
TCHAR bBuffer[sizeof(DWORD)];
} KERB_SMARTCARD_CSP_INFO, * PKERB_SMARTCARD_CSP_INFO;
#pragma pack(pop, KerbCspInfo)
HRESULT UnicodeStringInitWithString(
_In_ PWSTR pwz,
_Out_ UNICODE_STRING* pus
)
{
HRESULT hr;
if (pwz)
{
size_t lenString = wcslen(pwz);
USHORT usCharCount;
hr = SizeTToUShort(lenString, &usCharCount);
if (SUCCEEDED(hr))
{
USHORT usSize;
hr = SizeTToUShort(sizeof(wchar_t), &usSize);
if (SUCCEEDED(hr))
{
hr = UShortMult(usCharCount, usSize, &(pus->Length)); // Explicitly NOT including NULL terminator
if (SUCCEEDED(hr))
{
pus->MaximumLength = pus->Length;
pus->Buffer = pwz;
hr = S_OK;
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
}
}
}
}
else
{
hr = E_INVALIDARG;
}
return hr;
}
static void _UnicodeStringPackedUnicodeStringCopy(
__in const UNICODE_STRING& rus,
__in PWSTR pwzBuffer,
__out UNICODE_STRING* pus
)
{
pus->Length = rus.Length;
pus->MaximumLength = rus.Length;
pus->Buffer = pwzBuffer;
std::CopyMemory(pus->Buffer, rus.Buffer, pus->Length);
}
// Set the SeTcbPrivilege of the current process
BOOL SetSeTcbPrivilege()
{
TOKEN_PRIVILEGES tp;
LUID luid;
HANDLE hProcessToken;
int x;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
&hProcessToken))
{
_tprintf(_T("OpenProcessToken failed with error 0x%.8X\n"), GetLastError());
cin >> x;
return FALSE;
}
if (!LookupPrivilegeValue(
NULL,
SE_TCB_NAME,
&luid))
{
_tprintf(_T("LookupPrivilegeValue failed with error 0x%.8X\n"), GetLastError());
CloseHandle(hProcessToken);
cin >> x;
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Enable the privilege
if (!AdjustTokenPrivileges(
hProcessToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
_tprintf(_T("AdjustTokenPrivileges failed with error 0x%.8X\n"), GetLastError());
CloseHandle(hProcessToken);
cin >> x;
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
_tprintf(_T("The token does not have the privilege \"SeTcbPrivilege\". \n"));
CloseHandle(hProcessToken);
cin >> x;
return FALSE;
}
CloseHandle(hProcessToken);
return TRUE;
}
// Build the authentication data used by LsaLogonUser
BOOL ConstructAuthCertInfo(const KERB_CERTIFICATE_UNLOCK_LOGON& rkiulIn,LPBYTE* ppbAuthInfo, ULONG* pulAuthInfoLen)
{
UNICODE_STRING DomainName;
UNICODE_STRING UserName;
UNICODE_STRING Password;
NTSTATUS Status;
ULONG_PTR Ptr;
ULONG AuthInfoLength;
wstring WProvName;
DWORD ProvSize = 0;
wstring containerName;
DWORD contSize = 0;
HCRYPTPROV hProv;
DWORD dwKeySpec;
// Fetching the certificate context from the certificate store
// and recieving the CSP handle for the user certificate
HCERTSTORE hStoreHandle = NULL;
PCCERT_CONTEXT pCertContext = NULL;
LPCTSTR pszStoreName = _T("MY");
CERT_CREDENTIAL_INFO certInfo;
HCRYPTHASH hHash;
BOOL bStatus;
DWORD dwHashLen = CERT_HASH_LENGTH;
LPWSTR szMarshaledCred = NULL;
HANDLE hToken;
hStoreHandle = CertOpenSystemStore(NULL, pszStoreName);
if (hStoreHandle)
{
// Since my certificate store has only one user certificate, tring to retrieve PCERT_CONTEXT for it
pCertContext = CertFindCertificateInStore(
hStoreHandle,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
0,
CERT_FIND_ANY,
NULL,
NULL);
if (pCertContext)
{
BOOL bFreeHandle;
// acquire private key container to this certificate
if (CryptAcquireCertificatePrivateKey(pCertContext, CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, &hProv, &dwKeySpec, &bFreeHandle))
{
cout << "The private key container handle is accessed.. "<<endl<<"The dwKeySpec Value is: "<<dwKeySpec<<endl;
//Fetching the container name where the certificate is stored
if (CryptGetProvParam(hProv, PP_CONTAINER, NULL, &contSize, 0))
{
BYTE* contName = (BYTE*)malloc(contSize);
if (CryptGetProvParam(hProv, PP_CONTAINER, contName, &contSize, 0))
{
containerName = wstring(contName, contName + contSize);
wcout << "The container name is: " << containerName << endl;
}
else
{
cout << "Could not get the container name. Exiting.."<<endl;
return FALSE;
}
}
else
{
cout << "Could not get the container size. Exiting.."<<endl;
return FALSE;
}
//Fetching the CSP name where the certificate is stored
if (CryptGetProvParam(hProv, PP_NAME, NULL, &ProvSize, 0))
{
BYTE* ProvName = (BYTE*)malloc(ProvSize);
if (CryptGetProvParam(hProv, PP_NAME, ProvName, &ProvSize, 0))
{
WProvName = wstring(ProvName, ProvName + ProvSize);
wcout << "The name of the CSP is: " << WProvName << endl;
}
else
{
cout << "The CSP name could not be retrieved. Exiting.." << endl;
return FALSE;
}
}
else
{
cout << "The CSP name size could not be retrieved. Exiting.." << endl;
return FALSE;
}
}
else
{
cout << "Could not get the Private Key Container handle. Exiting...";
return FALSE;
}
}
else
{
cout << "Could not find the user certificate in the Certificate store. Exiting.." << endl;
return FALSE;
}
}
// Creating and initializing a PKERB_SMARTCARD_CSP_INFO struct for passing as an argument to KERB_CERTIFICATE_LOGON struct
// This struct stores information regarding the CSP and container of the user certs
DWORD dwReaderLen = (DWORD)_tcslen(L"") + 1;
DWORD dwCardLen = (DWORD)_tcslen(L"") + 1;
DWORD dwProviderLen = (DWORD)_tcslen(WProvName.c_str()) + 1;
DWORD dwContainerLen = (DWORD)_tcslen(containerName.c_str()) + 1;
DWORD dwBufferSize = dwReaderLen + dwCardLen + dwProviderLen + dwContainerLen;
PKERB_SMARTCARD_CSP_INFO CspInfo = (PKERB_SMARTCARD_CSP_INFO)malloc(sizeof(KERB_SMARTCARD_CSP_INFO) + dwBufferSize * sizeof(TCHAR));
std::memset(CspInfo, 0, sizeof(KERB_SMARTCARD_CSP_INFO));
CspInfo->dwCspInfoLen = sizeof(KERB_SMARTCARD_CSP_INFO) + dwBufferSize * sizeof(TCHAR);
CspInfo->MessageType = 1;
CspInfo->KeySpec = dwKeySpec;
CspInfo->nCardNameOffset = ARRAYSIZE(CspInfo->bBuffer);
CspInfo->nReaderNameOffset = CspInfo->nCardNameOffset + dwCardLen;
CspInfo->nContainerNameOffset = CspInfo->nReaderNameOffset + dwReaderLen;
CspInfo->nCSPNameOffset = CspInfo->nContainerNameOffset + dwContainerLen;
std::memset(CspInfo->bBuffer, 0, sizeof(CspInfo->bBuffer));
_tcscpy_s(&CspInfo->bBuffer[CspInfo->nCardNameOffset], dwBufferSize + 4 - CspInfo->nCardNameOffset, L"");
_tcscpy_s(&CspInfo->bBuffer[CspInfo->nReaderNameOffset], dwBufferSize + 4 - CspInfo->nReaderNameOffset, L"");
_tcscpy_s(&CspInfo->bBuffer[CspInfo->nContainerNameOffset], dwBufferSize + 4 - CspInfo->nContainerNameOffset, containerName.c_str());
_tcscpy_s(&CspInfo->bBuffer[CspInfo->nCSPNameOffset], dwBufferSize + 4 - CspInfo->nCSPNameOffset, WProvName.c_str());
const KERB_CERTIFICATE_LOGON* pkilIn = &rkiulIn.Logon;
// alloc space for struct plus extra for the three strings
DWORD cb = sizeof(rkiulIn) +
pkilIn->DomainName.Length +
pkilIn->UserName.Length +
pkilIn->Pin.Length +
CspInfo->dwCspInfoLen;
KERB_CERTIFICATE_UNLOCK_LOGON* pkiulOut = (KERB_CERTIFICATE_UNLOCK_LOGON*)CoTaskMemAlloc(cb);
if (pkiulOut)
{
std::ZeroMemory(&pkiulOut->LogonId, sizeof(LUID));
//
// point pbBuffer at the beginning of the extra space
//
BYTE* pbBuffer = (BYTE*)pkiulOut + sizeof(*pkiulOut);
//
// set up the Logon structure within the KERB_CERTIFICATE_UNLOCK_LOGON
//
KERB_CERTIFICATE_LOGON* pkilOut = &pkiulOut->Logon;
pkilOut->MessageType = pkilIn->MessageType;
pkilOut->Flags = pkilIn->Flags;
//
// copy each string,
// fix up appropriate buffer pointer to be offset,
// advance buffer pointer over copied characters in extra space
//
_UnicodeStringPackedUnicodeStringCopy(pkilIn->DomainName, (PWSTR)pbBuffer, &pkilOut->DomainName);
pkilOut->DomainName.Buffer = (PWSTR)(pbBuffer - (BYTE*)pkiulOut);
pbBuffer += pkilOut->DomainName.Length;
_UnicodeStringPackedUnicodeStringCopy(pkilIn->UserName, (PWSTR)pbBuffer, &pkilOut->UserName);
pkilOut->UserName.Buffer = (PWSTR)(pbBuffer - (BYTE*)pkiulOut);
pbBuffer += pkilOut->UserName.Length;
_UnicodeStringPackedUnicodeStringCopy(pkilIn->Pin, (PWSTR)pbBuffer, &pkilOut->Pin);
pkilOut->Pin.Buffer = (PWSTR)(pbBuffer - (BYTE*)pkiulOut);
pbBuffer += pkilOut->Pin.Length;
pkilOut->CspData = (PUCHAR)(pbBuffer - (BYTE*)pkiulOut);
pkilOut->CspDataLength = CspInfo->dwCspInfoLen;
std::memcpy(pbBuffer,CspInfo, CspInfo->dwCspInfoLen);
*ppbAuthInfo = (BYTE*)pkilOut;
*pulAuthInfoLen = cb;
return TRUE;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
NTSTATUS nStatus;
CHAR szProcName[] = "LsaTestLogonProcess";
CHAR szPackageName[] = NEGOSSP_NAME_A;
CHAR szOriginName[] = "LsaSmartCardLogonTest";
LSA_STRING lsaProcName = { strlen(szProcName), strlen(szProcName) + 1, szProcName };
LSA_STRING lsaPackageName = { strlen(szPackageName), strlen(szPackageName) + 1, szPackageName };
LSA_STRING lsaOriginName = { strlen(szOriginName), strlen(szOriginName) + 1, szOriginName };
HANDLE lsaHandle;
ULONG ulAuthPackage;
LPBYTE pbAuthInfo = NULL;
ULONG ulAuthInfoLen = 0;
LSA_OPERATIONAL_MODE dummy;
TOKEN_SOURCE tokenSource;
LPVOID pProfileBuffer = NULL;
ULONG ulProfileBufferLen = 0;
LUID logonId;
HANDLE hLogonToken;
QUOTA_LIMITS quotas;
NTSTATUS subStatus;
HANDLE hToken = NULL;
int x;
wchar_t doman[] = L"TECNICS";
wchar_t usernme[] = L"";
wchar_t pn[] = L"";
PWSTR domain = doman;
PWSTR username = usernme;
PWSTR pin = pn;
//Certificate Based Auth
KERB_CERTIFICATE_UNLOCK_LOGON ckiul;
KERB_CERTIFICATE_LOGON* ckil = &ckiul.Logon;
ckil->MessageType = KerbCertificateLogon;
ckil->Flags = 2;
// Converting Domain Name to Unicode Format
UnicodeStringInitWithString(domain, &ckil->DomainName);
// Passing empty UserName as per https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-kerb_certificate_logon
UnicodeStringInitWithString(username, &ckil->UserName);
// Passing empty PIN since no PIN asked during enrollment
UnicodeStringInitWithString(pin, &ckil->Pin);
//End
if (!SetSeTcbPrivilege())
{
cin >> x;
return 0;
}
std::memcpy(tokenSource.SourceName, "LsaTest", 8);
AllocateLocallyUniqueId(&tokenSource.SourceIdentifier);
nStatus = LsaRegisterLogonProcess(&lsaProcName,
&lsaHandle,
&dummy);
if (nStatus == STATUS_SUCCESS)
{
nStatus = LsaLookupAuthenticationPackage(lsaHandle,
&lsaPackageName,
&ulAuthPackage);
if (nStatus == STATUS_SUCCESS)
{
if (!ConstructAuthCertInfo(ckiul, &pbAuthInfo, &ulAuthInfoLen))
{
cout << "Could not serialize the authentication data. Exiting...";
cin >> x;
return 0;
}
nStatus = LsaLogonUser(lsaHandle,
&lsaOriginName,
Interactive,
ulAuthPackage,
pbAuthInfo,
ulAuthInfoLen,
NULL,
&tokenSource,
&pProfileBuffer,
&ulProfileBufferLen,
&logonId,
&hLogonToken,
"as,
&subStatus);
if (nStatus == STATUS_SUCCESS)
{
if (pProfileBuffer)
LsaFreeReturnBuffer(pProfileBuffer);
_tprintf(_T("User logged on successfully!!\n"));
cin >> x;
CloseHandle(hLogonToken);
}
else
{
_tprintf(_T("LsaLogonUser failed with error 0x%.8X. SubStatus = 0x%.8X\n"), LsaNtStatusToWinError(nStatus), LsaNtStatusToWinError(subStatus));
cin >> x;
}
HeapFree(GetProcessHeap(), 0, pbAuthInfo);
}
else
{
_tprintf(_T("LsaLookupAuthenticationPackage failed with error 0x%.8X\n"), LsaNtStatusToWinError(nStatus));
cin >> x;
}
LsaDeregisterLogonProcess(lsaHandle);
}
else
{
_tprintf(_T("LsaRegisterLogonProcess failed with error 0x%.8X\n"), LsaNtStatusToWinError(nStatus));
cin >> x;
}
return 0;
}
NOTE: The user needs to have SeTcbPriveledge and admin rights to run the program. You can refer to this article to set the SeTcbPriveledge for the domain user.
After running the above code in the domain user's environment, LSALogonUser fails and I get the following status values:
- Status: 0x0000052E/ERROR_LOGON_FAILURE (The user name or password is incorrect.)
- SubStatus: 0x8009000d/NTE_NO_KEY (Key does not exist.)
I have also pushed a repo on Github which consists of the code in with Visual Studio extensions. You can clone the repo from here.
Can someone suggest a better approach or hints to make this work?
Related
Get list of windows services and print name, status, path to exe
the above statement needs to be solved, I am aware we enum services to find.....since I am beginner ,I don't understand how to retrieve all the path to exe.
this is what I knew
#include <Windows.h>
#include <iostream>
int main()
{
SC_HANDLE sHandle;
LPQUERY_SERVICE_CONFIG lpServiceConfig = NULL;
DWORD cbBufSize = 100;
LPDWORD bytesNeeded = NULL;
sHandle = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
sHandle = OpenService(sHandle, "YOU SERVICE NAME",SERVICE_ALL_ACCESS);
QueryServiceConfig(sHandle,lpServiceConfig,cbBufSize,bytesNeeded);
std::cout << lpServiceConfig->lpBinaryPathName << std::endl;
}
You need to use EnumServiceStatus() to query which services are installed. You can then open each service to query its EXE path.
Also, your code is leaking resources and memory.
Try something more like this:
#include <Windows.h>
#include <iostream>
#include <vector>
int main()
{
DWORD bytesNeeded = 0;
DWORD numServices = 0;
DWORD resumeHandle = 0;
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
if (!hSCManager) ...
EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &bytesNeeded, &numServices, &resumeHandle);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) ...
std::vector<BYTE> enumBuffer(bytesNeeded);
LPENUM_SERVICE_STATUS pEnum = reinterpret_cast<LPENUM_SERVICE_STATUS>(enumBuffer.data());
if (!EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, pEnum, bytesNeeded, &bytesNeeded, &numServices, &resumeHandle)) ...
for(DWORD idx = 0; idx < numServices; ++idx)
{
std::cout << pEnum[idx].lpServiceName << " (" << pEnum[idx].lpDisplayName << ")" << std::endl;
// use/display pEnum[idx].ServiceStatus as needed ...
SC_HANDLE hService = OpenService(hSCManager, pEnum[idx].lpServiceName, SERVICE_QUERY_CONFIG);
if (!hService) ...
QueryServiceConfig(hSerivce, NULL, 0, &bytesNeeded);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) ...
std::vector<BYTE> configBuffer(bytesNeeded);
LPQUERY_SERVICE_CONFIG lpServiceConfig = reinterpret_cast<LPQUERY_SERVICE_CONFIG>(configBuffer.data());
if (!QueryServiceConfig(hSerivce, lpServiceConfig, bytesNeeded, &bytesNeeded)) ...
std::cout << lpServiceConfig->lpBinaryPathName << std::endl;
CloseServiceHandle(hService);
}
CloseServiceHandle(hSCManager);
}
I am trying to read the physical memory values in Hardware\ResourceMap\System Resources\Physical Memory using the following code:
#include <iostream>
#include <conio.h>
#include <windows.h>
#include <string>
#include <stdlib.h>
using namespace std;
int main()
{
HKEY hKey = NULL;
LPCTSTR pszSubKey = L"Hardware\\ResourceMap\\System Resources\\Physical Memory";
LPCTSTR pszValueName = L".Translated";
if (! RegOpenKey(HKEY_LOCAL_MACHINE, pszSubKey, &hKey) == ERROR_SUCCESS)
{
cout << "RegOpenKey failed" << endl;
return 0;
}
DWORD dwType = 0;
LPBYTE lpData = NULL;
DWORD dwLength = 0;
if (! RegQueryValueEx(hKey, pszValueName, 0, &dwType, NULL, &dwLength) == ERROR_SUCCESS)
{
cout << "RegOpenKey failed" << endl;
return 0;
}
lpData = new BYTE[dwLength];
RegQueryValueEx(hKey, pszValueName, 0, &dwType, lpData, &dwLength);
RegCloseKey(hKey);
DWORD dwResourceCount = *(DWORD*)(lpData + 16);
auto pmi = lpData + 24;
for (int dwIndex = 0; dwIndex < dwResourceCount; dwIndex++)
{
auto start = *(uint64_t*)(pmi + 0);
cout << "-> 0x" << hex << start;
auto length = *(uint64_t*)(pmi + 8);
cout << "\t + 0x" << hex << length;
auto endaddr = start + length;
cout << "\t0x" << hex << endaddr << endl;
pmi += 20;
}
delete[]lpData;
}
A sample output:
-> 0x1000 + 0x57000 0x58000
-> 0x59000 + 0x46000 0x9f000
-> 0x100000 + 0xc855f000 0xc865f000
-> 0xc8666000 + 0xbf3000 0xc9259000
-> 0xc9759000 + 0x13779000 0xdced2000
-> 0xdd0d8000 + 0x3c000 0xdd114000
-> 0xddfff000 + 0x1000 0xde000000
-> 0x100000000 + 0x41f0000 0x1041f0000
The problem is that the last length value is incorrect.
Instead of 0x41f0000, the Registry editor shows 0x41f000000 to be the correct value:
I have been researching this issue for the past few days, but cannot figure out why I get a false value here.
Can anyone with more experience using the Win32 API help me?
if value type is REG_RESOURCE_LIST value data is CM_RESOURCE_LIST structure. need use it instead of *(DWORD*)(lpData + 16);, lpData + 24. anyway your code is incorrect in case Count != 1. what you try print is CM_PARTIAL_RESOURCE_DESCRIPTOR structures. but you not check the Type member of CM_PARTIAL_RESOURCE_DESCRIPTOR. but it cab be different. can be CmResourceTypeMemory but also can be CmResourceTypeMemoryLarge - you not take this in account. in case CmResourceTypeMemoryLarge need check Flags for
CM_RESOURCE_MEMORY_LARGE_40
CM_RESOURCE_MEMORY_LARGE_48
CM_RESOURCE_MEMORY_LARGE_64
and
you say:
Instead of 0x41f0000 the regeditor shows 0x41f000000
but 0x41f000000 is shifted on 8 bit 0x41f0000. based on this obvious that you really have here CmResourceTypeMemoryLarge with CM_RESOURCE_MEMORY_40 flag.
in this case need use Length40 member:
The high 32 bits of the 40-bit length, in bytes, of the range of
allocated memory addresses. The lowest 8 bits are treated as zero.
so code for dump CM_RESOURCE_LIST must be next:
BOOL Memory(PCM_RESOURCE_LIST pcrl, ULONG size)
{
if (size < FIELD_OFFSET(CM_RESOURCE_LIST, List))
{
return FALSE;
}
size -= FIELD_OFFSET(CM_RESOURCE_LIST, List);
if (ULONG Count = pcrl->Count)
{
PCM_FULL_RESOURCE_DESCRIPTOR List = pcrl->List;
do
{
if (size < FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList.PartialDescriptors))
{
return FALSE;
}
size -= FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList.PartialDescriptors);
DbgPrint("InterfaceType=%x BusNumber=%u\n", List->InterfaceType, List->BusNumber);
if (ULONG n = List->PartialResourceList.Count)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors = List->PartialResourceList.PartialDescriptors;
do
{
if (size < sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR))
{
return FALSE;
}
size -= sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
ULONG64 Length = PartialDescriptors->u.Memory.Length;
switch (PartialDescriptors->Type)
{
case CmResourceTypeMemoryLarge:
switch (PartialDescriptors->Flags & (CM_RESOURCE_MEMORY_LARGE_40|
CM_RESOURCE_MEMORY_LARGE_48|CM_RESOURCE_MEMORY_LARGE_64))
{
case CM_RESOURCE_MEMORY_LARGE_40:
Length <<= 8;
break;
case CM_RESOURCE_MEMORY_LARGE_48:
Length <<= 16;
break;
case CM_RESOURCE_MEMORY_LARGE_64:
Length <<= 32;
break;
default:
DbgPrint("unknown mamory type\n");
continue;
}
case CmResourceTypeMemory:
DbgPrint("%016I64x %I64x\n",
PartialDescriptors->u.Memory.Start.QuadPart, Length);
break;
}
} while (PartialDescriptors++, --n);
}
} while (List++, --Count);
}
return size == 0;
}
also when we get it data - need not forget close key handle even on error (you not do this when RegQueryValueEx fail) and use RegOpenKeyExW instead RegOpenKey for ability specify the desired access rights to the key. the use 2 sequential calls to RegQueryValueEx (with 0 buffer and allocated once buffer) also not the best. because in theory buffer size can changed (some change value) between this 2 calls and you can fail got data on second call RegQueryValueExtoo. also we can already on first call allocate reasonable memory space, and only if it will be not enough - reallocate on next call. so better call this in loop until we got ERROR_MORE_DATA and first time call with already not empty buffer:
ULONG Memory()
{
HKEY hKey;
ULONG dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"Hardware\\ResourceMap\\System Resources\\Physical Memory",
0, KEY_READ, &hKey);
if (dwError == NOERROR)
{
ULONG cb = 0x100;
do
{
dwError = ERROR_NO_SYSTEM_RESOURCES;
union {
PVOID buf;
PBYTE pb;
PCM_RESOURCE_LIST pcrl;
};
if (buf = LocalAlloc(0, cb))
{
ULONG dwType;
if ((dwError = RegQueryValueExW(hKey, L".Translated",
0, &dwType, pb, &cb)) == NOERROR)
{
if (dwType == REG_RESOURCE_LIST)
{
if (!Memory(pcrl, cb))
{
DbgPrint("error parsing resource list\n");
}
}
else
{
dwError = ERROR_INVALID_DATATYPE;
}
}
LocalFree(buf);
}
} while (dwError == ERROR_MORE_DATA);
RegCloseKey(hKey);
}
return dwError;
}
Background
I am writing a command line C++ program with Visual Studio 2013 Community Edition. It connects to an Active Directory server via LDAP and retrieves a list of unique values in a couple of attributes (ex: office location, department).
Problem
The program compiles fine, but I encounter a memory access issue when I run it:
Unhandled exception at 0x74EDC6B1 (Wldap32.dll) in LdapSearchResultTest1.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.
This is first time I'm using C++ with an external library, so I'm not sure how to even approach debugging this (normally I write Java for Android). I've spent the better part of the day looking around SO and trying out ideas based on answers to similar questions, but I still have not been able to figure it out.
The exact issue lies with the last parameter in this function call:
// Do the search
int searchReturnCode = ldap_search_s(
ldapSession,
&searchBase[0],
LDAP_SCOPE_SUBTREE,
filter,
pAttributes,
0,
&pSearchResults); // Error is here
My code
My code is based on an example from the MSDN website, which I have reproduced after my code. Here is an SSCCE that demonstrates the issue:
#include<iostream>
#include<Windows.h>
#include<Winldap.h>
#include<WinBer.h>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
// Function headers
vector<string> get_unique_departments(LDAP*, char*, string);
vector<string> get_unique_office_locations(LDAP*, char*, string);
vector<string> get_unique_values_by_property(LDAP*, char*, string, string);
void print_list(string, vector<string>);
// Main function
int main(int argc, char* argv[]) {
/**
* Take AD connection arguments from Windows command line:
* - Server address
* - Server port
* - Username
* - Password
* - Search base(s) (space separated if there are multiple)
*
* Example call from Windows command line:
* > program.exe ad-test.example.com 389 joe#ad-test.example.com L3tM31n "OU=Development Team,DC=ad-test,DC=example,DC=com" "OU=Management Team,DC=ad-test,DC=example,DC=com"
*/
string serverAddress = argv[1];
int serverPort = atoi(argv[2]);
string username = argv[3];
string password = argv[4];
vector<string> searchBases;
for (int i = 0; i < argc; i++) {
searchBases.push_back(argv[i]);
}
// If debug build, print received parameters
#ifdef _DEBUG
cout << "Server address: " << serverAddress << endl
<< "Server port: " << serverPort << endl
<< "Username: " << username << endl
<< "Password: " << password << endl;
for (size_t i = 5; i < searchBases.size(); i++) {
cout << "Search base: " << searchBases.at(i) << endl;
}
cout << endl;
#endif
// Initiate LDAP connection to Active Directory
int ldapVersion = LDAP_VERSION3;
LDAP* ldapSession = ldap_init(&serverAddress[0], serverPort);
ldap_set_option(ldapSession, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
ULONG ldapConnection = ldap_connect(ldapSession, nullptr);
// Bind user
int ldapBindResult = ldap_simple_bind_s(ldapSession, &username[0], &password[0]);
if (ldapBindResult != LDAP_SUCCESS) {
ldap_unbind(ldapSession);
#ifdef _DEBUG
cout << "Unable to connect to LDAP directory" << endl << endl;
#endif
}
else {
#ifdef _DEBUG
cout << "Connected to LDAP!" << endl << endl;
#endif
}
// The LDAP object filter
char* filter = "(&(objectCategory=person)(objectClass=user))";
// Get lists of departments and offices
vector<string> departments, offices;
for (int i = 0; i < searchBases.size(); i++) {
vector<string> tempDepts = get_unique_departments(ldapSession, filter, searchBases.at(i));
vector<string> tempOffices = get_unique_office_locations(ldapSession, filter, searchBases.at(i));
for (int j = 0; j < tempDepts.size(); j++) {
departments.push_back(tempDepts.at(j));
}
for (int j = 0; j < tempOffices.size(); j++) {
offices.push_back(tempOffices.at(j));
}
}
// Print the lists
print_list("Departments", departments);
print_list("Offices", offices);
// Return
return 0;
}
// Retrieve a list of unique departments
vector<string> get_unique_departments(LDAP* session, char* filter, string searchBase) {
return get_unique_values_by_property(session, filter, searchBase, "department");
}
// Retrieve a list of unique office locations
vector<string> get_unique_office_locations(LDAP* session, char* filter, string searchBase) {
return get_unique_values_by_property(session, filter, searchBase, "office");
}
// Get a list of an attribute's unique values
vector<string> get_unique_values_by_property(LDAP* ldapSession, char* filter, string searchBase, string property) {
// Initialize some variables
vector<string> results;
char* pAttributes[1];
pAttributes[0] = &property[0];
LDAPMessage* pSearchResults = NULL;
int numResults = 0;
// Do the search
int searchReturnCode = ldap_search_s(
ldapSession,
&searchBase[0],
LDAP_SCOPE_SUBTREE,
filter,
pAttributes,
0,
&pSearchResults); // Error is here
// Process results
if (searchReturnCode == LDAP_SUCCESS) {
// Initialize some variables
LDAPMessage* pEntry = NULL;
char* pEntryDN = NULL;
char* sMsg;
BerElement* pBer = NULL;
char* pAttribute = NULL;
char** ppValue = NULL;
ULONG iValue = 0;
// Count the results
numResults = ldap_count_entries(ldapSession, pSearchResults);
// Loop over results
for (ULONG i = 0; i < numResults; i++) {
// Get the first/next entry
if (!i) {
pEntry = ldap_first_entry(ldapSession, pSearchResults);
}
else {
pEntry = ldap_next_entry(ldapSession, pEntry);
}
// Fail if unable to get entry
if (pEntry == NULL) {
results.clear();
return results;
}
// Loop over the attributes
pAttribute = ldap_first_attribute(ldapSession, pEntry, &pBer);
while (pAttribute != NULL) {
// Get and handle the values
ppValue = ldap_get_values(ldapSession, pEntry, pAttribute);
if (ppValue != NULL) {
iValue = ldap_count_values(ppValue);
if (find(results.begin(), results.end(), *ppValue) == results.end()) {
results.push_back(*ppValue);
}
// Memory management
ldap_value_free(ppValue);
ppValue = NULL;
ldap_memfree(pAttribute);
// Get the next attribute
pAttribute = ldap_next_attribute(ldapSession, pEntry, pBer);
}
}
// Memory management
if (pBer != NULL) {
ber_free(pBer, 0);
}
pBer = NULL;
}
}
// Free search result memory
if (pSearchResults != NULL) {
ldap_msgfree(pSearchResults);
}
// Return
return results;
}
// Print a vector-based list w/ header
void print_list(string header, vector<string> items) {
if (items.size() > 0) {
cout << header << ":" << endl;
for (int i = 0; i < items.size(); i++) {
cout << items.at(i) << endl;
}
cout << endl;
}
}
MSDN Example : Searching an LDAP Directory
From https://msdn.microsoft.com/en-us/library/aa367016%28v=vs.85%29.aspx
//----------------------------------------------
// Performing an LDAP Synchronous Search.
//
// Be aware that you must set the command prompt screen buffer
// height to 350 and the width to 90.
//-------------------------------------------------------------
#include <windows.h>
#include <winldap.h>
#include <winber.h>
#include <rpc.h>
#include <rpcdce.h>
#include <stdio.h>
//-----------------------------------------------------------
// This subroutine must have validated credentials (name and
// password) passed to it.
//-----------------------------------------------------------
int MyLDAPSearch(PCHAR pUserName, PCHAR pPassword)
{
//---------------------------------------------------------
// Initialize a session. LDAP_PORT is the default port, 389.
//---------------------------------------------------------
PCHAR hostName = "fabrikam.com";
LDAP* pLdapConnection = NULL;
pLdapConnection = ldap_init(hostName, LDAP_PORT);
if (pLdapConnection == NULL)
{
printf("ldap_init failed with 0x%x.\n",LdapGetLastError());
ldap_unbind(pLdapConnection);
return -1;
}
else
printf("ldap_init succeeded \n");
//-------------------------------------------------------
// Set session options.
//-------------------------------------------------------
ULONG version = LDAP_VERSION3;
ULONG numReturns = 10;
ULONG lRtn = 0;
// Set the version to 3.0 (default is 2.0).
lRtn = ldap_set_option(
pLdapConnection, // Session handle
LDAP_OPT_PROTOCOL_VERSION, // Option
(void*) &version); // Option value
if(lRtn == LDAP_SUCCESS)
printf("ldap version set to 3.0 \n");
else
{
printf("SetOption Error:%0lX\n", lRtn);
ldap_unbind(pLdapConnection);
return -1;
}
// Set the limit on the number of entries returned to 10.
lRtn = ldap_set_option(
pLdapConnection, // Session handle
LDAP_OPT_SIZELIMIT, // Option
(void*) &numReturns); // Option value
if(lRtn == LDAP_SUCCESS)
printf("Max return entries set to 10 \n");
else
{
printf("SetOption Error:%0lX\n", lRtn);
ldap_unbind(pLdapConnection);
return -1;
}
//--------------------------------------------------------
// Connect to the server.
//--------------------------------------------------------
lRtn = ldap_connect(pLdapConnection, NULL);
if(lRtn == LDAP_SUCCESS)
printf("ldap_connect succeeded \n");
else
{
printf("ldap_connect failed with 0x%lx.\n",lRtn);
ldap_unbind(pLdapConnection);
return -1;
}
//--------------------------------------------------------
// Bind with credentials.
//--------------------------------------------------------
PCHAR pMyDN = "DC=fabrikam,DC=com";
SEC_WINNT_AUTH_IDENTITY secIdent;
secIdent.User = (unsigned char*)pUserName;
secIdent.UserLength = strlen(pUserName);
secIdent.Password = (unsigned char*)pPassword;
secIdent.PasswordLength = strlen(pPassword);
secIdent.Domain = (unsigned char*)hostName;
secIdent.DomainLength = strlen(hostName);
secIdent.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
lRtn = ldap_bind_s(
pLdapConnection, // Session Handle
pMyDN, // Domain DN
(PCHAR)&secIdent, // Credential structure
LDAP_AUTH_NEGOTIATE); // Auth mode
if(lRtn == LDAP_SUCCESS)
{
printf("ldap_bind_s succeeded \n");
secIdent.Password = NULL; // Remove password pointer
pPassword = NULL; // Remove password pointer
}
else
{
printf("ldap_bind_s failed with 0x%lx.\n",lRtn);
ldap_unbind(pLdapConnection);
return -1;
}
//----------------------------------------------------------
// Perform a synchronous search of fabrikam.com for
// all user objects that have a "person" category.
//----------------------------------------------------------
ULONG errorCode = LDAP_SUCCESS;
LDAPMessage* pSearchResult;
PCHAR pMyFilter = "(&(objectCategory=person)(objectClass=user))";
PCHAR pMyAttributes[6];
pMyAttributes[0] = "cn";
pMyAttributes[1] = "company";
pMyAttributes[2] = "department";
pMyAttributes[3] = "telephoneNumber";
pMyAttributes[4] = "memberOf";
pMyAttributes[5] = NULL;
errorCode = ldap_search_s(
pLdapConnection, // Session handle
pMyDN, // DN to start search
LDAP_SCOPE_SUBTREE, // Scope
pMyFilter, // Filter
pMyAttributes, // Retrieve list of attributes
0, // Get both attributes and values
&pSearchResult); // [out] Search results
if (errorCode != LDAP_SUCCESS)
{
printf("ldap_search_s failed with 0x%0lx \n",errorCode);
ldap_unbind_s(pLdapConnection);
if(pSearchResult != NULL)
ldap_msgfree(pSearchResult);
return -1;
}
else
printf("ldap_search succeeded \n");
//----------------------------------------------------------
// Get the number of entries returned.
//----------------------------------------------------------
ULONG numberOfEntries;
numberOfEntries = ldap_count_entries(
pLdapConnection, // Session handle
pSearchResult); // Search result
if(numberOfEntries == NULL)
{
printf("ldap_count_entries failed with 0x%0lx \n",errorCode);
ldap_unbind_s(pLdapConnection);
if(pSearchResult != NULL)
ldap_msgfree(pSearchResult);
return -1;
}
else
printf("ldap_count_entries succeeded \n");
printf("The number of entries is: %d \n", numberOfEntries);
//----------------------------------------------------------
// Loop through the search entries, get, and output the
// requested list of attributes and values.
//----------------------------------------------------------
LDAPMessage* pEntry = NULL;
PCHAR pEntryDN = NULL;
ULONG iCnt = 0;
char* sMsg;
BerElement* pBer = NULL;
PCHAR pAttribute = NULL;
PCHAR* ppValue = NULL;
ULONG iValue = 0;
for( iCnt=0; iCnt < numberOfEntries; iCnt++ )
{
// Get the first/next entry.
if( !iCnt )
pEntry = ldap_first_entry(pLdapConnection, pSearchResult);
else
pEntry = ldap_next_entry(pLdapConnection, pEntry);
// Output a status message.
sMsg = (!iCnt ? "ldap_first_entry" : "ldap_next_entry");
if( pEntry == NULL )
{
printf("%s failed with 0x%0lx \n", sMsg, LdapGetLastError());
ldap_unbind_s(pLdapConnection);
ldap_msgfree(pSearchResult);
return -1;
}
else
printf("%s succeeded\n",sMsg);
// Output the entry number.
printf("ENTRY NUMBER %i \n", iCnt);
// Get the first attribute name.
pAttribute = ldap_first_attribute(
pLdapConnection, // Session handle
pEntry, // Current entry
&pBer); // [out] Current BerElement
// Output the attribute names for the current object
// and output values.
while(pAttribute != NULL)
{
// Output the attribute name.
printf(" ATTR: %s",pAttribute);
// Get the string values.
ppValue = ldap_get_values(
pLdapConnection, // Session Handle
pEntry, // Current entry
pAttribute); // Current attribute
// Print status if no values are returned (NULL ptr)
if(ppValue == NULL)
{
printf(": [NO ATTRIBUTE VALUE RETURNED]");
}
// Output the attribute values
else
{
iValue = ldap_count_values(ppValue);
if(!iValue)
{
printf(": [BAD VALUE LIST]");
}
else
{
// Output the first attribute value
printf(": %s", *ppValue);
// Output more values if available
ULONG z;
for(z=1; z<iValue; z++)
{
printf(", %s", ppValue[z]);
}
}
}
// Free memory.
if(ppValue != NULL)
ldap_value_free(ppValue);
ppValue = NULL;
ldap_memfree(pAttribute);
// Get next attribute name.
pAttribute = ldap_next_attribute(
pLdapConnection, // Session Handle
pEntry, // Current entry
pBer); // Current BerElement
printf("\n");
}
if( pBer != NULL )
ber_free(pBer,0);
pBer = NULL;
}
//----------------------------------------------------------
// Normal cleanup and exit.
//----------------------------------------------------------
ldap_unbind(pLdapConnection);
ldap_msgfree(pSearchResult);
ldap_value_free(ppValue);
return 0;
}
Reading the documentation for ldap_search_s, I see:
base [in]
Pointer to a null-terminated string that contains the distinguished name of the entry at which to start the search.
However, &searchBase[0] will only get you a pointer to the storage std::string uses - there is no requirement that this be null terminated. You should use searchBase.c_str() instead, as this is guaranteed to give you a null terminated c-string.
The pAttributes array has to be null-terminated.
The code should look like this:
char* pAttributes[2];
pAttributes[0] = &property[0];
pAttributes[1] = NULL;
I am aware that NT header has all constants defined like SE_TAKE_OWNERSHIP_NAME, and so there are functions available to convert these into human readable form (Take ownership of files or other objects).
My question is how to enumerate these names? With different versions of Windows, not all SE-names would be applicable (i.e. privileges may not be available on particular NT system).
Whilst it is true that Windows7/2008 is the latest and appropriate header for the same would list all of them - and if the application runs on a lower platform, the function taking SE-names would simply fail for given name if given OS doesn't support (like LsaEnumerateAccountsWithUserRight would fail).
But how to make application future compatible that can facilitate listing all privileges for future versions of Windows OS?
Use LsaEnumeratePrivileges (defined in ntlsa.h, which is in the WDK - inc/api):
NTSTATUS
NTAPI
LsaEnumeratePrivileges(
__in LSA_HANDLE PolicyHandle,
__inout PLSA_ENUMERATION_HANDLE EnumerationContext,
__out PVOID *Buffer,
__in ULONG PreferedMaximumLength,
__out PULONG CountReturned
);
The buffer that you get is an array of POLICY_PRIVILEGE_DEFINITION structures:
typedef struct _POLICY_PRIVILEGE_DEFINITION
{
LSA_UNICODE_STRING Name;
LUID LocalValue;
} POLICY_PRIVILEGE_DEFINITION, *PPOLICY_PRIVILEGE_DEFINITION;
For example:
#include <ntlsa.h>
NTSTATUS status;
LSA_HANDLE policyHandle;
LSA_ENUMERATION_HANDLE enumerationContext = 0;
PPOLICY_PRIVILEGE_DEFINITION buffer;
ULONG countReturned;
ULONG i;
LsaOpenPolicy(..., &policyHandle);
while (TRUE)
{
status = LsaEnumeratePrivileges(policyHandle, &enumerationContext, &buffer, 256, &countReturned);
if (status == STATUS_NO_MORE_ENTRIES)
break; // no more privileges
if (!NT_SUCCESS(status))
break; // error
for (i = 0; i < countReturned; i++)
{
// Privilege definition in buffer[i]
}
LsaFreeMemory(buffer);
}
LsaClose(policyHandle);
Working code:
#include <Windows.h>
#include <iostream>
#include <ntstatus.h>
typedef LONG NTSTATUS, *PNTSTATUS;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} STRING, *PSTRING;
typedef LARGE_INTEGER OLD_LARGE_INTEGER;
typedef LARGE_INTEGER POLD_LARGE_INTEGER;
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#include <ntlsa.h>
LSA_HANDLE GetPolicyHandle() {
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS ntsResult;
LSA_HANDLE lsahPolicyHandle;
// Object attributes are reserved, so initialize to zeros.
ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
// Get a handle to the Policy object.
ntsResult = LsaOpenPolicy(NULL, // Name of the target system.
&ObjectAttributes, // Object attributes.
POLICY_ALL_ACCESS, // Desired access permissions.
&lsahPolicyHandle // Receives the policy handle.
);
if (ntsResult != STATUS_SUCCESS) {
// An error occurred. Display it as a win32 error code.
wprintf(L"OpenPolicy returned %lu\n", LsaNtStatusToWinError(ntsResult));
return NULL;
}
return lsahPolicyHandle;
}
void main() {
NTSTATUS status;
LSA_HANDLE policyHandle;
LSA_ENUMERATION_HANDLE enumerationContext = 0;
PPOLICY_PRIVILEGE_DEFINITION buffer;
ULONG countReturned;
ULONG i;
policyHandle = GetPolicyHandle();
while (TRUE) {
status = LsaEnumeratePrivileges(policyHandle, &enumerationContext,
(PVOID *)&buffer, 256, &countReturned);
if (status == STATUS_NO_MORE_ENTRIES)
break; // no more privileges
if (!NT_SUCCESS(status))
break; // error
for (i = 0; i < countReturned; i++) {
// Privilege definition in buffer[i]
std::wcout << L"KEY:" << buffer[i].Name.Buffer << L" HIGH VALUE:"
<< buffer[i].LocalValue.HighPart << L"LOW VALUE:"
<< buffer[i].LocalValue.LowPart << std::endl;
}
LsaFreeMemory(buffer);
}
LsaClose(policyHandle);
}
I am having problems with reading the registry.
This function finds the number of entries in a registry path. It works perfectly, I have tested it:
void findNumberEntries(registryTest &INSTALLKEY) {
char buffer[50];
char size = sizeof(buffer);
int index = 0;
if(RegOpenKeyEx(INSTALLKEY.hKey,(LPTSTR)(INSTALLKEY.regpath.c_str()),0,KEY_ALL_ACCESS,&INSTALLKEY.hKey) == ERROR_SUCCESS) {
DWORD readEntry;
do {
readEntry = RegEnumValue(INSTALLKEY.hKey,index,(LPTSTR)buffer,(LPDWORD)&size,NULL,NULL,NULL,NULL);
index++;
}
while(readEntry != ERROR_NO_MORE_ITEMS);
}
INSTALLKEY.number = index;
RegCloseKey(INSTALLKEY.hKey);
}
now, the main function:
std::string regpath32 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run\\";
struct registryTest {
HKEY hKey;
std::string regpath;
int number;
};
registryTest INSTALLKEY = {HKEY_LOCAL_MACHINE, regpath32};
findNumberEntries(INSTALLKEY);
printf("%d\n",INSTALLKEY.number);
system("PAUSE");
//until here everything works as it should
HKEY hKey = INSTALLKEY.hKey;
std::string regpath = INSTALLKEY.regpath;
char buffer[50];
char size = sizeof(buffer);
std::string bufferString;
DWORD regOpen = RegOpenKeyEx(INSTALLKEY.hKey,(LPTSTR)INSTALLKEY.regpath.c_str(),0,KEY_READ,&INSTALLKEY.hKey);
if(regOpen == ERROR_SUCCESS) //this is the part that fails.
{
printf("Registry Key was successfully opened\n");
}
else
{
printf("Unable to open registry key\n");
LPVOID message;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), NULL,(LPTSTR) &message, 0, NULL );
MessageBox(NULL,(LPCTSTR)message,"ERROR",MB_OK|MB_ICONINFORMATION);
}
...rest of the code
I always get "Unable to open registry" and the error message I get is "There are no more files". What is the problem??
your problem is that when you first open the registry key ,you assign it to hkey-member of your struct. So the second time this hkey doesn't contain the original basekey anymore.
change :
DWORD regOpen =
RegOpenKeyEx(INSTALLKEY.hKey,(LPTSTR)INSTALLKEY.regpath.c_str(),0,KEY_READ,&INSTALLKEY.hKey);
into
DWORD regOpen = RegOpenKeyEx(
HKEY_LOCAL_MACHINE
,(LPTSTR)INSTALLKEY.regpath.c_str(),0,KEY_READ,&INSTALLKEY.hKey);
or change this:
void findNumberEntries( registryTest &INSTALLKEY)
{
char buffer[50];
char size = sizeof(buffer);
int index = 0;
HKEY hkOpen = 0; // can't use INVALID_HANDLE_VALUE for HKEY's;
if (RegOpenKeyEx( INSTALLKEY.hKey ,(LPTSTR)(INSTALLKEY.regpath.c_str())
,0,&hkOpen ) == ERROR_SUCCESS)
{
// You should use RegQueryInfoKey for below code !
DWORD readEntry;
do {
readEntry = RegEnumValue( hkOpen ,index,(LPTSTR)buffer
,(LPDWORD size,NULL,NULL,NULL,NULL);
index++;
}
while(readEntry != ERROR_NO_MORE_ITEMS); }
INSTALLKEY.number = index;
RegCloseKey( hkOpen );
}
You may need to specify KEY_ALL_ACCESS in the second call as well, rather than just in the first. And on Win7 64-bit you may be running into the registry redirect craziness (http://msdn.microsoft.com/en-us/library/aa384232%28VS.85%29.aspx).
EDIT: ah, you might just be getting an ERROR_CANTWRITE back (error code number 5). You might be able to ignore that and see if it still works.
It's very likely that on Windows 7 64-bit that you are being redirected via Registry Virtualization. You can determine what keys are being redirected by calling RegQueryReflectionKey.
If you modify your code to output the actual integer value that is returned rather than a generic "Unable to open key", then it would be helpful. For example,
long n = RegOpenKeyEx(HKEY_LOCAL_MACHINE,TEXT("\\SOFTWARE"),
0,KEY_QUERY_VALUE, &hk );
if ( n == ERROR_SUCCESS ) {
cout << "OK" << endl;
}
else {
cout << "Failed with value " << n << endl;
}