为了帮助读者实际了解以上所介绍的一些概念,下面我们来分析一段在DOS实模式下直接读取4GB内存的代码。通过该程序来分析CPU 的工作原理,揭开保护模式的神秘面纱,读者将会发现,保护模式其实与实模式一样简单和易于控制。在此基础上用四五十行C 语言程序做到进出保护模式和在实模式之下直接访问整个4GB内存空间。这个访问4GB内存的程 序是在实模式下使用的,它只是让CPU中的不可见部分有4GB大小访问权限。在进入保护模式 (CR0成为1)后,如果段寄存器不发生变化的话,则一切和实模式一样。所以CPU的保护位 为1时,后面的代码依然可以执行,而不是死机状态。同样的方法就不能用于分页,如果分页后的 内存与不分页前时对于执行的地方发生不同,如分页的指令在内存0X12345处,分页后这个 地方可能变成不存在,则计算机就只有出错重启。对于这个问题,本人做过多次实验,屡试不爽。2.4.1程序的意义此程序具有如下功能:Ø 不需要在保护模式状态下就可以直接把386的4GB内存读出来;Ø 利用此程序可直接在DOS中做物理设备的检测;Ø 理解GDT表的对应关系后,所谓386 32位模式也就很容易理解;Ø 在DOS下,可根据此类方法将中断向量表移到任意位置,达到反跟踪或其他等目的。2.4.2 程序代码程序代码如下所示。#include <dos.h>////////////////////////////////////////////////////////////////// 4G Memory Access// This Program Can Access 4G Bytes in DOS Real//Mode,Needn't in Protection Mode It Works.// The Program Enter 32 Bit Flat Mode a moment and//Only Load FS a 32 Bit Flat Mode Selector,Then Return//Real Mode.// Used The FS Can Access All 4G Memory till It be//reloaded.// ///////////////////////////////////////////////////////////////unsigned long GDT_Table<>={ 0, 0, //NULL - 00H0x0000FFFF, 0x00CF9A00, //Code32 - 08H Base=0 //Limit=4G-1 Size=4G 0x0000FFFF, 0x00CF9200 //Data32 - 10H Base=0 //Limit=4G-1 Size=4G};//Save The IDTR before Enter Protect Mode.unsigned char OldIDT<6>={0}; //NULL The IDTR,IDTR's Limit=0 will disable all //Interrupts,include NMI.unsigned char pdescr_tmp<6>={0}; #define KeyWait() {while(inportb(0x64)&2);}void A20Enable(void){ KeyWait(); outportb(0x64,0xD1); KeyWait(); outportb(0x60,0xDF); //Enable A20 with 8042. KeyWait(); outportb(0x64,0xFF); KeyWait();}void LoadFSLimit4G(void){A20Enable(); //Enable A20 //************************************** //* Disable ints & Null IDT * //************************************** asm { CLI //Disable inerrupts SIDT OldIDT //Save OLD IDTR LIDT pdescr_tmp //Set up empty IDT.Disable any//interrupts, } //Include NMI. //*************************************** //* Load GDTR * //*************************************** asm { //The right Code is Real,But BC++'s Linker NOT Work //with 32-bits Code. db 0x66 //32 bit Operation Prefix in 16 Bit DOS. MOV CX,DS //MOV ECX,DS db 0x66 //Get Data segment physical Address SHL CX,4 //SHL ECX,4 MOV word ptr pdescr_tmp<0>,(3*8-1) //MOV word ptr pdescr_tmp<0>,(3*8-1) db 0x66 XOR AX,AX //XOR EAX,EAX MOV AX,offset GDT_Table//MOV AX,offset GDT_Table db 0x66 ADD AX,CX //ADD EAX,ECX MOV word ptr pdescr_tmp<2>,AX //GDTR Base high16 bits db 0x66 SHR AX,16 //SHR EAX,16 MOV word ptr pdescr_tmp<4>,AX //GDTR Base high16 bits LGDT pdescr_tmp //Load GDTR} //************************************** //* Enter 32 bit Flat Protected Mode * //************************************** // Set CR0 Bit-0 to 1 Enter 32 Bit Protection //Mode,And NOT Clear machine perform cache,It Meaning //the after Code HAD Ready To RUN in 32 Bit Flat Mode, //Then Load Flat Selector to FS and Description into it's //Shadow register,After that,ShutDown Protection Mode //And ReEnter Real Mode immediately. // The FS holds Base=0 Size=4G Description and //it can Work in Real Mode as same as Pretect Mode, //untill FS be reloaded. // In that time All the other Segment Registers are //Not Changed,except FS.(They are ERROR Value holded in CPU). asm { MOV DX,0x10 //The Data32 Selector db 0x66,0x0F,0x20,0xC0 //MOV EAX,CR0 db 0x66 MOV BX,AX //MOV EBX,EAX OR AX,1 db 0x66,0x0F,0x22,0xC0 //MOV CR0,EAX//Set Protection enable bitJMP Flush } //Clear machine perform cache. Flush: //Now In Flat Mode,But The //CS is Real Mode Value. asm { //And it's attrib is 16-Bit Code //Segment. db 0x66 MOV AX,BX //MOV EAX,EBX db 0x8E,0xE2 //MOV FS,DX //Load FS now db 0x66,0x0F,0x22,0xC0 //MOV CR0,EAX //Return Real Mode.Now FS's Base=0 Size=4G LIDT OldIDT //LIDT OldIDT Restore IDTR STI //STI Enable INTR }}//With FS can Access All 4G Memory Now.But if FS be reloaded //in Real Mode It's Limit will Be Set to FFFFh(Size=64K),//then Can not used it// to Access 4G bytes Memory Again,Because FS is Segment:Offset//Memory type after If Use it to Access large than 64K will generate Execption 0D.//unsigned char ReadByte(unsigned long Address){ asm db 0x66 asm mov di,word ptr Address //MOV EDI,Address asm db 0x67 //32 bit Address Prefix asm db 0x64 //FS: asm mov al,byte ptr
//=MOV AL,FS: return _AL;}unsigned char WriteByte(unsigned long Address){ asm db 0x66 asm mov di,word ptr Address //MOV EDI,Address asm db 0x67 //32 bit Address Prefix asm db 0x64 //FS: asm mov byte ptr ,al //=MOV FS:,AL return _AL;}///////////////// Don't Touch Above Code /////////////#include <stdio.h>///////////////////////////// //////////////////////////////////打印出Addres s指向的内存中的数据///////////////////////////////////////////////////////////voidDump4G(unsigned long Address){ int i; int j; for(i=0;i<20;i++){ printf("%08lX: ",(Address+i*16)); for(j=0;j<16;j++) printf("%02X ",ReadByte(Address+i*16+j)); printf(" "); for(j=0;j<16;j++){ if(ReadByte(Address+i*16+j)<0x20) printf("."); else printf("%c",ReadByte(Address+i*16+j)); } printf("\n"); }}main(){ char KeyBuffer<256>; unsigned long Address=0; unsigned long tmp; LoadFSLimit4G(); printf("====Designed By Southern.1995.7.17====\n"); printf("Now you can Access The Machine All 4G Memory.\n"); printf("Input the Start Memory Physical to DUMP.\n"); printf("Press D to Cuntinue DUMP,0 to End & Quit.\n"); do { printf("-"); gets(KeyBuffer); sscanf(KeyBuffer,"%lX",&tmp); if(KeyBuffer<0>=='q') break; if(KeyBuffer<0>=='d') Address+=(20*16); else Address=tmp; Dump4G(Address); }while(Address!=0); return 0;}程序运行后,等用户从键盘输入一个字符。当输入“Q”字符时,整个程序将退出,当输入“ D”时,将在屏幕上显示一屏内存的数据,最左边为绝对地址,其后一列显示的是以十六进制位表示的内存的数据,后一列是数据所对应的ASCII码。
More abstracts about the 在DOS实模式下读取4GB内存(1)