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等等