程序原理:

 

CPU上电后,从ROM 中的BIOS开始运行。
BIOS是处在内存的最顶端64KB(FFFF0000H),还是1MB之下的64KB(F0000H)处呢?事实上,BIOS在这两个地方都同时出现。
在保护模式时,CS是08H的选择子,到了实模式时,CS还是08H,但地址不会突然变成80H加上偏移量。
也就是说,实模式与0特权级保护模式不分页时是一模一样的。
所以,实模式下一样可以处理通常被认为只有在保护模式才能做的事,比如访问整个机器的内存。
实际上,Intel本身就在使用这种办法,使得CPU上电时能从FFFFFFF0H处开始第一条指令。

程序功能:

  1. 不需要在保护模式状态下就可以直接把386的4GB内存读出来;
  2. 利用此程序可直接在DOS中做物理设备的检测。
  3. 在DOS下,可根据此类方法将中断向量表移到任意位置,达到反跟踪或其他等目的。

程序执行过程:

  程序运行后,等用户从键盘输入一个字符。

  当输入“Q”字符时,整个程序将退出;

  当输入“D”时,将在屏幕上显示一屏内存的数据:

    最左边为绝对地址,其后一列显示的是以十六进制位表示的内存的数据,后一列是数据所对应的ASCII码。

代码关键分析:

 

(1)IP=0000FFF0H

 

CS∶EIP等于FFFFFFF0H。

 

(2)段寄存器FS在实模式下无法装入4GB的地址和权限。

所以让CPU进入一会儿保护模式,在装入了FS之后马上回到实模式。

 

保护模式进入方式:建好GDT,把CR0寄存器的位0置上1。

把一个包含有4GB地址空间的值装入FS之后,就可返回实模式。

 

(3)预先可建好GDT如下:

 

unsigned long GDT-Table[]=
{
  0,0,                        //空描述符,必须为零
  0x0000FFFF,0xCF9A00,        //32位平面式代码段
  0x0000FFFF,0xCF9200         //32位平面式数据段
}

 

(4)进入保护模式时要关闭所有的中断:把IDTR的界限设置为0,CPU自动关闭所有中断,包括NMI。

返回实模式后恢复IDTR并开中断。

 

(5)A20地址线的控制对于正确访问整个内存也很重要。

在进入保护模式前,要让8042打开A20地址线,否则会出现4GB内存中的混乱。

(6)此程序用BC 3.1编译连接,其连接器不能为DOS程序处理32位寄存器,所以直接在代码中加入操作码前缀0x66和地址前缀0x67,以便让DOS实模式下的16位程序可用32位寄存器和地址。程序的右边以注释形式给出等效的32位指令。

 

注意:16位的指令中,mov al, byte ptr [BX]的指令码正好是32位的指令mov al, byte ptr[EDI]。

 

附代码:



#include <dos.h>

unsigned long   GDT_Table[]=

{
    0,  0,                         //NULL   - 00H
    0x0000FFFF, 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 bit
    JMP 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 that.
//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 [BX]        //=MOV AL,FS:[EDI]
    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 [BX],al        //=MOV FS:[EDI],AL
    return  _AL;
}

///// Don't Touch Above Code /
#include <stdio.h>
/
//打印出Address指向的内存中的数据
///
void    Dump4G(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");
        }

}

int main( void )
{
        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;
}