winsiderss / phnt

Native API header files for the System Informer project.
https://github.com/winsiderss/systeminformer
MIT License
957 stars 162 forks source link

Process Cloning Not Working in x86 Windows Config (C++) #33

Open SvarunSoda opened 1 month ago

SvarunSoda commented 1 month ago

I'm attempting to clone a process in C++ on Windows. All shown examples were compiled & ran with Visual Studio 2022 on a Windows 10 system.


The minimal example below (adapted from the following tutorial on process cloning in Windows) uses the NtCreateUserProcess function from this phnt library in order to perform the process clone, smillar to the Unix fork function:

#include <iostream>
#include <chrono>
#include <thread>

#include <phnt_windows.h>
#include <phnt.h>

int main()
{
    std::cout << "---- PROGRAM STARTED ----\n\n";

    // Prepare cloning variables
    HANDLE processHandle, threadHandle;
    PS_CREATE_INFO createInfo = {0};
    createInfo.Size = sizeof(createInfo);

    // Do the actual cloning
    NTSTATUS cloneStatus = NtCreateUserProcess(&processHandle, &threadHandle, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, NULL, NULL, PROCESS_CREATE_FLAGS_INHERIT_HANDLES, 0, NULL, &createInfo, NULL);

    // Print something from cloned process & exit it
    if (cloneStatus == STATUS_PROCESS_CLONED)
    {
        FreeConsole();
        AttachConsole(ATTACH_PARENT_PROCESS);

        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        std::cout << "This is from cloned process. (ID: " << (ULONG_PTR)NtCurrentTeb()->ClientId.UniqueProcess << ")\n";

        NtTerminateProcess(NtCurrentProcess(), STATUS_PROCESS_CLONED);
    }
    // Check if parent successfully created cloned process
    else
    {
        std::cout << "This is from original process. (ID: " << (ULONG_PTR)NtCurrentTeb()->ClientId.UniqueProcess << ")\n";

        if (!NT_SUCCESS(cloneStatus))
        {
            std::cout << "ERROR: Failed to clone process! (Status: " << cloneStatus << ")\n";
        }
        else
        {
            NTSTATUS waitStatus = NtWaitForSingleObject(processHandle, FALSE, NULL);

            NtClose(processHandle);
            NtClose(threadHandle);

            if (!NT_SUCCESS(waitStatus))
                std::cout << "ERROR: Cloned process failed to return! (Status: " << waitStatus << ")\n";
            else
                std::cout << "Cloned process successfully returned.\n";
        }
    }

    std::cout << "\n---- PROGRAM FINISHED ----\n\n";
    system("pause");

    return 0;
}

The example above can successfully clone a process in the x64 configuration in my VS instance:

---- PROGRAM STARTED ----

This is from original process. (ID: 14352)
This is from cloned process. (ID: 20648)
Cloned process successfully returned.

---- PROGRAM FINISHED ----

However, it appears that the example above, and other examples present in the cloning tutorial linked above, do not work properly in any x86 configuration. Attempting to run the example above in any x86 configuration results in only the parent process printing it's output:

---- PROGRAM STARTED ----

This is from original process. (ID: 20332)
Cloned process successfully returned.

---- PROGRAM FINISHED ----

The child process appears to terminate & return successfully, but it fails to print it's output.


The same behaviour is exhibited with the following slightly different example using the RtlCloneUserProcess function, also from this library:

#include <iostream>
#include <chrono>
#include <thread>

#include <phnt_windows.h>
#include <phnt.h>

int main()
{
    std::cout << "---- PROGRAM STARTED ----\n\n";

    // Prepare cloning variables
    RTL_USER_PROCESS_INFORMATION cloneInfo;

    // Do the actual cloning
    NTSTATUS cloneStatus = RtlCloneUserProcess(RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES, NULL, NULL, NULL, &cloneInfo);

    // Print something from cloned process & exit it
    if (cloneStatus == STATUS_PROCESS_CLONED)
    {
        FreeConsole();
        AttachConsole(ATTACH_PARENT_PROCESS);

        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        std::cout << "This is from cloned process. (ID: " << (ULONG_PTR)NtCurrentTeb()->ClientId.UniqueProcess << ")\n";

        NtTerminateProcess(NtCurrentProcess(), STATUS_PROCESS_CLONED);
    }
    // Check if parent successfully created cloned process
    else
    {
        std::cout << "This is from original process. (ID: " << (ULONG_PTR)NtCurrentTeb()->ClientId.UniqueProcess << ")\n";

        if (!NT_SUCCESS(cloneStatus))
        {
            std::cout << "ERROR: Failed to clone process! (Status: " << cloneStatus << ")\n";
        }
        else
        {
            NTSTATUS waitStatus = NtWaitForSingleObject(cloneInfo.ProcessHandle, FALSE, NULL);

            NtClose(cloneInfo.ProcessHandle);
            NtClose(cloneInfo.ThreadHandle);

            if (!NT_SUCCESS(waitStatus))
                std::cout << "ERROR: Cloned process failed to return! (Status: " << waitStatus << ")\n";
            else
                std::cout << "Cloned process successfully returned.\n";
        }
    }

    std::cout << "\n---- PROGRAM FINISHED ----\n\n";
    system("pause");

    return 0;
}

Also only printing the parent process's output when running in the x86 configuration.


I'm assuming that I'm missing some include files specifically for building these examples in the x86 configuration. The only additional include directory I have setup in these VS example projects is the phnt library, and below is the list of libraries included by the Linker (copied from the official examples):

ntdll.lib
kernel32.lib
user32.lib
gdi32.lib
winspool.lib
comdlg32.lib
advapi32.lib
shell32.lib
ole32.lib
oleaut32.lib
uuid.lib
odbc32.lib
odbccp32.lib

Has anyone else encountered simillar issues with the NtCreateUserProcess and RtlCloneUserProcess functions from this library when attempting to clone a Windows process in the x86 configuration?

Thanks for reading my post, any guidance is appreciated.

JustasMasiulis commented 1 month ago

Personally, I don't see how this is a phnt issue. Either way add suspended flag and debug the cloned process?

dmex commented 1 month ago

However, it appears that the example above, and other examples present in the cloning tutorial linked above, do not work properly in any x86 configuration. Attempting to run the example above in any x86 configuration results in only the parent process printing it's output:

There are only two supported cases for cloning on Windows:

1) The clone functions can only be used for creating snapshots while debugging: https://github.com/winsiderss/systeminformer/blob/44917dc744d8f3cc848341bc80cd250df469d663/phlib/util.c#L8403-L8590

2) The clone functions can only safely execute processes when used with an image compiled with the Native subsystem as they doesn't have dependencies on components like win32k and csrss: image