CreateProcessAsUserW processes not searching PATH

  c++, java, jna, winapi, windows

My goal is to run processes in the same way you’d run them in cmd.exe. For example:

C:UserszjosealDesktop>whoami.exe
antzjoseal

This works because cmd.exe searches for whoami.exe in the search path. I will also need environment variable evaluation.

I also need to execute the process as different users, so I’m forced to use the Windows API’s CreateProcessAsUserW instead of Java’s simple Runtime::exec or ProcessBuilder.

Unfortunately, this API function isn’t offering me the command-line interpreter features I mentioned above. When I run C:WindowsSystem32cmd.exe /c whoami.exe > whoami.txt, I get:

'whoami.exe' is not recognized as an internal or external command,
operable program or batch file.

It’s only when I run "C:WindowsSystem32cmd.exe /c C:WindowsSysWOW64whoami.exe > whoami.txt" (note the absolute path for whoami) that my program works.

What do I need to do in order to get the full command-line experience? According to the CreateProcessAsUserW docs, if I pass NULL to lpEnvironment, the new process inherits the environment of the calling process, so the PATH should be getting inherited.

Here is the main part of my code:

// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow
final STARTUPINFOW.ByReference startupInfoW =
    new STARTUPINFOW.ByReference();
startupInfoW.cb = startupInfoW.size();
startupInfoW.lpReserved = Pointer.NULL;
startupInfoW.lpDesktop = Pointer.NULL;
startupInfoW.lpTitle = Pointer.NULL;
startupInfoW.dwFlags
    = startupInfoW.dwX = startupInfoW.dwY
    = startupInfoW.dwXSize = startupInfoW.dwYSize
    = startupInfoW.dwXCountChars = startupInfoW.dwYCountChars
    = startupInfoW.dwFillAttribute
    = startupInfoW.wShowWindow
    = 0;
startupInfoW.cbReserved2  = 0;
startupInfoW.lpReserved2 = Pointer.NULL;
startupInfoW.hStdInput = startupInfoW.hStdOutput
    = startupInfoW.hStdError
    = Pointer.NULL;
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_information
final PROCESS_INFORMATION.ByReference processInformation =
    new PROCESS_INFORMATION.ByReference();
processInformation.hProcess = processInformation.hThread
    = Pointer.NULL;
processInformation.dwProcessId = processInformation.dwThreadId
    = 0;
// Converts string to char array with 0 as last element.
final char[] whoamiCmd = toCString(
    "C:WindowsSystem32cmd.exe /c whoami.exe > whoami.txt"
);
System.out.printf(
    "last err code = %dn",
    ErrHandlingApi.INSTANCE.GetLastError()
);
final boolean createProcessOk = MyProcessThreadsApi.INSTANCE
    .CreateProcessAsUserW(
        userPrimaryToken.getValue(),
        Pointer.NULL,
        whoamiCmd,
        Pointer.NULL,
        Pointer.NULL,
        false,
        WinBase.CREATE_UNICODE_ENVIRONMENT,
        new PointerByReference(),
        Pointer.NULL,
        startupInfoW,
        processInformation
    );
System.out.printf(
    "last err code = %dn",
    ErrHandlingApi.INSTANCE.GetLastError()
);
System.out.printf("ok = %bn", createProcessOk);
System.out.printf(
    "dwProcessId = %dn", processInformation.dwProcessId
);
public static char[] toCString(final String str) {
    final char[] cString = new char[str.length() + 1];
    for (int i = 0; i < str.length(); i++) {
        cString[i] = str.charAt(i);
    }
    // c-strings end in 0 for lack of bounds checking
    cString[cString.length-1] = 0;
    return cString;
}
C:UserszjosealDesktop>java -cp windows-credentials-poc-1.0-SNAPSHOT.jar Main
last err code = 0
last err code = 0
last err code = 0
last err code = 0
my-password
ptr.peer = 0
last err code = 0
last err code = 0
ok = true
ptr.peer = 1052
last err code = 0
last err code = 5
ok = true
dwProcessId = 8936

C:UserszjosealDesktop>'whoami.exe' is not recognized as an internal or external command,
operable program or batch file.

Source: Windows Questions

LEAVE A COMMENT