Unable to use the "runas" verb on programs requiring UAC - c++

I have the following C code that should run the default Windows program changepk.exe with an UAC prompt
ShellExecute(NULL, "runas", "C:\\Windows\\System32\\changepk.exe", 0, 0, SW_SHOWNORMAL);
(Side note that the output of the ShellExecute is 2).
However, when I try to execute the 'changepk.exe' with these lines nothing happens at all, but for 'notepad.exe' instead of 'changepk.exe' it works and gives me an UAC prompt. What could be the issue here and what are the potential ways around it?

Error 2 is ERROR_FILE_NOT_FOUND. Make sure that changepk.exe actually exists on your machine at that path.
More importantly, if your app is a 32bit EXE running on a 64bit Windows, then you are likely encountering the File System Redirector at work, which will redirect "C:\\Windows\\System32\\..." to "C:\\Windows\\SysWOW64\\..." for 32bit processes.
If that is the case, try using WOW64's sysnative alias to reach the 64bit System32 folder from a 32bit app, eg:
ShellExecute(NULL, "runas", "C:\\Windows\\Sysnative\\changepk.exe", 0, 0, SW_SHOWNORMAL);
Or, you can disable the redirector temporarily by using Wow64DisableWow64FsRedirection(), eg:
PVOID oldValue;
Wow64DisableWow64FsRedirection(&oldValue);
ShellExecute(NULL, "runas", "C:\\Windows\\System32\\changepk.exe", 0, 0, SW_SHOWNORMAL);
Wow64RevertWow64FsRedirection(oldValue);
The reason why NotePad works is because a 64bit Windows provides both 32bit and 64bit versions of NotePad, so you end up running the correct one regardless of where "C:\\Windows\\System32\\notepad.exe" actually maps to.

Related

ReOpenFile Windows API fails with "error invalid name" when reopening a file in C:\windows\system32

I would like to know whether anyone can explain a reason for ReOpenFile failing with "error-invalid-name", when CreateFile has successfully opened the file.
Here are the details:
I open a file C:\Windows\system32\unit_test.txt using CreateFile, and it opens fine. When I later attempt to change permissions using ReOpenFile, this fails with error code 123 (decimal) which the debugger shows as "ERROR_INVALID_NAME: The filename directory name or volume label syntax is incorrect".
ReOpenFile works fine if "unit_test.txt" is placed in a more conventional directory.
ReOpenFile does not take a filename parameter, but rather the handle that is returned from CreateFile.
The code that I have written is not requesting additional privileges nor conflicting privileges (which results in a different error), so I am curious as to why CreateFile would succeed and ReOpenFile fail in this one situation.
Admittedly the placing of the file in Windows\System32 was accidental, but if I can do it by accident, then a user of our software might stumble upon the same issue.
I have since created this example using identical function calls, but I can't get it to fail. It shows essentially what had been producing the error though:
int main ()
{
HANDLE h1 = ::CreateFile (_T("c:\\windows\\system32\\test.txt"),
GENERIC_READ | GENERIC_WRITE | DELETE,
FILE_SHARE_READ,
nullptr,
CREATE_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (h1 != INVALID_HANDLE_VALUE)
{
HANDLE h2 = ::ReOpenFile (h1,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_FLAG_SEQUENTIAL_SCAN);
::CloseHandle (h1);
if (h2 != INVALID_HANDLE_VALUE)
::CloseHandle (h2);
}
}
As soon as I recompiled my "minimal example" as 32-bit rather than 64-bit, it started to exhibit the problem of ReOpenFile failing.
This lead me to an article that explains the situation: Accessing files from System32 directory using 32 bit application on 64 bit machine
Quote from the article:
"
As you seen, on a computer that is running a 64-bit version of Windows Server 2003 or of Windows XP, a 32-bit application cannot access the following folder: %WinDir%\System32
This behavior occurs because Windows on Windows 64-bit (WOW64) provides file system redirection.
In a 64-bit version of Windows Server 2003 or of Windows XP, the %WinDir%\System32 folder is reserved for 64-bit applications. When a 32-bit application tries to access the System32 folder, access is redirected to the following folder:
%WinDir%\SysWOW64
By default, file system redirection is enabled.
As a walk-around solution, 32-bit applications can access the native system directory by substituting %windir%\Sysnative for %windir%\System32. WOW64 recognizes Sysnative as a special alias used to indicate that the file system should not redirect the access.
"
Sadly the fact that the 32-bit application cannot fully access the directory does not stop Microsoft Windows from setting it as the working directory for the application in certain situations that I'm not fully aware of (possibly relating to sandboxing in more recent versions of Windows).

Strange behaviour with RegQueryValueEx whitch return value of another register

I want to the path of Excel.exe from the registry. So i used the following:
I am using Windows64bits
RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_READ, &hKey)
with
szKey = "\Classes\CLSID\{ExcelCLSID}\LocalServer",
this register contains "C:\PROGRA~1\MIF5BA~1\Office15\EXCEL.EXE /automation"
Used this to get the Excel.exe Path
RegQueryValueEx(hKey, NULL, NULL, NULL, (BYTE*)szPath, &cSize)
So when building my solution for Platform x64, i get as path
C:\\PROGRA~1\\MIF5BA~1\\Office15\\EXCEL.EXE /automation
which is fine, but when building my solution for Win32 platform i get the
following path
"C:\\Program Files\\Microsoft Office\\Office15\\EXCEL.EXE" /automation
which is strange because this value is stored in the following registry
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\CLSID\{ExcelCLSID}\
am i missing something here?
The Windows registry maintains, more or less transparently, different information for 32-bit and 64-bit applications running in the same environment. The Wow6432Node you see indicates that the value for that key is specific to 32-bit applications running on a 64-bit version of Windows.
You can disable the default registry redirection (see RegDisableReflectionKey), but in the majority of cases this isn't what you want to do.
See the information on registry redirection here.

I want to open Msconfig from a button on my GUI

In my line of work I use msconfig a lot and I'm wanting to create a tool with various buttons on it so I can easily access my most commonly used programs. I'm using the Win32API to create the GUI and buttons, however I am having trouble getting msconfig to run. In my program I have
void callMsconfig()
{
ShellExecute(NULL,(LPCWSTR)L"open", (LPCWSTR)L"C:\\Windows\\System32\\msconfig.exe", NULL, NULL, SW_SHOWDEFAULT);
}
and an action statement so when the button is pressed the function is called. I've tried replacing ShellExecute() with system() and CreateProcess(). I've also replaced "open" with "runas" and the most I've gotten is the error saying C:\Windows\System32\msconfig.exe cannot be found when I know it is there because I've checked. What am I doing wrong? I'm using Windows 10 Home if that helps any.
If you have created a 32bit app that is running inside of the WOW64 emulator on 64bit systems, the C:\Windows\System32\ folder is silently redirected by WOW64 to the C:\Windows\SysWOW64\ folder, which does not have an msconfig.exe file. You need to account for that. Either create a 64bit executable, or use the Sysnative alias in 32bit code that runs inside of WOW64. Sysnative is documented on MSDN:
File System Redirector:
32-bit applications can access the native system directory by substituting %windir%\Sysnative for %windir%\System32. WOW64 recognizes Sysnative as a special alias used to indicate that the file system should not redirect the access. This mechanism is flexible and easy to use, therefore, it is the recommended mechanism to bypass file system redirection. Note that 64-bit applications cannot use the Sysnative alias as it is a virtual directory not a real one.
Try something more like this:
#include <shlwapi.h>
void callMsconfig()
{
BOOL IsWow64 = FALSE;
WCHAR szCmdLine[MAX_PATH] = {0};
IsWow64Process(GetCurrentProcess(), &IsWow64);
if (IsWow64)
{
GetWindowsDirectoryW(szCmdLine, MAX_PATH);
PathAppendW(szCmdLine, L"Sysnative");
}
else
{
GetSystemDirectoryW(szCmdLine, MAX_PATH);
}
PathAppendW(szCmdLine, L"msconfig.exe");
ShellExecuteW(NULL, NULL, szCmdLine, NULL, NULL, SW_SHOWDEFAULT);
}
However, do note that if UAC is enabled and you try to launch msconfig.exe from a 32bit process running inside of WOW64, the 32bit process MUST be running elevated or else ShellExecute() (and CreateProcess()) will fail to find the file correctly. I don't know why, but that is how it works. UAC Elevation is not required when launching msconfig.exe from a 64bit process.
If you don't want to elevate your entire program, you will have to make it launch a separate elevated process that can then launch msconfig.exe. You could just have the program launch a second copy of itself with a command-line parameter so it knows to just launch msconfig.exe and then exit itself. To launch an elevated process, you can use ShellExecute() specifying the runas verb.

ShellExecute fails: ERROR_FILE_NOT_FOUND

I'm trying to start my application with my own launcher with administrator rights. The launcher works on my 64Bit Win7 but not at my 32Bit WinXP computer.
The code I am using is this:
wstring fullAppPath = "myapp.exe";
ShellExecuteW(NULL,
L"runas", //admin rights
fullAppPath.c_str(),
NULL, //startup params
NULL, //default dir
SW_SHOWNORMAL
);
I use the MSVC 2010 32Bit compiler with the Qt IDE and the launcher-application and the main-application are in the same directory, which is not C:\Windows\System32.
On my WinXP computer I get anytime I start my launcher an ERROR_FILE_NOT_FOUND error. (value 2)
How can I solve this?
The solution was using an absolute path instead of a relative path.

How to call system() in an opened administrator program and gives it the same privileges?

I am writing a c++ application in Windows that runs as administrator. However, while calling the system() command, it seems that the command doesn't have admin privileges (can't create file in the C:\Program Files (x86)\ directory).
How can I avoid using CreateProcess ?
If you use system you can use:
system("runas /user:<admin-user> \"program.exe\"");
Or ShellExecute:
ShellExecute(hwnd, "runas", "program.exe", 0, 0, SW_SHOWNORMAL);
This Stackoverflow Question
details the CreateProcess method pretty well.