820 likes | 958 Views
第 10 章 PC 机的高级编程技术. 10.1 PC 机环境下软件对接口的访问层次. 应用程序. 操作系统 驱动程序. 间接访问. BIOS. 直接访问. 裸机. 1. 直接访问层次. 特点 :可以直接进行内存和端口的访问,也可以自行决定是否在实模式和保护模式间切换。通常所有的端口和内存都是对程序员开放的。 工具 :汇编语言或 C 语言。 应用 : BIOS 都是基于这种低级层次用汇编来编写的。驱动程序也有使用这种方法的。 优点 :能够编写速度最快,占用空间最小的有效代码。 缺点 :需要对硬件和接口有很熟悉的了解。
E N D
10.1 PC机环境下软件对接口的访问层次 应用程序 操作系统 驱动程序 间接访问 BIOS 直接访问 裸机
1.直接访问层次 特点:可以直接进行内存和端口的访问,也可以自行决定是否在实模式和保护模式间切换。通常所有的端口和内存都是对程序员开放的。 工具:汇编语言或C语言。 应用:BIOS都是基于这种低级层次用汇编来编写的。驱动程序也有使用这种方法的。 优点:能够编写速度最快,占用空间最小的有效代码。 缺点:需要对硬件和接口有很熟悉的了解。 要会汇编,或熟练使用C语言的指针。 不利于增加对新设备的控制。
2.BIOS访问层次 特点:通过BIOS提供的功能调用间接地对内存或端口访问,从而控制硬件。 工具:汇编语言或C语言。 应用:驱动程序有些会使用这些功能调用。需要获得高效率的应用程序也采用这种方法。 优点:能够编写速度较快的有效代码。 可以不需要编写直接对硬件进行控制的代码。 缺点:需要对底层信号有所了解。 增加对新设备的控制不是很方便,但好于低级层次。
3.驱动程序层次 特点:使用BIOS功能调用和直接内存访问的方法编写符合特定操作系统管理规范的设备驱动程序。 工具:VC++与DDK开发包,或第三方开发工具如DriverStudio。 应用:在操作系统层面上的设备控制,并为应用程序提供API支持。 优点:既控制硬件,又保证操作系统的完整与安全。 缺点:需要对底层信号有所了解。 需要对操作系统的各个管理模块有深入的了解。
4.应用层次 特点:使用操作系统各种驱动程序所提供的功能调用或API函数间接对硬件或内存进行访问。 工具:VC++、Delphi、Java等。 应用:编写面向终端用户的各类应用程序。 优点:无需对硬件控制有太多了解,只需完成应用层面的工作就可以 ,而且还保证操作系统的完整与安全。 缺点:需要了解大量的API和功能调用函数的功能。 由于是间接调用,所以代码的效率和编译系统有很大的关系。
(左移四位) 16位段地址 16位段内偏移 + = 20位物理地址 10.2 Pentium 4的内存管理——接口直接访问 10.2.1 实地址模式的存储管理(同PC/XT) 采用了内存分段的办法,内存分为若干段,段的大小根据需要决定,最大为64KB : 16位段地址 16位段内偏移 6417H∶0100H 6417H×10H + 0100H = 64170H + 0100H = 64270H
000000 存放用户程序和DOS驻留部分 640KB常规内存 0A0000 存放显存、网卡和部分用户的DOS驱动程序和BIOS 384KB高端内存 100000 64KB高内存区 存放部分DOS驻留程序 110000 大于1M以上的 扩展内存,DOS下不能直接访问,需要用XMS规范使用。可利用DOS调用或BIOS调用来使用。 1~4095MB扩展内存 ≈ ≈ FFFFFF
10.2.2 保护模式下使用的系统地址寄存器 • GDTR — 48位的全局描述符表寄存器 全局描述符表32位线性地址 16位界限值 • IDTR — 48位的中断描述符表寄存器 16位界限值 中断描述符表32位线性地址 • TR — 16位的任务状态段寄存器 TSS的16位选择字 • LDTR — 16位的局部描述符选择字寄存器 LDT的16位选择字
10.2.3 保护模式下Pentium 4的段式存储管理 1. 段式管理的地址变换 45 32 31 0 逻辑地址 段寄存器的15~2位 偏移量 段基址 段描述符 段表 物理地址 32位线性地址 Pentium 系列的虚拟地址空间是246=64TB。
D7 D0 段界限 7~0 0 段界限 15~8 1 基址 7~0 2 基址 15~8 3 基址 23~16 4 P DPL S TYPE 5 G D/B 段界限 19~16 0 AVL 6 7 基址 31~24 2. 段描述符
D7 D0 0 AVL G 段界限 19~16 D/B 用户的操作系统可用位 D/B位 G=0 段长以1字节为单位 粒度位 G=1 段长以4K字节为单位 D=1 使用32位操作系统和32位寻址方式 代码段(D位) D=0 使用16位操作系统和16位寻址方式 D/B位 B=1 使用ESP寄存器,上限为FFFFFFFFH 数据段(B位) B=0 使用SP寄存器,上限为FFFFH
E=0 ED W DPL S=1 P A E=1 C R 非系统段中的第5字节 扩展方向位 数据段标志 可写位 可执行位 D7 D0 兼容位 存在位 访问位 特权位 代码段标志 可读位 S=1是非系统段 S=0是系统描述符
选择符(段寄存器) 15 2 1 0 索引 Ti RPL Ti=0 Ti=1 LDT …… …… LDT 2 2 1 1 0 0 LDT GDT 选择符 界限 界限 基址 基址 GDTR LDTR
#include "stdafx.h" #include <stdio.h> #include <wtypes.h>// wtypes.h定义了WORDLONG, //DWORD,WORD等数据类型 DWORDLONG gdtr,savegdt; //下面是GDT中将创建的数据段描述符表,基地址0X00000F00, //段界限为0XFFFF,优先级为3的在内存中的可写数据段 WORD descriptor[4]= {0xFFFF, 0X0F00, 0XF200, 0X0040}; int result[10]; int main(int argc, char* argv[]) {_asm {pushebp sgdtgdtr// 将GDTR寄存器的内容读取到 //gdtr开始的6个字节中,其中 // 前两个字节给出GDT的界限值, //高4个字节给出GDT的基地址
movebp,dword ptr [gdtr+2] // 将gdt的基 // 地址读到EBP中 addebp,70h // 我们选择70H偏移下的段描述 // 符(GDT中第14个描述符) leaedi,savegdt movesi,ebp movsd // 以上4条指令保存原来在70H偏移上 movsd // 的描述符 movedi,ebp lea esi,descriptor; movsd // 把我们的数据段描述符装入70H movsd// 偏移上 pushes movax,0073h// 选择字为描述符偏移70H拼接上 // 低3位控制位元,其中Ti为0,表 // 示访问GDT,RPL为11,为3级优 // 先级,所以就为73H
moves,ax// ES装入选择字73H leaedi,result // 将存放输出结果的变量 //的地址放在EDI中 moveax,1 movebx,1 } _asm {movcx,10 a1:moves:[eax],eax addeax,4 loopa1// 上面4条指令将向物理地址 // 0X00000F00处写10个双字 } _asm {movcx,10 a2:moveax,es:[ebx] mov[edi],eax
addebx,4 addedi,4 loopa2// 以上从物理地址0X00000F00 // 处依次读出10个数据存放在 // result数组中 } _asm {popes popebp } printf("result="); for(int i=0;i<10;i++) printf("%d,",result[i]);// 输出结果 return 0; }
~ ~ ~ ~ 10.2.4 保护模式下Pentium 4的虚拟页式存储管理 主存 程序1 页面 程序2 页框 程序3
31 7 6 5 4 3 2 1 0 8 TSD DE PVI CR4 MCE 保留,缺省为全0 PAE PSE PGE VME PCE
31 22 21 12 11 0 偏移 页目录项号 32位线性地址 页面号 低12位 CR3 32位物理地址 高20位 页表 页目录 31 12 11 9 8 7 6 5 4 3 2 1 0 页目录项 0 A US P PCD PWT D 页表基地址31~12 AVL RW 31 12 11 9 8 7 6 5 4 3 2 1 0 页表项 A US P D PCD PWT RW AVL 页框基地址31~12 4KB分页方式 P=出现位,US=用户/监督位,PCD是页Cache禁止,D=Cache“脏”位,RW=读/写位,PWT=页写贯穿位,A=访问位,AVL=用户的操作系统可用位。而第7位(PS)在4KB分页中为0
31 22 21 0 32位线性地址 偏移 页目录项号 CR3 低22位 32位物理地址 高10位 页目录 31 22 8 7 6 5 4 3 2 1 0 A PCD US P 1 D PWT RW 页框基地址31~22 页目录项 4MB分页方式
31 5 4 3 2 1 0 PWT 32字节对齐的PDPT基地址 PCD CR3寄存器 63 36 35 12 11 6 5 4 3 2 1 0 PDPT项 PWT AVL P PCD 4KB对齐的页目录基地址(高24位) 31 30 29 21 20 12 11 0 PDPT项号 页目录项号 偏移 页面号 32位线性地址 CR3 低12位 36位物理地址 高24位 页目录指针表 页表 页目录 4×64位 512×64位 512×64位 63 36 35 12 11 9 8 7 6 5 4 3 2 1 0 0 A PCD US P PWT RW AVL 页目录项 4KB对齐的页表基地址 63 36 35 12 11 9 8 7 6 5 4 3 2 1 0 G 0 A PCD US P D PWT RW AVL 页表项 4KB对齐的页框基地址 Pentium III 36位地址下的4KB分页方式地址转换
31 30 29 21 20 0 PDPT项号 页目录项号 偏移 32位线性地址 低21位 CR3 36位物理地址 高15位 页目录指针表 页目录 4×64位 512×64位 63 36 35 21 20 12 11 9 8 7 6 5 4 3 2 1 0 页目录项 G 1 A PCD US P D PWT RW AVL 2MB对齐的页框基地址 Pentium III 36位地址下的2MB分页方式地址转换
应用软件 操作系统提供的接口 操作系统内核与VxD 计算机底层硬件 10.3 Windows 9x驱动程序编写 10.3.1 虚拟机与VxD的引入 特权级3 特权级0
Windows 9x SYSVM Win16程序 DOSVM DOSVM Win32程序 Win32程序 …… Win16程序 Win16程序 DOS程序 DOS程序 Win32地址空间 Win32地址空间 …… Win16程序 Win16地址空间
10.3.1保护模式下的I/O端口访问 IOPL用以表示指定的I/O操作处于特权级的哪一级。它在EFLAGS中。 CPL当前段的I/O优先级,它实际上是CS段选择符的第0~第1位。 执行in, out指令 是 CPL ≤ IOPL? 否 是 IOPM相关位=0? IOPM是对所有VM都起作用的权限机制,它以位(bit)来代表每个端口。某位为1,则该代表的端口被禁止访问;某位为0;则允许访问该位所代表的端口。 否 产生一个一般保护异常 进行I/O操作
V86模式下(DOSVM)的I/O端口访问 执行in, out指令 是 IOPM相关位=0? 否 进行I/O操作 产生一个一般保护异常 如何捕获一个端口的访问?
10.3.2 保护模式下对中断或异常的处理 外部中断和处理器异常 软中断 否 CPL ≤门描述符的DPL ? 是 访问各类门描述符 否 转移后代码段的DPL≤CPL ? 是 禁止访问 执行0特权级别中的中断处理程序
CPU根据中断向量表IDT调用相应的VMM异常处理程序,从而切换到0特权级别。 其他各种VxD可以通过安装回调例程来响应处理各种异常和中断,但是它们不能改变中断描述符表IDT中给出的各种VMM异常处理程序。 VMM异常处理程序通过使用CALL指令调用安装的各个回调例程或自身处理异常。 对于硬件中断,VMM则将控制权交给 VPICD(虚拟可编程中断控制器)处理,VPICD就调用其他VxD通过系统服务VPICD_Virtualize_IRQ安装的回调例程处理硬件中断。 一个回调例程要么处理中断和异常,要么忽略该中断,但是它必须使用RET指令返回到VMM异常处理程序。
硬件中断 IRET CALL INT x IRET RET RET VMM异常处理程序使用IRET或IRETD指令返回到虚拟机中,虚拟机中的应用程序重新获得控制从而继续执行。 VPICD Win32程序 VxD中的回调函数 异常处理程序 虚拟机
思路: 在中断描述符表中构造一个中断门描述符,使它的DPL=3,这样它就可以被用户级的程序访问,将该中断门描述符的段选择字设为028H,显然该选择字对应的代码段在GDT中,由于其基地址为00000000H,段界限为FFFFFFFFH,所以现在中断门的偏移量就实际给出了中断处理程序入口的线性地址,我们只要把一个过程作为中断处理程序,这个过程就处在核心级里了。 程序如下: 特殊应用:如何利用保护模式下中断的处理流程设法使自己从用户级转到核心级?
#include "stdafx.h" #include <stdio.h> #include <wtypes.h>// wtypes.h定义了DWORDLONG, //DWORD,WORD等数据类型 DWORD_cr0;// 用来保存CR0寄存器的值 void _declspec(naked)newint3(void)// 运行在核心级的 // 中断3处理程序 { _asm {moveax,cr0// 这是必须在核心级才能执行的 // 特权指令 mov_cr0,eax } _asmiretd// 中断返回(为什么不是ret?) }
int main(int argc, char* argv[]) {DWORDLONG idtr,saveidt; WORDnewgate[4]={0x0000,0x0028,0xee00,0x0000};// 中断门描述符,DPL=3 _asm {sidtidtr// 将IDTR的值存在idtr变量开 // 始的6个字节中 movebx,DWORD ptr[idtr+2]// 把IDT的 // 基地址读入EBX寄存器 addebx,24// 选择中断3作为进入核心级的 // 入口,中断3的门描述符 // 的地址是IDT基地址加上3*8 // (每个门描述符8个字节) movesi,ebx leaedi,saveidt
movsd // 保存原来中断3的门描述符 movsd // 到saveidt中 leaeax,newint3 movnewgate,ax shreax,16 mov[newgate+6],ax// 向新的中断描述符中填入 // 中断处理程序的入口偏移量 leaesi,newgate movedi,ebx movsd // 用新中断描述符在IDT中 movsd// 替换原来的中断3描述符 int3h// 触发中断3,使程序跳转到// 0级执行中断处理程序
leaesi,saveidt movedi,ebx movsd movsd// 恢复原来中断3的门描述符 } printf("cr0=0x%x",_cr0);// 输出结果 return 0; }
10.3.3 虚拟设备驱动程序(VxD)基础 1. VxD的文件结构 VxD的五个段 • VxD_CODE 保护模式下的代码段含设备驱动程序回调例程、服务程序、API接口函数和控制程序。 • VxD_DATA 保护模式下的数据段包含设备描述块、服务表、全局变量等。 • VxD_ICODE 保护模式下的初始化代码段初始化的时候用的服务程序和过程。初始化后被丢弃 • VxD_IDATA 保护模式下的初始化数据段初始化时用的数据。初始化后被丢弃。 • VxD_REAL_INIT 实模式下的初始化资料与代码该过程返回后被丢弃。
2. VxD的设备描述符块DDB typedef struct tagDDB { DWORDDDB_Next;// VMM使用这一项来指出 // 下一个DDB的地址 WORDDDB_SDK_Version; // 建立该VxD所使用的 // SDK/DDK的版本号 WORD DDB_Req_Device_Number;// 设备ID。 // UNDEFINED_DEVICE_ID表示 // 不使用唯一ID BYTEDDB_Dev_Major_Version;// VxD的主版本号 BYTEDDB_Dev_Minor_Version;// VxD的次版本号 WORDDDB_Flags;// DDB标志位 BYTEDDB_Name[8];// VxD的名字,不足8个字节必须 // 以空格补满 DWORDDDB_Init_Order; // 指定VxD的初始化顺序,如果没 // 有特别的初始化要求就使用 // UNDEFINED_INIT_ORDER DWORDDDB_Control_Proc;// 设备控制程序的地址 DWORDDDB_V86_API_Proc;// V86API程序的入口地址
DWORDDDB_PM_API_Proc;// 保护模式API程序的入口地址 DWORDDDB_V86_API_CSIP// V86入口点的CS:IP DWORDDDB_PM_API_CSIP;// 保护模式入口点的CS:IP DWORDDDB_Reference_Data;// 实模式初始化代码设置的参 // 考资料 DWORDDDB_VxD_Service_Table_Ptr;// VxD服务表的地址 DWORDDDB_VxD_Service_Table_Size;// VxD服务表中提 // 供的VxD服务的数目 } DDB;
3. VxD的加载与卸载 动态加载 hDevice=CreateFile("\\\\.\\myfirst.vxd", 0, 0, 0, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0); DeviceIoControl(hDevice,DIOC_MY_IO, NULL, 0, NULL, 0, NULL,NULL); CloseHandle(hDevice);
可以使DOS应用程序访问x86平台上的硬件,也可以支持Windows 9x下的对端口访问 10.4 Windows 2000/XP设备驱动程序设计 10.4.1 Windows 2000/XP的设备驱动程序 用于显示和打印设备的内核模式驱动程序 虚拟设备 驱动程序(VDD) 内核模式 驱动程序 是一种遵循电源管理协议并能在Windows 98和Windows 2000间实现源代码级兼容的PnP驱动程序 文件系统 驱动程序 保留设备 驱动程序 显示 驱动程序 PnP 驱动程序 WDM驱动程序 主要包括Windows NT早期版本的驱动程序,它直接控制一个硬设备而不用其他驱动程序帮助,可以不做修改地在Windows 2000中运行 在本地硬盘或网络上实现标准PC文件系统模型(包括多层次目录结构和命名文件概念) 类驱动程序 迷你驱动程序
10.4.2 WDM的基本结构 1. 基本概念 设备对象:系统为帮助软件管理硬件而创建的一个数据结构。一个物理设备可以有多个这样的数据结构。 物理设备对象:简称PDO,处于设备对象栈最底层的设备对象。每个物理设备被创建一个PDO 功能设备对象:简称FDO,在PDO之上,描述设备功能的相关数据结构。 过滤器设备对象:简称FiDOs,在FDO的上面或下面,分别称为上层过滤器和下层过滤器。用于修改现有功能驱动程序的行为。 设备对象栈:设备对象栈代表处理请求的驱动程序层次。
应用程序 Win32子系统 用户态 核心态 I/O系统服务 设备对象栈 I/O管理器 IRP 上层过滤器驱动程序 FiDO 功能驱动程序 FDO 下层过滤器驱动程序 FiDO 总线驱动程序 PDO 46页
2. 硬件设备的驱动程序种类 WDM驱动程序模型中,每个硬件设备可以有多个驱动程序: 功能驱动程序 :管理FDO所代表的设备,负责其初始化、处理I/O操作完成时产生的中断事件并为用户提供一种适当的设备控制方式。 过滤器驱动程序:用于监视和修改IRP流。硬件或软件人员可利用过滤器驱动程序修改现有功能驱动程序的行为。 总线驱动程序:负责管理硬件和计算机之间的连接。这个驱动程序实际上和同类设备共同拥有。如PCI总线驱动程序。
3. 设备驱动程序安装的顺序 硬件接入 总线驱动程序检测到新的硬件 用户使用控制面板中的“添加新硬件”向导安装一个设备 PnP管理器创建PDO PnP管理器为该设备和它的驱动程序在注册表的配置表中添加一些条目 PnP管理器可能需要调整已经分配给已存在设备的资源,使需要的资源对新设备可用
PnP管理器参照注册表中的信息查找与创建的PDO相关的过滤器和功能驱动程序PnP管理器参照注册表中的信息查找与创建的PDO相关的过滤器和功能驱动程序 按照INF文件的指令安装驱动程序 驱动程序被装入后,执行DriverEntry程序。来设置驱动程序中各个例程的入口地址 PnP管理器装入最底层的过滤器驱动程序并调用其AddDevice函数。该函数创建一个FiDO,从而在过滤器驱动程序和FiDO之间建立了水平连接
AddDevice把PDO连接到FiDO上 PnP管理器继续向上执行,依次装入并调用每个低层过滤器、功能驱动程序和每个高层过滤器,直到完成整个设备对象栈 PnP管理器给设备发送各种PnP IRP PnP管理器给发送“启动设备”PnP IRP告诉驱动程序已经给它分配了哪些资源 ,驱动程序使用这些资源分配启动它的设备 驱动程序处于等待处理IRP的状态