Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call "main" function programmatically in Windows

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:

  1. Load this EXE using auto hMod = LoadLibrary("console_app.exe")
  2. Fill Import table of this exe manually
  3. 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:

  1. It doesn't import GetCommandLine at all and do not call it
  2. After call _initterm call argc and argv arguments are available through cs:argc and cs:argv (see pictures below) enter image description here enter image description here
  3. 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?

like image 689
Dmitry Katkevich Avatar asked Jan 05 '17 12:01

Dmitry Katkevich


People also ask

Who invokes main () function?

In 'C', the "main" function is called by the operating system when the user runs the program and it is treated the same way as every function, it has a return type. Although you can call the main() function within itself and it is called recursion.

Can you call the main function?

Yes, we can call the main() within the main() function. The process of calling a function by the function itself is known as Recursion. Well,you can call a main() within the main() function ,but you should have a condition that does not call the main() function to terminate the program.

Which of the following defines the entry point into a C++ program?

From C/C++ programming perspective, the program entry point is main() function.

How do you write a main function in C++?

Before writing the body of function just declare the function. if we declare the function then we write the definition of function anywhere in the program. We always declare a function above the main function. In a function declaration, we use; in last.


1 Answers

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 is int 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 for int mainCRTStartup(void); and uses that, unless you specify another entry point with /ENTRY. The default implementation of mainCRTStartup calls GetCommandLine() to fill in argv[] before calling main(argc,argv). There are also other things in mainCRTStartup 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 what GetCommandLine 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,

/***
*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 *);

The MSVCRT DLL calls GetCommandLine() on behalf of the EXE.

like image 153
MSalters Avatar answered Sep 20 '22 12:09

MSalters