致电"主要"在Windows中以编程方式运行

时间:2017-01-05 12:09:37

标签: c++ main portable-executable systems-programming entry-point

我有第三方控制台应用程序。我需要从我的应用程序运行它,但我不能将它作为一个单独的进程运行(因为我需要使用它的依赖项:手动填充导入表,设置挂钩等)。所以我可能应该手动调用此可执行文件的main函数。以下是我尝试这样做的方法:

  1. 使用auto hMod = LoadLibrary("console_app.exe")
  2. 加载此EXE
  3. 手动填写此exe的导入表
  4. 获取此EXE的入口点并将其命名为
  5. 我坚持到最后一步。

    以下是我试图调用入口点的方法:

    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 _inittermargc提供argv来电cs:argccs:argv参数后(见下图) enter image description here enter image description here
    3. 我传递给我的主控制台应用程序的CMD参数被转移到 孩子EXE也是。
    4. 您能解释一下_initterm实际做了什么以及CMD参数实际存储在哪里?

2 个答案:

答案 0 :(得分:11)

您正在调用应用程序的入口点,而不是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 *);

MSVCRT DLL代表EXE调用GetCommandLine()

答案 1 :(得分:2)

可执行文件(EP)的入口点没有参数 - 因此您无法使用参数直接调用它。

通常的应用程序通过解析命令行获得参数。 [w]mainCRTStartup执行此操作 - 如果您将控制台应用程序链接到c / c ++运行时 - 这是真实的EP

所以如果你Fill Import table of this exe manually - 为GetCommandLineAGetCommandLineW函数设置例外 - 将其重定向到自我实现并返回自定义命令行。

但如果应用非静态关联CRT,则可以导入__getmainargs__wgetmainargs甚至_acmdln_wcmdln来自msvcrt.dll - 所以任务已经变得复杂了。

并假设relocs退出EXE,如果存在,则不处理TLS,不处理应用程序清单,可能的dl重定向等。

  

但我无法将其作为单独的流程运行

这不是真的。您可以而且必须将其作为单独的流程运行 - 这是最佳解决方案。

使用CREATE_SUSPENDED标记CreateProcess执行您的应用。在这里,您可以轻松设置您需要的任何CommandLine。您不需要手动而不是完全正确加载EXE,但系统会为您执行此任务。

创建流程后,您需要使用QueueUserAPC(但不是DLL !!)向自己注入CreateRemoteThread,最后调用ResumeThread

结果,您的DLL将在应用EXE之前的第一个EP主题中加载并执行 - 此处您可以执行所有必需的任务