CreateProcess执行过程(精简)

上传人:z****2 文档编号:185586918 上传时间:2023-02-04 格式:DOCX 页数:5 大小:23.18KB
返回 下载 相关 举报
CreateProcess执行过程(精简)_第1页
第1页 / 共5页
CreateProcess执行过程(精简)_第2页
第2页 / 共5页
CreateProcess执行过程(精简)_第3页
第3页 / 共5页
点击查看更多>>
资源描述
创建进程的过程分成六个阶段,发生于操作系统的三个部分中。 三个部分是:1,Windows客户端即某个应用进程的包括Kernel32.dll在内的动态连接库2, Windows的“执行体”、即内核(确切地说是内核的上层)3, 以及Windows子系统的服务进程Csrss中。 六个阶段是:1. 打开目标映像文件。2. 创建Windows的“执行体进程对象5,也就是内核中的“进程控制块”数据结构。3. 创建该进程的初始(第一个)线程,包括其堆栈、上下文、以及,执行体线程对象”,即 内核中的“线程控制块”数据结构。4. 将新建进程通知Windows子系统。5. 启动初始线程地运行(除非因为参数中的CREATE_SUSPENDED标志位为1而一创 建便被挂起)。6. 在新进程和线程的上下文中完成用户空间的初始化,包括装入所需的DLL,然后开 始目标程序的运行。BOOL WINAPI CreateProcess(入_inLPCTSTR IpApplicationNameA_in_outLPTSTR IpCommandLine,入_inLPSECURITY_ATTRIBUTES lpProcessAttributesR_inLPSECURITY_ATTRIBUTES IpThreadAttributes,入_inBOOL blnheritHandlesA_inDWORD dwCreationFlagsA_inLPVOID lpEnvironment:入_inLPCTSTR lpCurrentDirectory入_inLPSTARTUPINFO lpStartupInfo.A_outLPPROCESS_INFORMATION lpProcessInformationA);A在打开可执行映像之前执行一下步骤:看书上。第一阶段:打开目标映像文件在Win32位API中,创建进程是由CreateProcess()完成的。这实际上是个宏定义,根据 不同的情况定义成CreateProcessA ()或CreateProcessW()之一,这两个函数都在kernel32.dll 中(可以用工具depends观察)。两个函数的区别仅在于字符串的表达,前者采用ASCII字符, 而后者采用“宽字符”、即Unicode。实际上Windows的内部都采用宽字符,所以前者只是把 字符串转换成宽字符格式,然后调用后者。可以在Windows上运行的可执行软件有好几类,处理的方法自然就不一样: Windows的32位.exe映像,直接运行。 Windows的16位.exe映像,启动ntvdm.exe,以原有命令行作为参数。 DOS的.exe、.com、或.pif映像,启动ntvdm.exe,以原有命令行作为参数。 DOS的.bat或cmd批命令文件(脚本),启动cmd.exe,以原有命令行作为参数。 POSIX可执行映像,启动posix.exe,以原有命令行作为参数。 OS/2可执行映像,启动os2.exe,以原有命令行作为参数。这里面最重要的当然是32位的.exe映像,而最后两类现在已经很少见了。从对于除32 位.exe以外的各种映像的处理,读者不妨对比一下Wine对.exe映像的处理,看看这里有着 什么样的相似性。对于32位.exe映像,CreateProcess()首先打开映像文件,再为其(分配)创建一个 “Section、即内存区间。创建内存区间的目的当然是要把映像文件影射到这个区间,不过此时还不忙着映射,还 要看看。看什么呢?首先是看已经打开的目标文件是否一个合格的.exe映像(万一是DLL映 像?)。还要看的事就有点出乎读者意外了,看的是在“注册表”中的这个路径:HKLMSOFTWAREMicrosoftWindows NTCurrentVersionImage File Execution Options 用 depends 可以看到,ntdll.dll 中有个函数 LdrQueryImageFileExecutionOptions(),就是 专门干这个事的。如果上述路径下有以目标映像文件的文件名和扩展名为“键”的表项,例如“image.exe”, 而表项中又有名为“Debugger”的值,那么这个值(一个字符串)就替换了原来的目标文件名, 变成新的目标映像名,并重新执行上述的第一阶段操作。这样做的目的当然是为调试程序提 供方便,但是我们不妨设想:如果黑客或某个木马程序设法在注册表中加上了一个表项?这 时候用户以为是启动了程序A,而实际启动的却是B!。第二阶段:创建内核中的进程对象我们知道,Linux上的每个进程(线程)都有一个“进程控制块”、即task_struct数据结构, 与具体进程/线程有关的绝大部分信息都集中存储在这个数据结构中。而Windows则有所不 同。首先,Windows的进程和线程各有不同的“对象”、即数据结构,从概念上把线程和进程 分离开来。线程是具体的(执行)上下文,是CPU调度的单位和目标,而进程只是若干共享 地址空间和特性(如调度优先级)的线程的集合。于是,进程有进程的数据结构,线程有线程 的数据结构。这就好像是对一组task_struct数据结构“提取公因子”所形成的结果,这个举措 是很好理解的。进一步,Windows又把本可集中存储的的进程数据结构也拆分成好几个对象, 有的在内核中,有的则在用户空间。内核中与进程有关的对象有: EPROCESS。即 struct _EPROCESS,在“Internals”书中也称为“Process Block”。它代表 着 Windows的一个进程,E5表示“Executive”,微软把 Windows内核中的上层称为 “Executive”、以区别于下层的设备驱动和内存管理等成分、一般翻译成“执行体”。“Executive” 也有“管理”、“运行”的意思(所以CEO就是“总裁”)。 KPROCESS。这是EPROCESS内部的一个成分,其名称就叫“Pcb”。 W32PROCESS。下面将要讲到,在用户空间有个Windows子系统”的服务进程csrss。 这个服务进程为系统中的每个Windows应用进程都维持着一个数据结构,其中包含了一些 与窗口和图形界面有关的信息。而对于窗口和图形界面的操作原来也是由csrss在“客户”进 程的请求下实现的。但是,为了提高效率,后来把这部分功能移到了内核中。与此相应,有 关数据结构的一部分也需要移到内核中,就成了 W32PROCESS。既然KPROCESS是EPROCESS 一部分,实际上内核中与进程有关的对象实际上只有两 种,就是EPROCESS和W32PROCESS。不过这里没有包括打开对象表”,那也是每个进程 都有的(Linux内核中的“打开文件表”也在进程控制块的外面)。用户空间与进程有关的对象有:如上所述,把W32PROCESS数据结构移入内核以后,csrss仍需要为每个Windows 进程保持一些别的信息,所以csrss内部仍有按进程的相应数据结构。 PEB(Process Environment Block)、即“进程环境块”。PEB中记录着进程的运行参数、映像装入地址等等信息oPEB在用户空间中的位置是固定的,总是在0x7ffdf000。在Windows 中,用户空间和系统空间的分界线是2GB、即0x80000000,所以PEB在靠近用户空间顶端 的地方。所谓创建内核中的进程对象,实际上就是创建以EPROCESS为核心、为基础的相关数 据结构,这就是系统调用NtCreateProcess()要做的事情,主要包括:分配并设置EPROCESS数据结构。其他相关的数据结构的设置,例如“打开对象表”。为目标进程创建初始的地址空间。对目标进程的“内核进程块”KPROCESS进行初始化。将系统DLL的映像映射到目标进程的(用户)地址空间。将目标进程的映像映射到其自身的用户空间。设置好目标进程的“进程环境块” PEBo映射其他需要映射到用户空间的数据结构,例如与,当地语言支持”、即NLS有关的 数据结构。完成EPROCESS创建,将其挂入进程队列(注意受调度的是线程队列而不是进程队 列)。这里将系统DLL、实际上是ntdll.dll、映射到目标进程的用户空间是很关键的。这是因 为,除别的、主流的功能和作用外,ntdll.dll同时也起着相当于Linux中ELF“解释器”的作 用,也担负着为目标映像建立动态连接的任务。第三阶段:创建初始线程如上所述,进程只是个空架子,实际的运行实体是里面的线程。所以下一步就是创建目 标进程的初始线程,即其第一个线程。与EPROCESS相对应,线程的数据结构是ETHREAD,并且其第一个成分是数据结构 KTHREAD,称为 TCB。此外,就像进程有“进程环境块”PEB 样,线程也有“线程环境块”TEB, KTHREAD结 构中有个指针指向其存在于用户空间的TEBo前面讲过,PEB在用户空间的位置是固定的, PEB下方就是TEB,进程中有几个线程就有几个TEB,每个TEB占一个4KB的页面。NtCreateThread()调用 PspCreateThread(), PspCreateThread()调用 KeInitThread()。这个阶段的操作是通过系统调用NtCreateThread()完成的,主要包括:创建和设置目标线程的ETHREAD数据结构,并处理好与EPROCESS的关系(例如进 程块中的线程计数等等)。在目标进程的用户空间创建并设置目标线程的TEBo将目标线程在用户空间的起始地址设置成指向Kernel32.dll中的BaseProcessStart()或 BaseThreadStart(),前者用于进程中的第一个线程,后者用于随后的线程。用户程序在调用 NtCreateThread ()时也要提供一个用户级的起始函数(地址), BaseProcessStart ()和 BaseThreadStart()在完成初始化时会调用这个起始函数。ETHREAD数据结构中有两个成份, 分别用来存放这两个地址。调用KeInitThread设置目标线程的KTHREAD数据结构并为其分配堆栈和建立执行 环境。特别地,将其上下文中的断点(返回点)设置成指向内核中的一段程序KiThreadStartup, 使得该线程一旦被调度运行时就从这里开始执行。系统中可能登记了一些每当创建线程时就应加以调用的“通知”函数,调用这些函数。检查是否有权创建该线程。第四阶段:通知Windows子系统Windows、确切地说是Windows NT、当初的设计目标是支持三种不同系统的应用软件。 第一种是Windows本身的应用软件,即所谓“Native”Windows软件,这是微软开发Windows NT的真正目的。第二种是OS/2的应用软件,这是因为当时微软与IBM还有合作关系。第 三种是与Unix应用软件相似、符合POSIX标准的软件,那是因为当时美国的军方采购有这 样的要求。于是,在Windows内核上就有了所谓“Windows子系统”、“OS/2子系统”、和 “POSIX子系统”。子系统是由下列几个要素构成的。一、子系统进程csrss.exe。包括了对下列成分和功能的支持:控制台(字符型)窗口的操作。面向控制台/终端的应用本身不支持窗口操作(例如 窗口的移动、大化/小化、遮盖等等),但是又需要在窗口中运行,所以需要有额外的支持。进程和线程的管理。例如弹出一个对话窗,说某个进程没有响应,让使用者选 择是否结束该进程的运行,等等。每个Windows进程/线程再创建/退出时都要向csrss.exe 进程发出通知。 DOS软件和16位Windows软件在(32位)Windows上的运行。其它。包括对当地语言(输入法)的支持。这个进程之所以叫csrss,是“C/S Run-time SubSystem”的意思,csrss是Windows子系统 的服务进程。其实三个子系统都是C/S结构,但是OS/2子系统的服务进程称为os2ss,POSIX 子系统的服务进程称为Psxss。之所以如此,据Internals”说,是因为最初时三个子系统的服 务进程是合在一起的,就叫csrss,后来才把那两个子系统移了出来另立门户,但剩下的还 继续叫csrss。二、内核中的图形设备驱动、即Win32k.sys模块。其功能包括:视窗管理,控制着窗口的显示和各种屏幕输出(例如光标),还担负着从键盘、鼠 标等设备接收输入并将它们分发给各个具体应用的任务。为应用软件提供一个图形函数库。三、若干“系统 DLL”,如 Kernel32.dll、Advapi32.dll、User32.dl 1、以及 Gdi32.dll。上述的第二个要素Win32k.sys原先也是和csrss.exe合在一起的,这部分功能也由服务 进程在用户空间提供。应用进程通过进程间通信向csrss发出图形操作请求,由csrss完成 有关的图形操作。但是后来发现频繁的进程间通信和调度成了瓶颈,所以就把这一部分功能 剥离出来,移进了内核,这就是Win32k.sys。这一来,对于一般的32位Windows应用而言, 留给csrss、或者说必须要通过csrss办的事就很少了。但是,尽管如此,在创建WIndows 进程时还是要通知csrss,因为它担负着对所有WIndows进程的管理。另一方面,csrss在接 到通知以后就会在屏幕上显示那个沙漏状的光标,如果这是个有窗口的进程的话。注意这里向csrss发出通知的是父进程、即调用CreateProcess()的进程,而不是新创建出 来的进程,它还没有开始运行。至此CreateProcess()的操作已经完成,从CreateProcess()返回就退出了 kernel32.dll,回 到了应用程序或更高层的DLL中。这四个阶段都是立足于父进程的用户空间,在整个过程 中进行了多次系统调用,每次系统调用完成后都回到用户空间中。例如,在第二阶段中就调 用了 NtCreateProcess(),第三阶段中就调用了 NtCreateThread(),而整个创建进程的过程包 括了许多次系统调用(有些系统调用属于细节,所以上面并未提及)。现在,虽然父进程已经从库函数CreateProcess()中返回了,子进程的运行却还未开始, 它的运行还要经历下面的第五和第六两个阶段。第五阶段:启动初始线程新创建的线程未必是可以被立即调度运行的,因为用户可能在创建时把标志位 create_ suspended设成了 1。如果那样的话,就需要等待别的进程通过系统调用恢复 其运行资格以后才可以被调度运行。否则现在已经可以被调度运行了。至于什么时候才会被 调度运行,则就要看优先级等等条件了。如前所述,当进程的第一个线程首次受调度运行时,由于线程(系统空间)堆栈的设置, 首先执行的是KiThreadStartup。这段程序把目标线程的IRQL从DPC级降低到APC级,然 后调用内核函数 PspUserThreadStartup()。最后,PspUserThreadStartup()将用户空间 ntdll.dll 中的函数 LdrInitializeThunk()作为 APC 函数挂入APC队列,再企图“返回到”用户空间。所以,在返回用户空间的前夕,就会检查 APC函数的存在并加以执行(如果存在的话)。PspUserThreadStartup()返回KiThreadStartup() 时APC交付。于是,此时的CPU将两次进入用户空间。第一次是因为APC请求的存在而进入用户空 间,执行APC函数LdrInitializeThunk(),执行完毕以后仍回到系统空间。然后,第二次进入 用户空间才是“返回”用户空间。返回到用户空间的Kernel32.dll中的BaseProcessStart()或 BaseThreadStart(),对于进程中的第一个线程是BaseProcessStart。至于用户程序所提供的(线 程)入口,则是作为参数(函数指针)提供给BaseProcessStart()或BaseThreadStart()的,这两个 函数都会使用这指针调用由用户提供的入口函数。第六阶段:用户空间的初始化和DLL的连接用户空间的初始化和DLL的连接是由LdrlnitializeThunk()作为APC函数的执行来完成 的。在应用软件与动态连接库的连接这一点上,我们已经看到,不管是Linux、Windows、 还是Wine,都是一致的,那就是在用户空间完成: Linux的.so模块连接由“解释器”在用户空间完成。“解释器”相当于一个不需要事先连 接的动态库,因为它的入口是固定的。“解释器”的映像是由内核装入用户空间的。 Windows的DLL连接由ntdll.dll中的LdrlnitializeThunk()在用户空间完成。在此之前 ntdll.dll与应用软件尚未连接,但是已经被映射到了用户空间。函数LdrlnitializeThunk。在映 像中的位置是系统初始化时就预先确定并记录在案的,所以在进入这个函数之前也不需要连 接。
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 建筑环境 > 机械电气


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!