Search code examples
cwindowswidechar

CreateProcessW with target passed via stdin - cannot find the target


#include <stdio.h>
#include <windows.h>

#pragma warning(disable : 4996)
int main(int argc, wchar_t** argv)
{
    DWORD bytes_read;
    WCHAR* buffer[4096];
    LPWSTR str;
    STARTUPINFO  start_info = { 0 };
    PROCESS_INFORMATION process_info = { 0 };

    ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, 4096, &bytes_read, NULL);
    str = malloc(bytes_read);
    memcpy(str, buffer, bytes_read);

    // debug - stdout + file
    wprintf(L"\nTrying to run -> \"%LS\"", str);
    FILE *f = fopen("file.txt", "w");
    fwprintf(f, L"%LS", str);
    fclose(f);

    BOOL result = CreateProcessW(
        NULL,
        str,
        NULL,
        NULL,
        TRUE,
        CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS,
        NULL,
        NULL,
        &start_info,
        &process_info
    );

    if (!result) {
        DWORD err = GetLastError();
        WCHAR *errstr;

        FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL, err,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPWSTR)&errstr, 0, NULL
        );

        wprintf(L"\nError -> %ls", errstr);
        return 1;
    }

    return 0;
}

Running it yields following

C:\Users\Admin\source\repos\ConsoleApplication1\Debug                         
λ php -r "echo \"C:\\Windows\\System32\\PING.EXE\0\";"|ConsoleApplication1.exe

Trying to run -> "C:\Windows\System32\PING.EXE"                               
Error -> The system cannot find the file specified.  

So it seems it uses correct input. Also in file.txt there doesn't seem to be anything suspicious when viewed in hex editor

hex dump of a file

When hardcoding str like following instead of using stdin

wchar_t cmd[] = L"C:\\Windows\\System32\\PING.EXE";

It works.

What do I need to do in order to make CreateProcessW work with stdin input?


Solution

  • WCHAR* buffer[4096];
    

    This will create an array of character strings. You just want one wide char buffer.

    Use the L"%s" format specifier for testing:

    wprintf(L"%s\n", buffer)
    

    If possible, pass the Unicode text as command line, example

    php -r "echo \"Test...\0\";"|app.exe Unicode
    

    The Unicode text can contain UTF16 character sets.

    int main(void)
    {
        wprintf(L"GetCommandLineW %s\n", GetCommandLineW());
    
        DWORD bytes_read = 0;
        char buffer[4096] = { 0 };
        ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer,
            sizeof(buffer), &bytes_read, NULL);
    
        int codepage = CP_UTF8;//or CP_ACP
        int wlen = MultiByteToWideChar(codepage, 0, buffer, -1, 0, 0);
        wchar_t *wstr = malloc(wlen * 2);
        MultiByteToWideChar(codepage, 0, buffer, -1, wstr, wlen);
    
        wprintf(L"pipe %s\n", wstr);
    
        free(wstr);
        return 0;
    }