У меня стороннее консольное приложение. Мне нужно запустить его из моего приложения, но я не могу запустить его как отдельный процесс (потому что мне нужно работать с его зависимостями: заполнять таблицы импорта вручную, перехватывать настройки и т. Д.). Так что, вероятно, я должен позвонить main
Функция этого исполняемого файла вручную. Вот как я пытаюсь это сделать:
auto hMod = LoadLibrary("console_app.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 и обнаруживает, что:
GetCommandLine
вообще и не называйcall _initterm
вызов argc
а также argv
аргументы доступны через cs:argc
а также cs:argv
(см. фотографии ниже)Можете ли вы объяснить, пожалуйста, что _initterm
на самом деле и где CMD аргументы на самом деле хранятся?
Вы называете точку входа приложения, а не 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.
точка входа исполняемого файла (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
— и здесь вы можете выполнять все необходимые задачи