C++ REG_SZ to char* and Reading HKLM without Elevated Permissions - c++

So I've been trying to get a REG_SZ value out of the registry and store it as a char*. After looking around the internet this is what I came up with. The problem is that the value I get is not what is stored in the registry, I get a bunch of random garbage. How would I correctly get the value?
HKEY hKey;
char value[256];
// Open the key
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0\\", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
{
return "";
}
// Read the value
DWORD dwType = REG_SZ;
DWORD dwCount = sizeof(value);
if(RegQueryValueEx(hKey, "ProcessorNameString", NULL, &dwType, (LPBYTE)&value, &dwCount) != ERROR_SUCCESS)
{
RegCloseKey(hKey);
return "";
}
// Cleanup and return
RegCloseKey(hKey);
return value;
Also anther quick question. I remember that if my program inst running as admin of Vista/7 then it cant edit the HKLM but can it still read it?

(Updated, since previous answer was wrong.)
The problem may be that you're returing value, which is a stack-allocated buffer. This will work only if you declared your function as returning a char[256]--if you're trying to return a char*, then the caller is getting the address of the first byte in value, which is now pointing to invalid stack data. You should allocate value as a char* on the heap, which will allow you to return the pointer with impunity.
Whether or not you are allowed to read or edit the registry key depends on what ACLs are applied to the key you're reading. It is possible to set the permissions on the key in such a way that an unelevated user can't even read the key, but it's also possible to set the permissions such that all users can both read and write. The key that you're reading above should be readable by all users, but it won't be modifiable except by admins.

If your application has no manifest it may or may not read the real HKLM. If it ever tries to write to HKLM registry virtualization will kick in and divert writes and also reads to a virtualized per-user store. You are allowed to read HKLM when you're not an admin, so be sure to add a manifest with asInvoker to prevent virtualization.

The main question is already answered but relating to your access problem. You will have to add a manifest file to elevate your process to administrator if you want write access to the registry.

You should not return a non static locally declared variable; try to declare the variable as static char value[256];, it is still a bad practice, but could fix your problem;
If you still get garbage, maybe you're compiling with UNICODE defined. If so, you are calling RegQueryValueExW and you're getting a wide char string (no compile time error as the parameter is casted to (LPBYTE)). Try to disable UNICODE or define your string as a TCHAR.

Related

Can't load 64-bit key using RegLoadKey in 32-bit service

I need to open up and modify a user's registry key from a 32-bit service (note that the user is not logged in at the time.) I do the following:
//For simplicity error checks are not shown
//I also made sure to enable the following privileges:
// SE_RESTORE_NAME, SE_BACKUP_NAME
//"ntuser.dat" = is the file OS uses to load user's profile
if(RegLoadKey(HKEY_LOCAL_MACHINE, L"Test123", L"C:\\Users\\UserA\\ntuser.dat") == ERROR_SUCCESS)
{
HKEY hKey;
DWORD dwRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"Test123\\Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\TrayNotify"),
NULL, KEY_READ | KEY_WOW64_64KEY, &hKey);
//'dwRes' = is returned as 2, or ERROR_FILE_NOT_FOUND
RegUnLoadKey(HKEY_LOCAL_MACHINE, L"Test123");
}
The problem is that the Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify key isn't loaded, even though I know that it exists in the actual user profile. I can verify that by loading the user account and by using 64-bit regedit.
I suspect that this has something to do with the Wow64 redirection but I can't seem to understand what am I doing wrong?
EDIT: Added error check for the first API.
I think I got it. Two corrections to my original code:
First off, since Vista I need to load Usrclass.dat file for the classes hive and not ntuser.dat. It kinda makes sense because ntuser.dat is a part of a user's roaming profile and Classes\Local Settings does not fit into the picture well. So here's the location of the Usrclass.dat file, which contains non-roaming user data (mostly COM stuff, but some other settings as well):
%LocalAppData%\Microsoft\Windows\Usrclass.dat
The key to open after the user hive loads is:
Test123\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify
that is because the original HKCU\Software\Classes is redirected to HKU\<UserSID>_Classes that is stored in the Usrclass.dat file.

CreateProcessWithLogonW : unable to start process

Hi I am completely new to programming. And please someone help me.
I am trying to start a pocess from a service.
I need to start the new process by prompting user to enter admin credentials.
I was trying to use CreateProcessWithLogonW().
Am I using the right function.
I tried to give input username, password, domain as localhost. I gave full pathe to the .exe file that i need to start.
Here is the piece of code.
CreateProcessWithLogonW(L"Administrator",
L"localhost",
L"password",
0,
NULL,
L"c:\myupdates\myapp.exe",
NORMAL_PRIORITY_CLASS | CREATE_CONSOLE,
NULL,
NULL,
&si,
&pi);
Si.cb = sizeof(si);
Si.lpDesktop = L"winsta0\\default";
But the process never started. Can you guys tell me what I am doing wrong.
And what do I need to do to promt user to enter credentials of administrator instead of hardcoding it.
None of CreateProcess* functions will do any promting. They are low level APIs and know nothing about GUI.
If you want user to be prompted, use ShellExecuteEx with runas command. Windows will first ask a permission to elevate and then prompt for credentials.
You may want to escape the program string properly as well:
L"c:\myupdates\myapp.exe"
should at least be:
L"c:\\myupdates\\myapp.exe"
Frankly, there are a multitude of things wrong with this code, from improper setup of SI, to the parameters passed to the API itself. I suggest you read up more.
One problem is passing a string literal as the command-line argument, as that argument must be modifiable. From CreateProcessWithLogon() in relation to the command-line argument:
The function can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.
You also need to escape the backslashes. Change to:
WCHAR cmdLine[] = L"c:\\myupdates\\myapp.exe"; /* 'cmdLine' is a
copy of the string
literal. */
and pass cmdLine instead.
After any WINAPI function failure check GetLastError() as it will inform you of the reason for failure.
Maybe it's too late to help you. But it might be helpful for others, though. If you use the CreateProcessWithLogonW function and you are using the Default desktop just keep lpDesktop as NULL.
If lpDesktop is not Null you have to enter the user's sid (getting with LookupAccountNamean) as a ACE in desktops and winstation's DACL
So here are the steps you have to do to add an ACE for a desktop:
get Desktop handle with OpenDesktop, use the right dwDesiredAccess
get the Security Descriptor with GetSecurityInfo and DACL_SECURITY_INFORMATIONas securityinfo
get the DACL from your Security Descriptor
Add AddAccessAllowedAcewith the sid of your User's sid
Set the modified DACL to your Desktop handle
now repeat those steps for winsta0 winstation
The commantary of Mr. Furious in the documantary helped me alot to solve this problem.
The access violation is down to the lpCommandLine parameter. That is meant to a editable memory, LPWSTR and the API function does modify the buffer. But you pass a pointer to non-modifiable memory.
But there's a more fundamental problem. You say you want to prompt for credentials from a service. Services should not show UI and in modern versions of Windows, a service simply cannot show UI. Your design is flawed and you need to re-consider it.

try to change ActivePowerScheme: RegOpenKeyEx failed with error 0

I need to set ActivePowerScheme by changing it in registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes.
So I try to do it with winapi functions RegOpenKeyEx and RegSetValueEx
wchar_t *PowerScheme=TEXT("8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c");
HKEY hRootKey = HKEY_LOCAL_MACHINE;
PWCHAR sKey = TEXT("SYSTEM\\CurrentControlSet\\Control\\Power\\User\\PowerSchemes");
PWCHAR sActivePowerS = TEXT("ActivePowerScheme");
HKEY hKeyResult = NULL;
//open
if (RegOpenKeyEx(hRootKey,sKey,0,KEY_ALL_ACCESS,&hKeyResult)!=ERROR_SUCCESS) {
//it is always failing with error 0 !
DWORD dw = GetLastError();
}
But RegOpenKeyEx() is always failing with error 0, that means "Operation completed successfully". And RegSetValueEx() returns same value.
if(RegSetValueEx(hKeyResult,sActivePowerS,0,REG_SZ,
(BYTE *)PowerScheme,wcslen(PowerScheme))!=ERROR_SUCCESS) {
//it is always failing with error 0
DWORD dw = GetLastError();
}
And of course current power scheme doesn't change value. But according to msdn:
"If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code".
I will be grateful to any your answers.
P.S. it compiled in Windows 7 and executed with rights of admin
You are going about this the wrong way. You RARELY need to change stuff in the registry yourself.
Read Power Scheme Management on the MSDN site for the proper way of doing it.
As documentation states, RegOpenKeyEx does not update GetLastError, and return value is the error code itself. Would you mind checking it?
I'd bet you have ERROR_ACCESS_DENIED error here.
UPD: While this perhaps answers your question, you should consider using API suggested by RedX in order to update power management settings. Permissions on this registry key are set (for a reason!) in a way that even Administrators have only read permissions, and not write.
In the comments you state that RegOpenKeyEx returns ERROR_ACCESS_DENIED. This is because you request write access to a key to which you do not have sufficient rights because of UAC. You will need to run your process elevated to write to this key.
As others have correctly pointed out, you should not call GetLastError since RegOpenKeyEx does not set the last error value and instead returns the error code directly. More importantly you should be using the power management API rather than hacking the registry.
Even when you switch to the power management API you will still require administrator rights. You can arrange this by setting requestedExecutionLevel to requireAdministrator in your application manifest.
In Visual Studio you can make this change in the project configuration under Linker | Manifest File | UAC Execution Level.

Checking if a registry key exists

I am looking for a clean way to check if a registry key exists. I had assumed that RegOpenKey would fail if I tried to open a key that didn't exist, but it doesn't.
I could use string processing to find and open the parent key of the one I'm looking for, and then enumerate the subkeys of that key to find out if the one I'm interested in exists, but that feels both like a performance hog and a weird way to have to implement such a simple function.
I'd guess that you could use RegQueryInfoKey for this somehow, but MSDN doesn't give too many details on how, even if it's possible.
Update: I need the solution in Win32 api, not in managed code, .NET or using any other library.
The docs in MSDN seem to indicate that you should be able to open a key for read permission and get an error if it doesn't exist, like this:
lResult = RegOpenKeyEx (hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
if (lResult != ERROR_SUCCESS)
{
if (lResult == ERROR_FILE_NOT_FOUND) {
However, I get ERROR_SUCCESS when I try this.
Update 2: My exact code is this:
HKEY subKey = nullptr;
LONG result = RegOpenKeyEx(key, subPath.c_str(), 0, KEY_READ, &subKey);
if (result != ERROR_SUCCESS) {
... but result is ERROR_SUCCESS, even though I'm trying to open a key that does not exist.
Update 3: It looks like you guys are right. This fails on one specific test example (mysteriously). If I try it on any other key, it returns the correct result. Double-checking it with the registry editor still does not show the key. Don't know what to make of all that.
First of all don't worry about performance for stuff like this. Unless you are querying it 100x per sec, it will be more than fast enough. Premature optimization will cause you all kinds of headaches.
RegOpenKeyEx will return ERROR_SUCCESS if it finds the key. Just check against this constant and you are good to go.
RegOpenKey does return an error if the key does not exist. How are you using it? The expected return value is ERROR_FILE_NOT_FOUND.
From your code:
HKEY subKey = nullptr;
LONG result = RegOpenKeyEx(key, subPath.c_str(), 0, KEY_READ, &subKey);
if (result != ERROR_SUCCESS) {
I would look at the value of key and subPath and make sure they are what you expect, and that the key does not actually exist. What is the value of subKey afterwards? It is obviously opening something - try enumerating it to see what the keys and values under it are.
There is no issue with RegOpenKey not returning an error if the key does not exist - I would not try to assume there is some kind of weird OS bug in something as commonly used as the registry.
Maybe you have a registry key that is not visible to you, the user that is running the registry editor, but not to your code? A permissions problem perhaps? Is your code running as an elevated user in windows Vista or server 2008? Did you try running the registry editor as administrator?
Note that beside the "core" Registry functions that start with "Reg" there are also helper functions starting with "SHReg". These are intended for use by the Shell i.e. Explorer but are documented and can be used in normal applications too. They're typically thin wrappers that make some common tasks easier. They're part of the "Shell LightWeight API" (shlwapi.dll)

Edit Registry Values

I want to change the registry values on the pocketPC. I ran the following code:
if(enabled)
{
dwData = 120;
}
if(RegSetValueEx(HKEY_LOCAL_MACHINE, _T("System\\CurrentControlSet\\Control\\Power\\Timeouts\\BattSuspendTimeout"), 0, REG_DWORD, (LPBYTE)&dwData, sizeof(DWORD)))
{
return FALSE;
}
but it doesn't shange the registry entry. Does anyone know how to set registry key values with c++?
Thanks!
There are a two problems with what you are doing:
1: RegSetValueEx does not take a path, only a valuename. So you need to open the key path first.
e.g.
HKEY key;
if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\Power\\Timeouts", 0, 0, &key))
{
if(RegSetValueEx(key, _T("BattSuspendTimeout"), 0, REG_DWORD, (LPBYTE)&dwData, sizeof(DWORD)))
{
RegCloseKey(key);
return FALSE;
}
RegCloseKey(key);
}
2: That area of the registry requires Privileged code signing to work on all Windows Mobile devices. You can get away with it on most current touch-screen windows mobile devices if the user says "yes" to the unknown publisher question when the application is first run or installed. If you get a "Access Denied" error on the set, then you really need to be Privileged code signed for the set to work.
RegSetValueEx returns a descriptive error code. You can get a human-readable message out of this error code using FormatMessage and possibly via the Error Lookup tool, or the #ERR facility in VS. The code you have looks correct so see what the error message tells you.
How are you verifying the change? Keep in mind that making this change will not be reflected automatically in the device behavior and it probably won't show up in the Control Panel either (depends on if the CPL has already been loaded or not). The shell is unaware that you made the change and it doesn't poll the value - you have to tell it to go out and re-read. How to do it is documented in MSDN (basically you set a named system event).
Check out [VORegistry][1], it makes working with the registry so much easier.
[1]: http://www.voscorp.com/products/developer/winmobile/voregistry/index.htm VORegistry
Assuming that your looking with RegEdit, did you refresh (F5) the registry view?