Каков идеальный способ эмулировать замену процесса в Windows? - PullRequest
0 голосов
/ 05 июля 2018

Итак, в запросе функции , который я подал против Node.js , я искал способ заменить текущий процесс узла другим. В Linux и в друзьях (на самом деле, в любой POSIX-совместимой системе) это легко: используйте execve и друзей и называйте это день Но очевидно, что в Windows это не сработает, поскольку в нем есть только CreateProcess (которому делегируются execve и друзья, с асинхронным поведением ). И это не то, что люди не хотели до делают аналогичные , ведущие многочисленные повторяющиеся вопросы на этом сайте . (Это не дубликат, потому что он явно ищет обходной путь с учетом определенных ограничений, а не просто запрашивает прямую замену.)

Процесс замены имеет несколько аспектов, к которым необходимо обратиться:

  1. Все потоки консольного ввода-вывода должны быть перенаправлены в новый процесс.
  2. Все сигналы должны быть прозрачно перенаправлены в новый процесс.
  3. Данные из старого процесса должны быть уничтожены с использованием максимально возможного количества ресурсов.
  4. Все ранее существующие потоки и дочерние процессы должны быть уничтожены.
  5. Все существующие ранее дескрипторы должны быть уничтожены, кроме дескрипторов открытых файлов и именованных каналов / и т.д.
  6. Оптимально, память старого процесса должна быть минимальной после создания процесса.
  7. Для моего конкретного случая использования сохранение идентификатора процесса не важно.

А для моего конкретного случая есть несколько ограничений:

  1. Я могу контролировать начальный запуск процесса, а также местоположение моей функции «замена процесса».
  2. Я мог загружать произвольный нативный код с помощью надстроек при любом смещении стека.
    • Вывод: я даже не могу мечтать об отслеживании malloc вызовов, дескрипторов, манипулировании потоками или манипулировании процессами, чтобы отслеживать и освобождать их всех, поскольку переписывание DLL не совсем практично.
  3. У меня нет контроля над , когда вызывается моя "замена процесса". Он может быть вызван через надстройку, которая может быть вызвана либо через интерпретируемый код через FFI, либо через другую надстройку рекурсивно. Это может даже быть вызвано во время инициализации дополнения.
    • Вывод: у меня не было бы возможности узнать, что находится в стеке, даже если бы я отлично инструктировал свою сторону. И переписать все их call s и push es далеко не практично, и по очевидным причинам было бы слишком медленным.

Итак, вот суть того, о чем я думал: используйте что-то похожее на псевдотрамплин.

  1. Статически выделяют следующее:
    1. Один указатель для указателя стека.
    2. MAX_PATH + 1 символов для пути приложения + '\0'.
    3. MAX_PATH + 1 символов для текущего пути рабочего каталога + '\0'.
    4. 32768 символов для аргументов + '\0'.
    5. 32768 символов для окружающей среды + '\0'.
  2. При вводе установите глобальную ссылку на указатель стека на указатель стека.
  3. На "замену":
    1. Выполните соответствующую очистку процесса и заблокируйте / отпустите все, что можете.
    2. Установить указатель стека на сохраненный исходный глобальный.
    3. Завершить каждый дочерний поток.
    4. Убить каждого дочернего процесса.
    5. Свободно каждая открытая ручка .
    6. Если возможно (т.е. не в программе UWP), Для каждой кучи , уничтожить ее , если это не куча по умолчанию или временная куча (если оно существует).
    7. Если возможно, закройте каждую открытую ручку .
    8. Если возможно, walk куча по умолчанию и free каждый связанный с ней сегмент.
    9. Создайте новый процесс со статически размещенным файлом / arguments / environment / etc. без создания нового окна.
    10. Прокси всех будущих полученных сигналов, исключений и т. Д. Без каких-либо изменений в этом процессе. Стандартные сигналы просты , но не настолько, за исключением.
    11. Дождаться окончания процесса.
    12. Возврат с кодом завершения процесса .

Идея заключается в том, чтобы использовать батут на основе процесса и сбросить текущий размер процесса до абсолютного минимума при запуске нового созданного.

Но там, где я не очень знаком с Windows, я, вероятно, допустил немало ошибок здесь. Кроме того, вышесказанное кажется крайне неэффективным, и в некоторой степени это просто ужасно неправильно, потому что ядро ​​может просто освободить несколько страниц памяти, освободить кучу дескрипторов памяти и переместить часть памяти для следующего процесса. .

Итак, подведем итог: каков идеальный способ эмуляции замены процесса в Windows с наименьшими ограничениями?

Ответы [ 2 ]

0 голосов
/ 05 июля 2018

Учитывая, что я не понимаю, что на самом деле запрашивается, и я, конечно, смотрю на такие вещи, как «execve» с выражением «кто, черт возьми, когда-либо это так называет, ничто, кроме безумия не может когда-либо привести к этому», я все же смотрю на эта проблема, задавая себе вопрос:

если процесс-а был убит и заменен почти идентичным процессом-б - кто или что заметит?

Все, что содержит идентификатор процесса или дескриптор процесса, наверняка заметит. Это можно сделать, написав приложение-оболочку, которое загружает первый процесс узла, а при добавлении убивает его и загружает следующий. Внешние наблюдатели видят дескрипторы процесса упаковки и идентификатор не изменяется.

Очевидно, что это отрезало бы потоки stdin и stdout, подаваемые в приложения узла. Но опять же, процесс-обертка мог бы обойти это, передавая один и тот же набор наследуемых дескрипторов каждому процессу, запущенному узлом, путем правильного заполнения структуры STARTUPINFO, переданной в CreateProcess.

Windows не поддерживает сигналы, а те, которые во время фальшивой среды выполнения MS C имеют дело с внутренними ошибками, кроме одной, которая связана с закрытием окна интерактивной консоли с помощью ctrl-C, в чем уверено активное приложение Node.js в любом случае - или может быть передан из оболочки, так как при таком подходе приложения узла не будут работать на интерактивной консоли.

Кроме того, все остальное, по-видимому, является внутренней деталью приложения Node.js, поэтому не должно влиять на любое стороннее приложение, взаимодействующее с тем, что оно считает приложением с одним узлом, через свои потоки stdin / stdout.

0 голосов
/ 05 июля 2018

Windows имеет execve() и друзей, см .:

https://docs.microsoft.com/en-gb/cpp/c-runtime-library/reference/execve-wexecve

Насколько я знаю (кроме того факта, что в Windows нет сигналов), они имеют семантику, идентичную версиям POSIX, так зачем вам искать что-то еще?

- ИЛИ -

(возможно, лучше, потому что это дает вам больший контроль над такими вещами, как то, должны ли ручки, открытые в текущем процессе, наследоваться вызываемым процессом):

  1. CreateProcess (...);
  2. ExitProcess (...);

Там, где может потребоваться заменить шаг 1 на ShellExecute(), если вызываемый процесс требует повышения прав.

Делая так, вы получаете доступ к «голому железу» Windows API. Другого пути нет . Любые другие API, которые вы можете использовать для запуска нового процесса (включая семейство exec), построены поверх CreateProcess().

И все твои батутные вещи просто ... странные. Пожалуйста, не ходите туда, это никогда не сработает (что бы это ни было)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...