我们需要运行一个程序或者软件,双击之即可完成。不过从你双击到程序的窗口产生的这“短暂”的时间内,Windows为你做了很多的工作。

首先,系统有一个进程监测到了你的双击操作,这个进程就是系统shell,没错,就是资源管理器explorer.exe,不是IE浏览器了,那是另一个进程IExplorer.exe。你可以尝试打开任务管理器将这个进程结束掉,然后桌面的一切元素都没有了,任务栏,图标什么的都消失了。只剩下墙纸一张,此时,右键菜单也不复存在···因为平时负责这些东西的explorer.exe已经被你干掉。要恢复的话,在任务管理器中新建任务,运行explorer.exe即可。

系统shell感知你双击的操作后,取得你双击对象的完整路径,然后最终会调用一个叫做CreateProcessA/CreateProcessW的函数来创建一个新的进程。这个函数是ring3上的API函数,在内核中,即ring0里,与之对应的是NtCreateProcess函数来完成创建进程的任务(此为Windows 2000的做法,XP和Win7后稍有差别)。阅读WRK的源码可以知道,NtCreateProcess对参数做一个简单的验证随即转而调用NtCreateProcessEx。再看NtCreateProcessEx的源码,可以发现,真正完成进程创建的是PspCreateProcessa函数。所以说,你所双击而运行的所有进程都是资源管理器的子进程。

懂点进程知识的应该知道,创建进程的几个关键是建立新的运行环境,即建立4GB虚拟地址空间给进程使用,然后创建进程内核对象,以及内核管理进程的数据结构,这包括内核层的KPROCESS和执行体层的EPROCESS。然后是主线程的创建以及相关内核数据结构,同样是内核层的KTHREAD和执行体层的KTHREAD。主线程创建完成后将两个内核对象的句柄即进程ID和主线程ID封装在PROCESS_INFORMATION结构体中,然后CreateProcess函数返回。创建进程过程完成。这一部分主要是内核来完成,大概过程如此,细节过程这里不展开了。

主线程创建后,就得到了时间片,开始参与系统的线程调度。那么程序从哪里开始执行呢。PE文件中有一个OEP的术语描述的便是这个概念,OEP便是指的程序入口点。所谓入口点便是顾名思义就是主线程最开始执行的地方,许多病毒加壳技术其中一点就是对这个OEP进行处理。现在,我们来使用PEID来看一个程序(VC8.0编译)的OEP如图:

0x00011078乃是RVA(相对虚拟地址),要看在进程地址空间中的起始地址,还得加上PE文件的映射基址,默认为0x00400000,不过,可以通过编译器选项进行调整。不知道也没关系,将程序放入OllyDbg,在内存映射中可以看到程序的映射基址:

由图看到映射基址是0x00400000。那么由前面所述,程序执行的第一条指令应该位于0x00400000  +  0x00011078  =  0x00411078。没错,就是这样。切换到OllyDbg的主窗口,我们发现了,程序确实初始停在了这里,并且这里是一条jmp指令。

我们到Jmp的目的地0x00411800去看看那里是什么东东?

这是什么东西?先卖个关子,总之,这里是程序进来之后真正做的第一件事。

换个思路,我们打开VS2008写一个简单的程序,程序做什么并不重要,我们要看它的启动原理。

注意看调用堆栈窗口,因为我是使用UNICODE编码环境,故_tmain()就是wmain(),如果是ANSI编码就是最开始学程序时的main()函数了。以前写程序就想过一个问题,我们写的所有函数都会被我们自己直接或间接调用,但有一个函数例外,那就是main()函数。我们写了它但从不会去调用它,事实上也不可能去调用它,它是我们写来供操作系统调用的。这个说法很笼统,操作系统调用是什么意思?今天就来弄清这个疑问。从调用堆栈看到,我们的wmain函数是被_tmainCRTStartup函数调用的,这是个什么东西?再往前推是wmainCRTStartup调用的_tmainCRTStartup。这两个函数是做什么的,他们之间有什么关系?双击调用堆栈里的项即可转到对应的源代码,我们可以发现,这两个函数是在crtexe.c文件中实现的。阅读源码可以发现,有四个启动函数分别是:

mainCRTStartup()         ANSI  +  控制台程序

wmainCRTStartup()        UNICODE  +  控制台程序

WinMainCRTStartup()      ANSI  +  GUI程序

wWinMainCRTStartup()     UNICODE  +  GUI程序

这一点在《windows核心编程》中也有提到。不过我们可以更进一步一窥它们的实现代码:

就这么简单,先调用了__security_init_cookie(),然后是我们前面看到的_tmainCRTStartup()。

第一个函数是做什么的呢?这个是微软在VS2003后引入的防止缓冲区溢出攻击的技术。简单的说就是在调用函数的时候在栈里安装一个随机的cookie值,这一cookie值在内存的一个地方有备份,函数调用完成后需要检测这个cookie和备份的一不一致,以此来判断有没有栈溢出发生。那么,这个函数就是来初始化这个备份区域的数据的。

然后第二个函数调用_initterm()进行全局变量、对象初始化。之后,我们可以看到才是真正调用了我们的main()/wmain()/WinMain()/wWinMain()的地方。饶了一大圈,回答了开始的疑问了。

这两个函数是链接器在生产可执行文件的时候给我们链接进来的。

至此,我们来看看第一个函数wmainCRTStartup的汇编代码。如图:

请注意和我们前面使用OllyDbg调试时的图对比:

发现没有?一样的!我们之前留的那个问题的答案想必已经出来了,程序一进来从OEP处执行了jmp指令,这条指令转向了wmainCRTStartup开始了程序真正的起点!

小结:编译生成的exe文件,双击运行后,建立新进程的地址空间,然后主线程开始运行,程序一进来通过jmp指令来到前面列出的四个启动函数,它们进行__security_init_cookie操作后便调用最终的启动器_tmainCRTStartup。这个启动器干了几件大事,分别是,使用GetStartupInfo获取进程启动信息,然后使用_inititem初始化全局变量和对象,最后调用我们main、wmain、WinMain、wWinMain进入我们的程序。。。

说明:这里谈到的是使用VC编译器生成的exe文件形态,如果采用其他编译器,甚至直接采用汇编程序情况就不同了。甚至于.net平台的托管程序运行于CLR上,则又是另外一回事了。

【系统篇】从C/C++语言到进程启动背后的故事的更多相关文章

  1. Simpleperf分析之Android系统篇

    [译]Simpleperf分析之Android系统篇 译者按: Simpleperf是用于Native的CPU性能分析工具,主要用来分析代码执行耗时.本文是主文档的一部分,系统篇. 原文见aosp仓库 ...

  2. Android系统init进程启动及init.rc全解析

    转:https://blog.csdn.net/zhonglunshun/article/details/78615980 服务启动机制system/core/init/init.c文件main函数中 ...

  3. iOS开发:(线程篇-上)线程和进程

    iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcod ...

  4. 【朝花夕拾】Android性能篇之(六)Android进程管理机制

    前言        Android系统与其他操作系统有个很不一样的地方,就是其他操作系统尽可能移除不再活动的进程,从而尽可能保证多的内存空间,而Android系统却是反其道而行之,尽可能保留进程.An ...

  5. 海量日志实时收集系统架构设计与go语言实现

    日志收集系统应该说是到达一定规模的公司的标配了,一个能满足业务需求.运维成本低.稳定的日志收集系统对于运维的同学和日志使用方的同学都是非常nice的.然而这时理想中的日志收集系统,现实往往不是这样的. ...

  6. Android应用程序进程启动过程(后篇)

    前言 在前篇中我们讲到了Android应用程序进程启动过程,这一篇我们来讲遗留的知识点:在应用程序进程创建过程中会启动Binder线程池以及在应用程序进程启动后会创建消息循环. 1.Binder线程池 ...

  7. Android应用程序进程启动过程(前篇)

    在此前我讲过Android系统的启动流程,系统启动后,我们就比较关心应用程序是如何启动的,这一篇我们来一起学习Android7.0 应用程序进程启动过程,需要注意的是“应用程序进程启动过程”,而不是应 ...

  8. Android系统篇之—-编写系统服务并且将其编译到系统源码中【转】

    本文转载自:http://www.wjdiankong.cn/android%E7%B3%BB%E7%BB%9F%E7%AF%87%E4%B9%8B-%E7%BC%96%E5%86%99%E7%B3% ...

  9. MySQL 服务正在启动 .MySQL 服务无法启动。系统出错。发生系统错误 1067。进程意外终止。

    MySQL 服务正在启动 .MySQL 服务无法启动.系统出错.发生系统错误 1067.进程意外终止. 检查了一个晚上才发现是---配置问题 #Path to installation directo ...

随机推荐

  1. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  2. 大熊君学习html5系列之------XHR2(XMLHttpRequest Level 2)

    一,开篇分析 Hi,大家好!大熊君又和大家见面了,(*^__^*) 嘻嘻……,这系列文章主要是学习Html5相关的知识点,以学习API知识点为入口,由浅入深的引入实例, 让大家一步一步的体会" ...

  3. Linux C 字符串函数 strlen()、strcat()、strncat()、strcmp()、strncmp()、strcpy()、strncpy() 详解

      strlen(返回字符串长度) 表头文件 #include <string.h> 定义函数 size_t strlen(const char *s); 函数说明 strlen()用来计 ...

  4. html5 form表单常用标签

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. java5

    1:final关键字(掌握) (1)是最终的意思,可以修饰类,方法,变量. (2)特点: A:它修饰的类,不能被继承. B:它修饰的方法,不能被重写. C:它修饰的变量,是一个常量. (3)面试相关: ...

  6. java8

    1:Scanner的使用(了解) (1)在JDK5以后出现的用于键盘录入数据的类. (2)构造方法: A:讲解了System.in这个东西. 它其实是标准的输入流,对应于键盘录入 B:构造方法 Inp ...

  7. FixFFmpeg 修改官方编译的ffmpeg能在 XP 上运行的工具

    把 fixff.cmd 和 FixFFmpeg.exe 拷贝到 ffmpeg 所在的目录 运行 fixff.cmd 自动修复; fixffmpeg-20160924.7z

  8. C++库(Google Breakpad)

    Google Breakpad是什么? 一个开源的多平台崩溃报告系统. Google breakpad是一个非常实用的跨平台的崩溃转储和分析模块,它支持Windows,Linux和Mac和Solari ...

  9. [转]odoo常用openerp-server.conf配置参数详解

    参数 说明 用法 addons_path addons模块的查找路径,多个路径用逗号分隔 addons_path = E:\GreenOdoo8.0\source\openerp\addons csv ...

  10. Delphi:与VCL同步(Synchronize()、用消息来同步)

    看本文时,可以同时参考:Delphi中线程类 TThread实现多线程编程(事件.临界区.Synchronize.WaitFor……) 先说一下RTL和VCL RTL(Run-Time library ...