What is the difference between ShellExecute and CMD commands, if any? - c++

Execution wise, is any difference between those two commands, except the fact that the one is for C++ and the other is the CMD?
Because I'm having a strange problem, I have an .exe that it takes arguments. When I call this exe with it's arguments from the CMD it is working normally. But when I do the same thing from ShellExecute the program returns an error.
What could be wrong?
ShellExecute(
NULL,
_T("open"),
_T("C:\\connect\\connect.exe"),
_T("J11"),
NULL,
SW_SHOW);

Yes a very big difference.
CMD processes what you type then passes it to CreateProcessEx. CMD's Start command, run dialog, or double clicking a file, gets passed to one of the ShellExecuteEx functions, which in turn calls CreateProcessEx.
For CMD see Windows NT Shell Scripting, Chapter 2, The Windows NT Command Shell by Tim Hill (1998), available from MS's web site (only that chapter is available for free). CMD's preprocessing of what is passed to CreateProcessEx is detailed. CMD also does it own emulation of ShellExecute but it is under Windows 95 rules for ShellExecute, rather than the frequently updated shell32 implementation.
For ShellExecute see the ShellExecuteEx documentation.
To see CreateProcessEx rules see it's documentation.
I was going to paste the rules, but CMD's ran to pages.
The Return value tells you why your command isn't working.
Return Value
Returns a value greater than 32 if successful, or an error value that is less than or equal to 32 otherwise. The following table lists the error values. The return value is cast as an HINSTANCE for backward compatibility with 16-bit Windows applications. It is not a true HINSTANCE, however. The only thing that can be done with the returned HINSTANCE is to cast it to an int and compare it with the value 32 or one of the error codes below.
0 The operating system is out of memory or resources.
ERROR_FILE_NOT_FOUND The specified file was not found.
ERROR_PATH_NOT_FOUND The specified path was not found.
ERROR_BAD_FORMAT The .exe file is invalid (non-Microsoft Win32 .exe or error in .exe image).
SE_ERR_ACCESSDENIED The operating system denied access to the specified file.
SE_ERR_ASSOCINCOMPLETE The file name association is incomplete or invalid.
SE_ERR_DDEBUSY The Dynamic Data Exchange (DDE) transaction could not be completed because other DDE transactions were being processed.
SE_ERR_DDEFAIL The DDE transaction failed.
SE_ERR_DDETIMEOUT The DDE transaction could not be completed because the request timed out.
SE_ERR_DLLNOTFOUND The specified DLL was not found.
SE_ERR_FNF The specified file was not found.
SE_ERR_NOASSOC There is no application associated with the given file name extension. This error will also be returned if you attempt to print a file that is not printable.
SE_ERR_OOM There was not enough memory to complete the operation.
SE_ERR_PNF The specified path was not found.
SE_ERR_SHARE A sharing violation occurred.

Related

PE injection fails if injector gets launched by specific application?

Short disclaimer: As this question includes topics regarding hacking/pentesting, I'd like to state that this question is only asked for educational purposes as part of a school project. To prevent possible abuse, I will only post code that is necessary for understanding the problem.
To demonstrate dangers and vulnerabilities of Windows 10, I'm currently writing a small C++/WinAPI application that utilizes two common techniques:
A UAC bypass using the "fodhelper technique" (this works by simply setting a specific registry value to the path of the executable which is supposed to be elevated and then launching an automatically elevated Windows executable called "fodhelper.exe", which will then read the registry value and execute it as command/launch the specified application).
Performing PE injection, i.e. running a PE file from the address space of the current process (based on this example from github). The PE that gets injected in my program is a simple C++ Console Application (x86) that prints a message box. The shellcode is hardcoded in the injector binary (x86).
I managed to perform both of these techniques successfully in independent files. However, once I combine these two methods (i.e. first elevating, then injecting), a weird error appears.
Description of the problem
When the injector gets started manually (by double clicking), everything works fine, but when the injector is launched by System32\fodhelper.exe (x64) as a result of the UAC bypass, the following happens: After the injection has finished, the console window of the injected application appears, but instead of continuing the execution, I receive a bunch of error messages stating "The code execution cannot proceed because [garbage characters].dll was not found". This indicates that something went wrong with the offsets, and the Windows loader is trying to read the imports at a wrong position.
To summarize: The code injection works fine, unless the injector was started by fodhelper.exe. In this case the injected PE file is unable to run.
Things I have tried so far to find the origin of the issue
Debugging the injection using GetLastError and printing the various memory addresses used during the injection. There is no difference if the file is manually started (and the injection is successful) or if it gets started by fodhelper.exe (and the injection fails).
Replace the WriteProcessMemory calls with WriteFile to compare the output file when the injector gets manually launched or by fodhelper.exe. Both output files are exactly the same and runnable. This indicates that the injection itself is not the problem, but the Windows loader seems to act differently.
Manually elevating the injector using UAC or by using an elevated command prompt. In both cases, the injection is successful.
Copying fodhelper.exe to another location (for example to the desktop) and launching this copy. In this case, the injection is successful. The injection only fails if the injector gets started by the original fodhelper.exe in the System32 folder.
It seems that the injection behaves completely identical, but the indicators show that due to some unknown impact of fodhelper.exe that gets passed down to the injector, the Windows loader seems to behave differently.
I appreciate any explanation or assumption! Feel free to ask if you require more information.
Minimal reproducible example
(with limited debug info and comments):
https://0bin.net/paste/UPRIg12n#6nJvBok72UcDvIa56c-XEss7AibIh1Zrs+c3sUzvQMj
Note: See how the injection works if you exclude the elevateProcess function or manually elevate the exe with UAC, and how it fails when including said function.
Edit
According to the answer by user RbMm, this error is a result of a specific exploit protection attribute (PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY with the EnableModuleTamperingProtection value) that gets automatically applied onto fodhelper.exe and seemingly gets inherited by all child processes. According to this, removing/resetting this attribute when launching the target process should fix the error. So far I've tried the following, but couldn't achieve any change in the outcome:
DWORD resetPolicy = 0;
STARTUPINFOEXA siEx = { 0 };
SIZE_T AttributeListSize = 0;
siEx.StartupInfo.cb = sizeof(siEx);
InitializeProcThreadAttributeList(NULL, 1, 0, &AttributeListSize);
siEx.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, AttributeListSize);
InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &AttributeListSize);
UpdateProcThreadAttribute(siEx.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &resetPolicy, sizeof(resetPolicy), NULL, NULL);
...
CreateProcessA(CurrentFilePath, NULL, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT | CREATE_SUSPENDED, NULL, NULL, (LPSTARTUPINFOA)&siEx, &PI);
when process created via RunAs with elevation - the appinfo.dll call RAiLaunchAdminProcess function (this is in some svchost.exe) and this function, pass STARTUPINFOEX (and EXTENDED_STARTUPINFO_PRESENT flag) to CreateProcessAsUser. and here - lpAttributeList, in particular PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY attribute key is used for set several exploit mitigation policy for the child process (fodhelper.exe in your case). and here EnableModuleTamperingProtection is set for child process tree. effect of this - when system resolve import descriptor, it check (inside LdrpGetImportDescriptorForSnap) for this mitigation flag, and if it enabled - call LdrpCheckPagesForTampering api, it return true, if SharedOriginal is 0, this means this is a copy-on-write private copy of the EXE/IAT -- hence 'tampered' with.
after this LdrpMapCleanModuleView is called. at this point your try begin breaking
possible first public info about this, from Alex Ionescu -
LdrpCheckPagesForTampering/LdrpMapCleanModuleView (RS3) are pretty
cool antihollowing mitigations
(EPROCESS.EnableModuleTamperingProtection)
if you by self launch new process, you of course not call UpdateProcThreadAttribute for set PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY and in this case, your code sometime work. really only random and sometime - here exist many other errors and bad codding

Windows CreateFile Possible Error Codes

I'm playing around with abstracting Windows, Linux, and Mac File IO calls into macros (to avoid C runtime, so no fopen, fclose, etc...). I've actually got quite a bit working but I've run into a stumbling block.
I'm trying to boil down all of the possible errors that could be thrown by each of these platforms into a subset of common ones: Not Found, Exists, Invalid Access, etc.
Linux is obviously well document, Mac even has the most common ones, but Windows does not specify which errors are thrown for its native File I/O functions. We obviously need to use GetLastError(), but I could not find a reference to what the possible values would be.
How about this?
I'm writing a Windows application and using the CreatFile() API. I'd like to handle any errors as gracefully as I can, and perhaps recover from said error instead of telling the user "Crap! Can't do that." But the MSDN docs don't list the possible error codes that could be generated.
Does anyone have a reference to the possible error codes that Windows File functions may generate, specifically (for now) CreateFile()?
Windows supports installable file systems. Microsoft cannot possibly predict what kind of errors a 3rd party file system driver is going to generate so does not make any attempt to promise a strict sub-set of possible error codes.
So yes, you really do have to use GetLastError(). The FormatMessage() function is available to generate a readable string for the error code. In general, the user will get a decent error message that helps him diagnose the root cause. It isn't quite specific enough to tell that an earthquake in Chile severed an undersea communication cable but it will certainly help him to start looking at networking problems for example. You can use the CRT wrappers as well, but with the inevitable loss of specificity. That might be a service call that you have to handle instead of the user's IT staff.
to avoid C runtime
You're reinventing the wheel. C runtime was created so people could write platform-independent programs that (theorietcally) would compile anywhere, as long as you aren't using something platform-specific.
Right now, you're doing the same thing.
I'd advise to stop doing that and either use standard C file function, or cross-platform framework that support several platform. You could use either Boost or Qt 4.
Regarding your question
Does anyone have a reference to the possible error codes that Windows File functions may generate,
WinAPI documentation is available on MSDN and you should read it.
CreateFile
GetLastError
SystemErrorCodes
Does anyone have a reference to the possible error codes that Windows File functions may generate
Because CreateFile doesn't only deal with "files" (it also works with directories, pipes, physical devices, etc), you should assume that it can generate any operating system code that exists. According to msdn, there are 16000 codes total. Moral of the story: if you want to generate human-readable message, use FormatMessage. If documentation doesn't list possible error codes (for CreateFile), it automatically means, that CreateFile can produce any error code that exists.
As SigTerm stated, there's no definitive list of system error codes associated with CreateFile. Your best bet is searching for the keyword "ERROR" in the CreateFileA/CreateFileW documentation.
Otherwise, there's always web search engine. A quick DuckDuckGo search of "CreateFile" and "Error" reveals a slew of errors and error codes
You should also consult Microsoft's lengthy lists of System Error Codes and Win32 Error Codes.
Excerpts from CreateFileA/CreateFileW Documentation
param: dwShareMode
You cannot request a sharing mode that conflicts with the access mode that is specified in an existing request that has an open handle. CreateFile would fail and the GetLastError function would return ERROR_SHARING_VIOLATION (32).
param: dwCreationDisposition
CREATE_ALWAYS
If the specified file exists and is writable, the function overwrites the file, the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS (183).
CREATE_NEW
If the specified file exists, the function fails and the last-error code is set to ERROR_FILE_EXISTS (80).
OPEN_ALWAYS
If the specified file exists, the function succeeds and the last-error code is set to ERROR_ALREADY_EXISTS (183).
OPEN_EXISTING
If the specified file or device does not exist, the function fails and the last-error code is set to ERROR_FILE_NOT_FOUND (2).
TRUNCATE_EXISTING
If the specified file does not exist, the function fails and the last-error code is set to ERROR_FILE_NOT_FOUND (2).
Files
If you call CreateFile on a file that is pending deletion as a result of a previous call to DeleteFile, the function fails. The operating system delays file deletion until all handles to the file are closed. GetLastError returns ERROR_ACCESS_DENIED.
If CREATE_ALWAYS and FILE_ATTRIBUTE_NORMAL are specified, CreateFile fails and sets the last error to ERROR_ACCESS_DENIED if the file exists and has the FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_SYSTEM attribute. To avoid the error, specify the same attributes as the existing file.
When an application creates a file across a network, it is better to use GENERIC_READ | GENERIC_WRITE for dwDesiredAccess than to use GENERIC_WRITE alone. The resulting code is faster, because the redirector can use the cache manager and send fewer SMBs with more data. This combination also avoids an issue where writing to a file across a network can occasionally return ERROR_ACCESS_DENIED.
Consoles
If lpFileName is indeterminate "CON" and dwDesiredAccess is GENERIC_READ | GENERIC_WRITE, CreateFile fails; GetLastError returns ERROR_FILE_NOT_FOUND.
"CON" and GENERIC_READ implies a console handle for input.
"CON" and "GENERIC_WRITE` implies a console handle for output.
Pipes
If the CreateNamedPipe function was not successfully called on the server prior to this operation, a pipe will not exist and CreateFile will fail with ERROR_FILE_NOT_FOUND.
If there is at least one active pipe instance but there are no available listener pipes on the server, which means all pipe instances are currently connected, CreateFile fails with ERROR_PIPE_BUSY.
Various errors from across the internet
ERROR_INVALID_FUNCTION (1)
ERROR_FILE_NOT_FOUND (2)
ERROR_PATH_NOT_FOUND (3)
ERROR_ACCESS_DENIED (5)
ERROR_GEN_FAILURE (31) - A device attached to the system is not functioning. This can also occur when working with network devices e.g. virtual network adapters
ERROR_SHARING_VIOLATION (32) - The process cannot access the file because it is being used by another process.
ERROR_INVALID_PARAMETER (87) - "An application must meet certain requirements when working with files that are opened with FILE_FLAG_NO_BUFFERING: File access must begin at byte offsets within a file that are integer multiples of the volume sector size. File access must be for numbers of bytes that are integer multiples of the volume sector size. For example, if the sector size is 512 bytes, an application can request reads and writes of 512, 1024, 1536, or 2048 bytes, but not of 335, 981, or 7171 bytes. Buffer addresses for read and write operations should be sector aligned, which means aligned on addresses in memory that are integer multiples of the volume sector size. Depending on the disk, this requirement may not be enforced."
ERROR_CALL_NOT_IMPLEMENTED (120) - using P/Invoke and calling to coredll.dll for Windows Pocket PC
ERROR_INVALID_NAME (123)
ERROR_FILE_SYSTEM_VIRTUALIZATION_INVALID_OPERATION (181)
ERROR_FILE_TOO_LARGE (223)
ERROR_CONNECTION_COUNT_LIMIT (1238)
ERROR_MUTUAL_AUTH_FAILED (1397) - network auth e.g. serial COM port over Bluetooth
ERROR_NO_SYSTEM_RESOURCES (1450) - e.g. working with Serial Ports

CreateProcess and ShellExecute differences

What are the main differences between the two? I'm willing to run only another EXE from my (C++) application. Are there any differences when inheriting environments, security features etc?
The main difference between CreateProcess and ShellExecute is the following: CreateProcess is more oriented on low level and ShellExec on the high user lever which see the user in explorer.
For example using of CreateProcess one can use command line which length is more as MAX_PATH. It has 32,768 characters restriction. You can also use CreateProcess to start program (if you have enough permissions) on another windows desktop like on the Logon Screen.
Another example. You can use ShellExecute to start Control Panel or open any program which existed on the computer for editing of JPG filed for example. So you works with ShellExecute close to the corresponding actions in the Windows Explorer.
The main difference is in flexibility. ShellExecute is easier to use, but doesn't have a lot of flexibility. CreateProcess is a pain to use, but lets you do anything.
Just for example, with CreateProcess, you can specify handles (pipes or files) to use for the standard input/output/error streams in the child. ShellExecute doesn't give you want way to do that.
It's probably also worth noting that although ShellExecute can be used to start an executable directly, its primary intent is to "execute" document files -- for example, tell it to "execute" a "whatever.html", and it starts up your default web browser and loads the specified HTML file into it. You can do that using CreateProcess as well, but to do it, you (normally) start by calling FindExecutable to find the program associated with the data file in question, then execute that passing your data file as a parameter.
CreateProcess returns the handle and id for the started process and it's main thread in the PROCESS_INFORMATION structure

What causes WriteFile to return ERROR_ACCESS_DENIED?

We currently face the problem of a call to WriteFile (or, rather CFile::Write - but that just calls WriteFile internally) causing the Win32 error 5 ERROR_ACCESS_DENIED.
(EDIT: Note that we can't repro the behavior. All we have at the moment is a logfile indicating the source line where the CFile::Write was and containing as error ERROR_ACCESS_DENIED!)
(EDIT: The file is on a local drive and it is in fact a file and not a directory.)
Now, WriteFiles's documentation doesn't really help, and experimenting with a simple test-app yields the following results:
WriteFile will cause ERROR_ACCESS_DENIED if it is called for a file handle that is not opened for writing (i.e. is opened for reading only).
It will not cause ERROR_ACCESS_DENIED if
The handle is not valid or the file isn't open at all
The access rights, or the write protected flag for the file are modified after the file has been opened by the process. (If these are modified before the file is opened, then we never get to WriteFile because opening the file will fail.)
The file is somehow locked by another process/handle (This will at best result in error 32 ERROR_SHARING_VIOLATION).
That leaves us with the situation, that apparently the only possibility for this call to fail if the file was actually opened with the read flag instead of the write flag. However, looking at our code, this seems extremely unlikely. (Due to our tracing, we can be sure that WriteFile failed and we can be sure that the error is ERROR_ACCESS_DENIED, we cannot be 100.1% sure of the opening flags, because these are not traced out.)
Are there any other known circumstances where WriteFile (CFile::Write) would cause an ERROR_ACCESS_DENIED?
Note: To additionally clarify the context of this question:
The file was open, therefore it can't be a directory or somesuch
All tests I performed indicate that while the file is open it cannot be deleted, so the file should still have been there on the call to WriteFile
The file is located on a local drive and not on a network drive.
I should add that we're running on WIndows XP sp3 and the app is compiled with Visual Studio 2005.
The question was
What causes WriteFile to return
ERROR_ACCESS_DENIED?
and I stated in the question
WriteFile will cause
ERROR_ACCESS_DENIED if it is called
for a file handle that is not opened
for writing (i.e. is opened for
reading only).
After adding further logging for the open flags and another incident, it turns out this was correct. The logging for the open flags shows that at the point of error, the file object was opened with CFile::modeRead and therefore we got ERROR_ACCESS_DENIED.
Haven't found out yet which weird code path leads to this, but this just goes to show: Never trust your own code. :-)
(Oh, and btw. It wasn't ::WriteFile that failed, but the ::FlushFileBuffers API, but apparently that returns the same error.)
There is about a dozen different situations that might result in ERROR_ACCESS_DENIED. Internally, all WriteFile does is call NtWriteFile and map its (somewhat meaningful) NTSTATUS error code into a less meaningful HRESULT.
Among other things, ERROR_ACCESS_DENIED could indicate that the file is on a network volume and something went wrong with write permissions, or that the file is really not a file but a directory.
if you can debug it, you should. it could be a million things:
msdn is wrong (it happens a lot)
some app (virus?) is hooking WriteFile and causing different behavior
filesystem problem?
something wrong in your logging, or observations

Programmatically adding a directory to Windows PATH environment variable

I'm writing a Win32 DLL with a function that adds a directory to the Windows PATH environment variable (to be used in an installer).
Looking at the environment variables in Regedit or the Control Panel after the DLL has run shows me that my DLL has succeeded in adding the path to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment and HKEY_CURRENT_USER\Environment.
But when I start up a new Command Prompt (after running the DLL), the directory I added does not show up in the output of echo %PATH% and I can not access the executable that lives in that directory by typing its name.
I think my program is not doing a good job of notifying the system that the PATH has changed, or maybe it is notifying them before the change has fully taken effect. I read an article by Microsoft that says to broadcast the WM_SETTINGCHANGE message after changing an environment variable, and I am doing that with this code:
DWORD result2 = 0;
LRESULT result = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
(LPARAM)"Environment", SMTO_ABORTIFHUNG, 5000, &result2);
if (result == 0){ /* ... Display error message to user ... */ }
The order of my calls is: RegCreateKeyEx, RegSetValueEx, RegCloseKey, SendMessageTimeout
If I press "OK" in the Control Panel "Environment Variables" window, the changes made by my DLL to the PATH show up in newly-created command prompts, so there is something that the Control Panel is doing to propagate PATH changes; I want to figure out what it is and do the same thing.
Does anyone know what I should do?
I'm running 64-bit Windows Vista but I want this to work on all Windows XP, Vista and Windows 7 operating systems.
Update: The problem with the code I posted above is that I did not put the L prefix on the "Environment" string. Although it does not say it explicitly anywhere in the Microsoft documentation that I can find, the LPARAM needs to be a pointer to a WCHAR string (2-byte characters) as opposed to a CHAR string, which is what Visual Studio's compiler generates by default when I write a string literal. The solution to my problem was to change "Environment" to L"Environment". (I thought I already tried that before posting this question, but apparently I didn't try it correctly!) But anyone who wants a complete C++ solution for this task should look at Dan Moulding's answer.
It turns out there really isn't anything new under the sun. This has already been done before, at least once. By me. I created a DLL very much like what you describe for exactly the same purpose (for use in modifying the path from an NSIS installer). It gets used by the Visual Leak Detector installer.
The DLL is called editenv.dll. The source is available at github. I just tested the installer and it updated the system PATH environment variable, no problem. Based on what you've written, I don't see anything that stands out as being wrong. I also don't see anything obvious that's missing. But it may be worth a look at the editenv.dll source (you'd be most interested in EnvVar::set() in EnvVar.cpp, and possibly the pathAdd() and pathRemove() C APIs in editenv.cpp).
I have a program which calls the same Win32 API to yours to update the environment, and it works fine.
One thing to be careful of is how you are opening up the command prompt.
If you open up the command prompt by doing this:
Start -> Run -> cmd.exe
then the environment in the prompt shows that the new variable is set.
However, I also have a programmable function key on my keyboard which I have set to run the cmd.exe process. If I open a command prompt via that function key and then type env, it doesn't show the variable as being set.
I'm not sure why it works differently, but it must have something to do with the way the cmd.exe process is launched (although both are running under my user name, not SYSTEM).
How are you opening up the command prompt?