首页
社区
课程
招聘
[旧帖] [讨论]源码中可执行程序的真正入口点 0.00雪花
发表于: 2012-8-10 16:50 1517

[旧帖] [讨论]源码中可执行程序的真正入口点 0.00雪花

2012-8-10 16:50
1517
写windows程序时,都是先写个main()函数,然后再写自己的逻辑;然后编译,然后点击exe就能运行我们的程序了。其实我们所写的以main()函数开始的程序都是一个半成品,剩下的也是与系统息息相关的工作由编译器帮我们代劳了。
我们写的程序在编译器编译成为一个模块(可能是obj文件或其他形式),然后连接器会将一些所需要的库文件和刚才编译器生成的文件进行连接,最终生成一个exe文件,在所连接的库文件中就包含CRT运行时库,这就是我们今天谈论的主角。在运行时库里面有好一个已经定义如下的函数函数:

(1)mainCRTStartup(或 wmainCRTStartup) //使用 /SUBSYSTEM:CONSOLE 的应用程序

(2)WinMainCRTStartup(或 wWinMainCRTStartup)//使用 /SUBSYSTEM:WINDOWS 的应用程序

(3)_DllMainCRTStartup            //调用 DllMain(如果存在),DllMain 必须用 __stdcall 来定义

其中w开头的函数时unicode版本的,分割符‘//’后面的是入口点函数匹配的subsystem属性设置。

如果未指定 /DLL 或 /SUBSYSTEM (也就是subsystem选项)选项,则链接器将根据是否定义了 main 或 WinMain 来选择子系统和入口点。 函数 main、WinMain 和 DllMain 是三种用户定义的入口点形式。

在默认情况下,如果你的程序中使用的是main()或_main()函数,这连接器会将你的使用(1)中的函数连接到你的exe中;如果你的函数是以WinWain()函数开始的则连接器使用(2)中的函数连接进exe中;如果我们写的是DLL程序这连接进DLL的是(3)中的函数。

用我们写的程序最终生成的exe执行时,一开始执行的就是上面的函数之一,而不是我们程序所写的main或WinMain等。那么连接器为什么要这样做呢?这就是因为我们写的程序必须要使用到各种各样的运行时库函数才能正常工作,所有在执行我们自己写程序之前必须要先准备好所需要的一切库,噢,明白了吧,之所以要连接它们是因为他们肩负着很重要的使命,就是初始化好运行时库,准备我们的程序执行时调用。

修改入口点方法:proerties->Linker->Advanced->EntryPoint

入口函数与连接器的SubSystem的属性要一致的:proerties->Linker->System->SubSystem

如果未指定 /DLL 或 /SUBSYSTEM 选项,则链接器将根据是否定义了 main 或 WinMain 来选择子系统和入口点。 函数 main、WinMain 和 DllMain 是三种用户定义的入口点形式。

在WinMain函数被执行之前,有一系列复杂的加载动作,还要执行一大段启动代码。运行程序MyApp.exe时,操作系统的加载程序首先为进程分配一个4GB的虚拟地址空间,然后把程序MyApp.exe所占用的磁盘空间作为虚拟内存映射到这个4GB的虚拟地址空间中。一般情况下,会映射到虚拟地址空间中0X00400000的位置。加载一个应用程序的时间比一般人所设想的要少,因为加载一个PE文件并不是把这个文件整个一次性的从磁盘读到内存中,而是简单的做一个内存映射,映射一个大文件和映射一个小文件所花费的时间相差无几。当然,真正执行文件中的代码时,操作系统还是要把存在于磁盘上的虚拟内存中的代码交换到物理内存(RAM)中。但是,这种交换也不是把整个文件所占用的虚拟地址空间一次性的全部从磁盘交换到物理内存中,操作系统会根据需要和内存占用情况交换一页或多页。当然,这种交换是双向的,即存在于物理内存中的一部分当前没有被使用的页也可能被交换到磁盘中。

    接着,系统在内核中创建进程对象和主线程对象以及其它内容。

    然后操作系统的加载程序搜索PE文件中的引入表,加载所有应用程序所使用的动态链接库。对动态链接库的加载与对应用程序的加载完全类似。

    再接着,操作系统执行PE文件首部所指定地址处的代码,开始应用程序主线程的执行。首先被执行的代码并不是MyApp中的WinMain函数,而是被称为C Runtime startup code的WinMainCRTStartup函数,该函数是连接时由连接程序附加到文件MyApp.exe中的。该函数得到新进程的全部命令行指针和环境变量的指针,完成一些C运行时全局变量以及C运行时内存分配函数的初始化工作。如果使用C++编程,还要执行全局类对象的构造函数。最后,WinMainCRTStartup函数调用WinMain函数。

   WinMainCRTStartup函数传给WinMain函数的4个参数分别为:hInstance、hPrevInstance、lpCmdline、nCmdShow。

    hInstance:该进程所对应的应用程序当前实例的句柄。WinMainCRTStartup函数通过调用GetStartupInfo函数获得该参数的值。该参数实际上是应用程序被加载到进程虚拟地址空间的地址,通常情况下,对于大多数进程,该参数总是0X00400000。

    hPrevInstance:应用程序前一实例的句柄。由于Win32应用程序的每一个实例总是运行在自己的独立的进程地址空间中,因此,对于Win32应用程序,WinMainCRTStartup函数传给该参数的值总是NULL。如果应用程序希望知道是否有另一个实例在运行,可以通过线程同步技术,创建一个具有唯一名称的互斥量,通过检测这个互斥量是否存在可以知道是否有另一个实例在运行。

    lpCmdline:命令行参数的指针。该指针指向一个以0结尾的字符串,该字符串不包括应用程序名。

    nCmdShow:指定如何显示应用程序窗口。如果该程序通过在资源管理器中双击图标运行,WinMainCRTStartup函数传给该参数的值为SW_SHOWNORMAL。如果通过在另一个应用程序中调用CreatProcess函数运行,该参数由CreatProcess函数的参数lpStartupInfo(STARTUPINFO.wShowWindow)指定。

3帖齐全,坐等会员

[课程]FART 脱壳王!加量不加价!FART作者讲授!

收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 1737
活跃值: (110)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
对于新手很有参考价值,但是,我想知道的是,启动代码里面的函数介绍,他们的功能是怎样的?
2012-10-3 10:44
0
雪    币: 17
活跃值: (3498)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
从基础学习学习,
2012-10-3 20:21
0
游客
登录 | 注册 方可回帖
返回
//