zoukankan      html  css  js  c++  java
  • Windows Internals 6th chap3

    Page 79 在windows上,trap指当中断或者异常发生时,处理器打断线程的正常执行而跳往预定义好的位置(trap handler, IDT)去执行的机制。

    Page 80 中断和异常的区别:中断是异步事件,可能在任意时间发生,和当前处理器正在执行什么无关,通常由外设产生;异常时同步事件,通常由执行某条cpu指令而产生,可重复,比如内存访问违例,调试器异常,系统调用等。

    Page 81 由于中断或异常当断了当前线程的正常执行,所以必须保存当前线程的状态,以便中断或者异常处理完毕之后继续执行。 当前线程状态保存于当前线程的内核栈上,被称为trap frame。

    Page 81 软件和硬件均可产生中断,硬件中断主要来自外设,软件中断主要用来发起线程调度(DPC),或者异步断入线程的执行(APC)。

    Page 82 外部设备通过中断线连接到PIC,PIC再直接连到CPU的中断线。当发生中断时,CPU通过PIC拿到中断向量,以中断向量作为索引,跳转到IDT中相应的entry。IDT共有256个entry,中断和异常共用。IRQs到中断向量的映射通过对PIC编程做到。 IRQ represents a interrupt controller input.

    Page 86 IRQLs -- Windows上引入的虚拟中断优先级机制,可以说,是虚拟了一个逻辑的中断控制器。x86系统共有32个级别,x64/IA64共16个级别。级别越高代表优先级越高,高优先级的中断可以抢占低优先级的中断。IRQ到IRQL的映射通常由HAL去做。(IRQ也有硬件决定的优先级,映射时会考虑这个吗?In some cases, IRQ and IRQL are directly related. In the PIC, there was a single vector base that the IRQ was added to, producing the vector number interrupted by that IRQ. That means higher IRQ, higher IRQL. In other cases, the concepts are not as directly related. An I/O APIC input can interrupt any processor, at any vector. So a low numbered IRQ can be assigned any arbitrary IRQL in an APIC environment. Different architectures might implement these concepts completely differently.

    Page 90 IRL到IRQL的映射--实现细节,对非HAL开发人员来说,工作在虚拟的IRQL上即可。

    Page 90 预定义的IRQL

    1) Interprocessor interrupt level

    2) Clock -- 时钟中断

    3) Profile level -- windows的sampling profiler在这个级别实现。当xperf trace flags加上profile选项其实就enable了system profiling timer,该timer工作于profile level,相应的ISR handler会记录CPU的执行栈。

    Page 104 软件中断主要用于:1)发起线程调度 2)处理定时器过期 3)non-time critical的中断处理 4)在某个特定线程里异步执行特定操作 5)异步I/O操作

    Page 105 每个CPU都有一个DPC队列,OS和驱动可以往队列里加入DPC对象。加入的时候可以指定DPC的优先级和目标CPU。当CPU的IRQL级别要降到DISPATCH_LEVEL以下是,CPU会drain DPC队列。

    Page 107 Threaded DPC: DPC routine运行于passive level实时线程(优先级31),可以抢占大部分用户线程,但会被DPC,APC,或更高优先级的线程抢占。主要给有需长时间运行DPC的驱动用,以免 给用户带来可感知的lag(因非threaded DPC会抢占所有的用户线程)。

    Page 110 APC提供了一种在特定线程上下文(从而特定进程地址空间)执行系统代码或者用户代码的机制。待执行的APC被放于目标线程的APC Queue中(per thread,DPC是per CPU)。当需要queue一个APC时,内核会将该APC加入目标线程的APC queue,并发起一个APC中断。

    有三种APC:Special kernel APC, normal kernel APC,和user mode APC。

    内核态APC可以打断线程的执行并运行相应的APC例程,不需要改线程的同意。

    用户态APC必须得到该线程的同意才能执行。

    Special APC运行于APC level, normal APC运行于passive level。

    Page 112 RTC timer, windows系统的脉搏,通常设为15.6ms(64次中断每秒)。应用程序需要的话,可以设为更小的时间间隔。因为timer对系统如此重要,windows维护了所有修改系统时钟的进程和修改时钟时的调用栈,可以用powercfg /energy生产相关的信息。同时,进程的EPROCESS也保留了相关的信息。

    Page 114 时钟中断的两个主要作用:1)记录系统时间 2)记录逻辑运行时间,比如进程/线程执行时间,系统tick time。Windows上定时器有两种,相对定时器(指定的是相对于当前时间的超时时间)和绝对定时器(指定定时器超时的绝对时间),调整系统时间对绝对定时器有影响,但对相对定时器无影响,因为它的超时时间是相对于设置timer时的流逝时间。

    Page 116 时钟中断运行于极高的IRQL上,所以通常只做很少的工作,如果发现有timer到期,或者进程/线程时间片用光,会请求DPC,把相应工作放到DPC中去做。

    Page 123 每个异常有两轮处理机会。WER即使在crashing线程当前栈被严重破坏的情况下产生crash dump,做法是发生未处理异常时kernel利用ALPC给wer service发消息,后续由wer处理。相比而言,xp下,所有err处理都是在出问题的线程,所以不可靠。

    Page 132 系统调用过程 1. eax - 系统调用号 edx - 指向用户态栈上的参数 2. int 0x2e/sysenter进入内核执行系统调用分发器(system service dispatcher, KiSystemService)。

    Page 135 KiSystemService复制用户栈上的参数到内核栈,复制多少字节通过查找argument table得到,argument table记录了每个系统调用的所有参数size,然后执行相应的函数KiServiceTable[EAX]. 如果某些参数指向用户态buffer,真正的调用的函数会去probe该buffer(如果previous mode是user mode)。以NtCreateFile为例:

    NTSTATUS NtCreateFile(  PHANDLE FileHandle,  ACCESS_MASK DesiredAccess,
      POBJECT_ATTRIBUTES ObjectAttributes,  PIO_STATUS_BLOCK IoStatusBlock,
      PLARGE_INTEGER AllocationSize,  ULONG FileAttributes,  ULONG ShareAccess,
      ULONG CreateDisposition,  ULONG CreateOptions,  PVOID EaBuffer,
      ULONG EaLength);

    KiSystemService会从edx指向的用户栈上复制11*4字节到内核栈。内核态的NtCreateFile在执行的时候会去Probe FileHandle, ObjectAttributes,IoStatusBlock,AllocationSize,EaBuffer去确认这些地址不是内核态地址并且调用者对这些buffer有相应的读或写权限。

    Page 136 用户态程序通过系统调用可以间接调用内核态的相应函数,内核态模块或者驱动程序也可以调用内核态的相应函数。内核中,每个native API都有两个版本,NtXxx 和ZwXxx

    用户态程序可以通过ntdll调用任意一个,其效果是一样的。 见如下汇编:

    0: kd> u ntdll!NtReadFile
    ntdll!NtReadFile:
    77f761e8 b8b7000000       mov     eax,0xb7
    77f761ed ba0003fe7f       mov     edx,0x7ffe0300
    77f761f2 ffd2             call    edx
    77f761f4 c22400           ret     0x24

    0: kd> u ntdll!ZwReadFile
    ntdll!NtReadFile:
    77f761e8 b8b7000000       mov     eax,0xb7
    77f761ed ba0003fe7f       mov     edx,0x7ffe0300
    77f761f2 ffd2             call    edx
    77f761f4 c22400           ret     0x24

    内核态模块调用NtXxx和ZwXxx效果有巨大差别:

    1)调用NtXxx相当于直接调用实现函数,previous mode不变

    2)调用ZwXxx会经历用户态调用的流程,eax - 调用号,edx - 指向内核栈上的参数,调用KiSystemService, 最终执行具体的实现函数,由于经历了系统调用,previous mode被设置为kernel mode

    0: kd> u nt!ZwReadFile
    nt!ZwReadFile:
    80504d4c b8b7000000       mov     eax,0xb7
    80504d51 8d542404         lea     edx,[esp+0x4]
    80504d55 9c               pushfd
    80504d56 6a08             push    0x8
    80504d58 e89e550300       call    nt!KiSystemService (8053a2fb)
    80504d5d c22400           ret     0x24

    这里的重点是previous mode。 Previous mode被内核用来判断请求到底是来自用户态还是内核态。 系统默认信任内核态的调用而不信任用户态的调用。所有来自用户态的调用其参数必须被严格检查。 所有来自内核态的调用,其参数都不会被检查,默认是有效的。 所以如果驱动调用NtXxx,则其权限等同与当前线程上下文,若为user,则NtXxx的所有参数都必须是用户态地址且有相应的访问权限;若为kernel,则不做任何检查。如果驱动调用ZwXxx则权限为kernel。

    Refer to: http://www.osronline.com/article.cfm?name=osrntapi.zip&id=257。 注意看其提供的代码。

    handle table: per-process handle table & global handle table

    Page 232 Image Loader (Ldr)实现于ntdll.dll中,是所用程序中首先得到执行的用户态代码。

    Page 233 Ldr的主要工作:

    1) 对进程进行必要的初始化,比如创建初始堆,TLS 2)解析IAT表,找到所有依赖的dll。解析导出表,确认所有导入的函数都确实存在。

    3) 运行时动态加载/卸载模块 4) 动态patch 5) 处理manifest 6) 处理application compatibility database,加载shim dll

    7) API sets及API重定向

  • 相关阅读:
    禅道开源版本安装
    NATAPP内网穿透实现
    nginx部署前端项目
    docker-compose部署微服务
    python编写猜数字游戏
    Linux命令(用户管理、组和时间管理)
    Linux命令(文本编辑器)
    Linux的简单命令(防火墙篇)
    什么是泛型
    spring bean 的作用域之间有什么区别
  • 原文地址:https://www.cnblogs.com/littledot/p/3455711.html
Copyright © 2011-2022 走看看