RegCreateKeyEx succeeding, but no key added - c++

I am trying to get into C++ programming on Windows for reverse engineering purpose, and I am stuck trying to had a Windows registry key. The functions RegCreateKey and RegSetValueEx are returning ERROR_SUCCESS, but the key is missing when checking the registry.
Here is the code:
void AddRunKey() {
wchar_t subkey[512];
wchar_t cmd[512];
wcscpy_s(subkey, L"Test");
wcscpy_s(cmd, L"%windir%\system32\cmd.exe");
HKEY runKey;
long res;
res = RegCreateKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurentVersion\\Run", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &runKey, NULL);
if (res != ERROR_SUCCESS) {
std::cout << "fail\n";
}
res = RegSetValueEx(runKey, subkey, 0, REG_EXPAND_SZ, (BYTE*)cmd, wcslen(cmd) + 1);
if (res != ERROR_SUCCESS) {
std::cout << "fail\n";
}
RegCloseKey(runKey);
}
int _tmain() {
AddRunKey();
}
I compiled it on Visual Studio, release mode, 64 bits on a Windows 10 - 64 bits virtual machine. No errors are returned when running the code.
When opening the Windows registry editor, under HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run, the key cannot be found
What is causing this behavior ? How can I fix it ?
EDIT (updated the key path): RegCloseKey returns 0

I see several mistakes in your code.
You need to escape the \ characters in your file path.
You misspelled CurentVersion in the key path. It needs to be CurrentVersion instead.
You are calling RegSetValueEx() and RegCloseKey() unconditionally whether RegCreateKeyEx() succeeds or fails.
You need to specify the value size in the last parameter of RegSetValueEx() in bytes and not in characters.
Try this instead:
void AddRunKey() {
wchar_t subkey[512];
wchar_t cmd[512];
wcscpy_s(subkey, L"Test");
wcscpy_s(cmd, L"%windir%\\system32\\cmd.exe");
HKEY runKey;
long res = RegCreateKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", 0, NULL, 0, KEY_SET_VALUE, NULL, &runKey, NULL);
if (res != ERROR_SUCCESS) {
std::cout << "fail\n";
}
else
{
res = RegSetValueEx(runKey, subkey, 0, REG_EXPAND_SZ, (BYTE*)cmd, (wcslen(cmd) + 1) * sizeof(cmd[0]));
if (res != ERROR_SUCCESS) {
std::cout << "fail\n";
}
RegCloseKey(runKey);
}
}
int _tmain() {
AddRunKey();
}

Related

How to save a persistent value for application access across multiple runs?

As the title states, I would like to store a variable (which will always be an positive integer < 10,000) for my application to access and process as needed across multiple runs.
My current implementation simply saves the value to a file, in the current directory, and then reads it in when needed.
#include<fstream>
int x = 5;
std::ofstream write_file(file_handle);
write_file << value;
write_file.close();
However, I'm not too keen on the idea of having an orphaned text file if the user decides to place the .exe on their desktop.
So, what other options do I have to store the value?
I'm primarily concerned with Windows 8+.
There is nothing wrong with using a file, but you don't have to (and should not) store it in the same folder as the .exe file, as it may not work depending on where the .exe is located (for instance, non-admins can't write to Program Files).
Windows sets aside special folders in the user's profile just for application-generated data, so you should store the file in one of those folders instead. Use the Win32 SHGetFolderPath() or SHGetKnownFolderPath() function to discover where those special folders are located, and then you should create a sub-folder for your application's use (you can even use SHGetFolderPathAndSubDir() for that purpose).
For example:
#include <fstream>
#include <filesystem>
#include <windows.h>
#include <shlobj.h>
namespace fs = std::filesystem;
fs::path pathToMyValueFile()
{
WCHAR szPath[MAX_PATH];
if (SHGetFolderPathAndSubDirW(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, L"MyApp", szPath) != S_OK) {
throw ...;
}
return fs::path(szPath) / L"value.dat";
/* alternatively:
if (SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, szPath) != S_OK)
throw ...;
fs::path folder = fs::path(szPath) / L"MyApp";
fs::create_directory(folder);
return folder / L"value.dat";
*/
}
...
int value = 0;
std::ifstream read_file(pathToMyValueFile());
if (read_file.is_open()) {
read_file >> value;
read_file.close();
}
...
int value = 5;
std::ofstream write_file(pathToMyValueFile());
if (write_file.is_open()) {
write_file << value;
write_file.close();
}
In the future, if the user ever uninstalls your app, be sure to delete that subfolder.
Otherwise, you can store the value in the Windows Registry instead. Create a new key for your application under HKEY_CURRENT_USER\Software or HKEY_LOCAL_MACHINE\Software as needed, and then you can create values inside that key.
For example:
#include <fstream>
#include <windows.h>
int readMyValue()
{
int value;
HKEY hKey;
LSTATUS lRes = RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\MyApp", 0, KEY_QUERY_VALUE, &hKey);
if (lRes == ERROR_SUCCESS)
{
DWORD size = sizeof(value);
lRes = RegQueryValueExA(hKey, "Value", NULL, NULL, (LPBYTE)&value, size);
RegCloseKey(hKey);
}
if (lRes != ERROR_SUCCESS)
{
if (lRes != ERROR_FILE_NOT_FOUND)
throw ...;
value = 0;
}
return value;
}
void saveMyValue(int value)
{
HKEY hKey;
LSTATUS lRes = RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\MyApp", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL);
if (lRes == ERROR_SUCCESS)
{
DWORD size = sizeof(value);
lRes = RegSetValueExA(hKey, "Value", 0, REG_DWORD, (BYTE*)&value, sizeof(value));
RegCloseKey(hKey);
}
if (lRes != ERROR_SUCCESS)
throw ...;
/* alternatively:
if (RegSetKeyValueA(HKEY_CURRENT_USER, "Software\\MyApp", "Value", REG_DWORD, &value, sizeof(value)) != ERROR_SUCCESS)
throw ...;
*/
}
...
int value = readMyValue();
...
int value = 5;
saveMyValue(value);
If your app is uninstalled later, be sure to delete the Registry key.

I am getting error 2 in my winreg function

My code:
HKEY hKey;
char *path = "SYSTEM\\CurrentControlSet\\Control\\IDConfigDB\\Hardware Profiles\\0001\\HwProfileGuid";
LONG result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0, KEY_ALL_ACCESS, &hKey);
QString q = QString::number(result);
if (result == ERROR_SUCCESS) {
QMessageBox messageBox1;
messageBox1.critical(0,"Error", "Success");
messageBox1.setFixedSize(500,200);
} else {
QMessageBox messageBox2;
messageBox2.critical(0,"Error", q);
messageBox2.setFixedSize(500,200);
}
The error I am getting:
Where the key is in my Registry:
I think the problem is related to the way I put the info in the path variable, but I am not sure.
You have HwProfileGuid in the wrong place.
HwProfileGuid is a value inside of the 0001 key, but you are trying to open HwProfileGuid as a sub-key of 0001 instead, which is why you are getting error 2 (ERROR_FILE_NOT_FOUND), because there is no sub-key named HwProfileGuid.
Also, KEY_ALL_ACCESS is too many rights to request just to read a value from a key. Use KEY_QUERY_VALUE instead. Don't request more rights than you actually need.
Try this:
const char *path = "SYSTEM\\CurrentControlSet\\Control\\IDConfigDB\\Hardware Profiles\\0001";
const char *valueName = "HwProfileGuid";
char guid[40] = {0};
HKEY hKey;
LONG result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0, KEY_QUERY_VALUE, &hKey);
if (result == ERROR_SUCCESS) {
DWORD size = sizeof(guid);
result = RegQueryValueExA(hKey, valueName, NULL, NULL, reinterpret_cast<LPBYTE>(guid), &size);
RegCloseKey(hKey);
}
QMessageBox messageBox;
if (result == ERROR_SUCCESS) {
messageBox.critical(0, "Success", guid);
} else {
messageBox.critical(0, "Error", QString::number(result));
}
messageBox.setFixedSize(500, 200);
Alternatively, you can use RegGetValueA() instead of using
RegOpenKeyExA()+RegQueryValueExA():
const char *path = "SYSTEM\\CurrentControlSet\\Control\\IDConfigDB\\Hardware Profiles\\0001";
const char *valueName = "HwProfileGuid";
char guid[40] = {0};
DWORD size = sizeof(guid);
QMessageBox messageBox;
LSTATUS result = RegGetValueA(HKEY_LOCAL_MACHINE, path, valueName, RRF_RT_REG_SZ, NULL, guid, &size);
if (result == ERROR_SUCCESS) {
messageBox.critical(0, "Success", guid);
} else {
messageBox.critical(0, "Error", QString::number(result));
}
messageBox.setFixedSize(500, 200);
Dont pass Computer\\HKEY_LOCAL_MACHINE to the string. just SYSTEM\\CurrentControlSet\\Control\\IDConfigDB\\Hardware Profiles\\0001 Because HKEY_LOCAL_MACHINE are exist in RegOpenKey First Parameter. And dont try to open the value of key just the key RegSetKeyValue() For writing value of key
here the working code:
HKEY hKey;
LONG result = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\IDConfigDB\\Hardware Profiles\\0001", 0, KEY_ALL_ACCESS, &hKey);
cout << result << "\n";

Writing & reading to windows registry with windows API

I am trying to write&read from the windows register:
Writting:
std::string path = "c:\\"
LPCTSTR str_data = TEXT(path.c_str());
auto size = static_cast<DWORD>(strlen(str_data));
LONG setRes = RegSetValueEx(*key, TEXT("DumpFolder"), 0, REG_EXPAND_SZ, (LPBYTE)str_data, size);
Reading:
char str_data[1028];
DWORD keyType;
DWORD size;
auto sk = TEXT("SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps");
auto status = RegGetValue(HKEY_LOCAL_MACHINE, sk, TEXT("DumpFolder"), RF_RT_REG_EXPAND_SZ, &keyType, str_data, &size);
Writing appears to work fine, at least it looks fine in regedit.exe.
Reading fails with ERROR_INVALID_PARAMETER = 87. If I change RF_RT_REG_EXPAND_SZ to RRF_RT_ANY, it works in debug mode, but still fails in release with error code ERROR_MORE_DATA = 234. I tried:
std::string path = "c:\\";
path = path + "\0" (it should be null terminated anyway
but it doesn't help
UPDATE
First of all, thanks for answers, I understand the thing a little better now. Unfortunately, I am still unable to read the string successfully.
Here is the test example combined from the answer below:
HKEY registry_key;
LPCTSTR sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting";
// open registry key
auto openRes = RegOpenKey(HKEY_CURRENT_USER, sk, &registry_key);
// set default dump options
HKEY default_key;
auto createRes = RegCreateKey(registry_key, "LocalDumps", &default_key);
if (createRes != ERROR_SUCCESS) {
auto b = createRes;
}
std::string path = "c:\\";
LONG setRes = RegSetValueExA(default_key, "DumpFolder", 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size() + 1);
std::string str_data;
DWORD size = 0;
const char *sak = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
auto status = RegGetValueA(HKEY_CURRENT_USER, sak, "DumpFolder", RRF_RT_REG_EXPAND_SZ, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1)) {
str_data.resize(size - 1);
status = RegGetValueA(HKEY_CURRENT_USER, sk, "DumpFolder", RRF_RT_REG_EXPAND_SZ, NULL, &str_data[0], &size);
}
Writing again works fine (checked in regedit, and the return error code). On the other hand, reading the size of string register sets the size to 0 and returns error code 87 = ERROR_INVALID_PARAMETER.
Apparently, I am still missing something. (the project is set to multy-byte character set)
SOLUTION
After fixing things proposed by the answers below, the following code worked for me:
#include <Windows.h>
#include <string>
#include <iostream>
#define reg_type HKEY_LOCAL_MACHINE
void main() {
const std::string reg_path = "Software\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps";
const std::string dump_folder = "DumpFolder";
const std::string path = "c:\\";
// WRITING
HKEY default_key;
auto status = RegCreateKeyExA(reg_type, reg_path.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_QUERY_VALUE, NULL, &default_key, NULL);
if (status != ERROR_SUCCESS) {
std::cout << "Creating key failed.";
return;
}
status = RegSetValueExA(default_key, dump_folder.c_str(), 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size() + 1);
if (status != ERROR_SUCCESS) {
std::cout << "Setting key value failed.";
return;
}
// READING
std::string str_data;
DWORD size = 0;
status = RegGetValueA(default_key, "", dump_folder.c_str(), RRF_NOEXPAND | RRF_RT_REG_EXPAND_SZ, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1)){
str_data.resize(size - 1);
status = RegGetValueA(default_key, "", dump_folder.c_str(), RRF_NOEXPAND | RRF_RT_REG_EXPAND_SZ, NULL, &str_data[0], &size);
std::cout << "Successfully read key value: " << str_data;
} else {
std::cout << "Unable to retrive value. Error: " << status;
}
RegCloseKey(default_key);
}
I found, that RegGetValueA should be called with a
RRF_NOEXPAND | RRF_RT_REG_EXPAND_SZ
flag, which appears strange, but is described in header where it is defined, so I guess it is correct. If using only
RRF_RT_REG_EXPAND_SZ
error 87 occurs ERROR_INVALID_PARAMETER.
On the writing side:
std::string uses char elements, but TCHAR maps to either char or wchar_t depending on whether your code is compiled with UNICODE defined or not.
The TEXT() macro only works with compile-time literals, you can't use it with runtime data. TEXT(path.c_str()) is an invalid type-cast, and won't even compile if UNICODE is enabled.
You are clearly working with char data, so you should be using the char-based API functions instead of the TCHAR-based functions.
You are also not following one of the most important rules of RegSetValueEx():
For string-based types, such as REG_SZ, the string must be null-terminated. With the REG_MULTI_SZ data type, the string must be terminated with two null characters... The size of the information pointed to by the lpData parameter, in bytes. If the data is of type REG_SZ, REG_EXPAND_SZ, or REG_MULTI_SZ, cbData must include the size of the terminating null character or characters.
std::string::c_str() returns a pointer to null-terminated data, but you are not including the null terminator when reporting the size of the data you are writing to the Registry. RegGetValue() knows how to deal with that mistake, but RegGetValueEx() does not. You might not be the only person to ever read the value, so make sure you include the null terminator properly.
Try this instead:
std::string path = "c:\\";
LONG setRes = RegSetValueExA(*key, "DumpFolder", 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size()+1);
On the reading side:
You are getting errors because you are not telling RegGetValue() how large your str_data buffer is. You have to set your size variable to the size of str_data, in bytes, before you pass it in.
Try this instead:
char str_data[1028];
DWORD size = sizeof(str_data);
DWORD dwFlags = RRF_RT_REG_EXPAND_SZ;
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
auto status = RegGetValueA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps", "DumpFolder", RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND, NULL, str_data, &size);
Alternatively:
std:string str_data;
DWORD size = 0;
const char *sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
const DWORD dwFlags = RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND;
auto status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1))
{
str_data.resize(size-1);
status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, &str_data[0], &size);
}
UPDATE: your new code fails because you have introduced new bugs.
You are using legacy Registry functions that are meant for 16bit apps. You need to use RegOpenKeyEx/RegCreateKeyEx instead of RegOpenKey/RegCreateKey, and then you can specify only the specific access rights that you actually need (create subkeys, set values, read values, etc). Even better, RegCreateKeyEx() creates missing keys for you, so you don't need to manually open a parent key separately just to create a new subkey.
Also, you changed HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER, but not consistently. Some of your steps use one root, other steps use the other root. You are not able to read back the value you are writing because you are not reading from the same key you wrote to.
Try this instead:
LPCSTR sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps";
HKEY default_key;
auto status = RegCreateKeyExA(HKEY_LOCAL_MACHINE, sk, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &default_key, NULL);
if (status == ERROR_SUCCESS)
{
std::string path = "c:\\";
status = RegSetValueExA(default_key, "DumpFolder", 0, REG_EXPAND_SZ, (LPCBYTE)path.c_str(), path.size() + 1);
RegCloseKey(default_key);
}
LPCSTR sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
std::string str_data;
DWORD size = 0;
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
const DWORD dwFlags = RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND;
auto status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1))
{
str_data.resize(size - 1);
status = RegGetValueA(HKEY_LOCAL_MACHINE, sk, "DumpFolder", dwFlags, NULL, &str_data[0], &size);
}
On the other hand, when you have to make multiple API calls to read a value (ie, to query the size, then query the data), you should explicitly open the parent key first:
const char *sk = "SOFTWARE\\Microsoft\\Windows\\Windows Error reporting\\LocalDumps";
std:string str_data;
HKEY default_key;
auto status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, sk, 0, KEY_QUERY_VALUE, &dumps_key);
if (status == ERROR_SUCCESS)
{
DWORD size = 0;
// NOTE: when using RRF_RT_REG_EXPAND_SZ, RRF_NOEXPAND is *required* prior to Windows 8.1!
const DWORD dwFlags = RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND;
status = RegGetValueA(default_key, "", "DumpFolder", dwFlags, NULL, NULL, &size);
if ((status == ERROR_SUCCESS) && (size > 1))
{
str_data.resize(size-1);
status = RegGetValueA(default_key, "", "DumpFolder", dwFlags, NULL, &str_data[0], &size);
}
RegCloseKey(default_key);
}

Getting a proper value from RegQueryValueEx

I am trying to extract a value from the windows registry of type REG_SZ, using RegQueryValueEx, I got the value except it was riddled with strange "\000" before each letter.To show you what I mean here are some images:
Value I want(It is a device name of a wireless adapter)
Value I got:
here is the code:
HKEY hlistkey = NULL;
HKEY hkey = NULL;
int dwIndex=0;
string devName = returndevName(); //return current selected device name using iphlpapi.h
WCHAR KeyNameBuf[512];
DWORD keyNameSizBuf = 512;
char buffer[512];
RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002bE10318}") ,0,KEY_READ, &hlistkey );
if(!hlistkey)
{
cout << "failed" << endl;
}
while(RegEnumKeyEx(hlistkey,dwIndex++,KeyNameBuf,&keyNameSizBuf,0,NULL,NULL,NULL) == ERROR_SUCCESS )
{
RegOpenKeyEx(hlistkey, KeyNameBuf, 0, KEY_READ | KEY_SET_VALUE, &hkey);
if(hkey)
{
keyNameSizBuf = 512;
if(RegQueryValueEx(hkey,TEXT("NetCfgInstanceId"), 0,NULL,(LPBYTE)buffer,&keyNameSizBuf ) == ERROR_SUCCESS )
{
if(strcmp(buffer,devName.c_str() ) ==0)
{
//set value here
}
}
RegCloseKey(hkey);
}
}
}
comparing buffer and devName would not be the same because of the extra null characters .If I cast buffer to a string I simply got a "{" which is the first value.I need to get the value of the devename in the registry before I can change the "NetworkAddress" in the registry.
Since you are using WCHAR, I assume you are compiling with Unicode support. If this is true, then also the buffer needs to be WCHAR.

c++ RegSetValueEx sets only one char value in registry

I'm casting (char * ) on data and i'm getting only one char value in the registry. if
i don't use the casting msvc 2010 tells me that the argument type LPCTSTR is incompatible with const char *.
can someone help me?
HKEY hKey;
LPCTSTR sk = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run");
LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS , &hKey);
if (openRes==ERROR_SUCCESS) {
printf("Success opening key.");
} else {
printf("Error opening key.");
}
LPCTSTR value = TEXT("SomeKey");
LPCTSTR data = L"TestData\0";
LONG setRes = RegSetValueEx (hKey, value, 0, REG_SZ, (LPBYTE)data, strlen(data)+1);
if (setRes == ERROR_SUCCESS) {
printf("Success writing to Registry.");
} else {
printf("Error writing to Registry.");
}
cout << setRes << endl;
LONG closeOut = RegCloseKey(hKey);
if (closeOut == ERROR_SUCCESS) {
printf("Success closing key.");
} else {
printf("Error closing key.");
}
strlen(data) is probably returning a value of 1, as strlen expects a char* and L"TestData\0" is wide. Use TEXT("TestData\0") and call _tcslen(data).
Note that RegSetValueEx expects the sizeof the data, so use _tcslen(data) * sizeof(TCHAR)
Where are you casting data?
Either way, it looks like you may be working with wide characters, but you seem to be using "plain old" strlen - instead of wcslen or some other function intended to work with wide-character strings.
replace the L"TestData" by _T("TestData"); and strlen(data)+1 by tcslen(data) * sizeof(TCHAR));
so your code looks like this :
LPCTSTR value = TEXT("SomeKey");
LPCTSTR data = TEXT("TestData");
LONG setRes = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, _tcslen(data) * sizeof(TCHAR));