Вызов кода из дочернего процесса

Тема в разделе "WASM.ASSEMBLER", создана пользователем Jin X, 13 фев 2018.

  1. Jin X

    Jin X Active Member

    Публикаций:
    0
    Регистрация:
    15 янв 2009
    Сообщения:
    199
    Адрес:
    Кольца Сатурна
    Процесс (родитель) запускает другой процесс (дочку). Дочка должна вызвать функции родителя. Как это сделать оптимальнее всего? Под оптимальностью я подразумеваю высокую скорость, т.е. минимальные задержки (и желательно чтобы разброс этих задержек был тоже минимален, т.е. если при первом вызове мы потратили 1000 тактов, то при втором – тоже ≈1000).
    Короче, нужно сделать что-то типа небольшого API для дочки. При этом родитель, в принципе, свободен и может заниматься только (ну или почти только) обработкой API. Когда дочка вызывает функцию родителя, она должна остановиться и дождаться завершения работы этой функции родителя.
    Для дочки весь этот процесс должен быть максимально простым, в идеале – через 1-2 вызова WinAPI, безо всяких там поисков процесса родителя, затем OpenProcess и прочей бодяги.

    Если будет передача параметров и возвращаемое значение, будет замечательно, но это не критично. Важнее всего просто тупо запуск функции.

    И ещё: можно ли (и как) запустить дочерний процесс с своём адресном пространстве (и тогда каким-то простым способом получить адрес "главной" API-функции родителя и вызывать его через call)?

    p.s. Да, можно сделать не через запуск, а просто создать DLL-ку и подцепить её к дочке, и там уже использовать в лёгкую, но мне надо именно через запуск а-ля CreateProcess.
     
    Последнее редактирование: 13 фев 2018
  2. HESH

    HESH Member

    Публикаций:
    1
    Регистрация:
    20 мар 2008
    Сообщения:
    86
    Чем CreateRemoteThread не устраивает ?
     
  3. Jin X

    Jin X Active Member

    Публикаций:
    0
    Регистрация:
    15 янв 2009
    Сообщения:
    199
    Адрес:
    Кольца Сатурна
    Не пользовался никогда.

    Как я понял, функция запускает код, который находится в стороннем (remote) процессе, так? Т.е. дочерний процесс может запустить так функцию родителя. Но надо же ещё адрес этой функции передать как-то дочке.

    Коду передаётся указатель. А как он получит доступ к данным по переданному адресу, когда эти данные находятся в другом процессе (адресном пространстве)?
     
  4. HESH

    HESH Member

    Публикаций:
    1
    Регистрация:
    20 мар 2008
    Сообщения:
    86
    Так.
    Через lpParameter
    Перед созданием потока в дочернем процессе должна быть выделена область памяти для этих нужд через VirtualAllocEx, заполнить ее если надо данными через WriteProcessMemory, ну и далее по плану.
     
  5. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    410
    QueueUserApc
     
  6. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    1.843
    Jin X,

    > Дочка должна вызвать функции родителя.

    Не корректная и не понятная постановка задачи. Выполнение кода в чужом адресном пространстве невозможно. Процессор выполняет код в течении кванта лишь в одном ап. Всё множество способов удалённого исполнения кода основано на IPC, есть даже штатные апи, типо RtlRemoteCall(). Опишите вопрос понятным образом. Обычно подобные проблемы упираются в защиту.
     
  7. Fail

    Fail Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2012
    Сообщения:
    405
    При работающем CFG это недоступно?
     
  8. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    1.843
    Fail,

    CFG это примитивный механизм защиты, если вы будите вызывать описанные в карте адреса, то проблем конечно не возникнет. Да и однозначно на ваш вопрос ответить нельзя - есть куча механизмов защиты, а помимо их у меня давно свои разработки, так что я начинаю в них путаться, так как память о них становится не важной и они забываются, когда есть более общие методы.
     
    Fail нравится это.
  9. superakira

    superakira Active Member

    Публикаций:
    0
    Регистрация:
    2 июн 2008
    Сообщения:
    292
    Indy_, ты обсуждаешь кфг, с человеком, который не использовал CreateRemoteThread ...
    Jin X, читни рихтера, там целая глова норм языком описана, про удаленный поток. либо ипс замути на шаренной памяти/пайпах, на это наврени рпц уже. грузить пе в твоем случае не вариант вообще
     
  10. unc1e

    unc1e Active Member

    Публикаций:
    2
    Регистрация:
    28 июл 2017
    Сообщения:
    259
    от себя и от RET +2 за пайпы
     
  11. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    1.896
    Jin X,
    COM заюзай, там все уже реализовано. Флаг CLSCTX_LOCAL_SERVER как раз для этого - когда объект создается и существует в одном процессе, а методы интерфейса дергаются в другом. Система сама займется передачей данных между процессами и синхронизацией.
     
  12. Jin X

    Jin X Active Member

    Публикаций:
    0
    Регистрация:
    15 янв 2009
    Сообщения:
    199
    Адрес:
    Кольца Сатурна
    Спасибо за множество вариантов, но как-то всё усложнили, ИМХО :)
    Задача была сделать всё максимально просто (для дочки), при этом очень быстро (в плане вызова).
    Но ощущение, что совсем просто это сделать не получится (да и что быстро – не факт).
    Короче говоря, достичь уровня простоты и скорости, приближённых к вызову DLL-функций, как я понял, надежды мало.
    p.s. А так, можно же у родителя создать окно, а дочка делает FindWindow и потом тупо SendMessage(WM_USER+). Куда уж проще? Но вряд ли это будет достаточно быстро (не замерял, правда).
     
  13. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    1.896
    COM же, ну :) Он для этого и проектировался. Более того, родитель может быть не просто в другом процессе, а в другом процессе на другом компе в другой стране и для дочки это будет так же просто, как вызвать функцию DLL.

    WM_USER просто передаст wParam и lParam как есть, т.е. если в последнем передашь указатель на структуру или строку, то в другой процесс дойдет только указатель, но не сами данные. WM_COPYDATA надо юзать для передачи чего-либо кроме двордов через оконные сообщения между процессами.
     
  14. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    1.843
    Jin X,

    Самый эффективный механизм это нэйтив lpc. Пайпы слишком тормозные, что бы говорить про профайл. lpc мощный механизм, но если сравнить со своей реализацией, то тоже не фонтан". Межпроцессный синхронный обмен данными можно элементарно построить на быстрых синхронизациях и разделяемой памяти.
     
  15. RET

    RET Well-Known Member

    Публикаций:
    17
    Регистрация:
    5 янв 2008
    Сообщения:
    808
    Адрес:
    Jabber: darksys@sj.ms
    RtlRemoteCall , да она так же работает, как и обычное внедрение.
    NTSTATUS
    NTAPI
    RtlRemoteCall(
    IN HANDLE Process,
    IN HANDLE Thread,
    IN PVOID CallSite,
    IN ULONG ArgumentCount,
    IN PULONG Arguments,
    IN BOOLEAN PassContext,
    IN BOOLEAN AlreadySuspended
    );
    NtSuspendThread - >NtWriteVirtualMemory-> NtSetContextThread->NtResumeThread
     
  16. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    410
    Для чего создавать окно? Можно через PostThreadMessage послать очереди сообщений потока, но это асинхронная функция. Используя WaitForSingleObject можно сделать синхронный вызов через PostThreadMessage и QueueUserApc.
    Передать адрес функции можно через командную строку, переменные среды, пайпы и т.д.
    Используя маршалинг можно сделать вообще как обычный вызов метода COM.
    Также можно юзать RPC, вот пример
     
  17. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    410
    Можно сделать вручную через объекты-события для синхронизации. Вот небольшой пример "вызова" функции из процесса родителя (без проверок ошибок):
    Родитель:
    Код (C++):
    1. void foo();
    2.  
    3. struct tSharedMem {
    4.     HANDLE hEvents[2];
    5. };
    6.  
    7. int _tmain(int argc, _TCHAR* argv[])
    8. {
    9.     HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(tSharedMem), L"mymap");
    10.     tSharedMem *pShared = (tSharedMem*)MapViewOfFile(hMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, sizeof(tSharedMem));
    11.     SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
    12.  
    13.     pShared->hEvents[0] = CreateEvent(&sa, FALSE, FALSE, NULL);
    14.     pShared->hEvents[1] = CreateEvent(&sa, FALSE, FALSE, NULL);
    15.  
    16.     STARTUPINFO si;
    17.     PROCESS_INFORMATION pi;
    18.  
    19.     GetStartupInfo(&si);
    20.  
    21.     CreateProcess(L"child.exe", NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
    22.  
    23.     CloseHandle(pi.hProcess);
    24.     CloseHandle(pi.hThread);
    25.  
    26.     BOOL bState = TRUE;
    27.  
    28.     while (bState) {
    29.         switch (SignalObjectAndWait(pShared->hEvents[0], pShared->hEvents[1], INFINITE, FALSE)) {
    30.         case WAIT_OBJECT_0:
    31.             foo();
    32.             break;
    33.         default:
    34.             std::cout << "an error occured\r\n";
    35.             bState = FALSE;
    36.         }
    37.     }
    38.  
    39.     CloseHandle(pShared->hEvents[1]);
    40.     CloseHandle(pShared->hEvents[0]);
    41.     UnmapViewOfFile(pShared);
    42.     CloseHandle(hMap);
    43.  
    44.     return 0;
    45. }
    46.  
    47. void foo() {
    48.     std::cout << "called from other process\r\n";
    49. }
    50.  
    Дочерний процесс каждую секунду вызывает foo (тоже без проверок, демонстрационный вариант):
    Код (C++):
    1. struct tSharedMem {
    2.     HANDLE hEvents[2];
    3. };
    4.  
    5. int _tmain(int argc, _TCHAR* argv[])
    6. {  
    7.     HANDLE hMap = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, L"mymap");
    8.     tSharedMem *pShared = (tSharedMem*)MapViewOfFile(hMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, sizeof(tSharedMem));
    9.  
    10.     while (true) {
    11.         Sleep(1000);
    12.         // Вызываем функцию из родительского процесса каждую секунду
    13.         SignalObjectAndWait(pShared->hEvents[1], pShared->hEvents[0], INFINITE, FALSE);
    14.     }
    15.  
    16.     return 0;
    17. }
    18.  
    19.  
     
    Jin X нравится это.
  18. Jin X

    Jin X Active Member

    Публикаций:
    0
    Регистрация:
    15 янв 2009
    Сообщения:
    199
    Адрес:
    Кольца Сатурна
    И разве это будет работать быстро??? И разве это предельно просто?

    В том-то и дело, что асинхронная.
    А используя окно можно сделать просто через SendMessage. Что проще?
    Ну, вообще я думал об этом, только не через FileMapping, а используя именные Event'ы, хотя shared memory всё равно пригодится, чтобы передавать параметры или тупо номер функции. Ну или запускать разные функции, используя несколько Event'ов, ожидая их в родительской проге через WaitForMultipleObjects.

    Вариант с COM интересен, но опять же, это не 2 строчки кода для дочки, а описание интерфейса (особенно приятно делать его в MASM32) + всякие там CoInitialize, CoCreateInstance... Хотя, бесспорно очень универсальный вариант.
    Но будет ли это работать быстро при межпроцессовом взаимодействии?

    Читаю "LPC", думаю: "Что это?"
    Какой именно Native IPC? Конкретный вариант, плиз. Типа того, что привёл Thetrik?

    Ещё раз повторюсь, что простота (причём, предельная простота, буквально из пары строк) – это важнейший критерий. И скорость тоже.
    Короче, надо померить скорость SendMessage, пока что это самое простое...
     
    Последнее редактирование: 14 фев 2018
  19. Jin X

    Jin X Active Member

    Публикаций:
    0
    Регистрация:
    15 янв 2009
    Сообщения:
    199
    Адрес:
    Кольца Сатурна
    Каким образом система поймёт, что это указатель и сколько нужно передать данных? Каковы при этом накладные расходы (скорость)?
     
  20. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    410
    Без окна.