How to use RegCopyTree - c++

I've been playing around with the C++ registry functions, and I'm trying to get RegCopyTree working, but every time I try, I get an error like
ERROR_FILE_NOT_FOUND
or
ERROR_ACCESS_DENIED.
I am running the program as administrator, and all other registry functions work fine.
Here is the code I'm using:
HKEY destinationKey;
RegCreateKeyEx(getRootKeyFromCode(rootKeyCode),
destinationKeyPathNative, 0, NULL, 0, 0, NULL,
&destinationKey, NULL);
RegCopyTree(INSERT_ROOT_KEY_HERE,
INSERT_ORIGINAL_KEY_PATH_HERE, destinationKey);
RegCloseKey(destinationKey);
I've removed the error handling and some other irrelevant parts.

Destination key handle should have write access, to be able to copy to it. Calling RegCreateKeyEx() without specifying access mode either fails or doesn't grant write access. Try with KEY_WRITE or KEY_CREATE_SUB_KEY as sixth argument.

IMHO, the implementation of RegCopyKey() is ultimately broken.
The following code snippet:
HKEY source = nullptr;
LONG l1 = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"CLSID\\{44EC053A-400F-11D0-9DCD-00A0C90391D3}", 0, KEY_READ /* KEY_ALL_ACCESS */, &source);
HKEY destination = nullptr;
LONG l2 = RegCreateKeyEx(HKEY_CURRENT_USER, L"Software\\Substitute", 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &destination, nullptr);
LONG l3 = RegCopyTree(source, nullptr, destination);
yields l1 = 0 (NO_ERROR), l2 = 0 (NO_ERROR), l3 = 5 (ACCESS_DENIED).
The source key (COM entry for atl.dll) exists and the destination key does not exist before calling this code. The error occurs even if I start the process with elevated privileges and open the source key with KEY_ALL_ACCESS.
By examining the registry, I found out that RegCopyTree sets the attributes of the destination key to KEY_READ. Subsequently, it is unable to create any more subkeys. So basically, RegCopyTree is tumbling over its own feet.
Microsoft was unable to tackle this problem, too; see the last answer in The RegCopyTree Function
Using RegQueryValueEx and RegSetValueEx, I can manually copy the contents from source to destination, so permissions and visibility shouldn't be the problem.
The code snippet is trivial enough. Is there still something I am overlooking?

You should use administrator authority to run your application or you call registry functions will be failed.

Related

Proper use of RegOpenCurrentUser

I went here and made a test program to see if it actually disables the task manager. Basically a simple bool switch on then switch off to see if the task manager was actually disabled. It works as intended when i compiled and ran it.
Edit: the code now looks like this
#include <iostream>
#include <Windows.h>
using namespace std;
void LockTaskManager(bool Lock);
void main(void) {
LockTaskManager(true);
cout << "Testing task manager disable." << endl;
getchar();
LockTaskManager(false);
cout << "Testing task manager enabled." << endl;
getchar();
}
void LockTaskManager(bool Lock)
{
HKEY currKey;
DWORD dwDisposition;
DWORD dwType, dwSize;
DWORD value;
if (Lock)
value = 1;
else
value = 0;
LRESULT lResult = RegOpenCurrentUser(KEY_WRITE, &currKey);
if (RegCreateKeyEx(currKey,
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\system"),
0,
NULL,
0,
KEY_SET_VALUE,
NULL,
&currKey,
&dwDisposition) == ERROR_SUCCESS)
{
dwType = REG_DWORD;
dwSize = sizeof(DWORD);
RegSetValueEx(currKey, TEXT("DisableTaskMgr"), 0, dwType, (PBYTE)&value, dwSize);
RegCloseKey(currKey);
}
}
However, after i moved the .exe to a guest user on the same computer, it does not disable the task manager. So i went to look into how and why it worked, from microsoft's MSDN here i found that HKEY_CURRENT_USER does not change and requires the use of RegOpenCurrentUser to set the current key to the user that is running that program. i know about this post but the answer is not conclusive.
So with that said, I wish to know the correct way to approach this. The goal here is to make whoever that runs the .exe of this code be unable to run task manager.
FYI, Just so whoever reads this knows, i intend to use this as a defense mechanism in the event a flag is triggered, i want to stop malicious entities from killing this process through task manager.
what is HKEY_CURRENT_USER ? this is really \REGISTRY\USER\<UserSid> where <UserSid> some sid. when process first time use HKEY_CURRENT_USER (root of the current user key is yet not opened) system query current user sid ( TokenUser ), convert sid to string, append \REGISTRY\USER\ prefix, open and cache opened key. when process next time use, HKEY_CURRENT_USER - used already opened and cached key. even if thread is impersonating - this change nothing. however some time we need access different user key, after impersonating. exactly for this situation and RegOpenCurrentUser and used. this api query current thread (or process) token for TokenUser, format path based on current user sid, open \REGISTRY\USER\<UserSid1> and return handle to you. it not cache this handle, instead you must close it, when you no longer need the returned handle.
so at first senseless use RegOpenCurrentUser if you not impersonating current thread.
at second, this code always senseless:
LRESULT lResult = RegOpenCurrentUser(KEY_READ, &hkey);
if (RegCreateKeyEx(HKEY_CURRENT_USER,..
you not use returned hKey anyway. what sense open it in this case ?
need use it in place HKEY_CURRENT_USER !
LRESULT lResult = RegOpenCurrentUser(KEY_READ, &hkey);
if (RegCreateKeyEx(hKey,..
why code not worked under guest ? when you call RegCreateKeyEx and resulting key (system in your case) yet not exist - you need have KEY_CREATE_SUB_KEY access to the parent (Policies key). however by default guest have not any write access to key. you simply have not KEY_CREATE_SUB_KEY access. and KEY_SET_VALUE you also have not. sure that under guest call RegCreateKeyEx return ERROR_ACCESS_DENIED to you.

I'm using QDir().isReadable to check if a drive is readable. In the Qt Creator it runs fine, but when I run the exe it keeps giving me errors

I'm using it like this:
if(QDir("G:/").isReadable()){
qDebug("G Is readable!"); //Do something with G:/
}
As I said, in Qt Creator it runs fine without a problem, it checks if the drive is readable, and if so it prints it to the console, if not, it does nothing.
But when I run the .exe file, it keeps giving me errors each time it does the check (every 2 seconds) if the drive isn't readable.
"There is no disk in the drive. Please insert a disk into drive G:."
I don't want this error to keep appearing, what do I do?
Edit: I think it's the isReadable function that causes the problem, is there any other way to do what I want to do? Or maybe should I write the code myself?
This message is generated by Windows.
There is a workaround for users that have applications that cannot be fixed. The error messages may be suppressed by setting 2 to registry key ErrorMode in:
Computer\HKEY_LOCAL\MACHINE\SYSTEM\CurrentControlSet\Control\Windows
It looks that if QDir::isReadable() is called after removing the media it triggers that error. QDir::exists() always returns true if the drive letter is present in the system, so it cannot be used here.
For now I see that it is possible to check removable media using native Windows API, see the answer to How to detect if media is inserted into a removable drive/card reader
The following code is able to detect that the media is removed without triggering the error:
#include <windows.h>
HANDLE hDevice = CreateFile (L"\\\\.\\G:", // like "\\.\G:"
FILE_READ_ATTRIBUTES, // read access to the attributes
FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, OPEN_EXISTING, 0, NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
// not valid device
return;
}
WORD cbBytesReturned;
bool bSuccess = DeviceIoControl (hDevice, // device to be queried
IOCTL_STORAGE_CHECK_VERIFY2,
NULL, 0, // no input buffer
NULL, 0, // no output buffer
(LPDWORD)&cbBytesReturned, // # bytes returned
NULL); // synchronous I/O
CloseHandle(hDevice); // close handle
if (bSuccess && QDir("G:/").isReadable()) {
// G is readable
}

RegOpenKey / RegOpenKeyEx returns 2 (file not found) on Windows 7, while key exists

I am trying to read a value from HKEY_CURRENT_USER\Software\Classes on Windows 7 as a standard user, and although the key exists, I get an error. Both codes below don't success:
l = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Classes", 0, KEY_READ, &hKey);
// RegOpenKeyEx doesn't success either
l = RegOpenKey(HKEY_CURRENT_USER, L"Software\\Classes", &hKey);
This code is located in a dll called by an application doing many things (I don't know all that it does).
However, a simple app with just RegOpenKey on the same computer with the same account works perfectly...
Can anyone tell me what could cause the problem and the differences between the two?
The test app is written in c++, while the dll is written in c.
EDIT: Problem solved, by just removing the "L" before L"Software\Classes"...
Likely, the code is running as a different user or its current user isn't in synch with the cached registry key for the process. See RegOpenCurrentUser.
I solved the problem by passing "Software\Classes" instead of L"Software\Classes" to the function.

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)