windows系统

0x00 基本概念

Windows API

​ 定义:是用户模式的系统编程。既指32为也指64为windows编程接口

​ 1按照风格分为c语言风格和com风格,后者主要是加入了面向对象

​ 2从windows8开始,提供了一种名为WinRT的API和运行时,(非Windows RT这个版本的windows),给uwp应用使用的。本质上不是新增原生api,而是对原来的原生api包装了一下。

​ 3.NET Framework 是为了c#等语言开发和运行使用的,包含两个部分:运行时和类库,本质都是对原生api的封装。.NET平台运行的代码成为托管代码。.NET本身也是用户模式的一个框架,和其他原生应用本无区别。

服务、函数、例程

​ 在不同语境下,这三个术语含义不同:

​ 1Windows API函数, Windows API中已经公开的可调用的子例程,例如CreateProcess

​ 2原生系统调用(服务),操作系统中未公开(没有对应的符号表),但是可从用户模式调用的底层服务,例如NtCreateUserProcess

​ 3内核支持函数,window系统内部,只能从内核模式调用的子例程。例如驱动程序可以调用ExAllocatePoolWithTag例程,从windows系统堆中分配内存。

​ 4windows服务,由windows服务控制管理器启动的进程,类似于linux中的at和cron等定时任务。(注册表将windows设备驱动程序定义为windows服务,但是其实不是的)

​ 5动态链接库dll,可调用的子例程相互链接城的二进制文件,可以动态加载。例如Msvcrt.dll, (c运行时库)和Kernel32.dll(windows api子系统库之一)。windoiws用户模式组件和应用程序大量使用了DLL。注意.NET程序集也会编译DLL,但是不能导出非托管子例程。

进程

​ 进程和程序区别,后者是一个静态指令序列,而前者是一种容器,包含执行程序实例时会用到的一系列资源。一个进程包含以下元素:

​ 1一块私有的虚拟地址空间

​ 2一个可执行的程序,定义了可初始代码和数据,映射到虚拟地址空间

​ 3一个已经到打开句柄列表,句柄用于映射各种系统资源,如信号量(semaphore),同步对象,以及文件

​ 4一个安全上下文,用于确定与进程相关的用户、安全组、特权、属性、声明、能力、用户账户控制(uac)、虚拟化状态、访问令牌、相关沙箱信息

​ 5一个进程id,唯一标识符

​ 6至少一个执行线程

​ 对于进程相关详细内容,会在后面的章节展开,这里只要知道,可以通过windows自带的task manager 或者sysinternals工具箱查看即可。

线程

​ 线程是windows调度的实体,一个进程至少有一个线程,线程包含以下要素:

​ 1代表线程状态的一系列cpu寄存器内容(是内容,不是每个线程都有专属寄存器)

​ 2两个,内核模式栈和用户模式栈

​ 3一个供子系统,运行时库和dll使用的,名为线程本地储存(TLS)的私有储存区域

​ 4线程id,唯一标识符

​ 线程有时也会有自己的安全上下文。内核线程切换的开销很大,为了减少开销,window提供列两种机制:纤程(协程)和用户模式线程(UMS user model switch thread)

​ 协程是当执行流需要等待io时,注册等待,主动放弃执行切换到下一个执行流,等下次机会再切换回来,有一个机制来保证唤醒。

作业

​ 作业是一组进程作为整体的资源。用于对进程模型进行扩展。本质是为了弥补windows没有进程树这种模型,不好管理多个进程之间的关系。

虚拟内存

​ windows实现了线性地址空间的虚拟内存系统,让每个进程以为自己能够获得一个极大的私有地址空间,但是这个虚拟的内存空间,和实际的物理内存不一致。由windows配合操作系统负责这种映射,从而保证了不同进程之间互相不操作彼此的内存。

​ 在32位x86系统中,由于地址总线只有32位,因此,能够访问的虚拟地址空间最大为4GB,windows将低2GB给进程作为私有储存,高2GB给内核使用。

​ 在64为系统中,由于可寻址的方位太大了,因此windows只使用了部分地址空间,其中较低的一部分给到进程使用,较高的一部分给内核使用,中间一部分不映射。

​ 物理内存数远远少于进程运行过程中所需要的虚拟内存总数,因此windows会将一部分地址映射到磁盘上,当线程访问到这部分地址时,从磁盘读入到物理内存中。

内核模式和用户模式

​ 为防止用户应用程序访问或者修改操作系统的重要数据,windows将代码运行模式分为两种,内核模式和用户模式。用户应用程序运行在用户模式下,操作系统(系统服务和设备驱动程序)运行在内核模式下。这中区别访问依赖cpu的支持。

​ 内核模式行运行访问所有的内存,以及cpu指令,而用户模式下只允许访问对应的进程内存及部分cpu指令。

​ 由于内核模式可以访问所有内存,包含系统内核,所以windows为了安全起见,加载第三方驱动时需要签名。

​ 用户程序在调用系统服务是,需要从用户模式切换到内核模式。特权模式的切换并不意味着线程的切换。但不论是线程切换还是特权模式切换,cpu开销均很大。

​ 当cpu没有仍和其他线程可执行时,会执行idle进程的线程。通过对idle线程的执行时间占比,可以推出cpu使用率。

虚拟机监控程序

​ 为了提供虚拟化服务,所有现代硬件都提供了虚拟化功能,直接运行的时虚拟机监控软件(hypervisor),由hypervisor运行来宾计算机。因此hypervisor拥有比内核模式更高的特权级。这种特权称为虚拟信任特权级 Vitrual Trust Level。 VTL。

​ hypervisor有两个作用,1运行管理多个来宾实例,2保护并监视单个实例,提供更高特权的保护

​ windows实现了自己的hypervisor HYPER-V,并实现了一系列基于虚拟化的安全性保障(Vitualization Based Security) VBS服务

​ 1Device Guard,通过在更更低级,更高的特权级,校验内核的完整性,提供签名验证

​ 2Hyper Guard 保护内核及虚拟机监控程序有关的数据和代码

​ 3Credential Guard 保护账户的凭据和密文

固件

​ windows需要保证内核安全,内核安全由内核自己和虚拟机监控程序hypervisor保护。内核自己保护自己不太行,因为第三方驱动程序和其同等特权。因为主要依赖hypervisor。那么hypervisor是否能安全的加载并验证,就很重要。而这个是由启动加载程序(boot loader)的职责。但是boot loader是否安全加载,也需要检查,这个是由主板上的固件,以前是bios,现在是uefi的职责。

​ uefi的secure boot就负责检查boot loader和内核的签名,因此,如果内核或者boot loader被修改,就需要关闭secure boot

对象和句柄

​ 在windows中,内核对象是静态定义的对象类型的实例。对象类型由操作系统定义的数据类型,针对该类型操作的函数以及一些属性构成。

​ 开发windows应用程序中,遇见的概念,进程对象,线程对象,文件对象,都是windows创建和管理的内核对象。

对象属性是对象中的数据字段,定义的对象的部分状态。例如进程包含优先级,访问令牌对象的指针。

对象方法是操作对象的手段,可以用读取或者更改对象属性。

​ 对象和普通数据结构的本质差异是,对象内部结构是不透明的,必须通过调用对象方法(或者对象服务)才能获取,操作。

​ windows内部有对象管理器,用来实现以下操作:

​ 1为系统资源提供已于理解的名称。

​ 2跨越进程共享资源和数据

​ 3保护资源免遭未经授权的访问

​ 4引用跟踪,释放不适用的资源

​ windows操作系统中并非所有数据结构都是对象,只有需要共享,保护,命令,并对用户模式程序可见的数据才有必要放到对象中。,只由windows内部使用的数据结构不是对象。

安全性

​ windows的安全功能表现在以下方面:

​ 1为可共享的系统对象提供按情况决定的访问控制

​ 2针对用户和主题,对他们发起的操作执行审核与问责

​ 3登陆时身份验证

​ 4防止未经授权的用户访问其他用户已经不再使用的资源,如空闲内存和磁盘

​ windows的访问控制分为以下3种形式:

​ 1酌情决定的访问控制:根据访问方的安全上下文和被访问方的访问控制列表对比,决定是否放行。

​ 2特权访问控制:有特权的主体可以访问没有预先给他分配访问权限的资源

​ 3强制完整性控制:单独提供的额外安全控制,比如为应用提供的沙箱机制,本来应用对其他文件或者内存有权访问,但是在沙箱里后,只允许访问指定的。

注册表

​ 注册表本质是是一个系统数据库,包含了启动和配置系统所必须的信息,控制windows运行的系统级原件设置,安全数据库,以及每个用户的配置信息,甚至还为内存里的部分数据提供了访问接口(比如加载了那些驱动程序,驱动程序使用了哪些资源等)

​ 大部分注册表键位于系统及的配置根键HKEY_LOCAL_MACHINE下。

Unicode

​ windows内部使用unicode,具体使用的是utf-16le,因此windows api中提供了两种版本 Ansi版和Unicode版,前者本质是转成unicode后使用后者。再次注意unicode是是编码的约定方式,每个字符用哪个编码,但是具体的实现方式多种多样,比如utf-8 utf-16 gbk等等