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了



版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangpengk7788/article/details/53999213

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

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

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

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

VS如何生成管理员权限打开的exe

C++ vs2008 win7 64编译增加: 项目->右键属性->配置属性->链接器->清单文件->UAC执行级别(requireAdministrator) C# ...
  • qingweiwei1993
  • qingweiwei1993
  • 2016-10-21 11:04:15
  • 98

【抄摘MSDN】常用运行库

抄摘MSDN这篇文章对 .net的webform应用有用,以供查询参考常用运行库 发布日期: 1/4/2005 | 更新日期: 1/4/2005Jay Allen、Mark Davis、Heidi H...
  • godling
  • godling
  • 2008-02-20 22:09:00
  • 716

vs2005打开exe文件查看资源

1、打开软件 2、文件->打开->文件 选择exe 打开 就可以了
  • penglijiang
  • penglijiang
  • 2012-02-27 18:17:16
  • 1724

windows 可执行文件分析

windows可执行文件是什么? 是具有PE文件格特性的文件,例如:.exe、dll、ocx等文件。 注:(这里只是让大家能明了一些,其实,可执行与否,和后缀没有什么关系,后缀只是windows方...
  • he702477275
  • he702477275
  • 2013-01-06 14:11:53
  • 991

例说Exe程序作为DLL进行加载

 作 者: nbw时 间: 2008-07-17,23:05链 接: http://bbs.pediy.com/showthread.php?t=68730例说Exe程序作为DLL进行加载调用第三方e...
  • iiprogram
  • iiprogram
  • 2008-07-31 09:59:00
  • 1441

VS2010静态编译生成exe可执行文件

debug版 1、项目->配置属性->常规->MFC的使用:在静态库中使用MFC 2、项目 -> 配置属性->C/C++->代码生成->运行库 :选择 多线程调试(/MTd)     release版...
  • AMDS123
  • AMDS123
  • 2016-11-22 17:35:30
  • 6782

运行exe找不到DLL,运行库的重要性。

欢迎纠正,大神勿喷。
  • sld30976746
  • sld30976746
  • 2014-05-06 09:40:50
  • 1029

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

前面看到在使用静态运行库的EXE的启动代码做了很多的工作,下面再来看看使用动态库的EXE的启动代码 使用/mtd选项生产一个EXE,在我们的main函数开头下断,查看调用堆栈可以看到程序在被...
  • wangpengk7788
  • wangpengk7788
  • 2017-01-04 00:41:36
  • 481
收藏助手
不良信息举报
您举报文章:VS下EXE可执行文件启动代码剖析(5)使用动态运行库的EXE
举报原因:
原因补充:

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