首先声明本帖首发于赏金论坛,希望大家能多多关注这个正在成长的论坛www.sgoldcn.com,最近正在出一套编写操作系统雏形的教程http://www.sgoldcn.com/thread.php?fid=84,顺便整理了下保护模式的知识,逆向分析了下Windows的保护模式相关代码。
     
调试工具: vmware+windbg双机调试, bochs
   vmware+windbg双机调试网上有很多资料,我这里就不说了(缺点是不能分析windows设置GDT,IDT的过程)
   bochs我这里说下,大家可以去下载最新的版本的bochs,我没有制作bochs虚拟磁盘,比较麻烦,想到一个最简单的方法,直接使用xp2 winpe的ISO镜像,最新版的bochs不需要自己编写脚本,用bochs.exe可以直接生成配置文件
    设置步骤如下:
    1.双击bochs.exe设置虚拟机引导和磁盘

2.设置引导设备为CD-ROM

3.设置引导设备为*.iso镜像

     点击save可以保存生成*.bxrc配置文件

   用bochs主要是能看到一些硬件信息,相当于硬件调试器,但是从bochsdbg的输出结果来看bochs现在还没支持MSR指令,windbg无法观察内核设置分段,分页等代码,而bochs可以模拟硬件,能详尽分析这部分代码;所以我在分析时大部分用依赖与bochs.
 (bochs的命令是兼容GDB,与windbg不同,我下了份源码,试着改下命令,发现找不到头绪,如果有牛人改好,请发出了分享下.)
   
       做好配置文件*.bxrc可以使用bat脚本   "C:\Program Files\Bochs-2.4.6\bochsdbg" -q -f "D:\内核笔记\winpe XP.bxrc"  来启动调试器bochsdbg,bochs的命令可以在网上搜索下,有很多参考整理,bochs的文档是最好的指南.

Windows进入保护模式的步骤:
1.       分段准备GDT,加载GDT
2.       分页式寻址支持虚拟地址 

一.保护模式分段寻址选择
      如果未开启分页,任然沿用seg:offset方式寻址;
GDT(Global Descriptor Table Register,全局描述符表寄存器):描述符数组    有以下特点:
1、段寄存器中存放段选择子Selector
2、GDTR中存放着段描述符表的首地址
3、通过选择子根据GDTR中的首地址,就能找到对应的段描述符
4、段描述符中有段的物理首地址,就得到段在内存中的首地址
5、加上偏移量,就找到在这个段中存放的数据的真正物理地址。 
寻址示意图如下:


    分段寻址有一个重要的数据结构,GDT数组,在进入保护模式前准备好这个数组,由lgdt设置gdt
IDT(Interrupt Descriptor Table,中断描述符)也是类似于GDT的一个数组,是为了描述中断异常而设置的
   这里我先不解释描述符(Descriptor),我们可以暂时理解为描述内存信息(段基地,段界限,段属性等)的数据结构.

(1.)bochs观察GDT表与IDT表
第一次打开bochsdbg,会在准备好BIOS时断下输入命令 c(contine)
代码:
<bochs:1> c
跑一分钟左右(注意时间不要太短),就可以用bochs命令查看:
代码:
<bochs:2> info gdt 
Global Descriptor Table (base=0x000000000003f000, limit=1023):           (GDTR信息)
GDT[0x00]=??? descriptor hi=0x00000000, lo=0x00000000              (GDT表第一项不用,每位设置为0)
GDT[0x01]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, 32-bit         
GDT[0x02]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write, 
GDT[0x03]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Rea 
GDT[0x04]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write 
GDT[0x05]=32-Bit TSS (Busy) at 0x00024460, length 0x00077 
GDT[0x06]=Data segment, base=0x00000000, limit=0x00001fff, Read/Write 
GDT[0x07]=Data segment, base=0x00000000, limit=0x00000fff, Read/Write, 
GDT[0x08]=Data segment, base=0x00000400, limit=0x0000ffff, Read/Write 
GDT[0x09]=??? descriptor hi=0x00000000, lo=0x00000000 
GDT[0x0a]=32-Bit TSS (Available) at 0x00023b7e, length 0x0006f 
GDT[0x0b]=Code segment, base=0x00020000, limit=0x0000ffff, Execute/Read, 16-bit 
代码:
<bochs:3> info idt 
Interrupt Descriptor Table (base=0x000000008003f400, limit=2047              (IDTR 信息)
IDT[0x00]=32-Bit Interrupt Gate target=0x0008:0x80408bff, DPL=0 
IDT[0x01]=32-Bit Interrupt Gate target=0x0008:0x80408d7c, DPL=0
 
注意这两个命令都可以直接加索引查看指定项或者范围,如下:
代码:
<bochs:4> info gdt 0 2 
Global Descriptor Table (base=0x000000000003f000, limit=1023): 
GDT[0x00]=??? descriptor hi=0x00000000, lo=0x00000000 
GDT[0x01]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, Accesse 
GDT[0x02]=Data segment, base=0x00000000, limit=0xffffffff, Read/Write, Accessed 
GDT[0x03]=Code segment, base=0x00000000, limit=0xffffffff, Execute/Read, 32-bit
(2)windbg查看gdt表,idt表查看GDT表基地址
代码:
kd> r gdtr
gdtr=8003f000                     (注意这时已经打开分页机制,基地址是虚拟地址)
查看GDT表界限
代码:
kd> r gdtl
gdtl=000003ff
同样查看idt表有
代码:
kd> r idtr
idtr=8003f400    (基地址,基地址是虚拟地址)
代码:
kd> r idtl
idtl=000007ff      (界限)
观察gdt数组表项(使用dg [n] [n])
代码:
kd> dg 0 20
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0000 00000000 00000000 <Reserved> 0 Nb By Np Nl 00000000
0008 00000000 ffffffff Code RE Ac 0 Bg Pg P  Nl 00000c9b
0010 00000000 ffffffff Data RW Ac 0 Bg Pg P  Nl 00000c93
0018 00000000 ffffffff Code RE    3 Bg Pg P  Nl 00000cfa
0020 00000000 ffffffff Data RW Ac 3 Bg Pg P  Nl 00000cf3
  
观察idt数组表项(使用 !idt  [-a])
代码:
  kd> !idt -a 
Dumping IDT: 
00: 8053f1ac nt!KiTrap00
01: 8053f324 nt!KiTrap01
02: Task Selector = 0x0058
03: 8053f6f4 nt!KiTrap03
04: 8053f874 nt!KiTrap04
05: 8053f9d0 nt!KiTrap05
06: 8053fb44 nt!KiTrap06
07: 805401ac nt!KiTrap07
           
综合bochs调试结果可以看出,windows设置GDT表大小为1024即1024/8 = 128个描述符,IDT表大小为2048,共有2048/8 = 256个表项.          
下一节我教大家怎么分析windows设置分段分页的代码.
 
 winpe光盘镜像我上传到网盘:http://u.115.com/file/dngwjcju
  我bochs配置文件与启动脚本,大家可以参考下:bochs.rar