`

(第三章 5)保护模式下,读写大地址内存 & 从32位保护模式跳回16位实模式(一)

阅读更多

一、主要功能

        在本章最基本代码(P25、chapter3/a/)的基础上实现大地址(超过1M)的读写。在前面程序的基础上新建一个段,这个段以5MB为基址,远远超过1MB的界限。先读出开始处8字节的内容,然后写入一个字符串,再从中读出8字节。
        如果读写成功的话,两次读出的内容应该不同,而且第二次读出的内容应该就是我们写进的字符串。字符串是保存在数据段中的,也是新增加的。

 

************************************************************程序流程************************************************************


 

 

************************************************************程序流程************************************************************

 

注意:

 

1. 几个段的解释

        数据段(选择子为SelectorData)访问相关代码标记为红色(PMMessage将在保护模式下直接显示;StrTest则在保护模式下先被拷贝到SelectorTest对应的段,然后取出后显示);

        测试段(择子为SelectoTest)访问相关代码标记绿色(这个段的段基址设置很大,只是为了展示保护模式访问大地址的能力。它的描述符中段基址不用在[SECTION .s16]中精确设置,只需要设置为一个很大的值即可);

        Normal(选择子为SelectorNormal)段表示为紫色,这个选择子在代码最后准备跳回实模式的段[SECTION .s16code]中有用到。

        其中,我认为关键的一点是实模式和保护模式内存访问方式的区别:

        同样采用[ds:esi]这样的形式——两者的esi均表示偏移量(当然,实模式中应该为si);ds含义不同。在实模式中,ds表示段基值;而在保护模式中,ds会在32位代码段开始处被设置为段对应的选择子设置方法如下。

[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS	32]
LABEL_SEG_CODE32:
mov	ax, SelectorData
mov	ds, ax	 ; 数据段选择子
mov	ax, SelectorTest
mov	es, ax	 ; 测试段选择子
... ...

 

2. 代码段访问到>1MB内存地址,并不表示代码段中的指令本身在>1MB的地址

从这个程序可以看到,保护模式下可以访问超过1MB内存,但保护模式([SECTION .32])的代码仍然在1MB以内

 

3. 如何证明保护模式下可以访问超过1MB的地址:

 

进入32位代码段后,在保护模式下调用子例程"call TestRead --> call TestWrite --> call TestRead",在这些子例程中均出现访问大地址的代码,如下:

 

;                            段基址,        段界限 , 属性
LABEL_DESC_TEST:   Descriptor 0500000h,   0ffffh, DA_DRW
...

[SECTION  .s32]	;进入这个32段前设置了cr0[0]=1,即进入本段就在“保护模式”下了
[BITS	32]
	...
	mov	ax,	SelectorTest	;注意到测试段(Test段)的段基址为0500000h远大于1M
	mov	es,	ax

TestRead:
	...
	mov	al,	[es:esi]
	...
TestWrite:
	...
	mov	[es:edi],	al
	...

 

 

二、代码

%include "pm.inc" ; 常量, 宏, 以及一些说明

org 0100h
jmp LABEL_BEGIN

[SECTION .gdt]
; GDT
;                            段基址,        段界限 , 属性
LABEL_GDT:         Descriptor    0,              0, 0         ; 空描述符
LABEL_DESC_NORMAL: Descriptor    0,         0ffffh, DA_DRW   ; Normal 描述符,段基址在此设置为0(后面没有再重设)
LABEL_DESC_CODE32: Descriptor    0, SegCode32Len-1, DA_C+DA_32; 非一致代码段, 32
LABEL_DESC_CODE16: Descriptor    0,         0ffffh, DA_C      ; 非一致代码段, 16
LABEL_DESC_DATA:   Descriptor    0,      DataLen-1, DA_DRW    ; Data
LABEL_DESC_STACK:  Descriptor    0,     TopOfStack, DA_DRWA+DA_32; Stack, 32 位
LABEL_DESC_TEST:   Descriptor 0500000h,     0ffffh, DA_DRW  
LABEL_DESC_VIDEO:  Descriptor  0B8000h,     0ffffh, DA_DRW    ; 显存首地址
; GDT 结束

 

; 注意:
;    16位实模式段基址是cs<<4+LABEL_SEG_CODE16
;    32位保护模式段基址是cs<<4+LABEL_SEG_CODE32
;    数据段基址是ds<<4+LABEL_DATA
;          堆栈段基址是ds<<4+LABEL_STACK
; 它们都将在16位实模式段[SECTION .s16]开始被设置

GdtLen equ $ - LABEL_GDT ; GDT长度
GdtPtr dw GdtLen - 1 ; GDT界限
dd 0 ; GDT基地址

; GDT 选择子
SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT
SelectorData equ LABEL_DESC_DATA - LABEL_GDT
SelectorStack equ LABEL_DESC_STACK - LABEL_GDT
SelectorTest equ LABEL_DESC_TEST - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
; END of [SECTION .gdt]

[SECTION .data1] ; 数据段
ALIGN 32
[BITS 32]
LABEL_DATA:
SPValueInRealMode dw 0
; 字符串
PMMessage: db "In Protect Mode now. ^-^", 0 ; 在保护模式中显示
OffsetPMMessage equ PMMessage - $$
StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
OffsetStrTest equ StrTest - $$
DataLen equ $ - LABEL_DATA
; END of [SECTION .data1]



; 全局堆栈段
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 512 db 0

TopOfStack equ $ - LABEL_STACK - 1

; END of [SECTION .gs]


[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax

mov es, ax
mov ss, ax
mov sp, 0100h

mov [LABEL_GO_BACK_TO_REAL+3], ax
mov [SPValueInRealMode], sp

; 初始化 16 位代码段描述符
mov ax, cs
movzx eax, ax
shl eax, 4
add eax, LABEL_SEG_CODE16
mov word [LABEL_DESC_CODE16 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE16 + 4], al
mov byte [LABEL_DESC_CODE16 + 7], ah

; 初始化 32 位代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE32
mov word [LABEL_DESC_CODE32 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE32 + 4], al
mov byte [LABEL_DESC_CODE32 + 7], ah

; 初始化数据段描述符

; 仍然采用16位计算物理地址的方法(16位段基值左移4位,再加偏移量),但以前是加法器帮我们做的。

; 现在我们自己来计算这个物理地址(20bit),就需要用到32bit寄存器eax

xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_DATA

; 将计算出来的物理地址设置到数据段描述符对应位置
mov word [LABEL_DESC_DATA + 2], ax
shr eax, 16
mov byte [LABEL_DESC_DATA + 4], al
mov byte [LABEL_DESC_DATA + 7], ah


; 初始化堆栈段描述符
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_STACK
mov word [LABEL_DESC_STACK + 2], ax
shr eax, 16
mov byte [LABEL_DESC_STACK + 4], al
mov byte [LABEL_DESC_STACK + 7], ah

; 为加载 GDTR 作准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ; eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址

; 加载 GDTR
lgdt [GdtPtr]

; 关中断
cli

; 打开地址线A20
in al, 92h
or al, 00000010b
out 92h, al

; 准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax

; 真正进入保护模式
jmp dword SelectorCode32:0 ; P35讲解为何要用dword

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax

mov sp, [SPValueInRealMode]   //SPValueInRealMode到底有什么用呢,实模式下的sp为什么要这么精心的保存起来??????

in al, 92h ; `.
and al, 11111101b ;  | 关闭 A20 地址线
out 92h, al ; /

sti ; 开中断

mov ax, 4c00h ; `.
int 21h ; /  回到 DOS
; END of [SECTION .s16]


[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS 32]

LABEL_SEG_CODE32:
mov ax, SelectorData
mov ds, ax ; 数据段选择子

mov ax, SelectorTest
mov es, ax ; 测试段选择子

mov ax, SelectorVideo
mov gs, ax ; 视频段选择子
mov ax, SelectorStack
mov ss, ax ; 堆栈段选择子

mov esp, TopOfStack


; 下面显示一个字符串
mov ah, 0Ch ; 0000: 黑底    1100: 红字
xor esi, esi
xor edi, edi
mov esi, OffsetPMMessage ; 源数据偏移
mov edi, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。(屏幕为25行80列,一个位置对应两个字节)
cld


.1:
lodsb
test al, al
jz .2
mov [gs:edi], ax
add edi, 2
jmp .1
.2: ; 显示完毕

call DispReturn

call TestRead
call TestWrite
call TestRead


; 到此停止,下面跳到的是最后一个段[SECTION .s16code],这个段准备跳回实模式
jmp SelectorCode16:0

; ------------------------------------------------------------------------
TestRead:
xor esi, esi
mov ecx, 8
.loop:
mov al, [es:esi]
call DispAL
inc esi
loop .loop

call DispReturn

ret
; TestRead 结束-----------------------------------------------------------


; ------------------------------------------------------------------------
TestWrite:
push esi
push edi
xor esi, esi
xor edi, edi
mov esi, OffsetStrTest ; 源数据偏移
cld

 

; 下面.1和.2的效果实际上就是将[ds:esi]若干字符放到[es:edi]若干位置中

;  注意到ds在32位代码段开始时被置为SelectorData;es在32位代码段开始时被置为SelectorTest
.1:
lodsb                          

lodsb 指令:从esi 指向的源地址中逐一读取一个字符(默认数据段用SelectorData作选择子),送入AL 中; (见本博客《汇编

; (NASM)》部分)。注意到前面已将SelectorData放入ds中了。所以实际上lodsb就是“ mov    al,    [ds:esi] ”

;mov ax, SelectorData
;mov ds, ax ; 数据段选择子


test al, al                     ; 检测到最后一个字符00h,zf=0,就会跳转到.2
jz .2
mov [es:edi], al
inc edi

jmp .1


.2:
pop edi
pop esi

ret
; TestWrite 结束----------------------------------------------------------


; ------------------------------------------------------------------------
; 显示 AL 中的数字
; 默认地:
; 数字已经存在 AL 中
; edi 始终指向要显示的下一个字符的位置
; 被改变的寄存器:
; ax, edi
; ------------------------------------------------------------------------
DispAL:
push ecx
push edx

mov ah, 0Ch ; 0000: 黑底    1100: 红字
mov dl, al
shr al, 4
mov ecx, 2
.begin:
and al, 01111b
cmp al, 9
ja .1
add al, '0'
jmp .2
.1:
sub al, 0Ah
add al, 'A'
.2:
mov [gs:edi], ax
add edi, 2

mov al, dl
loop .begin
add edi, 2

pop edx
pop ecx

ret
; DispAL 结束-------------------------------------------------------------


; ------------------------------------------------------------------------
DispReturn:
push eax
push ebx
mov eax, edi
mov bl, 160
div bl
and eax, 0FFh
inc eax
mov bl, 160
mul bl
mov edi, eax
pop ebx
pop eax

ret
; DispReturn 结束---------------------------------------------------------

SegCode32Len equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]


; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
; 回忆其描述符的定义如下,

; LABEL_DESC_NORMAL: Descriptor    0,         0ffffh, DA_DRW   ; Normal 描述符,段基址在此设置为0(后面没有再重设)

 

 

; 书上P43最上面:在准备结束保护模式回到实模式之前,需要加载一个合适的描述符选择子(SelectorNormal==>(1)段基址:0 (2)段界限:0ffffh (3)属性:DA_DRW即可读写数据段)到有关段寄存器(ds,es,fs,gs,ss)。

 


mov ax, SelectorNormal
mov ds, ax
mov es, ax
mov fs, ax
mov gs, a
x
mov ss, ax


mov eax, cr0
and al, 11111110b
mov cr0, eax

LABEL_GO_BACK_TO_REAL:
jmp 0:LABEL_REAL_ENTRY    ;段间直接转移指令(far jump),跳回到16位实模式段[SECTION .s16]

 

; 这里使用“段间直接转移指令”,也称为“远转移far jump”。
; A. “直接”:目标地址像立即数一样,“直接”在指令的机器代码中就是直接寻址方式;
; B. “段间”:从当前代码段跳转到另一个代码段,此时需要更改CS段地址和IP偏移地址。

; LABEL_GO_BACK_TO_REAL:
; jmp 0:LABEL_REAL_ENTRY

; 第二行代码在这里指定了IP偏移是LABEL_REAL_ENTRY(这是跳回16位实模式段[SECTION .s16]的一个偏移);
; 第二行代码并没有指定CS段地址(直接复0),但是在前面的16位实模式段[SECTION .s16]中实际上直接操作了机器码,设置其CS段地址为cs的值(如下所示)

; (1) 实模式下长跳转指令:

;    BYTE1        |         BYTE2                 BYTE3         |        BYTE4                 BYTE5

;     OEAh         |                        Offset                          |                     Segment 

 

; (2) 在跳入32位代码段前的那个16位代码段对这里远跳转的段基址进行设置

; [SECTION .s16]

; [BITS16]

; LABEL_BEGIN:

; movax, cs

; movds, ax

; moves, ax

; movss, ax

; movsp, 0100h

; mov[LABEL_GO_BACK_TO_REAL+3], ax

 

Code16Len equ $ - LABEL_SEG_CODE16

; END of [SECTION .s16code]

 

 

  • 大小: 88.6 KB
分享到:
评论

相关推荐

    MaxDOS 5.6s U盘版

    很好用的U盘系统盘制作工具这次出的所谓MaxDOS 密码读取工具,其实只是读取了安装日志LOG里的设置值,并不是真正的MD5值被编译,本来不想保留这个东西的,但是如果不保留这个LOG文件的话,又无法完成自动卸载,怕有些人又...

    windows环境下32位汇编语言程序设计

    笔者从事汇编编程已经有十几年的历史了,从8086时代的DOS汇编编程开始到当前的Win32汇编编程,从一个初学者到现在能利用Win32汇编来解决大部分编程需求,中间也经过了很长时间的摸索和大量的挫折,所以笔者很清楚...

    微机课后题目答案 答案

    答:分段部件形成的32位线性地址中高10位作为寻址页目录表的偏移量,与控制寄存器CR3中页目录表基地址共同形成一个32位的地址指向页表中的一个页项,即为一个页面描述符。该页面项中高20位作为页面基地址,线性地址...

    softice 用户手册中文版

    Ring-3 16位保护模式(16位Win程序) 地址内容 使用INT 0x41 .DOT命令 理解从R-3到R-0的转变 第七章 使用断点 第八章 ----------------------------------------------------------- (...很累人那!今天就...

    自己动手写操作系统(含源代码).part2

    书中的实例操作系统采用IA32作为默认平台,所以保护模式也作为必备知识储备收入书中,而这是传统的操作系统实践书籍经常忽略的。总之,只要是开发自己的操作系统中需要的知识,书中都尽量涉及,以便于读者参考。 ...

    自己动手写操作系统(含源代码).part1

    书中的实例操作系统采用IA32作为默认平台,所以保护模式也作为必备知识储备收入书中,而这是传统的操作系统实践书籍经常忽略的。总之,只要是开发自己的操作系统中需要的知识,书中都尽量涉及,以便于读者参考。 ...

    代码优化:有效使用内存.part3

    第3章高速缓存子系统 3.1SRAM的工作原理 3.1.1历史概况 3.1.2内核 3.1.3触发器的设计 3.1.4逻辑非元件(取反器)的设计 3.1.5SRAM阵列的设计 3.1.6封装接口的设计 3.1.7读写时序图 3.1.8静态存储器的类型 3.2高速缓存...

    基于 AVR 的单片嵌入式系统原理与实践应用

    在32个通用工作寄存器中,有6个寄存器可以合并成为3个16位的,用于对数据存储器空间进行间接寻址的间接地址寄存器(存放地址指针),以实现高效的地址计算。这3个16位的间接地址寄存器称为:X寄存器,Y寄存器和Z...

    代码优化:有效使用内存.part2

    第3章高速缓存子系统 3.1SRAM的工作原理 3.1.1历史概况 3.1.2内核 3.1.3触发器的设计 3.1.4逻辑非元件(取反器)的设计 3.1.5SRAM阵列的设计 3.1.6封装接口的设计 3.1.7读写时序图 3.1.8静态存储器的类型 3.2高速缓存...

    代码优化:有效使用内存.part1

    第3章高速缓存子系统 3.1SRAM的工作原理 3.1.1历史概况 3.1.2内核 3.1.3触发器的设计 3.1.4逻辑非元件(取反器)的设计 3.1.5SRAM阵列的设计 3.1.6封装接口的设计 3.1.7读写时序图 3.1.8静态存储器的类型 3.2高速缓存...

    Rootkit的学习与研究

    保护模式篇章第三部分:直接访问硬件 1)修改iopl,ring3直接访问硬件 2)追加tss默认I/O许可位图区域 3)更改tss I/O许可位图指向 5。detour 修改函数执行路径,可用于对函数的控制流程进行重定路径。 1)...

    ROOTKIT专题的研究

    保护模式篇章第三部分:直接访问硬件 1)修改iopl,ring3直接访问硬件 2)追加tss默认I/O许可位图区域 3)更改tss I/O许可位图指向 5。detour 修改函数执行路径,可用于对函数的控制流程进行重定路径。 1)...

Global site tag (gtag.js) - Google Analytics