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.
Related
I'm building a mixer app and need to get the user-friendly names for each audio session.
I tried:
IAudioSessionControl::GetDisplayName() method, but it returned empty string for each session.
Calling QueryFullProcessImageName() and GetModuleFileNameEx(), but they only output C:\Users\ since I have Cyrillic letters in the path. But even if they did output the full path, I assume it would end with something like ...\brave.exe (correct me if I'm wrong), which is not a user-friendly name like Brave Browser in Windows' built-in mixer.
I also tried getting all process names and PIDs like this, and later match them to session process IDs, which successfully gave me names like chrome.exe or steam.exe, but they are still, again, not quite what I want:
std::vector<std::pair<DWORD, wchar_t*>> processes;
HANDLE handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
Process32First(handle, &entry);
do {
processes.emplace_back(entry.th32ProcessID, entry.szExeFile);
} while (Process32Next(handle, &entry));
CloseHandle(handle);
What I want is to retrieve names like in the built-in mixer app, ie Steam, Chrome Browser, Zoom Meetings, etc.
Is there a way to achieve this? Or does Microsoft use some kind of black magic here?
Calling QueryFullProcessImageName() and GetModuleFileNameEx(), but they only output C:\Users\ since I have Cyrillic letters in the path.
Then you are simply not displaying the path correctly. The presence of Cyrillic characters is not a problem.
I also tried getting all process names and PIDs like this, and later match them to session process IDs, which successfully gave me names like chrome.exe or steam.exe, but they are still, again, not quite what I want
That code doesn't work properly. You are storing pointers in your std::vector that point to a single wchar_t[] buffer which is modified on each loop iteration. You should instead store std::wstring instead of wchar_t* so each path is copied.
What I want is to retrieve names like in the built-in mixer app, ie Steam, Chrome Browser, Zoom Meetings, etc.
Once you have the path to the EXE, you can use GetFileVersionInfo() and VerQueryValue() to retrieve the app's human-readable FileDescription. See Retrieve File Description for an Application using VerQueryValue.
I have a registry entry I can't seem to read in C++, but it shows up in Regedit.
Using the following C++ snippet:
openResult=RegOpenKeyEx( HKEY_LOCAL_MACHINE, _TEXT("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full"), 0, KEY_READ, &root);
readResult1=RegQueryValueEx(root, _TEXT("InstallPath"), NULL, NULL, data1, &size);
readResult2=RegQueryValueEx(root, _TEXT("fake_entry"), NULL, NULL, data2, &size);
I get Error 2, ERROR_FILE_NOT_FOUND for my second RegQueryValueEx() call.
As you can see in the image below, my fake_entry exists.
I created this entry via Regedit.
Microsoft's Registry Keys Affected by WOW64 does not include the location I'm trying to read, and as you can see in the picture below, my fake_entry is not in the Wow6432Node location.
Yes, I understand this isn't a registry location I should be changing. I stumbled upon this as I was debugging my code and am curious why my added fake_entry doesn't work.
Yes, I've read about Registry Redirector.
Yes, I've read this question.
Yes, I tried reading fake_entry at SOFTWARE\\Wow6432Node\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full with the same error.
Running Windows 7, 64 bit, C++ in Visual Studio 2010, using ASCII character encoding.
Am I misunderstanding the Registry Redirector?
Is there a problem with my code?
Are there some sort of permission settings on certain portions of the Registry? I'm obviously missing something. Please point me in the right direction.
Try
openResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, _TEXT("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full"), 0, KEY_READ|KEY_WOW64_64KEY, &root);
according to MSDN you should use either KEY_WOW64_64KEY or KEY_WOW64_32KEY for WOW64 access
You are misreading the table of keys affected by WOW64. From the very top of that page:
The following table lists registry keys that are redirected, shared by both 32-bit and 64-bit applications, or redirected and reflected on 64-bit Windows. Subkeys of the keys in this table inherit the parent key's behavior unless otherwise specified. If a key has no parent listed in this table, the key is shared.
The parent of your key is HKLM\Software which is redirected. So your key is also redirected. It inherits that from its parent, as the documentation that I quoted explains.
You'll need to read the 64 bit view using KEY_WOW64_64KEY.
I'm trying to use GetDiskFreeSpaceEx in my C++ win32 application to get the total available bytes on the 'current' drive. I'm on Windows 7.
I'm using this sample code: http://support.microsoft.com/kb/231497
And it works! Well, almost. It works if I provide a drive, such as:
...
szDrive[0] = 'C'; // <-- specifying drive
szDrive[1] = ':';
szDrive[2] = '\\';
szDrive[3] = '\0';
pszDrive = szDrive;
...
fResult = pGetDiskFreeSpaceEx ((LPCTSTR)pszDrive,
(PULARGE_INTEGER)&i64FreeBytesToCaller,
(PULARGE_INTEGER)&i64TotalBytes,
(PULARGE_INTEGER)&i64FreeBytes);
fResult becomes true and i can go on to accurately calculate the number of free bytes available.
The problem, however, is that I was hoping to not have to specify the drive, but instead just use the 'current' one. The docs I found online (Here) state:
lpDirectoryName [in, optional]
A directory on the disk. If this parameter is NULL, the function uses the root of the current disk.
But if I pass in NULL for the Directory Name then GetDiskFreeSpaceEx ends up returning false and the data remains as garbage.
fResult = pGetDiskFreeSpaceEx (NULL,
(PULARGE_INTEGER)&i64FreeBytesToCaller,
(PULARGE_INTEGER)&i64TotalBytes,
(PULARGE_INTEGER)&i64FreeBytes);
//fResult == false
Is this odd? Surely I'm missing something? Any help is appreciated!
EDIT
As per JosephH's comment, I did a GetLastError() call. It returned the DWORD for:
ERROR_INVALID_NAME 123 (0x7B)
The filename, directory name, or volume label syntax is incorrect.
2nd EDIT
Buried down in the comments I mentioned:
I tried GetCurrentDirectory and it returns the correct absolute path, except it prefixes it with \\?\
it returns the correct absolute path, except it prefixes it with \\?\
That's the key to this mystery. What you got back is the name of the directory with the native api path name. Windows is an operating system that internally looks very different from what you are familiar with winapi programming. The Windows kernel has a completely different api, it resembles the DEC VMS operating system a lot. No coincidence, David Cutler used to work for DEC. On top of that native OS were originally three api layers, Win32, POSIX and OS/2. They made it easy to port programs from other operating systems to Windows NT. Nobody cared much for the POSIX and OS/2 layers, they were dropped at XP time.
One infamous restriction in Win32 is the value of MAX_PATH, 260. It sets the largest permitted size of a C string that stores a file path name. The native api permits much larger names, 32000 characters. You can bypass the Win32 restriction by using the path name using the native api format. Which is simply the same path name as you are familiar with, but prefixed with \\?\.
So surely the reason that you got such a string back from GetCurrentDirectory() is because your current directory name is longer than 259 characters. Extrapolating further, GetDiskFreeSpaceEx() failed because it has a bug, it rejects the long name it sees when you pass NULL. Somewhat understandable, it isn't normally asked to deal with long names. Everybody just passes the drive name.
This is fairly typical for what happens when you create directories with such long names. Stuff just starts falling over randomly. In general there is a lot of C code around that uses MAX_PATH and that code will fail miserably when it has to deal with path names that are longer than that. This is a pretty exploitable problem too for its ability to create stack buffer overflow in a C program, technically a carefully crafted file name could be used to manipulate programs and inject malware.
There is no real cure for this problem, that bug in GetDiskFreeSpaceEx() isn't going to be fixed any time soon. Delete that directory, it can cause lots more trouble, and write this off as a learning experience.
I am pretty sure you will have to retrieve the current drive and directory and pass that to the function. I remember attempting to use GetDiskFreeSpaceEx() with the directory name as ".", but that did not work.
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.
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.