VS下EXE可执行文件启动代码剖析(5)使用动态运行库的EXE

原创 2017年01月04日 00:41:36

前面看到在使用静态运行库的EXE的启动代码做了很多的工作,下面再来看看使用动态库的EXE的启动代码


使用/mtd选项生产一个EXE,在我们的main函数开头下断,查看调用堆栈可以看到程序在被系统加载完成之后,我们程序的入口由Crtexe.c文件中的__tmainCRTStartup开始

下面是代码


static
int
__tmainCRTStartup(
         void
         );

#ifdef _WINMAIN_

#ifdef WPRFLAG
int wWinMainCRTStartup(
#else  /* WPRFLAG */
int WinMainCRTStartup(
#endif  /* WPRFLAG */

#else  /* _WINMAIN_ */

#ifdef WPRFLAG
int wmainCRTStartup(
#else  /* WPRFLAG */
int mainCRTStartup(
#endif  /* WPRFLAG */

#endif  /* _WINMAIN_ */
        void
        )
{

        __security_init_cookie();

        return __tmainCRTStartup();
}
跟使用静态运行库时一样的   同样先是创建了一个cookier然后 四种情况下调用的是同一个函数__tmainCRTStartup



直接上代码  在代码中注释,相比使用静态库,使用动态库的启动代码只完成了很有限的工作。


__declspec(noinline)
int
__tmainCRTStartup(
        void
        )
{
#ifdef _WINMAIN_
        _TUCHAR *lpszCommandLine;
        STARTUPINFOW StartupInfo;
        BOOL inDoubleQuote=FALSE;

        GetStartupInfoW( &StartupInfo );
#endif  /* _WINMAIN_ */

#ifdef _M_IX86
        /*
         * Enable app termination when heap corruption is detected on
         * Windows Vista and above. This is a no-op on down-level OS's
         * and enabled by default for 64-bit processes.
         */
                           //在XPSP3 VISTA 及之后的版本中增加了一个Heap管理特性,当Heap管理器发现进程中有任何使用中的Heap出现错误的时候,系统终止进程
        if (!_NoHeapEnableTerminationOnCorruption) //如果该全局标准为0表示 需要开启该特性
        {
            HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);   //设置开启
        }
#endif  /* _M_IX86 */

        /*


        __try   //
        {
  
            void *lock_free=0;
            void *fiberid=((PNT_TIB)NtCurrentTeb())->StackBase;  获取栈基址
            int nested=FALSE;
            while((lock_free=InterlockedCompareExchangePointer((volatile PVOID *)&__native_startup_lock, fiberid, 0))!=0)
            {                                                     //如果__native_startup_lock为0 把fiberid的值给它


                if(lock_free==fiberid)
                {
                    nested=TRUE;
                    break;
                }

                Sleep(1000);
            }

            if (__native_startup_state == __initializing)  //在混合模式的情况下如果其他本地地方在初始化本地代码,那么出错,不能同时初始化
            {
                _amsg_exit( _RT_CRT_INIT_CONFLICT);
            }
            else if (__native_startup_state == __uninitialized)
            {
                __native_startup_state = __initializing;
#ifndef _SYSCRT
                if (_initterm_e( __xi_a, __xi_z ) != 0)
                {
                    return 255;                                         //初始化C全局对象
                }
#else  /* _SYSCRT */
                _initterm((_PVFV *)(void *)__xi_a, (_PVFV *)(void *)__xi_z);  

#endif  /* _SYSCRT */ 
            }
            else
            {
                has_cctor = 1;
            }

            /*
            * do C++ constructors (initializers) specific to this EXE
            */
            if (__native_startup_state == __initializing) 
            {
                _initterm( __xc_a, __xc_z );                       //初始化C++全局对象
                __native_startup_state = __initialized;
            }
            _ASSERTE(__native_startup_state == __initialized);
            if(!nested)
            {
                /* For X86, the definition of InterlockedExchangePointer wrongly causes warning C4312 */
#pragma warning(push)
#pragma warning(disable:4312)
                InterlockedExchangePointer((volatile PVOID *)&__native_startup_lock, 0);
#pragma warning(pop)
            }


             */
            if (__dyn_tls_init_callback != NULL &&
                _IsNonwritableInCurrentImage((PBYTE)&__dyn_tls_init_callback))
            {
                __dyn_tls_init_callback(NULL, DLL_THREAD_ATTACH, NULL);            //初始化TLS相关的
            }

            /* Enable buffer count checking if linking against static lib */
            _CrtSetCheckCount(TRUE);

#ifdef _WINMAIN_
            /*
             * Skip past program name (first token in command line).
             * Check for and handle quoted program name.
             */
#ifdef WPRFLAG
            lpszCommandLine = (wchar_t *)_wcmdln;
#else  /* WPRFLAG */
            lpszCommandLine = (unsigned char *)_acmdln;
#endif  /* WPRFLAG */

            while (*lpszCommandLine > SPACECHAR ||
                   (*lpszCommandLine&&inDoubleQuote)) {
                /*
                 * Flip the count from 1 to 0 or 0 to 1 if current character
                 * is DOUBLEQUOTE
                 */
                if (*lpszCommandLine==DQUOTECHAR) inDoubleQuote=!inDoubleQuote;
#ifdef _MBCS
                if (_ismbblead(*lpszCommandLine)) {
                    if (lpszCommandLine) {
                        lpszCommandLine++;
                    }
                }
#endif  /* _MBCS */
                ++lpszCommandLine;
            }

            /*
             * Skip past any white space preceeding the second token.
             */
            while (*lpszCommandLine && (*lpszCommandLine <= SPACECHAR)) {
                lpszCommandLine++;
            }                                                //去掉命令行参数中程序自身路径的部分

#ifdef WPRFLAG
            mainret = wWinMain(
#else  /* WPRFLAG */
            mainret = WinMain(
#endif  /* WPRFLAG */
                       (HINSTANCE)&__ImageBase,
                       NULL,
                       lpszCommandLine,
                       StartupInfo.dwFlags & STARTF_USESHOWWINDOW
                        ? StartupInfo.wShowWindow
                        : SW_SHOWDEFAULT                               //调用我们的main函数
                      );
#else  /* _WINMAIN_ */

#ifdef WPRFLAG
            __winitenv = envp;
            mainret = wmain(argc, argv, envp);
#else  /* WPRFLAG */
            __initenv = envp;
            mainret = main(argc, argv, envp);
#endif  /* WPRFLAG */

#endif  /* _WINMAIN_ */

            /*
             * Note that if the exe is managed app, we don't really need to
             * call exit or _c_exit. .cctor should be able to take care of
             * this.
             */
            if ( !managedapp )
                exit(mainret);

            if (has_cctor == 0)
                _cexit();         //退出代码,

        }
        __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
        {
            /*                                                    //发生异常调用退出函数 终止进程
             * Should never reach here
             */

            mainret = GetExceptionCode();

            /*
             * Note that if the exe is managed app, we don't really need to
             * call exit or _c_exit. .cctor should be able to take care of
             * this.
             */
            if ( !managedapp )
                _exit(mainret);

            if (has_cctor == 0)
                _cexit();
        } /* end of try - except */

        return mainret;
}


使用动态库的EXE的启动代码  除了 初始化全局对象外,并没有做其他实质性的初始化工作,然后就调用了main函数 将执行权限交给了我们

因此可以推断对CRT的初始化工作,运行库的DLL文件中,当Msvcrtxx.DLL被加载到后,DLL的启动代码完成了之前我们分析的,使用静态库的exe的启动代码所做的工作。


于是我在VC\crt\src  下搜索包含_ioinit的文件,发现crtlib.c文件中的_CRTDLL_INIT 函数调用了这个初始化io的函数

__CRTDLL_INIT 开始地方下断  果然在这里


代码

BOOL WINAPI
_CRTDLL_INIT(
        HANDLE  hDllHandle,
        DWORD   dwReason,
        LPVOID  lpreserved
        )
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        /*
         * The /GS security cookie must be initialized before any exception
         * handling targetting the current image is registered.  No function
         * using exception handling can be called in the current image until
         * after __security_init_cookie has been called.
         */
        __security_init_cookie();
    }

    return __CRTDLL_INIT(hDllHandle, dwReason, lpreserved);

很明显就是这个运行库DLL的启动代码了   跟EXE的如出一辙

__declspec(noinline)
BOOL __cdecl
__CRTDLL_INIT(
        HANDLE  hDllHandle,
        DWORD   dwReason,
        LPVOID  lpreserved
        )
{
        if ( dwReason == DLL_PROCESS_ATTACH ) {
            if ( !_heap_init() )    /* initialize heap */   //分配堆

                return FALSE;

            if(!_mtinit())          /* initialize multi-thread */  多线程支持
            {
                _heap_term();       /* heap is now invalid! */

                return FALSE;       /* fail DLL load on failure */
            }

            if (_ioinit() < 0) {    /* inherit file info */  初始IO

                _mtterm();

                _heap_term();       /* heap is now invalid! */

                return FALSE;       /* fail DLL load on failure */
            }

            _aenvptr = (char *)__crtGetEnvironmentStringsA();   获取环境变量

            _acmdln = GetCommandLineA();
            _wcmdln = GetCommandLineW();

#ifdef _MBCS
            __initmbctable();
#endif  /* _MBCS */


            if (_setenvp() < 0 ||   /* get environ info */
                _cinit(FALSE) != 0)  /* do C data initialize */
            {
                _ioterm();          /* shut down lowio */
                _mtterm();          /* free TLS index, call _mtdeletelocks() */
                _heap_term();       /* heap is now invalid! */
                return FALSE;       /* fail DLL load on failure */
            }
            proc_attached++;

        }
        else if ( dwReason == DLL_PROCESS_DETACH ) {

           ..................................   清理工作省略
        }

        else if ( dwReason == DLL_THREAD_ATTACH )
        {...........................................清理工作省略

          }
        return TRUE;
}


一切都是那么的熟悉有木有,所有的初始化工作这个DLL都给做了,EXE安心的调用它提供的库函数就OK了



版权声明:本文为博主原创文章,未经博主允许不得转载。

【VS开发】C++调用外部程序

关于三个SDK函数:WinExec, ShellExecute,CreateProcess的其他注意事项: 【1】定义头文件 必须定义以下两个头文件: [cpp] view ...
  • LG1259156776
  • LG1259156776
  • 2016年08月25日 09:10
  • 1813

在vs下调用exe程序

在vs下调用exe程序
  • xiaotu_dandan
  • xiaotu_dandan
  • 2010年12月08日 14:54
  • 1675

VC中调用和运行其它应用程序(比如使用cmd.exe的功能)

问题提出:          我们先前在cmd.exe中
  • snowbird13
  • snowbird13
  • 2014年11月19日 14:42
  • 1454

在vc中怎么调用exe文件并传递参数

http://www.cnblogs.com/aivdesign/articles/1240429.html 1.调用SHELL HINSTANCE ShellExecute( HWND hw...
  • moyumoyu
  • moyumoyu
  • 2011年09月11日 16:24
  • 14326

C++程序中调用exe可执行文件

[本文整理自:http://blog.csdn.net/moyumoyu/article/details/6767621] 当项目比较大的时候,通常会分开来开发,如果分开的部分只是一些函数或类,整合的...
  • tuling56
  • tuling56
  • 2013年12月27日 14:59
  • 2921

【VS开发】C++调用外部程序

关于三个SDK函数:WinExec, ShellExecute,CreateProcess的其他注意事项: 【1】定义头文件 必须定义以下两个头文件: [cpp] view ...
  • LG1259156776
  • LG1259156776
  • 2016年08月25日 09:10
  • 1813

C/C++执行外部程序(调用外部exe程序)

C中的函数: 1.exec() 函数家族: exec() 家族的函数将会创建一个新的进程来执行程序。(不能设置权限,只是单纯的传值执行exe) _execl, _wexecl _execv, _wex...
  • u011164819
  • u011164819
  • 2016年12月02日 10:53
  • 2737

VS下EXE可执行文件启动代码剖析

当我们在学C/C++语言编程的时候,老师总是跟我们说程序是从Main函数开始的,然而当我第一次用OD分析一个可以执行文件EXE的时候,找这个Main函数就花了我好长时间,后来才知道在Windows下不...
  • wangpengk7788
  • wangpengk7788
  • 2016年12月28日 22:19
  • 295

VS下EXE可执行文件启动代码剖析(2)_mtinit函数

紧接上文,来看看另外一个函数_mtinit,这个函数定义在tidtable.c文件中 /**** *_mtinit() - Init multi-thread data bases * *Purpos...
  • wangpengk7788
  • wangpengk7788
  • 2016年12月30日 05:08
  • 552

DLL动态链接库嵌入exe可执行文件

  • 2013年05月03日 12:34
  • 362KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:VS下EXE可执行文件启动代码剖析(5)使用动态运行库的EXE
举报原因:
原因补充:

(最多只允许输入30个字)