I have a third-party console application. I need run it from my application but I cannot run it as a separate process (because I need to work with its dependencies: fill Import tables manually, setup hooks etc.). So probably I should call main
function of this executable manually. Here is how I'm trying to do this:
- Load this EXE using
auto hMod = LoadLibrary("console_app.exe")
- Fill Import table of this exe manually
- Get entry point of this EXE and call it
And I'm stuck with the last step.
Here is how I'm trying to call entry point:
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);
}
It works. But it works as if there is no arguments.
Where am I wrong? How can I run an executable inside my process with arguments? Thanks.
UPDATE:
I've investigated how my particular third-party exe gets cmd arguments and found that:
- It doesn't import
GetCommandLine
at all and do not call it - After
call _initterm
callargc
andargv
arguments are available throughcs:argc
andcs:argv
(see pictures below) - CMD arguments that I pass to my main console app are transferred to child EXE too.
Can you explain, please, what _initterm
actually do and where CMD arguments are actually stored?
You're calling the entry point of the application, not
int main(int, char**)
. Now you may have read that the entry point of a C++ program isint main(int, char**)
but that's just a C++ perspective.The Win32 perspective is different; the entry point is a
int (*)(void);
. The Visual Studio linker looks forint mainCRTStartup(void);
and uses that, unless you specify another entry point with/ENTRY
. The default implementation ofmainCRTStartup
callsGetCommandLine()
to fill inargv[]
before callingmain(argc,argv)
. There are also other things inmainCRTStartup
which you might want to happen: run global ctors, initialize the CRT state, ...Of course, that's assuming the other program was compiled with Visual C++, but whatever language it's been written in, it must be calling
GetCommandLine
.Now, for your problem, here's an interesting observation:
GetCommandLine()
returns a writeable pointer. You can overwrite the existing command line. Of course, if you control the import tables, you decide whatGetCommandLine
means. (Remember, as usual there are A and W variants).One warning: the MSVCRT isn't designed to be initialized twice, neither the static version nor the DLL one. So practically speaking you can't use it, and that will hurt.
[edit] Your update shows a call to
_initterm
. That's a MSVCRT function, as I already hinted. Specifically,The MSVCRT DLL calls
GetCommandLine()
on behalf of the EXE.