This question already has answers here:
CreateProcess() fails with an access violation [duplicate]
(3 answers)
Closed 8 years ago.
I work with WinAPI and I have a function that creates a new process:
void new_process() {
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
TCHAR szCommandLine[] = TEXT("NOTEPAD");
CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
}
But when I call this function from main(), it's doesn't work:
void new_process(TCHAR szCommandLine[]) {
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
}
int _tmain(int argc, _TCHAR* argv[]) {
new_process(TEXT("NOTEPAD"));
return 0;
}
Where is my mistake?
The problem is explained here. MSDN says -
The Unicode version of this function, CreateProcessW, 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.
When you pass TEXT("NOTEPAD") as the parameter to the function new_process(TCHAR szCommandLine[]) which in turn passes this as the lpCommandLine parameter to the CreateProcess() API, it will try to modify this location as quoted from MSDN above.
Since the parameter TEXT("NOTEPAD") is a CONSTANT memory, when the CreateProcess() tries to modify this memory as quoted above, it will cause memory access violation.
So ideally you should call the function new_process(TCHAR szCommandLine[]) from the main() function as given below.
TCHAR APP_NAME[] = TEXT("NOTEPAD");
new_process(APP_NAME);
Hope this help!
Related
I do this
HWND lParentWinHandle = GetForegroundWindow();
if (!CreateProcessA(NULL, lArgs, NULL, NULL, FALSE,
CREATE_NEW_CONSOLE, NULL, NULL,
&StartupInfo, &ProcessInfo))
{ ... }
// at this point a new window appears in the foreground.
HWND lChildWinHandle = GetForegroundWindow();
SetParent(lChildWinHandle , lParentWinHandle );
but lParentWinHandle and lChildWinHandle have the same value !
If you want to get the handle of the new process. You should use the process and thread handle of the PROCESS_INFO structure.
And according to the documentation you should wit with WaitForInputIdle until you start the search.
Code is similar to this (it is just a dirty sample no error checking):
BOOL CALLBACK EnumThreadWndProc(HWND hWnd, LPARAM lParam)
{
hWndMain = hWnd; // got the main window...
return FALSE; // ...stop enum
}
// ...
STARTUPINFO siStartInfo = {0};
PROCESS_INFORMATION piProcessInfo = {0};
CreateProcessA(NULL, pCmd,
NULL, NULL, FALSE, 0, NULL, NULL,
&siStartInfo,&piProcessInfo);
if(WaitForInputIdle(piProcessInfo.hProcess, 5000)==0)
{
EnumThreadWindows(piProcessInfo.dwThreadId,
EnumThreadWndProc, NULL);
}
else
{
// Could not start program
...
}
// ...
But please read Is it legal to have a cross-process parent/child or owner/owned window relationship?
Trying to run notepad by using CreateProcess:
void _tmain( int argc, TCHAR *argv[] )
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
// Start the child process.
if( !CreateProcess( "notepad.exe", // No module name (use command line)
NULL, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
printf( "CreateProcess failed (%d).\n", GetLastError() );
return;
}
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
Have error in output 2. This means ERROR_FILE_NOT_FOUND. Where I should start seek for a problem? Does it means it can't find notepad.exe?
The lpApplicationName parameter must provide the full path to the application, not only its name.
More exactly:
The string can specify the full path and file name of the module to
execute or it can specify a partial name. In the case of a partial
name, the function uses the current drive and current directory to
complete the specification. The function will not use the search path.
This parameter must include the file name extension; no default
extension is assumed.
But you're supposedly not in the current folder of notepad.exe. So if you want use the name only, set it in the second argument, lpCommandLine. And note that this string is not a constant because it is defined as an LPTSTR parameter this way:
BOOL WINAPI CreateProcess(
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine, [...]
So you have 2 variants:
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
WCHAR CommandLine[] = L"notepad.exe";
if( CreateProcess( 0, // No module name (use command line)
CommandLine, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
or
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
if( CreateProcess( L"c:\\windows\\notepad.exe",
0, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
I am trying to find a way to allow a single process to be launched as an elevated user but prohibit this process from launching any children with it's token--in a way, sort of "sealing" the token. The reasoning behind this is to prevent a user from launching cmd.exe and gaining full access to the system.
I've looked through the process privilege constants and I'm not seeing anything that could be used to implement this functionality.
I don't think you can seal the token but you can control child process creation with job objects:
static BOOL SpawnProcessAndTerminateGrandchildren(PTSTR Cmdline)
{
HANDLE hJob = CreateJobObject(0, 0);
if (!hJob) return false;
JOBOBJECT_BASIC_LIMIT_INFORMATION jobli;
jobli.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
jobli.ActiveProcessLimit = 1;
BOOL retval = SetInformationJobObject(hJob, JobObjectBasicLimitInformation, &jobli, sizeof(jobli));
PROCESS_INFORMATION pi;
if (retval)
{
STARTUPINFO si;
ZeroMemory(&si, sizeof(si)), si.cb = sizeof(si);
retval = CreateProcess(0, Cmdline, 0, 0, false, CREATE_SUSPENDED|CREATE_BREAKAWAY_FROM_JOB|CREATE_NEW_CONSOLE, 0, 0, &si, &pi);
}
if (retval)
{
if (AssignProcessToJobObject(hJob, pi.hProcess)) // This can fail if we are already in a job
{
ResumeThread(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
}
else
TerminateProcess(pi.hProcess, ERROR_OPERATION_ABORTED);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
CloseHandle(hJob);
return retval;
}
int main(int argc, ...)
{
TCHAR cmd[] = TEXT("cmd.exe /k regedit"); // cmd.exe is our child, regedit is the grandchild spawned by cmd.exe
SpawnProcessAndTerminateGrandchildren(cmd);
return 0;
}
If you want more control you can use JobObjectAssociateCompletionPortInformation so you get the JOB_OBJECT_MSG_NEW_PROCESS message each time a new child process is created.
I have an application that needs to monitor the primary drive for file changes via ReadDirectoryChangesW. However, when UAC is enabled, it doesn't work.
All of the Windows API calls succeed, but I'm not notified of any changes.
I can work around this by individually monitoring each directory in the root, but this is a problem, because it can potentially cause a blue screen if there are too many directories.
Is there an acceptable way to get around UAC and receive file change notifications on the entire primary drive?
The relevant CreateFile and ReadDirectoryChangesW is below. In the case where it doesn't work, directory is C:\. If I monitor any secondary drive (i.e. E:\, F:\, G:\) it works as expected. None of the calls return errors.
HANDLE fileHandle = CreateFileW(directory.c_str(), FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
BOOL success = ReadDirectoryChangesW(fileHandle, watched.buffer.data(),
watched.buffer.size(), TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL, &watched.overlapped, NULL);
Interestingly, the .NET System.IO.FileSystemWatcher does work correctly, and it uses the exact same functions and parameters as I'm using, but it behaves correctly.
First it is best for applications that use the ReadDirectoryChangesW API to run elevated make a manifest file for you app and set requireAdministrator as the requestedExecutionLevel level. Check here for reference.
Try removing FILE_SHARE_WRITE from the CreateFile call if you are using it.
Another option is to make your program run as a service, im not sure how applicable this is to your needs. You could post some code as to how you are getting the file handle and what are you passing to ReadDirectoryChangesW
Here's some working test code, for future reference.
#include <Windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
HANDLE filehandle;
BYTE buffer[65536];
DWORD dw;
FILE_NOTIFY_INFORMATION * fni;
OVERLAPPED overlapped = {0};
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (overlapped.hEvent == NULL)
{
printf("CreateEvent: %u\n", GetLastError());
return 1;
}
filehandle = CreateFile(L"C:\\",
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (filehandle == INVALID_HANDLE_VALUE)
{
printf("CreateFile: %u\n", GetLastError());
return 1;
}
for (;;)
{
if (!ReadDirectoryChangesW(filehandle, buffer, sizeof(buffer),
TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL, &overlapped, NULL))
{
printf("ReadDirectoryChangesW: %u\n", GetLastError());
return 1;
}
printf("Queued OK.\n");
if (!GetOverlappedResult(filehandle, &overlapped, &dw, TRUE))
{
printf("GetOverlappedResult: %u\n", GetLastError());
return 1;
}
printf("%u bytes read.\n", dw);
fni = (FILE_NOTIFY_INFORMATION *)buffer;
for (;;)
{
printf("Next entry offset = %u\n", fni->NextEntryOffset);
printf("Action = %u\n", fni->Action);
printf("File name = %.*ws\n",
fni->FileNameLength / 2,
fni->FileName);
if (fni->NextEntryOffset == 0) break;
fni = (FILE_NOTIFY_INFORMATION *)
(((BYTE *)fni) + fni->NextEntryOffset);
}
}
printf("All done\n");
return 0;
}
You can adjust the privileges of your process yourself like this:
// enable the required privileges for this process
LPCTSTR arPrivelegeNames[] = { SE_BACKUP_NAME,
SE_RESTORE_NAME,
SE_CHANGE_NOTIFY_NAME
};
for (int i=0; i<(sizeof(arPrivelegeNames)/sizeof(LPCTSTR)); ++i)
{
CAutoGeneralHandle hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken.GetPointer()))
{
TOKEN_PRIVILEGES tp = { 1 };
if (LookupPrivilegeValue(NULL, arPrivelegeNames[i], &tp.Privileges[0].Luid))
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
}
}
}
This works also for non-privileged processes (a.k.a. normal user processes).
Consider the following code:
unsigned int __stdcall func( LPVOID ) {
LRESULT result = ::PostThreadMessage( ::GetCurrentThreadId(), 0, 0, 0 );
return 0;
}
int wmain() {
_beginthreadex( NULL, 0, func, NULL, 0, NULL );
...
}
Why does ::PostThreadMessage succeed? I think that it should fail because a message queue should not be created by that moment
Because you are calling PostThreadMessage() on the current thread, the system is able to create the message queue on demand. If you were calling PostThreadMessage() and passing the ID of a thread other than the calling thread, then it would fail if that thread did not have a message queue.
For example, consider the following variant of your code:
unsigned int __stdcall func( LPVOID ) {
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
unsigned int threadID;
_beginthreadex( NULL, 0, func, NULL, 0, &threadID );
LRESULT result = ::PostThreadMessage( threadID, 0, 0, 0 );
DWORD error = ::GetLastError();
return 0;
}
Because we are now attempting to post the message from the main thread, to the worker thread, result comes back as 0 (i.e. an error), and error is set to ERROR_INVALID_THREAD_ID as described by the documentation for PostThreadMessage().
If the function fails, the return value is zero. To get extended error information, call GetLastError. GetLastError returns ERROR_INVALID_THREAD_ID if idThread is not a valid thread identifier, or if the thread specified by idThread does not have a message queue.