第2章 Windows核心概念

第2章 Windows核心概念

对Windows的工作原理了解越深入,从Sysinternals工具中获得的价值就越多。为了帮助大家更好地理解一些常被误解的话题,本章将概括介绍几个与各种Sysinternals工具密切相关的Windows概念。《Windows Internals》(Microsoft Press,2012年出版)[1]是目前对Windows核心操作系统组件介绍得最为完善和全面的图书。你正在阅读的这本书只是对Windows内存管理等复杂概念进行了最为浅显的介绍,毕竟本书的主题是Sysinternals工具而非Windows,在深度和详细程度上无法比拟《Windows Internals》。另外本书也没有详细介绍Windows架构或其他基本概念,本书会假设你已经了解这些内容,例如“注册表是什么”,或“TCP和UDP有什么区别”。

本书涵盖的话题以及这些话题适用的工具包括:

  • 管理权利,以及如何使用管理权利运行某个程序(适用于大部分工具)
  • 进程、线程和作业(Process Explorer、Process Monitor、PsTools、VMMap、ProcDump、TCPView、RAMMap)
  • 用户模式和内核模式(Process Explorer、Process Monitor、Autoruns、VMMap、ProcDump、DebugView、LiveKd、TCPView、RAMMap、LoadOrder)
  • 句柄(Process Explorer、Handle)
  • 应用程序隔离(Process Explorer、Process Monitor、AccessChk、WinObj、Sysmon、PsGetSid)
  • 调用栈[2](Call stack)和符号(Symbol),包括调用栈是什么,符号又是什么,以及如何在Sysinternals工具中配置符号(Process Explorer、Process Monitor、VMMap)
  • 会话、窗口站(Window station)、桌面,以及窗口消息(Window message)(Process Explorer、Process Monitor、PsExec、AdInsight、Desktops、LogonSessions、WinObj、RegJump)

管理权利

为保护敏感系统资源防止修改或披露给未经授权的实体,Windows NT始终提供了丰富的访问控制模型。在这个模型中,用户账户通常可获得管理权利或用户权利。管理员可以完整不受限制地访问计算机以及所包含的全部资源,而用户则会受到限制而无法更改操作系统配置,也无法访问属于其他用户的数据。然而由于一些历史原因,直到最近,Windows计算机上的最终用户依然会经常获得管理访问权利,因此很多用户并不清楚这两种权利之间存在差别(时至今日,就算Windows 10计算机上创建的第一个本地用户账户依然是Administrators组的成员)。

注意

 

就算没有明确包含在Administrators组中,如果能够配置或控制在更高权限上下文中运行的软件,那么用户依然等同于对计算机有了管理控制权。例如允许用户控制被管理员或服务使用的系统级文件或注册表位置(类似于Windows Vista之前系统中的Power Users组);为用户提供等价于管理员的特权,如调试、获取所有权、还原,或加载驱动等特权;或启用Windows Installer的AlwaysInstallElevated策略,让任何用户运行的任何MSI文件都使用System账户运行。

过去多年来,希望提高安全性并降低风险的组织都已开始将最终用户全面转换为“非管理员”模式。随着Windows Vista中开始引入用户账户控制(UAC),包括Administrators组成员在内的用户运行的大部分程序都在以普通用户的权利运行,不再使用管理权利。然而有时候又必须使用管理员权利运行某些程序。

很多Sysinternals工具需要具备管理权利,但大部分工具在用户权利下也可以正常使用所有功能。同时还有一些工具可以在标准用户权利下正常运行,但某些功能需要具备管理权利,因此其在使用标准用户权利时会运行在一种部分降级模式下。

如果使用Administrators组成员账户(对于未加入域的计算机,默认情况下只有创建的第一个账户属于Administrators组成员),Backup Operators等高特权组成员账户,或分配了“管理员等价”特权的账户登录运行Windows Vista或更新版系统的计算机,则本地安全机构(LSA)会为该用户创建两个登录会话,每个会话具备一个不同的访问令牌(本书第9章中介绍的LogonSessions工具可以枚举这些会话)。其中一个令牌代表用户的完整权利,以及所有组关系和特权;另一个是经过筛选的令牌,基本上等价于标准用户的权利,其中会禁用所有高特权组并删除高级特权。系统会使用这个筛选后的令牌创建该用户的初始进程,例如Userinit.exe和Explorer.exe,同时该令牌会被所有子进程继承。使用用户的完整令牌启动进程需要首先进行UAC提升,这一过程是由应用程序信息(Appinfo)服务进行协调的。虽然系统中依然提供了Runas.exe命令,但无法调用Appinfo服务,因此其效果与Windows XP中有所不同。如果使用Runas.exe启动一个程序并指定了管理账户,则目标程序将在该账户的“标准用户”版本下运行[3]

可通过下列任何一种方式对新进程触发UAC提升。

  • 程序文件包含的清单指出该程序需要提升。Sysinternals某些始终需要提升的GUI工具,例如Disk2Vhd和RAMMap就包含这样的清单(用户可以用第9章介绍的SigCheck工具查看映像文件的清单)。
  • 用户右键单击程序,从上下文菜单中选择【以管理员身份运行】,借此明确要求对该程序的运行进行提升。
  • Windows自发判断出要运行的是遗留的老版本安装程序(安装程序检测功能默认已启用,但可通过安全策略将其关闭)。
  • 所要运行的应用程序关联了某种需要提升的兼容性模式或Shim[4]

如果已经使用管理令牌运行了父进程,子进程会直接从父进程继承该令牌,无需进行UAC提升。按照惯例,需要管理权利的控制台工具(例如Sysinternals LogonSessions)无需UAC提升,但用户必须从提升后的命令行窗口或Windows PowerShell控制台中运行这样的工具。

一旦触发后,UAC提升的过程可通过3种方式完成。

  • 静默 在无需最终用户介入的情况下完成提升。该选项仅适用于Administrators组成员用户。默认情况下,在Windows 7和更新版系统中,某些Windows命令已经启用了静默提升。用户可以通过安全策略为所有提升请求启用静默提升。
  • 要求得到同意 用户会被询问是否允许提升,并能看到包含是/否选项的对话框(如图2-1所示)。该选项仅适用于Administrators组成员用户,且是这些用户的默认设置(适用于Windows 7中除了默认静默提升之外的其他所有提升)。
  • 要求提供凭据 用户会被要求提供管理员账户的凭据(如图2-2所示)。这是针对所有非管理员账户的默认做法,同时也是非管理员用户唯一可以触发的UAC提升。通过配置安全策略可以让管理员用户也进行这种提升。

请注意,可以通过安全策略禁用标准用户的UAC提升。在配置该策略后,用户需要提升时将收到错误信息。

禁用用户账户控制后,Windows会恢复为类似Windows XP的运行模式。此时LSA不再创建筛选的令牌,Administrators组成员运行的程序将始终具备管理权利。此外系统将不再显示提升提示,但可以通过Runas.exe以管理权利启动程序。这里需要注意的是,禁用UAC的同时会禁用Internet Explorer保护模式,此时Internet Explorer将使用已登录用户的完整权利运行。禁用UAC还会关闭文件和注册表虚拟化,这个功能使得很多在Windows XP上要求具备管理权利的应用程序能够使用标准用户权利运行。对于Windows 8以及更新的系统,禁用UAC后将无法运行“商店”应用[5]

图2-1 Windows 7“要求得到同意”的提升

图2-2 Windows 7“要求提供凭据”的提升

进程、线程和作业

虽然表面上看程序和进程非常类似,但其实它们有着本质不同。程序是一种静态的指令序列,而进程则是执行程序所需资源所在的容器。从最高的抽象程度来看,一个Windows进程可以包含下列内容。

  • 一个名为进程ID(PID)的唯一标识符。
  • 至少一个执行线程。同一个进程中的多个线程可以完整访问该进程容器所引用的全部资源。
  • 一个私有虚拟地址空间,这是一组可供进程存储和引用数据与代码的虚拟内存地址。
  • 一个可执行程序,其中定义了要映射至进程虚拟地址空间的初始代码和数据。
  • 一个清单,列出了到各种系统资源,例如信号灯(Semaphores)、通信端口以及文件的所有已打开句柄。
  • 一个名为访问令牌的安全上下文,其中包含了用户、安全组、特权、UAC虚拟化状态、LSA登录会话ID以及远程桌面服务会话ID等信息。

每个进程还保存了父进程的PID记录,然而如果父进程退出,这些信息并不能更新,因此进程有可能引用已不存在的父进程,或引用被分配了原始父进程PID的另一个进程。不过进程记录父进程的PID只是为了参考。

Windows为这种进程模型提供了一个名为“作业”的扩展。作业对象的主要职责是让一组进程能够作为整体进行统一管理和操作。例如可以使用作业批量终止一组进程,而无须一次终止一个,同时这种做法中发出调用的进程无需知道组中包含了哪些进程。作业对象还可用于对某些属性进行控制,或对进程以及作业相关联的其他进程进行限制。例如作业可以在每处理器或作业范围内,针对用户模式的运行时间和已提交的虚拟内存强制执行限制。Windows Management Instrumentation(WMI)会将自己的提供程序载入到一个由作业控制的单独的主机进程中,借此对内存的消耗和同一时间可运行的WMI提供程序主机进程的总数进行限制。

如上文所述,进程只是一种容器。从技术上来说,实际运行的并非进程而是线程。线程是进程内部Windows安排执行的实体,其中包含下列基本组件。

  • 代表处理器状态的一组CPU寄存器内容,其中包括用于确定线程将要执行的下一条机器指令的指令指针。
  • 两个栈,一个供内核模式执行的线程使用,一个供用户模式执行的线程使用。
  • 一个供子系统、运行时库,以及动态链接库(DLL)使用的,名为线程本地存储(TLS)的专用存储区域。
  • 一个名为线程ID(TIP)的唯一标识符。进程ID和线程ID将通过同一个名称空间生成,因此绝不会重叠。
  • 线程有时有自己的安全上下文,通常多线程服务器应用程序会使用这种上下文模拟所服务客户端的安全上下文。

虽然线程会有自己的执行上下文,但(除了属于该进程的其他资源)进程中的每个线程会共享该进程的虚拟地址空间,这意味着进程中一个线程使用的数据结构无法确保其不被同一进程中的其他线程读取或修改。然而线程无法引用其他进程的地址空间,除非其他进程将自己的部分专用地址空间设置为共享内存部分(在Windows API中这叫做文件映射对象),或除非一个进程有权打开另一个进程以使用跨进程内存功能。

默认情况下线程没有自己的访问令牌,但需要时可以获取令牌,这样每个线程即可在不影响进程中其他线程的前提下模拟不同的安全上下文,包括模拟远程Windows系统中运行的进程。

用户模式和内核模式

为了防止用户应用程序访问或修改重要的操作系统数据,Windows使用了两种处理器访问模式:用户模式和内核模式。System进程之外的其他所有进程都运行在用户模式下(即Intel x86和x64体系结构的Ring 3),而设备驱动和操作系统组件,如执行体(Executive)和内核只能运行在内核模式下。内核模式代表处理器的一种执行模式(x86和x64架构下的Ring 0),可用于访问所有系统内存和CPU指令。通过为底层操作系统软件提供高于用户模式进程的特权,进程可为操作系统的设计者提供必要的基础,确保执行恶意行为的应用程序无法影响系统整体的稳定性。

注意

 

不要将用户模式和内核模式之间的差别与用户权利和管理权利的差别混淆。这里的“用户模式”并不意味着“只具备标准的用户特权”。

虽然每个Windows进程有自己的私有内存空间,但内核模式的操作系统和设备驱动代码将共享同一个虚拟地址空间,该空间也包含在每个进程的地址空间中。操作系统会用处理器读写页面时必须所处的访问模式为每个虚拟内存页面添加标记。系统空间中的页面只能从内核模式访问,用户地址空间的所有页面均可从用户模式访问。

用户模式进程的线程可在调用系统服务时从用户模式切换为内核模式。例如对Windows ReadFile API的调用最终将调用Windows中实际负责处理从文件中读取数据操作的内部例程(Routine)。由于该例程将访问内部系统数据结构,因此必须运行在内核模式下。从用户模式到内核模式的转换是使用一个特殊的处理器指令实现的,该指令会让处理器切换至内核模式的系统服务调度函数。操作系统会执行相应的内部函数,对于ReadFile需要执行的是NtReadFile内核函数。内核服务函数会验证参数并使用安全引用监视器(Security Reference Monitor)执行相应的访问检查,随后才执行所请求的操作。函数执行完成后,操作系统会将处理器重新切换为用户模式。

因此用户模式进程的线程在执行过程中部分时间处于用户模式下,部分时间处于内核模式下,这是一种正常情况。实际上因为大部分图形和窗口系统也运行在内核模式下,承载图形密集型应用程序的进程可能大部分时间都处于内核模式而非用户模式。我们可以在Process Explorer的CPU用量图表中看到这两种模式:图表中的红色部分代表处于内核模式的时间,绿色部分代表处于用户模式的时间。

句柄

内核模式的Windows系统核心是通过Ntoskrnl.exe实现的,其中包含各种子系统,例如内存管理器、进程管理器、I/O管理器以及配置管理器(注册表),这些都属于执行体的一部分。这些子系统中的每一个都需要通过对象管理器定义一个或多个类型(Type),以代表暴露给应用程序的资源。例如配置管理器定义的Key对象可代表打开的注册表键;内存管理器定义的Section对象可代表共享内存;执行体可定义Semaphore、Mutant(互斥(Mutex)的内部名称,可用于互相排斥)以及Event同步对象(这种对象可对操作系统内核子系统定义的基本数据结构进行封装);I/O管理器定义的File对象代表打开的设备驱动资源实例以及文件系统文件;进程管理器可创建Thread和Process对象。每一版Windows都会引入新的对象类型,Windows 7共定义了42个,Windows 8.1定义了46个,Windows 10定义了53个对象类型。若要查看特定版本Windows定义的所有对象类型,可用管理权利运行(第15章中介绍的)WinObj工具,并在Object Manager名称空间中进入ObjectTypes目录。

当应用程序需要使用上述某类资源时,必须首先调用相应的API以创建或打开该资源。例如CreateFile函数可以打开或创建文件,RegOpenKeyEx函数可以打开注册表键,CreateSemaphoreEx函数可以打开或创建信号灯。如果函数操作成功,Windows会在进程的句柄表中为该对象分配一个引用,句柄表由执行体负责维持,并会将新增句柄表项的索引返回给应用程序。

应用程序将使用该句柄值针对资源执行后续操作。为了查询或操作资源,应用程序会将句柄值传递给API函数,例如ReadFile、SetEvent、SetThreadPriority以及MapViewOfFile。通过为句柄表创建索引,系统可以查找句柄引用的对象,并定位相应句柄项,句柄项中包含了到对象的指针。句柄项还存储了进程在打开对象的时刻所具备的访问权,这样即可确保不会让进程针对对象执行无权限的操作。举例来说,如果进程成功打开了一个文件并进行读取访问,但尝试使用该句柄写入文件,则此时函数将失败。

当进程不再需要访问某个对象时,即可释放到该对象的句柄,此时通常会将句柄值传递给CloseHandle API(请注意,某些资源管理器需要使用不同API释放资源)。当进程退出时,所有依然在处理的句柄都将关闭。

应用程序隔离

在Windows Vista之前的Windows系统中,以特定用户身份运行的任何进程可以全面控制以同一个用户身份运行的其他所有进程。Windows Vista引入了一种名为强制完整性控制(MIC)的机制,可根据相对可信度将用户的不同进程区别对待。除了保护提升后的进程,MIC还提供了Internet Explorer、Microsoft Office、Google Chrome,以及Adobe Reader等程序所用的“沙箱”技术的底层基础。

进程的分配和运行需要在某一完整性级别(IL)下进行,完整性级别是一个代表进程可信度的数值。提升后的应用会在高完整性下运行,普通的用户应用在中等完整性下运行,保护模式下的Internet Explorer等低权利进程在低完整性下运行。相应地,每个对象的安全描述符都包含一个完整性标签,其中包含了完整性级别和策略。策略定义了是否允许或拒绝来自完整性更低的进程的“读取”“写入”或“执行”访问请求。如果某个对象不包含该标签,则被视为默认的中等完整性,不允许从完整性更低的进程执行“写入”操作。Windows会为所有进程对象分配一条策略,禁止来自任何完整性更低的进程发起的“读取”和“写入”请求。这样可以保护完整性更高的进程的内存空间不被低完整性进程查看或修改。

若要查看每个进程的完整性级别,可使用本书第3章介绍的Process Explorer,若要查看对象的完整性标签,可以使用本书第9章介绍的AccessChk。

MIC,尤其是低完整性的沙箱无疑可以保护用户防范很多来自互联网的攻击,但这种技术也有局限。尤其是“完整性”是一维的。运行在某一完整性级别下的进程,在面对相同或更高完整性级别的其他进程时无法获得保护。换句话说,在进程A面对进程B可获得保护的同时,进程B面对进程A无法获得保护。

应用容器

微软在Windows 8中提供了一种全新的应用程序模型,这种模型彻底改变了应用程序安全性的处理方式。该模型的目标在于保护用户的数据和隐私,保护企业网络,进一步保护系统完整性,让应用通过一种受控的方式获得完成工作所需的特权。为了实现这些目标,需要在应用程序之间进行保护,这就必须通过强大的应用程序标识和容器机制对应用程序访问系统资源的能力进行限制,同时保护应用程序自己的资源不被其他应用程序访问。这种全新的机制必须足够轻巧,因为用户有可能同时运行成百上千个应用程序。这种想法催生了应用容器这一概念。

应用容器是对Windows安全模型的一种扩展,可以将某一应用相关联的所有进程作为整体进行保护。每个应用会包含绝对唯一的标识,应用的标识会被纳入使用一种全新安全标识符(SID)创建的访问令牌。当一个应用容器内的一个进程请求访问资源时,Windows安全访问检查机制会应用比传统非应用容器进程更严格的规则,只有在资源明确允许访问的情况下才允许访问。

为了对应用添加标识,这种应用模型引入了一种名为AppX的全新打包机制,可将应用的所有资产封装为一个包,并通过发行商的证书进行数字签名。应用的标识可包含应用发行商设置的应用名称,后跟一个下划线,以及发行商标识的哈希。例如Microsoft Office OneNote的标识是Microsoft.Office. OneNote_8wekyb3d8bbwe。借此便可将程序包的标识与发行商的代码签名证书紧密捆绑在一起。Windows使用这样的标识控制对系统中资源的访问。此外Windows会在一种名为应用容器(App Container)的容器中运行该应用相关的进程。

应用容器可包含下列内容。

  • 应用容器的SID,其形式为S-1-15-2-XXXXXXXX,包含在进程令牌中。SID是通过应用的标识以密码学方式推导出的。
  • 零个或多个功能(Capability)SID,其形式为S-1-15-3-XXXXXXXX,包含在进程令牌中。
  • 一个专用的每用户AppData目录,包含供该应用存储信息的子目录,系统可以使用子目录存储有关该应用的信息,此外还有仅在应用运行时加载的专用注册表根键。
  • 一个该应用专用的对象管理器名称空间。
  • 一个用于存储应用二进制文件的专用安装目录,该目录面对用户隐藏,为了防止篡改其中的文件,还会对权限进行限制。

Process Explorer可以通过进程属性对话框的安全选项卡显示运行中应用的应用容器和功能SID。在图2-3所示的界面中可以看到,Microsoft Office OneNote有一个应用容器SID和6个功能SID。

图2-3 应用容器中运行的Microsoft Office OneNote所具备的安全上下文

功能

应用容器内运行的应用对系统的访问极为有限。这种应用处于前端时可以接受输入,可以在屏幕上绘制像素,可以将数据存储在自己的私有数据存储中,除此之外几乎什么都不能做。为了提供更多功能,应用必须能对系统进行更广泛的访问,例如确定用户位置或将文档存储到用户的“文档”文件夹。但大部分应用只需要访问计算机上的部分功能。例如一个简单的秒表应用,就没必要使用计算机的摄像头或与家庭/工作网络上的其他计算机通信。应用模型通过两种方式让应用可以对系统进行更广泛的访问:功能(Capability)和代理(Broker)。

应用模型定义了应用可能需要的多个功能,例如互联网客户端、定位、摄像头。应用需要在自己的AppX包中通过清单文件声明自己需要的功能,这样用户在安装应用前就可以知道这个应用需要访问哪些功能。应用运行过程中,这些功能是通过进程令牌以功能SID的方式代表的。例如除非访问令牌中包含与麦克风有关的功能SID,否则应用容器内运行的应用将无法访问计算机的麦克风。诸如定位等敏感功能不仅要求在清单中声明,同时当首次运行该应用,并且应用尝试访问这样的功能时,还会通过交互式的方法要求用户进行确认。

一些功能是由众所周知的SID代表的,并能通过(本书第7章中介绍的)PsGetSid直接转换为人工易读的名称,但其他很多功能SID无法使用公开提供的接口进行转换。例如在图2-3中,令牌中有四个功能SID已经转换为易读的名称,但另外两个无法转换。访问软件和硬件证书或智能卡的功能通常由SID S-1-15-3-9代表。在上述截图中,最后一个功能SID(以“9977”结尾)对应了摄像头功能。

应用可以使用代理作为声明所需功能的备选方法。代理是一种在应用容器之外运行的进程,通常运行在中等完整性级别下。应用可以调用Windows运行时(WinRT)API以通过代理请求访问受保护的资源,代理将决定是否允许访问并代表应用执行访问。代理决定是否允许访问的最常用方法是使用Authentic User Gesture(AUG)。例如应用可以调用WinRT API以向用户显示File Open选择器,借此让用户选择文件。因为该用户界面运行在应用容器之外,并处于更高完整性级别,应用无法修改其中的内容。如果用户选择了文件,代理会打开文件并将其返回给应用,随后应用即可访问该文件。这种访问的实现并不需要更改文件权限(更改权限会导致文件永远能被访问),而是在应用的进程中为代理打开的文件句柄创建副本。应用关闭这个对象句柄后,除非再次通过代理,否则将无法重新访问这个对象。

应用容器资源

大部分应用需要保存状态和用到的其他数据。因此Windows会在文件系统中为每个应用容器分配专用的目录结构,并分配一个只在应用运行时加载的注册表根键和一个单独的对象管理器名称空间。这些内容配置的权限使得应用容器能够恰当地访问所需内容,且无法访问其他所有应用容器。

Windows会在%LOCALAPPDATA%\Packages下为每个应用容器创建每用户目录结构。例如OneNote的AppData目录位于%LOCALAPPDATA%\Packages\Microsoft.Office.One Note_8wekyb3d8bbwe。图2-4显示了允许OneNote的应用容器SID、用户、Administrators以及System账户访问的容器子目录。其他子目录则包含了该应用的Web缓存、本地状态以及漫游配置。

图2-4 为OneNote的应用容器提供的目录安全描述符为用户和应用容器提供了完整控制权限

AppData目录的Settings子目录包含一个Settings.dat文件,这是该应用的私有注册表根键。图2-5显示了OneNote加载该根键并访问其中所存储注册表数据的过程。这些私有根键的根键名称为\REGISTRY\A{guid},其中GUID是在每次加载根键时动态生成的。下一次启动应用时,注册表根键将使用不同的名称加载。

图2-5 OneNote加载并使用存储在应用容器目录结构中的注册表根键

在对象管理器中,应用容器还有自己专用的Named Objects容器。该容器会在应用启动时创建,并仅在应用运行过程中存在。与其他应用容器资源类似,该容器的权限使得应用容器能够进行必要的访问且无法访问其他应用容器进程。当应用创建诸如互斥等对象时,会创建到自己的Named Objects容器中。借此可防范蹲点(Squatting)攻击,这种攻击中一个进程会使用通常属于其他进程的名称创建对象,意在模拟其他进程进而窃取信息或攻击调用方。

图2-6显示了OneNote的Named Objects容器。如图所示,容器路径为\Sessions\n\App ContainerNamedObjects\SID,其中n是远程桌面服务的会话ID[6],SID为应用容器SID。

图2-6 远程桌面服务会话1中OneNote应用容器的私有对象名称空间

应用容器的访问检查

当应用容器内的进程请求访问一个对象时,除了“传统”非应用容器访问检查序列[7]之外,Windows安全引用监视器还会执行一组稍作修改的检查。不仅调用方必须通过强制完整性检查和酌情进行的访问检查,资源也必须显式允许调用方令牌中的应用容器SID、调用方令牌中的一个或多个功能SID,或所有应用容器访问。就算资源允许Everyone访问,或资源的DACL为空,如果对象的DACL没有同时对上述至少一种内容提供显式访问许可,则访问请求依然会被拒绝。如果调用方未能通过强制完整性检查和“传统的”酌情访问检查,访问一样会被拒绝并且不再执行额外的检查(应用容器的强制完整性检查放宽了一个规则:尽管应用容器在低完整性级别下运行,但依然可以获准访问包含中等完整性标签的对象)。

图2-7 AccessChk显示所有应用容器对Ntdll.dll具备“读取”访问的权限

很多系统级的资源需要允许所有应用容器访问。例如每个进程需要能加载Ntdll.dll。Windows定义了一个全新的常用SID:S-1-15-2-1,该SID代表“APPLICATION PACKAGE AUTHO RITY\ALLAPPLICATION PACKAGES”。图2-7显示了Ntdll.dll允许所有应用容器执行“读取”访问,此外Users、Administrators以及System也可以读取访问。这样应用容器内的进程就可以加载Ntdll.dll,但前提是进程以Users或Administrators成员,以及System或Trusted Installer账户的身份运行。

受保护进程

按照设计,强制完整性控制主要是为了保护应用和用户数据防范低可信度应用的威胁。应用容器按照设计是为了在沙箱应用之间提供保护。这两项机制在设计上均不是为了保护用户进程或数据防范通常以中等完整性级别运行的交互式用户桌面进程。受保护进程按照设计提供了一种截然不同的用途:通过创建壁垒保护进程防范其他用户进程和管理员。

受保护进程最早出现在Windows Vista中。最开始该机制唯一的用途在于,通过对负责处理内容的进程(如Audiodg.exe)可执行的操作进行限制,增加对有版权内容(如高质量音视频媒体内容)进行盗版的技术难度。Windows 8.1大幅增强并完善了受保护进程这一技术。现在该技术的主要目的为保护关键系统进程,例如反恶意软件进程或管理用户凭据等敏感信息的进程。

通常来说,任何具备Debug Programs特权的进程都可以请求对其他任何进程进行任何访问,哪怕目标进程的安全描述符并不允许所请求的访问。例如,调用方可以读取或修改目标进程的内存,注入代码,挂起并恢复线程,并可终止进程[8]。获得管理权利的敌人可以轻松地破坏反恶意软件系统,使用通过Lsass.exe盗取的凭据开展“哈希传递(Pass the Hash)”攻击[9]。受保护的进程改变了这样的访问规则,就算System账户和其他管理员也无法对这些敏感进程执行几乎任何控制或访问。

Windows会根据进程映像文件中的特殊数字签名将某些进程指定为受保护的。一些进程将始终受到保护,例如System进程、Smss.exe、Wininit.exe以及Services.exe。通过确保每个受保护进程的所有祖先(Ancestor)进程同样获得保护,Windows为这种保护创建了信任链。如果映像文件包含特殊签名,则通过配置设置也可以对其他进程,例如Lsass.exe以及反恶意软件进程等其他服务提供这样的保护。

当调用方试图访问被Windows保护的进程时,除非调用方本身也是受保护进程且具备更高保护级别,否则Windows内核最多只能允许少量受限制的权利,调用方无法通过这些权利读写内存或向进程中注入代码。类似的限制也适用于访问受保护进程的线程时发起的请求。此外进程将只加载包含特殊签名的DLL,这样不可信的代码就无法在进程中执行。该技术还可以防止应用程序兼容性Shim引擎将Shim DLL载入进程。

Windows定义了多种类型的受保护进程:

  • PsProtectedSignerAuthenticode
  • PsProtectedSignerCodeGen
  • PsProtectedSignerAntimalware
  • PsProtectedSignerLsa
  • PsProtectedSignerWindows
  • PsProtectedSignerWinTcb

上述每一类进程应用了不同的代码签名限制,包括哪些签名者有权为进程映像文件签名,哪些签名者有权为DLL签名,以及签名所需的哈希算法。每一类进程还会对所允许的访问权利强制应用略有差异的限制。举例来说,假设某进程应用了PsProtectedSigner Authenticode保护,可以为调用方提供PROCESS_TERMINATE权利,但使用PsProtected SignerAntimalware保护的进程无法提供这种权利。

每类保护可被标记为“受保护进程”或“受保护进程轻型(Protected process light)”。相比非轻型,“轻型”变体的保护级别略低,主要用在当一个受保护进程试图访问另一个受保护进程时。我们可以使用本书第3章介绍的Process Explorer工具查看哪些进程受到了保护以及具体的保护类型。

有关受保护进程的详细信息请参阅《Windows Internals》(第6版)合著者Alex Ionescu的下列博客文章:

http://www.alex-ionescu.com/?p=97

http://www.alex-ionescu.com/?p=116

http://www.alex-ionescu.com/?p=146

http://www.nosuchcon.org/talks/2014/D3_05_Alex_ionescu_Breaking_protected_processes.pdf

调用栈和符号

包括Process Explorer、Process Monitor,以及VMMap在内的很多Sysinternals工具会在特定操作中显示所执行代码路径的详细信息,这种信息也叫做调用栈(Call stack)。通过将符号(Symbol)与进程地址空间内的模块结合在一起,可以针对代码路径,尤其是Windows操作系统中的代码获得更有意义的上下文信息。了解调用栈和符号,以及在Sysinternals工具中配置这些内容的方法,将能帮助我们针对进程行为获得更深入的见解,进而更准确地判断各种问题的根源。

调用栈是什么?

进程中的可执行代码通常会被组织为一系列离散函数的集合。为了执行自己的任务,一个函数可以调用另一个函数(子函数)。运行完毕的函数会将控制权交回给调用自己的函数。

图2-8是为了演示这一过程虚构的例子。MyApp.exe中包含一个名为HelperFunctions.dll的DLL,该DLL中包含一个名为EncryptThisText的函数,可对传递过来的文本进行加密。

图2-8 函数调用序列范例

在执行完一定的前序操作后,EncryptThisText调用Crypt32.dll中的Windows API Crypt EncryptMessage。某些时候CryptEncryptMessage需要分配一些内存并调用Msvcrt.dll中的内存分配函数malloc。当malloc完成自己的工作分配了所请求的内存后,执行过程会从CryptEncryptMessage停止的位置恢复。当CryptEncryptMessage完成自己的任务后,控制权会交回给EncryptThisText调用CryptEncryptMessage的位置。

调用栈是一种可以让系统知道如何将控制权返回给一系列调用方,在函数之间传递参数,以及存储本地函数变量的构造。该构造遵循了“后进先出(Last in, first out)”的原则,按照与加入时完全相反的顺序移除项。当一个函数即将调用子函数时,会将从子函数返回后下一个待执行指令的内存地址(即“返回地址”)置于栈最顶层。当子函数调用另一个函数时,会将自己的返回地址添加至栈。在从函数返回时,系统会获取位于栈最顶层的任何地址,并开始从这个位置执行代码。

按照惯例,调用栈中返回地址将显示为module!function+offset的形式,其中module是包含该函数的可执行映像文件名称,offset是从函数开始位置经过的字节数(以十六进制形式表示)。如果函数名不可用,地址将直接显示为“module+offset”。在上文虚构的例子中执行的malloc,其调用栈应该是这样的:

msvcrt!malloc+0x2a
crypt32!CryptEncryptMessage+0x9f
HelperFunctions!EncryptThisText+0x43
MyApp.exe+0x25d8

如上所示,调用栈不仅可以告诉我们所执行的代码,而且可以告诉我们程序是如何运行到这一位置的。

符号是什么?

检查线程在调用栈中的开始地址或返回地址时,通过检查已加载模块列表及其地址范围,调试器可以很轻松地知道线程所属的模块。然而在用编译器将开发者的源代码转换为计算机指令时,并不能保留原始函数名,不过有一个例外,包含导出表的DLL可以列出函数的名称和偏移量并供其他模块使用。然而导出表无法包含库的内部函数名,也无法包含按照设计需要在运行时发现的COM入口点名称。

注意

 

载入用户模式进程的可执行文件通常可能是可由新进程启动的EXE文件,或可载入现有进程的DLL文件。然而EXE和DLL文件并非只能使用这两种文件扩展名。使用COM或SCR扩展的通常也是EXE文件,使用ACM、AX、CPL、DRV以及OCX扩展的通常也是DLL文件。安装程序通常会提取并运行使用TMP扩展的EXE文件。

在创建可执行文件时,编译器和链接器也可以创建相应的符号文件(默认扩展名为PDB)。符号文件包含的各种数据,例如模块中函数的名称和入口点偏移量,这些并非运行可执行代码所必须的,但其能在调试过程中提供极大的帮助。借助这些信息,调试器可以接受各种内存地址并轻松地确定前序地址最接近的函数。如果没有符号,调试器将只能使用导出的函数(如果存在的话),但这些函数可能与所执行的代码没有任何关系。一般来说,返回地址的偏移量越大,所报告函数名称的准确性就越低。

注意

 

在报告调用栈时,Sysinternals工具只能使用原生(非托管)符号文件,无法报告JIT编译的.NET程序集中的函数名。

符号文件必须与相应的可执行文件同时生成,否则内容将不够准确,调试引擎可能会拒绝使用。使用老版本Microsoft Visual C++创建的符号文件只能用于Debug构建,除非开发者明确更改构建配置。新版本现在已经可以为Release构建创建符号文件,并可将符号文件写入可执行文件所在目录。Microsoft Visual Basic 6也可以创建符号文件,但默认无法执行该操作。

符号文件可以包含不同级别的细节信息。完整符号文件(有时候也叫做私有符号文件)通常包含公开符号文件不具备的细节,例如所定义符号在原文件中的路径和行数,函数参数名和类型,以及变量名和类型。对外发布符号文件的软件公司通常发布的只是公开的符号文件,完整符号文件一般只供公司内部使用。

Windows调试工具可以根据需要从符号服务器(Symbol server)下载恰当的符号文件。这些服务器可以存储特定可执行文件多个不同构建对应的符号文件,调试工具会下载与所调试映像相匹配的符号文件(调试工具会使用每个可执行文件的文件头中存储的时间戳和校验信息作为唯一标识符)。

微软提供了可以通过网络访问的符号服务器,并通过该服务器免费提供了适用于Windows的公开符号文件。通过安装Windows调试工具并配置Sysinternals工具使用微软的符号服务器,即可轻松地看到我们的进程所调用的Windows函数。

图2-9显示了Process Monitor捕获的一个事件的调用栈。栈中显示的MSVBVM60.DLL(帧15以及17~21)意味着这是一个Visual Basic 6程序,因为MSVBVM60.DLL是Visual Basic 6的运行时DLL。MSVBVM60帧较大的偏移量意味着该模块的符号不可用,因此这里显示的名称可能并非实际调用函数的名称。帧14显示了在主执行文件(LuaBugs_VB6.exe)中调用了一个名为Form1::cmdCreate_Click的函数。这个帧还显示了源文件路径,意味着这个第三方模块具备完整的符号信息。随后该函数调用了Wshom.ocx中的CWshShell::RegWrite(帧13),意味着这个Visual Basic 6程序使用了一个Windows Script Host ActiveX写入注册表。CWshShell::Reg Write调用了同一模块中的一个内部函数(帧12),随后调用了Kernel32.dll中公开的Reg CreateKeyExA Windows API(帧11)。执行过程通过Kernel32内部函数(帧8~10)传递给Ntdll.dll中的ZwCreateKey原生API(帧7)。至此所有这些函数都在用户模式下执行,这一点可以通过帧列中显示的“U”图标确定,但是从帧6开始程序切换至内核模式,这里显示了“K”图标。内核函数(帧0~6)两个字母的前缀代表函数所属的执行组件,例如“Cm”代表负责注册表的配置管理器(Configuration Manager),“Ob”代表对象管理器(Object Manager)。这个栈跟踪是在处理CmpCallCallBacks(帧0)时捕获的。请注意帧0~13显示的符号信息全部来自Process Monitor按需从微软符号服务器下载的Windows公开符号。

图2-9 Process Monitor调用栈以及来自符号文件的信息

符号的配置

使用符号的Sysinternals工具需要两个信息,如图2-10所示:要使用的Dbghelp.dll位置,以及符号路径。使用完整符号信息显示源文件的Sysinternals工具还需要配置源代码路径。

图2-10 Process Explorer的配置符号对话框

Dbghelp.dll是微软的调试引擎DLL之一,提供了处理调用栈、加载符号文件以及将进程内存地址解析为名称所需的功能。仅Windows调试工具中包含的Dbghelp.dll版本可以支持从符号服务器下载文件。Windows中自带的,位于%SystemRoot%\ System32目录下的Dbghelp.dll文件只能使用本地存储的符号文件。在首次运行时,Sysinternals工具会检测调试工具的默认安装位置,并使用在这个位置找到的Dbghelp.dll,如果没找到则会使用位于%SystemRoot%\System32的默认版本。

Windows调试工具可从http://www.microsoft.com/whdc/devtools/debugging/default.mspx下载。我们可以下载独立版本的调试工具安装程序,或通过Windows SDK安装。为了获得该调试工具,必须运行SDK安装程序,并选择所需的调试工具选项。对于再发行版的调试工具,可以分别针对x86、x64,以及IA64体系结构下载独立版的调试工具。通过再发行版安装程序可以很方便地在没有安装完整SDK的计算机上安装调试器。

符号路径可以告诉调试引擎,如果没有在默认位置找到符号文件,该去哪里寻找这些文件。调试引擎首先会在可执行文件的目录以及符号文件最初的创建位置(如果可执行文件包含这些信息的话)这两个默认位置查找,如果没找到则会检查我们指定的符号路径。

符号路径可以包含文件系统路径和符号服务器位置。首次运行时Sysinternals工具会将自己的符号路径设置为_NT_SYMBOL_PATH环境变量的值。如果未定义该变量,工具会将自己的符号路径设置为srv*https://msdl.microsoft.com/download/symbols,这样即可使用微软公开的符号服务器,但不会将下载的符号文件保存在本地缓存中。

文件系统目录和符号服务器位置可同时包含在符号路径中,并使用分号进行分隔。工具将按照在路径中出现的顺序依次搜索每个路径。上文曾经提过,符号服务器位置使用了srvDownstreamStoreSymbolServer这样的形式,例如对于下面这样的符号路径:

C:\MySyms;srv*C:\MSSymbols*https://msdl.microsoft.com/download/symbols

调试引擎会首先搜索默认位置,随后搜索C:\MySyms,通常建议将我们自己应用程序的私有符号文件保存在这里。如果都没找到所需符号文件,引擎会搜索C:\MSSymbols,如果依然没找到,最后将查询符号服务器。如果符号服务器上提供了所需文件,调试引擎会将其下载至C:\MSSymbols。

有关符号路径、符号服务器、源代码路径以及调试引擎所用环境变量的详细信息请参阅调试工具的文档。

 

窍门 如果只需要微软提供的公开符号,可将符号路径设置为: srvc:\symbolshttps://msdl.microsoft.com/download/symbols 这样调试引擎就可以优先搜索C:\Symbols下的缓存,并在需要时从微软的公开符号服务器下载符号文件,下载的内容可保存在缓存中,下次使用无需重新下载。如果目录不存在,调试引擎会自动创建C:\Symbols。

会话、窗口站、桌面和窗口消息

Process Explorer、Process Monitor、PsExec、AdInsight、Desktops以及LogonSessions等Sysinternals工具的描述中都提到了会话、会话ID、控制台会话、会话0、交互式和非交互式窗口站(Window station)以及“同一桌面”上运行的其他程序。虽然这些概念并不那么广为人知,但对Windows平台各种问题的解决非常关键。

先来概括看看图2-11所示的层次结构,随后本节将定义并解释这些内容。最外层是远程桌面服务(RDS)会话,也叫做终端服务(TS)会话。每个对话包含一个或多个窗口站,窗口站中包含了桌面。这些安全对象每个都分配了专用的资源,同时这些桌面与LSA创建的登录会话之间存在松散的关系。虽然很多有关Windows的文档并未清晰界定LSA登录会话和RDS会话之间的差别,但它们实际上是截然不同的实体。

图2-11 会话、窗口站以及桌面之间的关系

远程桌面服务会话

远程桌面服务可以让一台计算机支持多个交互式用户会话。该技术最早出现在Windows NT 4.0 Terminal Server Edition中,但直到Windows XP开始才包含在Windows客户端操作系统内。该技术支持的功能包括快速用户切换、远程桌面、远程协助、远程应用程序本地集成(RAIL,即RemoteApps)以及虚拟机集成功能。Windows客户端系统(Windows XP、Windows Vista、Windows 7、Windows 8.x以及Windows 10)中的这个技术有个重要局限,即同一时间只允许使用一个交互式会话。也就是说,在同时打开的多个已断开会话中,进程依然可以继续运行,但只有一个会话可以更新显示设备上显示的内容并处理键盘和鼠标的输入操作。此外这个局限还进一步体现在:加入域的Windows XP计算机最多只能支持一个交互式会话。举例来说,如果用户登录到控制台,你可以使用同一个账户通过远程桌面登录到该计算机并继续运行该会话,但除非第一个用户先注销,否则无法使用其他用户账户登录。

远程桌面服务会话可通过一个始于会话0,数值持续增加的会话ID加以识别。为了在会话之间实现隔离,对于编号为1以及大于1的会话,Windows会在对象管理器中为每个会话定义一个全局名称空间,以及一个会话专用的“本地”名称空间。这个全局名称空间承担了会话0中进程的本地名称空间(WinObj可以通过图形化视图显示对象管理器名称空间,本书第15章将详细介绍该功能)。

系统进程和Windows服务始终运行在远程桌面服务会话0中。在Windows XP和Windows Server 2003中,登录到计算机的第一个交互式用户也将使用会话0,因此可以使用与服务相同的本地名称空间。Windows XP和Windows Server 2003只在需要时创建会话1以及后续会话,如果在第二个用户登录前第一个用户已注销,则第二个用户也将使用会话0。因此在加入域的Windows XP系统中只存在会话0。

在Windows Vista以及更新的系统中,服务将运行在会话0中,但出于安全原因所有交互式用户会话都运行在会话1以及后续会话中。这种对用户进程和系统进程进行的进一步分隔机制也叫做会话0隔离。

 

注意 “控制台会话”这个称呼有时会被误解为会话0的同义词。控制台会话是指与本地连接的键盘、显卡以及鼠标相关联的远程桌面服务会话。如果一台计算机上的所有活跃会话都是远程桌面会话,那么控制台会话将保持连接并显示登录界面。在Windows XP/Windows Server 2003上这个会话可能是,也可能不是会话0,但在Windows Vista以及更新的系统中绝对不是会话0。

窗口站

每个远程桌面服务会话包含一个或多个命名窗口站。窗口站是一种安全对象,其中包含一个剪贴板、一个原子表(Atom table)[10]以及一个或多个桌面。每个进程都需要关联至一个窗口站。在会话内部,仅名为WinSta0的窗口站可以显示用户界面或接收用户输入。在会话1和后续会话中,Windows仅创建一个WinSta0窗口站(如图2-12所示)。在会话0中,除WinSta0之外Windows还会为关联了服务的每个LSA登录会话创建一个单独的窗口站,并将登录会话的本地唯一标识符(LUID)包含在窗口站的名称中。例如以System身份运行的服务进程位于Service-0x0-3e7$窗口站中,以Network Service身份运行的服务进程位于Service-0x0-3e4$窗口站中。这些窗口站无法显示用户界面或接收用户输入。

图2-12 WinObj显示了会话2的私有名称空间中包含的交互式窗口站

PsExec -s cmd.exe可在Service-0x0-3e7$窗口站中运行命令行界面并将其控制台I/O重定向至PsExec。PsExec的-i选项可供我们指定远程桌面服务会话,并在其WinSta0窗口站中运行目标进程。本书第7章将介绍PsExec。

配置为以System身份运行的服务也可以配置为允许服务与桌面交互。这样配置后的服务将在会话0的WinSta0窗口站,而非Service-0x0-3e7$窗口站中运行。如果交互式用户也位于会话0中,那么通过这种方式即可让服务通过界面显示和键盘鼠标等输入操作与最终用户直接进行交互。现在看来这并不是一种好做法,下面将介绍原因。微软也意识到这样做的问题,因此在使用会话0隔离后这种做法完全不可行了(Interactive Services Detection服务,即UI0Detect提供了一种不完整的缓解措施)。

桌面

每个窗口站可包含一个或多个桌面。桌面是一种提供了逻辑显示界面的安全对象,应用程序可以用窗口的形式在桌面上渲染自己的界面。

 

注意 此处提到的“桌面”与Windows资源管理器外壳名称空间的桌面抽象没有任何关系。此外Windows 10的多桌面功能无法为这里所讨论的“桌面”类型创建新实例,但Sysinternals Desktops工具可以做到这一点。

多个桌面均可包含UI,但同一时间只能显示一个桌面。交互式窗口站中通常有3个桌面:Default、Screen-saver以及Winlogon。Default桌面是用户应用程序默认运行的桌面(Sysinternals Desktops工具最多可以额外创建3个运行应用程序的桌面,本书第11章将详细介绍该工具)。Screen-saver桌面供Windows在启用密码保护的情况下运行屏幕保护。Winlogon桌面也叫做安全桌面,按下Ctrl+Alt+Del后Windows会将控制权交给这个桌面,此外默认情况下UAC提升对话框也会显示在这个桌面上。Winlogon桌面通过权限保护仅限以System身份运行的程序访问,借此可保护涉及密码输入操作的操作安全。

随着将进程关联给窗口站,进程中的每个线程也会关联给该窗口站内的桌面。虽然一个进程中的特定线程也可关联给不同桌面,但通常都会关联到同一个桌面。

Process Explorer(将在本书第3章介绍)和Process Monitor(将在本书第5章介绍)等多个Sysinternals工具可以识别进程所属的会话ID。虽然没有哪个工具可以直接识别进程相关联的窗口站或桌面,但可通过Process Explorer的Handle视图,通过打开的到窗口站或桌面对象的句柄这种方式了解此类信息。例如在图2-13中,Process Explorer显示了一个以System身份在会话0中运行的进程,该进程打开了到\Default桌面以及\Windows\ WindowStations\Service-0x0-3e7$窗口站的句柄。

图2-13 会话0中的一个进程打开了到桌面和窗口站对象的句柄

窗口消息

与控制台应用程序不同,Windows应用程序是事件驱动的。每个创建有窗口对象的线程包含一个可接收消息的队列。这些GUI线程会等待窗口消息的抵达,随后将处理这些消息。这些消息可以告诉窗口需要做什么或发生了什么。例如可以通过消息告诉窗口“自行重绘”“移动到屏幕坐标(x,y)”“自行关闭”“回车键被按下了”“鼠标右键在坐标(x,y)位置被按下了”或“用户正在注销”。

窗口管理器负责协调窗口消息。消息可从同一桌面下运行的任何线程发送至任何窗口,但窗口管理器不允许程序将窗口消息发送给不同桌面上的窗口。Process Monitor的/Terminate/WaitForIdle命令必须从运行目标Procmon实例的同一个桌面上调用,因为它们需要使用窗口消息告诉现有实例自行关闭,并需要通过这种方式确定目标实例已经准备好以窗口消息的形式处理命令。

窗口消息可用于模拟鼠标或键盘操作。RegJump以及Process Monitor和Autoruns的“跳转到”功能就是通过这种方式在注册表中定位到特定键的。由于物理击键操作和GUI程序收到的窗口消息均是某种级别的抽象,因此实际上目标程序无法绝对区分到底是真正按下了键盘上的按键,或是其他程序通过发送窗口消息的方式模拟了这样的操作(不仅仅是Windows,所有窗口化系统均是如此)。

除了32位Windows系统引入了对多线程的支持外,窗口消息的这种架构甚至可以追溯至Windows 1.0时代,因此其中遗留了很多古老的设计思路。尤其是窗口对象不具备安全描述符或访问控制列表。因此让服务在用户的桌面上显示窗口是一种很糟糕的做法,用户程序将可以向以System身份运行的进程所拥有的窗口发送畸形的或特别构造的消息,若能成功利用这种机制将能直接控制这些进程(这种做法通常也叫做粉碎窗口攻击(Shatter attack))。如果用户还不是管理员,此时将能非常容易地提升自己的特权。这也是交互式用户不再登录到会话0的主要原因。

对于Windows Vista以及更新版系统默认模式下的“标准用户”,可以通过UAC提升让使用管理员权利运行的应用程序与非管理员进程在同一个桌面上运行,因此需要通过额外的保护措施降低针对提升后进程所拥有的窗口进行粉碎窗口攻击的风险,这样的机制叫做用户界面特权隔离(UIPI)。

使用UIPI的情况下,窗口管理器在对窗口消息进行协调时将能更改目标的状态(例如按钮点选消息),窗口管理器会对发送该消息的进程与接收该消息的窗口所属进程的完整性级别(IL)[11]进行对比。如果发送方的IL低于接收方,则UIPI会阻止这样的消息。因此RegJump工具和类似的Jump To功能必须至少与注册表编辑器使用同样高的IL执行。此外如果发送方位于应用容器中,那么UIPI只允许这样的信息发送给同一应用容器内的其他窗口。

有关MIC和UIPI的详细信息请参阅Windows Vista完整性机制技术参考资料:http://msdn.microsoft.com/library/bb625964.aspx。


[1] 在撰写本文时,此书的最新版本是《Windows Internals》(第6版),分为上下两部,作者Mark E. Russinovich、David A. Solomon,以及Alex Ionescu(Microsoft Press,2012年出版)。

[2] 译注:按照微软的官方译法,Stack这个词在很多情况下都译作“堆栈”:https://www.microsoft.com/language/zh- cn/Search.aspx?sString=stack&langID=zh-cn,然而“堆”这个字容易被误理解为“Heap”,因此本书统一将Stack译作“栈”。

[3] 在启用UAC的情况下,这一规则存在一个例外情况。除非启用“用户账户控制:用于内置管理员账户的管理员批准模式”这个安全选项,否则UAC令牌的筛选和“管理员批准模式”将不适用于系统内置的Administrator账户。该账户运行的任何程序始终可以获得完整的管理权利。该安全选项默认并未启用,不过内置的Administrator账户默认处于禁用状态。

[4] 译注:Shim是一种小型函数库,用于透明地拦截API调用,修改传递的参数、自身处理操作、或把操作重定向到其他地方。Shim主要用于解决遗留应用程序在新版Windows系统上的兼容性问题。详细介绍可参考:http://www.freebuf.com/news/48878.html。

[5] 译注:“商店”应用是指在Windows 8以上系统中,通过系统中的应用商店安装的所有“Modern”应用。

[6] 有关会话的详细信息请参阅本章“会话、窗口站、桌面和窗口消息”一节。

[7] 针对非应用容器程序执行的访问检查步骤在《Windows Internals》(第6版)上篇(Microsoft Press,2012年出版)第6章“安全性”中有详细介绍。

[8] Debug Programs无疑是一种强大的特权,只能分配给Administrators。一些安全指南建议就算对Administrators也不要指派该特权。但这个建议有些问题,因为这会干扰到合法的管理任务,同时攻击者其实可以很容易地绕过。

[9] 除非启用了Windows 10的凭据保护(Credential Guard)功能。

[10] 有关原子表的详细信息请参阅:https://msdn.microsoft.com/library/windows/desktop/ms649053(v=vs.85).aspx。

[11] 完整性级别在本章上文“应用程序隔离”一节已进行了介绍。

目录

  • 版权
  • 版权声明
  • 内容提要
  • 序言
  • 前言
  • 关于作者
  • 第1部分 入门
  • 第1章 Sysinternals工具入门
  • 第2章 Windows核心概念
  • 第3章 Process Explorer
  • 第4章 Autoruns
  • 第2部分 使用指导
  • 第5章 Process Monitor
  • 第6章 ProcDump
  • 第7章 PsTools
  • 第8章 进程和诊断工具
  • 第9章 安全工具
  • 第10章 Active Directory工具
  • 第11章 桌面工具
  • 第12章 文件工具
  • 第13章 磁盘工具
  • 第14章 网络和通信工具
  • 第15章 系统信息工具
  • 第16章 其他工具
  • 第3部分 排错——“难解之谜”
  • 第17章 错误信息
  • 第18章 崩溃
  • 第19章 挂起和性能迟钝
  • 第20章 恶意软件
  • 第21章 理解系统行为
  • 第22章 开发者排错

相关技术

推荐用户