Что самое близкое, что есть в Windows, к fork()?

Я думаю, вопрос говорит сам за себя.

Я хочу раскошелиться на окнах. Какая операция наиболее похожа и как ее использовать.

15 ответов

Решение

Cygwin имеет полнофункциональный fork() в Windows. Таким образом, если использование Cygwin является приемлемым для вас, тогда проблема решается в том случае, если производительность не является проблемой.

В противном случае вы можете посмотреть, как Cygwin реализует fork(). Из довольно старого документа архитектуры Cygwin:

5.6. Создание процесса Вызов fork в Cygwin особенно интересен, поскольку он плохо отображается поверх Win32 API. Это очень затрудняет правильную реализацию. В настоящее время ветвь Cygwin является реализацией без копирования при записи, аналогичной той, что была в ранних версиях UNIX.

Первое, что происходит, когда родительский процесс разветвляет дочерний процесс, - это то, что родительский объект инициализирует пространство в таблице процессов Cygwin для дочернего процесса. Затем он создает приостановленный дочерний процесс с помощью вызова Win32 CreateProcess. Затем родительский процесс вызывает setjmp для сохранения своего собственного контекста и устанавливает указатель на него в области общей памяти Cygwin (общей для всех задач Cygwin). Затем он заполняет дочерние разделы.data и.bss, копируя из своего собственного адресного пространства в приостановленное дочернее адресное пространство. После инициализации адресного пространства дочернего объекта дочерний процесс запускается, пока родительский объект ожидает мьютекса. Ребенок обнаруживает, что он разветвлен и выполняет прыжки в длину с использованием сохраненного буфера перехода. Затем дочерний объект устанавливает мьютекс, на котором ожидает родительский элемент, и блокирует другой мьютекс. Это сигнал для родителя скопировать свой стек и кучу в дочерний элемент, после чего он освобождает мьютекс, которого ожидает дочерний элемент, и возвращается из вызова fork. Наконец, потомок выходит из режима блокировки последнего мьютекса, воссоздает все отображенные в памяти области, переданные ему через общую область, и возвращается из самого fork.

Хотя у нас есть некоторые идеи относительно того, как ускорить реализацию форка за счет уменьшения количества переключений контекста между родительским и дочерним процессами, форк почти всегда будет неэффективным в Win32. К счастью, в большинстве случаев порожденное семейством вызовов, предоставляемое Cygwin, можно заменить пару fork/exec с небольшими усилиями. Эти вызовы отображаются чисто поверх Win32 API. В результате они намного эффективнее. Изменение программы драйвера компилятора для вызова spawn вместо fork было тривиальным изменением и увеличило скорость компиляции на двадцать-тридцать процентов в наших тестах.

Однако, spawn и exec представляют свои трудности. Поскольку в Win32 нет никакого способа сделать настоящий exec, Cygwin должен изобрести свои собственные идентификаторы процессов (PID). В результате, когда процесс выполняет несколько вызовов exec, будет несколько PID Windows, связанных с одним PID Cygwin. В некоторых случаях заглушки каждого из этих процессов Win32 могут задерживаться, ожидая выхода из exec'd процесса Cygwin.

Похоже, много работы, не так ли? И да, это неопрятно.

РЕДАКТИРОВАТЬ: документ устарел, пожалуйста, посмотрите этот отличный ответ для обновления

Я, конечно, не знаю подробностей об этом, потому что я никогда не делал этого, но у нативного API NT есть возможность ветвить процесс (подсистема POSIX в Windows нуждается в этой возможности - я не уверен, что подсистема POSIX даже больше не поддерживается).

Поиск ZwCreateProcess() должен получить некоторые дополнительные сведения - например, этот фрагмент информации от Максима Шацких:

Наиболее важным параметром здесь является SectionHandle. Если этот параметр имеет значение NULL, ядро ​​разветвит текущий процесс. В противном случае этот параметр должен быть дескриптором объекта раздела SEC_IMAGE, созданного в файле EXE перед вызовом ZwCreateProcess().

Обратите внимание, что Коринна Виншен указывает, что Cygwin обнаружил, что использование ZwCreateProcess() все еще ненадежно:

Икер Арисменди написал:

> Because the Cygwin project relied solely on Win32 APIs its fork
> implementation is non-COW and inefficient in those cases where a fork
> is not followed by exec.  It's also rather complex. See here (section
> 5.6) for details:
>  
> http://www.redhat.com/support/wpapers/cygnus/cygnus_cygwin/architecture.html

Этот документ довольно старый, 10 лет или около того. Хотя мы все еще используем вызовы Win32 для эмуляции fork, метод заметно изменился. В частности, мы больше не создаем дочерний процесс в приостановленном состоянии, кроме случаев, когда конкретным источникам данных требуется специальная обработка в родительском процессе, прежде чем они будут скопированы в дочерний процесс. В текущей версии 1.5.25 единственным случаем для приостановленного дочернего элемента являются открытые сокеты в родительском элементе. Предстоящий релиз 1.7.0 вообще не будет приостановлен.

Одна из причин не использовать ZwCreateProcess заключалась в том, что до версии 1.5.25 мы все еще поддерживаем пользователей Windows 9x. Однако две попытки использовать ZwCreateProcess в системах на базе NT потерпели неудачу по той или иной причине.

Было бы очень хорошо, если бы этот материал был лучше или вообще документирован, особенно пара структур данных и как подключить процесс к подсистеме. Хотя fork не является концепцией Win32, я не вижу плохого в том, чтобы было проще реализовать fork.

Ну, на самом деле окна не имеют ничего подобного. Тем более что fork можно использовать для концептуального создания потока или процесса в *nix.

Итак, я должен сказать:

CreateProcess() / CreateProcessEx()

а также

CreateThread() (Я слышал, что для приложений C, _beginthreadex() лучше).

Люди пытались реализовать форк на Windows. Это самая близкая вещь, которую я могу найти:

Взято из: http://doxygen.scilab.org/5.3/d0/d8f/forkWindows_8c_source.html

static BOOL haveLoadedFunctionsForFork(void);

int fork(void) 
{
    HANDLE hProcess = 0, hThread = 0;
    OBJECT_ATTRIBUTES oa = { sizeof(oa) };
    MEMORY_BASIC_INFORMATION mbi;
    CLIENT_ID cid;
    USER_STACK stack;
    PNT_TIB tib;
    THREAD_BASIC_INFORMATION tbi;

    CONTEXT context = {
        CONTEXT_FULL | 
        CONTEXT_DEBUG_REGISTERS | 
        CONTEXT_FLOATING_POINT
    };

    if (setjmp(jenv) != 0) return 0; /* return as a child */

    /* check whether the entry points are 
       initilized and get them if necessary */
    if (!ZwCreateProcess && !haveLoadedFunctionsForFork()) return -1;

    /* create forked process */
    ZwCreateProcess(&hProcess, PROCESS_ALL_ACCESS, &oa,
        NtCurrentProcess(), TRUE, 0, 0, 0);

    /* set the Eip for the child process to our child function */
    ZwGetContextThread(NtCurrentThread(), &context);

    /* In x64 the Eip and Esp are not present, 
       their x64 counterparts are Rip and Rsp respectively. */
#if _WIN64
    context.Rip = (ULONG)child_entry;
#else
    context.Eip = (ULONG)child_entry;
#endif

#if _WIN64
    ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Rsp,
        MemoryBasicInformation, &mbi, sizeof mbi, 0);
#else
    ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Esp,
        MemoryBasicInformation, &mbi, sizeof mbi, 0);
#endif

    stack.FixedStackBase = 0;
    stack.FixedStackLimit = 0;
    stack.ExpandableStackBase = (PCHAR)mbi.BaseAddress + mbi.RegionSize;
    stack.ExpandableStackLimit = mbi.BaseAddress;
    stack.ExpandableStackBottom = mbi.AllocationBase;

    /* create thread using the modified context and stack */
    ZwCreateThread(&hThread, THREAD_ALL_ACCESS, &oa, hProcess,
        &cid, &context, &stack, TRUE);

    /* copy exception table */
    ZwQueryInformationThread(NtCurrentThread(), ThreadBasicInformation,
        &tbi, sizeof tbi, 0);
    tib = (PNT_TIB)tbi.TebBaseAddress;
    ZwQueryInformationThread(hThread, ThreadBasicInformation,
        &tbi, sizeof tbi, 0);
    ZwWriteVirtualMemory(hProcess, tbi.TebBaseAddress, 
        &tib->ExceptionList, sizeof tib->ExceptionList, 0);

    /* start (resume really) the child */
    ZwResumeThread(hThread, 0);

    /* clean up */
    ZwClose(hThread);
    ZwClose(hProcess);

    /* exit with child's pid */
    return (int)cid.UniqueProcess;
}
static BOOL haveLoadedFunctionsForFork(void)
{
    HANDLE ntdll = GetModuleHandle("ntdll");
    if (ntdll == NULL) return FALSE;

    if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory &&
        ZwCreateThread && ZwGetContextThread && ZwResumeThread &&
        ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose)
    {
        return TRUE;
    }

    ZwCreateProcess = (ZwCreateProcess_t) GetProcAddress(ntdll,
        "ZwCreateProcess");
    ZwQuerySystemInformation = (ZwQuerySystemInformation_t)
        GetProcAddress(ntdll, "ZwQuerySystemInformation");
    ZwQueryVirtualMemory = (ZwQueryVirtualMemory_t)
        GetProcAddress(ntdll, "ZwQueryVirtualMemory");
    ZwCreateThread = (ZwCreateThread_t)
        GetProcAddress(ntdll, "ZwCreateThread");
    ZwGetContextThread = (ZwGetContextThread_t)
        GetProcAddress(ntdll, "ZwGetContextThread");
    ZwResumeThread = (ZwResumeThread_t)
        GetProcAddress(ntdll, "ZwResumeThread");
    ZwQueryInformationThread = (ZwQueryInformationThread_t)
        GetProcAddress(ntdll, "ZwQueryInformationThread");
    ZwWriteVirtualMemory = (ZwWriteVirtualMemory_t)
        GetProcAddress(ntdll, "ZwWriteVirtualMemory");
    ZwClose = (ZwClose_t) GetProcAddress(ntdll, "ZwClose");

    if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory &&
        ZwCreateThread && ZwGetContextThread && ZwResumeThread &&
        ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose)
    {
        return TRUE;
    }
    else
    {
        ZwCreateProcess = NULL;
        ZwQuerySystemInformation = NULL;
        ZwQueryVirtualMemory = NULL;
        ZwCreateThread = NULL;
        ZwGetContextThread = NULL;
        ZwResumeThread = NULL;
        ZwQueryInformationThread = NULL;
        ZwWriteVirtualMemory = NULL;
        ZwClose = NULL;
    }
    return FALSE;
}

Как уже упоминалось в других ответах, NT (ядро, лежащее в основе современных версий Windows) имеет эквивалент Unix fork(). Проблема не в этом.

Проблема в том, что клонирование всего состояния процесса, как правило, неразумно. Это так же верно в мире Unix, как и в Windows, но в мире Unix fork() используется постоянно, и библиотеки предназначены для работы с этим. Библиотеки Windows - нет.

Например, системные библиотеки DLL kernel32.dll и user32.dll поддерживают частное соединение с серверным процессом Win32 csrss.exe. После вилки на клиентской стороне этого соединения есть два процесса, которые могут вызвать проблемы. Дочерний процесс должен сообщить csrss.exe о своем существовании и установить новое соединение, но для этого нет интерфейса, потому что эти библиотеки не были разработаны с учетом fork().

Итак, у вас есть два варианта. Один из них - запретить использование kernel32 и user32 и других библиотек, которые не предназначены для разветвления, включая любые библиотеки, которые прямо или косвенно ссылаются на kernel32 или user32, то есть практически все из них. Это означает, что вы вообще не можете взаимодействовать с рабочим столом Windows и застряли в своем собственном отдельном мире Unixy. Это подход, используемый различными подсистемами Unix для NT.

Другой вариант - прибегнуть к какой-то ужасной хитрости, чтобы попытаться заставить неосведомленные библиотеки работать с fork(). Это то, что делает Cygwin. Он создает новый процесс, позволяет ему инициализироваться (включая регистрацию с помощью csrss.exe), затем копирует большую часть динамического состояния из старого процесса и надеется на лучшее. Меня поражает, что это когда-либо работает. Он определенно не работает надежно - даже если он не выходит из строя случайным образом из-за конфликта адресного пространства, любая используемая вами библиотека может оставаться в неработающем состоянии. Утверждение текущего принятого ответа о том, что Cygwin имеет "полнофункциональную fork()"... сомнительно.

Резюме: В среде, подобной Interix, вы можете выполнить вилку, вызвав fork(). В противном случае попробуйте отучить себя от этого желания. Даже если вы нацелены на Cygwin, не используйте fork(), если в этом нет крайней необходимости.

До того, как Microsoft представит свою новую опцию "Подсистема Linux для Windows", CreateProcess() была самая близкая вещь Windows должна fork(), но Windows требует, чтобы вы указали исполняемый файл для запуска в этом процессе.

Создание процессов в UNIX сильно отличается от Windows. это fork() call в основном дублирует текущий процесс почти полностью, каждый в своем собственном адресном пространстве, и продолжает их запускать отдельно. Хотя сами процессы разные, они все еще работают с одной и той же программой. Смотрите здесь для хорошего обзора fork/exec модель.

Возвращаясь к другому пути, эквиваленту Windows CreateProcess() это fork()/exec() пара функций в UNIX.

Если вы переносили программное обеспечение на Windows и не возражали против уровня перевода, Cygwin предоставил необходимую вам возможность, но она была довольно хитрой.

Конечно, с новой подсистемой Linux, Windows ближе всего fork() на самом деле fork():-)

Следующий документ содержит некоторую информацию о переносе кода из UNIX в Win32: https://msdn.microsoft.com/en-us/library/y23kc048.aspx

Среди прочего, это указывает на то, что модель процесса сильно отличается в двух системах, и рекомендует рассмотреть CreateProcess и CreateThread, где требуется поведение, аналогичное fork().

"как только вы захотите получить доступ к файлу или printf, то мне будет отказано"

  • Вы не можете съесть свой пирог и съесть его тоже... в msvcrt.dll printf() основан на API консоли, который сам по себе использует lpc для взаимодействия с консольной подсистемой (csrss.exe). Соединение с csrss инициируется при запуске процесса, что означает, что любой процесс, который начинает свое выполнение "в середине", пропустит этот шаг. Если у вас нет доступа к исходному коду операционной системы, нет смысла пытаться подключиться к csrss вручную. Вместо этого вы должны создать свою собственную подсистему и, соответственно, избегать консольных функций в приложениях, которые используют fork().

  • После того, как вы внедрили свою собственную подсистему, не забудьте также продублировать все дескрипторы родителя для дочернего процесса;-)

"Кроме того, вам, вероятно, не следует использовать функции Zw*, если вы не находитесь в режиме ядра, вы, вероятно, должны использовать вместо этого функции Nt*".

  • Это неверно При доступе в пользовательском режиме нет абсолютно никакой разницы между Zw*** Nt***; это просто два разных (ntdll.dll) экспортированных имени, которые ссылаются на один и тот же (относительный) виртуальный адрес.

ZwGetContextThread (NtCurrentThread (), & context);

  • получение контекста текущего (запущенного) потока путем вызова ZwGetContextThread неверно, может привести к сбою и (из-за дополнительного системного вызова) также не является самым быстрым способом выполнения задачи.

Ваши лучшие варианты - CreateProcess() или CreateThread (). Больше информации о портировании здесь.

Семантика fork() необходима там, где дочернему элементу необходим доступ к фактическому состоянию памяти родителя, поскольку моментальный вызов fork() вызывается. У меня есть часть программного обеспечения, которая полагается на неявный мьютекс копирования памяти при вызове функции fork(), что делает невозможным использование потоков. (Это эмулируется на современных платформах *nix с помощью семантики копирования при записи / обновления памяти).

Самым близким, который существует в Windows как системный вызов, является CreateProcess. Лучшее, что можно сделать, - это чтобы родитель заморозил все другие потоки во время копирования памяти в пространство памяти нового процесса, а затем разморозил их. Ни класс Cygwin frok [sic], ни код Scilab, которые разместил Эрик де Кортис, не делает заморозку потоков, что я вижу.

Кроме того, вам, вероятно, не следует использовать функции Zw *, если вы не находитесь в режиме ядра, вам, вероятно, следует использовать вместо этого функции Nt *. Есть дополнительная ветка, которая проверяет, находитесь ли вы в режиме ядра, и, если нет, выполняет все проверки границ и проверку параметров, которые всегда выполняет Nt *. Таким образом, вызывать их из пользовательского режима очень немного менее эффективно.

В Windows нет простого способа эмулировать fork ().

Я предлагаю вам использовать темы вместо.

Большинство хакерских решений устарели. У Винни-фаззера есть версия вилки, которая работает в текущих версиях Windows 10 (хотя для этого требуются системные смещения, и это тоже может легко сломаться).

https://github.com/sslab-gatech/winnie/tree/master/forklib

Ближе всего говорите... Дайте подумать... Наверное, это fork():)

Подробнее см. В Interix реализует fork()?

Если вам нужно только создать подпроцесс и ждать его, возможно, достаточно _spawn* API в process.h. Вот больше информации об этом:

https://docs.microsoft.com/en-us/cpp/c-runtime-library/process-and-environment-control https://en.wikipedia.org/wiki/Process.h

Актуальные ссылки на ответы Эрика де Куртиса ЗДЕСЬ :

Поскольку его ответ является наиболее достойным явным ответом, я посчитал, что этот «повторный пост» удобен.

вилкаWindows.h

      #ifndef __FORK_WINDOWS_H__
#define __FORK_WINDOWS_H__
 
#include "BOOL.h"
 
/**
* simulate fork on Windows
*/
int fork(void);
 
/**
* check if symbols to simulate fork are present
* and load these symbols
*/
BOOL haveLoadedFunctionsForFork(void);
 
#endif /* __FORK_WINDOWS_H__ */

forkWindows.c (удаленный)

      #include <windows.h>
#include <WinNT.h>
#include <setjmp.h>
#include "forkWindows.h"

typedef LONG NTSTATUS;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG ProcessId;
    UCHAR ObjectTypeNumber;
    UCHAR Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

typedef struct _OBJECT_ATTRIBUTES
{
    ULONG Length;
    HANDLE RootDirectory;
    PVOID /* really PUNICODE_STRING */  ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;       /* type SECURITY_DESCRIPTOR */
    PVOID SecurityQualityOfService; /* type SECURITY_QUALITY_OF_SERVICE */
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

typedef enum _MEMORY_INFORMATION_
{
    MemoryBasicInformation,
    MemoryWorkingSetList,
    MemorySectionName,
    MemoryBasicVlmInformation
} MEMORY_INFORMATION_CLASS;

typedef struct _CLIENT_ID
{
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;

typedef struct _USER_STACK
{
    PVOID FixedStackBase;
    PVOID FixedStackLimit;
    PVOID ExpandableStackBase;
    PVOID ExpandableStackLimit;
    PVOID ExpandableStackBottom;
} USER_STACK, *PUSER_STACK;

typedef LONG KPRIORITY;
typedef ULONG_PTR KAFFINITY;
typedef KAFFINITY *PKAFFINITY;

typedef struct _THREAD_BASIC_INFORMATION
{
    NTSTATUS                ExitStatus;
    PVOID                   TebBaseAddress;
    CLIENT_ID               ClientId;
    KAFFINITY               AffinityMask;
    KPRIORITY               Priority;
    KPRIORITY               BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;

typedef enum _SYSTEM_INFORMATION_CLASS { SystemHandleInformation = 0x10 } SYSTEM_INFORMATION_CLASS;

typedef NTSTATUS (NTAPI *ZwWriteVirtualMemory_t)(IN HANDLE               ProcessHandle,
        IN PVOID                BaseAddress,
        IN PVOID                Buffer,
        IN ULONG                NumberOfBytesToWrite,
        OUT PULONG              NumberOfBytesWritten OPTIONAL);

typedef NTSTATUS (NTAPI *ZwCreateProcess_t)(OUT PHANDLE            ProcessHandle,
        IN  ACCESS_MASK        DesiredAccess,
        IN  POBJECT_ATTRIBUTES ObjectAttributes,
        IN  HANDLE             InheriteFromProcessHandle,
        IN  BOOLEAN            InheritHandles,
        IN  HANDLE             SectionHandle    OPTIONAL,
        IN  HANDLE             DebugPort        OPTIONAL,
        IN  HANDLE             ExceptionPort    OPTIONAL);

typedef NTSTATUS (WINAPI *ZwQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS SystemInformationClass,
        PVOID SystemInformation,
        ULONG SystemInformationLength,
        PULONG ReturnLength);
typedef NTSTATUS (NTAPI *ZwQueryVirtualMemory_t)(IN  HANDLE ProcessHandle,
        IN  PVOID BaseAddress,
        IN  MEMORY_INFORMATION_CLASS MemoryInformationClass,
        OUT PVOID MemoryInformation,
        IN  ULONG MemoryInformationLength,
        OUT PULONG ReturnLength OPTIONAL);

typedef NTSTATUS (NTAPI *ZwGetContextThread_t)(IN HANDLE ThreadHandle, OUT PCONTEXT Context);
typedef NTSTATUS (NTAPI *ZwCreateThread_t)(OUT PHANDLE ThreadHandle,
        IN  ACCESS_MASK DesiredAccess,
        IN  POBJECT_ATTRIBUTES ObjectAttributes,
        IN  HANDLE ProcessHandle,
        OUT PCLIENT_ID ClientId,
        IN  PCONTEXT ThreadContext,
        IN  PUSER_STACK UserStack,
        IN  BOOLEAN CreateSuspended);

typedef NTSTATUS (NTAPI *ZwResumeThread_t)(IN HANDLE ThreadHandle, OUT PULONG SuspendCount OPTIONAL);
typedef NTSTATUS (NTAPI *ZwClose_t)(IN HANDLE ObjectHandle);
typedef NTSTATUS (NTAPI *ZwQueryInformationThread_t)(IN HANDLE               ThreadHandle,
        IN THREAD_INFORMATION_CLASS ThreadInformationClass,
        OUT PVOID               ThreadInformation,
        IN ULONG                ThreadInformationLength,
        OUT PULONG              ReturnLength OPTIONAL );

static ZwCreateProcess_t ZwCreateProcess = NULL;
static ZwQuerySystemInformation_t ZwQuerySystemInformation = NULL;
static ZwQueryVirtualMemory_t ZwQueryVirtualMemory = NULL;
static ZwCreateThread_t ZwCreateThread = NULL;
static ZwGetContextThread_t ZwGetContextThread = NULL;
static ZwResumeThread_t ZwResumeThread = NULL;
static ZwClose_t ZwClose = NULL;
static ZwQueryInformationThread_t ZwQueryInformationThread = NULL;
static ZwWriteVirtualMemory_t ZwWriteVirtualMemory = NULL;

#define NtCurrentProcess() ((HANDLE)-1)
#define NtCurrentThread() ((HANDLE) -2)
/*! we use really the Nt versions - so the following is just for completeness */
#define ZwCurrentProcess() NtCurrentProcess()
#define ZwCurrentThread() NtCurrentThread()
#define STATUS_INFO_LENGTH_MISMATCH      ((NTSTATUS)0xC0000004L)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)

/* setjmp env for the jump back into the fork() function */
static jmp_buf jenv;

/* entry point for our child thread process - just longjmp into fork */
static int child_entry(void)
{
    longjmp(jenv, 1);
    return 0;
}

static BOOL haveLoadedFunctionsForFork(void)
{
    HMODULE ntdll = GetModuleHandle("ntdll");
    if (ntdll == NULL)
    {
        return FALSE;
    }
 
    if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory &&
            ZwCreateThread && ZwGetContextThread && ZwResumeThread &&
            ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose)
    {
        return TRUE;
    }
 
    ZwCreateProcess = (ZwCreateProcess_t) GetProcAddress(ntdll, "ZwCreateProcess");
    ZwQuerySystemInformation = (ZwQuerySystemInformation_t) GetProcAddress(ntdll, "ZwQuerySystemInformation");
    ZwQueryVirtualMemory = (ZwQueryVirtualMemory_t) GetProcAddress(ntdll, "ZwQueryVirtualMemory");
    ZwCreateThread = (ZwCreateThread_t) GetProcAddress(ntdll, "ZwCreateThread");
    ZwGetContextThread = (ZwGetContextThread_t) GetProcAddress(ntdll, "ZwGetContextThread");
    ZwResumeThread = (ZwResumeThread_t) GetProcAddress(ntdll, "ZwResumeThread");
    ZwQueryInformationThread = (ZwQueryInformationThread_t) GetProcAddress(ntdll, "ZwQueryInformationThread");
    ZwWriteVirtualMemory = (ZwWriteVirtualMemory_t) GetProcAddress(ntdll, "ZwWriteVirtualMemory");
    ZwClose = (ZwClose_t) GetProcAddress(ntdll, "ZwClose");
 
    if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory &&
            ZwCreateThread && ZwGetContextThread && ZwResumeThread &&
            ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose)
    {
        return TRUE;
    }
    else
    {
        ZwCreateProcess = NULL;
        ZwQuerySystemInformation = NULL;
        ZwQueryVirtualMemory = NULL;
        ZwCreateThread = NULL;
        ZwGetContextThread = NULL;
        ZwResumeThread = NULL;
        ZwQueryInformationThread = NULL;
        ZwWriteVirtualMemory = NULL;
        ZwClose = NULL;
    }
    return FALSE;
}

int fork(void)
{
    HANDLE hProcess = 0, hThread = 0;
    OBJECT_ATTRIBUTES oa = { sizeof(oa) };
    MEMORY_BASIC_INFORMATION mbi;
    CLIENT_ID cid;
    USER_STACK stack;
    PNT_TIB tib;
    THREAD_BASIC_INFORMATION tbi;
 
    CONTEXT context = {CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT};
 
    if (setjmp(jenv) != 0)
    {
        return 0;    /* return as a child */
    }
 
    /* check whether the entry points are initilized and get them if necessary */
    if (!ZwCreateProcess && !haveLoadedFunctionsForFork())
    {
        return -1;
    }
 
    /* create forked process */
    ZwCreateProcess(&hProcess, PROCESS_ALL_ACCESS, &oa, NtCurrentProcess(), TRUE, 0, 0, 0);
 
    /* set the Eip for the child process to our child function */
    ZwGetContextThread(NtCurrentThread(), &context);
 
    /* In x64 the Eip and Esp are not present, their x64 counterparts are Rip and
    Rsp respectively.
    */
#if _WIN64
    context.Rip = (ULONG)child_entry;
#else
    context.Eip = (ULONG)child_entry;
#endif
 
#if _WIN64
    ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Rsp, MemoryBasicInformation, &mbi, sizeof mbi, 0);
#else
    ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Esp, MemoryBasicInformation, &mbi, sizeof mbi, 0);
#endif
 
    stack.FixedStackBase = 0;
    stack.FixedStackLimit = 0;
    stack.ExpandableStackBase = (PCHAR)mbi.BaseAddress + mbi.RegionSize;
    stack.ExpandableStackLimit = mbi.BaseAddress;
    stack.ExpandableStackBottom = mbi.AllocationBase;
 
    /* create thread using the modified context and stack */
    ZwCreateThread(&hThread, THREAD_ALL_ACCESS, &oa, hProcess, &cid, &context, &stack, TRUE);
 
    /* copy exception table */
    ZwQueryInformationThread(NtCurrentThread(), ThreadMemoryPriority, &tbi, sizeof tbi, 0);
    tib = (PNT_TIB)tbi.TebBaseAddress;
    ZwQueryInformationThread(hThread, ThreadMemoryPriority, &tbi, sizeof tbi, 0);
    ZwWriteVirtualMemory(hProcess, tbi.TebBaseAddress, &tib->ExceptionList, sizeof tib->ExceptionList, 0);
 
    /* start (resume really) the child */
    ZwResumeThread(hThread, 0);
 
    /* clean up */
    ZwClose(hThread);
    ZwClose(hProcess);
 
    /* exit with child's pid */
    return (int)cid.UniqueProcess;
}

... с уважением !

Другие вопросы по тегам