Wichilie
Wichilie

Reputation: 127

Pipe from injected .dll frozen until process termination

I'm trying to pipe some data from a foreign process to my program through an injected .dll with a pipe set up. However, ReadFile() only returns once the foreign program is exited (after which it will give the correct output). Is this standard behavior, or am I using pipes (or something else) wrong?

The relavent parts of the code are below (some parts omitted to keep it concise):

main.cpp:

int main() {
    // Create a duplex pipe-based communication channel
    STARTUPINFO startupInfo = {};
    HANDLE parent_IN, parent_OUT;
    if (!IntraProcessComms(startupInfo, parent_IN, parent_OUT)) {
        std::cout << "Could not set up communication necessities" << std::endl;
        return 1;
    }

    // Launch program and obtain the handle
    PROCESS_INFORMATION processInfo = {};
    if (!CreateProcess(
            nullptr,                                                        // path
            LPSTR("[the program path]"),                                    // commands
            nullptr,                                                        // handler inheritable
            nullptr,                                                        // thread inheritable
            true,                                                           // handler inheritance from caller
            0,                                                              // creation flag
            nullptr,                                                        // environment
            nullptr,                                                        // directory
            &startupInfo,                                                   // startup info
            &processInfo)) {                                                // process info
        std::cout << "Could not launch program" << std::endl;
        return 2;
    }

    // Inject the DLL (omitted here; it is injected successfully)
    if (!InjectDLL(processInfo.hProcess, "scraper.dll")) {
        std::cout << "Could not inject DLL" << std::endl;
        return 3;
    }

    CloseHandle(processInfo.hProcess);
    CloseHandle(processInfo.hThread);
    std::cout << "Successfully set up" << std::endl;

    char buffer[BUFSIZ];
    ZeroMemory(buffer, BUFSIZ);
    ReadFile(parent_IN, buffer, sizeof(buffer), nullptr, nullptr);
    std::cout << "Read: \"" << buffer << "\"" << std::endl;
    return 0;
}

bool IntraProcessComms(STARTUPINFO &si, HANDLE &parent_IN, HANDLE &parent_OUT) {
    HANDLE child_IN, child_OUT;
    SECURITY_ATTRIBUTES securityAttr {
        sizeof(SECURITY_ATTRIBUTES),                                        // size
        nullptr,                                                            // security
        true                                                                // inheritable
    };

    // Create pipes from the child to the parent and vice-versa
    if (!CreatePipe(
            &parent_IN,
            &child_OUT,
            &securityAttr,
            0)
    || !CreatePipe(
            &child_IN,
            &parent_OUT,
            &securityAttr,
            0))
        return false;

    // Ensure only the correct handles are inherited
    if (!SetHandleInformation(
            parent_IN,
            HANDLE_FLAG_INHERIT,
            0)
    || !SetHandleInformation(
            parent_OUT,
            HANDLE_FLAG_INHERIT,
            0))
        return false;

    // Set up startupinfo
    si.cb = sizeof(STARTUPINFO);
    si.hStdError = child_OUT;
    si.hStdOutput = child_OUT;
    si.hStdInput = child_IN;
    si.dwFlags = STARTF_USESTDHANDLES;

    return true;
}

scraper.dll:

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
    if (fdwReason == DLL_PROCESS_ATTACH) {
        // stdout was redirected by IntraProcessComms()
        std::cout << "Send from .dll";
    }
    return true;
}

(There might be some obvious mistakes/weird practices, I'm new to C++)

Upvotes: 0

Views: 163

Answers (1)

1201ProgramAlarm
1201ProgramAlarm

Reputation: 32732

Things written to std::cout will be internally cached until either the buffer gets full or it is flushed. The pipe will not see any of the written data until this flushing occurs.

You can call std::cout.flush() after writing your data, or append a << std::endl to your output statement to add a newline and flush the buffer. If you are only going to send complete messages (i.e., one << to cout, rather than several to compose a message) you can use the unitbuf flag within the stream to flush the output after every output operation (std::cout << std::unitbuf << "Send from .dll";) You can use nounitbuf to turn buffering back on.

Upvotes: 1

Related Questions