Звоните «основной» функционировать программно в Windows

У меня стороннее консольное приложение. Мне нужно запустить его из моего приложения, но я не могу запустить его как отдельный процесс (потому что мне нужно работать с его зависимостями: заполнять таблицы импорта вручную, перехватывать настройки и т. Д.). Так что, вероятно, я должен позвонить main Функция этого исполняемого файла вручную. Вот как я пытаюсь это сделать:

  1. Загрузите этот EXE с помощью auto hMod = LoadLibrary("console_app.exe")
  2. Заполните таблицу импорта этого exe вручную
  3. Получить точку входа этого EXE-файла и вызвать его

И я застрял на последнем шаге.

Вот как я пытаюсь вызвать точку входа:

void runMain(HINSTANCE hInst)
{
typedef BOOL(WINAPI *PfnMain)(int, char*[]);

auto imageNtHeaders = ImageNtHeader(hInst);
auto pfnMain = (PfnMain)(DWORD_PTR)(imageNtHeaders->OptionalHeader.AddressOfEntryPoint + (DWORD_PTR)hInst);

char* args[] = { R"(<console_app_path>)", R"(arg1)", R"(arg2)" };
pfnMain(3, args);
}

Оно работает. Но это работает так, как будто здесь нет аргументы.

Где я не прав? Как я могу запустить исполняемый файл внутри мой процесс с аргументами? Благодарю.

Я исследовал как мой конкретный сторонний exe получает аргументы cmd и обнаруживает, что:

  1. Не импортирует GetCommandLine вообще и не называй
  2. После call _initterm вызов argc а также argv аргументы доступны через cs:argc а также cs:argv (см. фотографии ниже)
    введите описание изображения здесь
    введите описание изображения здесь
  3. CMD-аргументы, которые я передаю в главное консольное приложение, передаются в
    дочерний EXE тоже.

Можете ли вы объяснить, пожалуйста, что _initterm на самом деле и где CMD аргументы на самом деле хранятся?

12

Решение

Вы называете точку входа приложения, а не int main(int, char**), Теперь вы, возможно, прочитали, что точка входа в программу на C ++ int main(int, char**) но это всего лишь перспектива C ++.

Перспектива Win32 другая; точка входа является int (*)(void);, Компоновщик Visual Studio ищет int mainCRTStartup(void); и использует это, если вы не укажете другую точку входа с /ENTRY, Реализация по умолчанию mainCRTStartup звонки GetCommandLine() заполнить argv[] перед звонком main(argc,argv), Есть и другие вещи в mainCRTStartup что может случиться: запустить глобальные ctors, инициализировать состояние CRT, …

Конечно, это предполагает, что другая программа была скомпилирована с Visual C ++, но на каком бы языке она не была написана, она должна вызывать GetCommandLine,

Теперь, для вашей проблемы, вот интересное наблюдение: GetCommandLine() возвращает записываемый указатель. Вы можете перезаписать существующую командную строку. Конечно, если вы контролировать таблицы импорта, вы решаете, что GetCommandLine средства. (Помните, как обычно, есть варианты A и W).

Одно предупреждение: MSVCRT не предназначен для инициализации дважды, ни статической версии, ни версии DLL. Так что практически вы не можете использовать это, и это будет больно.

[редактировать]
Ваше обновление показывает звонок _initterm, Это функция MSVCRT, как я уже намекнул. В частности,

/***
*crtexe.c - Initialization for console EXE using CRT DLL
*
*       Copyright (c) Microsoft Corporation. All rights reserved.
*
...
/*
* routine in DLL to do initialization (in this case, C++ constructors)
*/
extern int __cdecl _initterm_e(_PIFV *, _PIFV *);
extern void __cdecl _initterm(_PVFV *, _PVFV *);

DLL-вызовы MSVCRT GetCommandLine() от имени EXE.

11

Другие решения

точка входа исполняемого файла (EP) не имеет аргументов — поэтому вы и не можете напрямую назвать это аргументами.

обычное приложение получило аргументы при разборе командной строки. [w]mainCRTStartup сделать это — если у вас есть консольное приложение, связанное с C / C ++ Runtime — это реально EP ,

так что если вы Fill Import table of this exe manually — установить исключение для GetCommandLineA а также GetCommandLineW функции — перенаправьте его на самореализацию и верните свою пользовательскую командную строку.

но если приложение используется не статично связанный CRT он может импортировать __getmainargs или же __wgetmainargs или даже _acmdln, или же _wcmdln от msvcrt.dll — так уже задача становится сложной.

и вы предполагаете, что перемещение выходит в EXE, ты не справишься TLS если он существует, вы не обрабатываете манифест приложения, возможные перенаправления dl и т. д.

но я не могу запустить его как отдельный процесс

это неправда. Вы можете и должны запускать его как отдельный процесс — это лучшее решение.

запустить приложение CreateProcess с CREATE_SUSPENDED флаг. Здесь вы можете легко установить любую CommandLine, которая вам нужна. вам не нужно вручную и не полностью правильно загрузить EXE но система сделает эту задачу за вас.

после того, как процесс создан, вам нужно внедрить себя DLL к нему с помощью QueueUserAPC (но нет CreateRemoteThread !!) и наконец позвони ResumeThread

как результат ваш DLL будет загружен и выполнен первым EXE нить, непосредственно перед применением EP — и здесь вы можете выполнять все необходимые задачи

2

По вопросам рекламы [email protected]