<span style="font-size:14px;">摘要:在<span lang="en-US">pmtest4</span>中,我们已经看到,对于非一致性代码,如何从低特权级转移向高特权级。但是我们该怎样从高特权级别,转移到低特权级别呢?本文,主要是为你解答这些疑问,展现实现这种特权级别转移的方法。另外,我们将在实践中探讨,为什么要设定<span lang="en-US">RPL</span>,是不是相对与<span lang="en-US">DPL</span>多此一举呢?在调试环节,我们将通过改动代码,排查相关错误来深入理解特权级检测机制。 </span><h1><a target=_blank name="t0"></a><span style="font-size:18px;color:#ff0000;">一、预备理论</span></h1><div><span style="font-size:18px;color:#ff0000;"> </span></div><span style="font-size:14px;"><strong><span lang="en-US">1.</span>关于堆栈</strong> 我们都知道,可以通过<span lang="en-US">call</span>和<span lang="en-US">jmp</span>实现长跳转或者短跳转(段内转移或者段间转移)。但是<strong><span lang="en-US">jmp</span>和<span lang="en-US">call</span>毕竟是有分别的,其中<span lang="en-US">call</span>指令是会影响到堆栈的</strong>:对于短调用,<span lang="en-US">call</span>指令执行时,下一条指令的<span lang="en-US">eip</span>入栈,<span lang="en-US">ret</span>指令执行时候,这个<span lang="en-US">eip</span>就会从堆栈中弹出。 短调用<span lang="en-US">call</span>指令执行前后堆栈的变化如下图所示(注意带有参数的<span lang="en-US">ret</span>指令): 长调用的情况与此相似,不同的是<span lang="en-US">call</span>指令执行时,被压栈的还有<span lang="en-US">cs</span>。 <img src="https://img-blog.csdn.net/20140304172631843?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdHJvY2hpbHVzZXM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /> </span>
<span style="font-size:14px;"> <strong><span lang="en-US"> </span></strong></span>
<span style="font-size:14px;"><strong><span lang="en-US">2.</span>特权级变换</strong> 注意到:如果我们采用<span lang="en-US">call</span>指令进行长跳转,而且特权级别发生了变换,这个时候就发生了堆栈切换,比如从<span lang="en-US">A</span>切换到了<span lang="en-US">B</span>。但是我们的数据保存在<span lang="en-US">A</span>上面,<strong>如何从<span lang="en-US">B</span>中返回呢?原来,<span lang="en-US">intel</span>提供了一种机制,能够将<span lang="en-US">A</span>上的一些内容<span lang="en-US">copy</span>到<span lang="en-US">B</span>堆栈中。同时,<span lang="en-US">A</span>堆栈要切换到<span lang="en-US">B</span>堆栈,但是它怎么知道<span lang="en-US">B</span>堆栈在哪里呢?答案:原来这个内容保存在任务的<span lang="en-US">TSS</span>结构中,<span lang="en-US">TSS</span>是任务状态段。</strong>每个任务都最多有四个堆栈段,但是只有一个<span lang="en-US">esp</span>和<span lang="en-US">ss</span>,<span lang="en-US">TSS</span>就解决了堆栈切换方面的数据保存问题。<span lang="en-US">TSS</span>的相关介绍,可以参考:保护模式及其编程——任务切换<span lang="en-US"><a target=_blank target="_blank" href="http://blog.csdn.net/trochiluses/article/details/19768579">http://blog.csdn.net/trochiluses/article/details/19768579</a></span>。 比如,我们目前是<span lang="en-US">ring 3</span>,需要转移到<span lang="en-US">ring 1</span>,那时,堆栈将自动切换到<span lang="en-US">ss1</span>和<span lang="en-US">esp1</span>。(由于只是从外层到内层切换,才从<span lang="en-US">TSS</span>中取得堆栈信息,所以,<span lang="en-US">TSS</span>中没有<span lang="en-US">ring 3</span>相关的堆栈信息。那么从内层向外层切换,如何获得目的地的信息呢)。 切换过程中有关堆栈的处理,可以参考:保护模式及其编程——保护的详尽意义:通过调用门转移特权级<span lang="en-US"><a target=_blank target="_blank" href="http://blog.csdn.net/trochiluses/article/details/19573651">http://blog.csdn.net/trochiluses/article/details/19573651</a></span> <strong>好了,我们已经知道<span lang="en-US">call------ret</span>分别实现特权级高低转化,可能我们平时一直先使用<span lang="en-US">call</span>,然后再使用<span lang="en-US">ret</span>,以至于你对它的印象好像<span lang="en-US">if</span>和<span lang="en-US">else</span>,实际上不是这样的,我们通过<span lang="en-US">retf</span>指令,单独使用就可以实现特权级别从<span lang="en-US">0</span>到<span lang="en-US">3</span>,从高到低。</strong> </span><h1><a target=_blank name="t1"></a><span style="font-size:18px;color:#ff0000;">二、代码分析</span></h1><span style="font-size:14px;"><span lang="en-US">pmtest5.a</span>:我们需要增加一个<span lang="en-US">ring 3</span>的代码段,堆栈段,另外为了在<span lang="en-US">rign 3</span>下打印字符,需要将<span lang="en-US">video</span>段的<span lang="en-US">DPL</span>改成<span lang="en-US">3.</span>我们先来写一个测试版本<span lang="en-US">a:</span>程序执行流程和前面没有太大区别,在保护模式重打印字符串之后,我们让程序进入<span lang="en-US">ring 3</span>代码段,并且死循环,需要添加如下代码。 </span>
<span style="font-size:14px;"></span><div class="dp-highlighter bg_plain"><div style="display: block;" class="bar"><div class="tools"><strong>[plain]</strong> <a target=_blank href="http://blog.csdn.net/trochiluses/article/details/20473941#" class="ViewSource" title="view plain">view plain</a><span data-mod="popu_168"> <a target=_blank href="http://blog.csdn.net/trochiluses/article/details/20473941#" class="CopyToClipboard" title="copy">copy</a></span><span data-mod="popu_169"> <a target=_blank href="http://blog.csdn.net/trochiluses/article/details/20473941#" class="PrintSource" title="print">print</a></span><a target=_blank href="http://blog.csdn.net/trochiluses/article/details/20473941#" class="About" title="?">?</a><span class="tracking-ad" data-mod="popu_167"><a target=_blank href="https://code.csdn.net/snippets/217080" target="_blank" title="在CODE上查看代码片" style="text-indent:0;"><img src="https://code.csdn.net/assets/CODE_ico.png" alt="在CODE上查看代码片" style="position:relative;top:1px;left:2px;" height="12" width="12" /></a></span><span class="tracking-ad" data-mod="popu_170"><a target=_blank href="https://code.csdn.net/snippets/217080/fork" target="_blank" title="派生到我的代码片" style="text-indent:0;"><img src="https://code.csdn.net/assets/ico_fork.svg" alt="派生到我的代码片" style="position:relative;top:2px;left:2px;" height="12" width="12" /></a></span></div></div><ol start="1"><li class="alt"><span><span>push SelectorStack3 </span></span></li><li><span>push TopOfStack3 </span></li><li class="alt"><span>push SelecorCodeRing3 </span></li><li><span>push 0 </span></li></ol></div>
<span style="font-size:14px;"> 但是,如果我们去改动</span><span style="font-family:Arial,Helvetica,sans-serif;font-size:14px;">SelectorStack3和</span><span style="font-family:Arial,Helvetica,sans-serif;font-size:14px;">SelecorCodeRing3的描述符中RPL的字段,变成0,发现</span><span style="font-size:14px;">这个程序<span lang="en-US">pmtest5.asm</span>并没有像我们想象的那样,进入<span lang="en-US">ring3.</span>显然,<span lang="en-US">retf</span>的指令没有被正确执行。想一想,为什么?</span>
<span style="font-size:14px;"><span lang="en-US"> pmtest5.b</span>:在<span lang="en-US">a</span>的基础上,我们让程序在<span lang="en-US">ring3</span>代码段,进入死循环之前,调用调用门。在<span lang="en-US">pmtest4</span>的和<span lang="en-US">a</span>的基础上,我们需要做如下改动: 改动调用门的描述符和选择符到<span lang="en-US">ring3</span>;添加<span lang="en-US">TSS</span>以便能够从低特权级别转移到高特权级,并进行相关初始化。 最后,我们让程序在调用门的目标段,也就是<span lang="en-US">ring 0</span>中,跳入局部任务。 总结一下,<span lang="en-US">pmtest5</span>的执行流程:实模式<span lang="en-US">---</span>保护模式<span lang="en-US">32b</span>代码段<span lang="en-US">ring 0---</span>通过<span lang="en-US">retf</span>进入<span lang="en-US">ring 3</span>的代码段——通过调用门,进入<span lang="en-US">ring 0</span>的目标代码段——通过<span lang="en-US">jmp</span>,跳转到<span lang="en-US">LDT</span>局部任务。 </span><h1><a target=_blank name="t2"></a><span style="font-size:18px;color:#ff0000;">三、调试过程中发现的错误与分析</span></h1><span style="font-size:14px;"> <strong>1)为什么要有<span lang="en-US">RPL</span></strong>,我们都知道有“别名段”,那么别名段意味着什么呢?同一个段,可以对应着不同的<span lang="en-US">selector</span>,也就能够对应不同的<span lang="en-US">RPL</span>,这样就有利于实现自由的权限管理。 我们回忆,最开始的<span lang="en-US">retf</span>并没有生效,猜想到可能是权限的问题。我们从<span lang="en-US">ring0 </span>经过<span lang="en-US">retf </span>到<span lang="en-US">ring 3</span>; <span lang="en-US">call</span>,从<span lang="en-US">ring 3--->ring 0</span>,要求:<span lang="en-US">CPL<=DPL_G,RPL<=RPL_G,CPL>=DPL_B;</span>变化以后<span lang="en-US">CPL=DPL_B=0</span> <span lang="en-US">retf</span>是相反的过程,<span lang="en-US">ring 0--->ring 3,</span>要求:<span lang="en-US">CPL<DPL_B</span>,<span lang="en-US">RPL<DPL_B!!!</span>注意,<span lang="en-US">retf</span>只能用于不同特权级之间,所以没有等于,这也就是为什么开始转移失败了。</span>
<span style="font-size:14px;"><strong>2)堆栈切换的特权级检查</strong></span>
<span style="font-size:14px;"> 更改TSS段中,将ss对应部分改成selectorstack3,然后就能在调试bochs的时候收到如下信息:</span>
<span style="font-size:14px;">00082857633e[CPU0 ] call_protected: SS selector.rpl != CS descr.dpl <strong>注明:开启出错信息的方法,在.bochsrc中添加对于log信息的注释<span lang="en-US">#log</span></strong></span><span style="font-family:Arial,Helvetica,sans-serif;"><span style="white-space:normal"> </span></span>
<span style="font-size:14px;">程序代码如下:</span>
%include "head.inc"
org 0100h
jmp LABEL_BEGIN
[SECTION .gdt]
;GDT base, length, attr
LABEL_GDT: Descriptor 0, 0, 0
LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW
LABEL_DESC_CODE16: Descriptor 0, 0FFFFH, DA_C
LABEL_DESC_DATA: Descriptor 0, SegDataLen-1, DA_DRW
LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_C+DA_32
LABEL_DESC_CODE_DEST: Descriptor 0, SegCodeDestLen-1,DA_C+DA_32
LABEL_DESC_LDT: Descriptor 0, LDTLen-1, DA_LDT
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW + DA_DPL3
LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1,DA_C + DA_32 + DA_DPL3
LABEL_DESC_STACK3: Descriptor 0, TopOfStack3,DA_DRWA + DA_32 + DA_DPL3
LABEL_DESC_TSS: Descriptor 0, TSSLen-1, DA_386TSS
LABEL_CALL_GATE_TEST: Gate SelectorCodeDest,0,0,DA_386CGate + DA_DPL3
GdtLen equ $-LABEL_GDT
GdtPtr dw GdtLen-1;注意,长度都是实际长度减1
dd 0 ;段基地址,注意,这里之所以没有直接制定,是因为还没有确定保护模式下gdt的基地址
;选择子
SelectorData equ LABEL_DESC_DATA - LABEL_GDT
SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorStack equ LABEL_DESC_STACK - LABEL_GDT
SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT
SelectorLDT equ LABEL_DESC_LDT - LABEL_GDT
SelectorCodeDest equ LABEL_DESC_CODE_DEST - LABEL_GDT
SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT +SA_RPL3
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
SelectorCodeRing3 equ LABEL_DESC_CODE_RING3 - LABEL_GDT +SA_RPL3
SelectorStack3 equ LABEL_DESC_STACK3 - LABEL_GDT +SA_RPL3
SelectorTSS equ LABEL_DESC_TSS - LABEL_GDT
;end of section gdt
;--------------------------------------------------------------
[SECTION .data]
[BITS 32]
ALIGN 32
LABEL_SEG_DATA:
SPValueInRealModel dw 0
PMMessage: db "Coming into protect mode now !",0
OffsetPMMessage equ PMMessage - $$
StrTest: db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
OffsetStrTest equ StrTest - $$
SegDataLen equ $ - LABEL_SEG_DATA
;end of section data
;-----------------------------section:global stack------------------
[SECTION .gs]
ALIGN 32
[BITS 32]
LABEL_SEG_STACK:
times 512 db 0
TopOfStack equ $-LABEL_SEG_STACK-1
;----------------------section:LDT----------------------------------
[SECTION .ldt]
align 32
[bits 32]
LABEL_SEG_LDT:
LABEL_DESC_CODEA: Descriptor 0,CodeALen -1,DA_C+DA_32
LDTLen equ $- LABEL_SEG_LDT
SelectorCodeA equ LABEL_DESC_CODEA - LABEL_SEG_LDT + SA_TIL
;end of ldt segment
;-------------------------------section:codeA------------------------
[section .codeA]
align 32
[bits 32]
LABEL_SEG_CODEA:
mov ax,SelectorVideo
mov gs,ax
mov ah,0ch
mov al,'L'
mov edi,(2*80+0)*2
mov [gs:edi],ax
jmp SelectorCode16:0
CodeALen equ $-LABEL_SEG_CODEA
;end of secion codeA
;------------------------------section:s16 begin---------------------
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
xchg bx,bx
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h
mov [LABEL_GO_BACK_TO_REAL+3],ax
mov [SPValueInRealModel],sp
;for segment code32
xor eax,eax
mov eax,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
;for segment code16
xor eax,eax
mov ax,cs
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
;for segment data
xor eax,eax
mov eax,ds
shl eax,4
add eax,LABEL_SEG_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
;for segment ldt
xor eax,eax
mov eax,ds
shl eax,4
add eax,LABEL_SEG_LDT;
mov word [LABEL_DESC_LDT +2],ax
shr eax,16
mov byte [LABEL_DESC_LDT + 4],al
mov byte [LABEL_DESC_LDT + 7],ah
;for segment codeA
xor eax,eax
mov eax,ds
shl eax,4
add eax,LABEL_SEG_CODEA;
mov word [LABEL_DESC_CODEA +2],ax
shr eax,16
mov byte [LABEL_DESC_CODEA + 4],al
mov byte [LABEL_DESC_CODEA + 7],ah
;for segment stack
xor eax,eax
mov eax,ds
shl eax,4
add eax,LABEL_SEG_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
;no need for video base
;for segment dstcode
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_CODE_DEST
mov word [LABEL_DESC_CODE_DEST +2],ax
shr eax,16
mov byte [LABEL_DESC_CODE_DEST +4],al
mov byte [LABEL_DESC_CODE_DEST +7],ah
;for segment code ring 3
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_CODE_RING3
mov word [LABEL_DESC_CODE_RING3 +2],ax
shr eax,16
mov byte [LABEL_DESC_CODE_RING3 +4],al
mov byte [LABEL_DESC_CODE_RING3 +7],ah
;for segment code stack 3
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_STACK3
mov word [LABEL_DESC_STACK3 +2],ax
shr eax,16
mov byte [LABEL_DESC_STACK3 +4],al
mov byte [LABEL_DESC_STACK3 +7],ah
;for segment code stack 3
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_TSS
mov word [LABEL_DESC_TSS +2],ax
shr eax,16
mov byte [LABEL_DESC_TSS +4],al
mov byte [LABEL_DESC_TSS +7],ah
xor eax,eax
mov ax,ds
shl eax,4
add eax, LABEL_GDT
mov dword [GdtPtr +2 ],eax
lgdt [GdtPtr]
cli
in al,92h
or al,02h
out 92h,al
mov eax,cr0
or eax,1
mov cr0,eax
jmp dword SelectorCode32:0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LABEL_REAL_ENTRY: ;come here from protect model
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,[SPValueInRealModel]
in al,92h
and al,11111101b
out 92h,al
sti
mov ax,4c00h
int 21h
;end of section .s16
;------------------------section:code32,start of protect model--------
[SECTION .s32]
[BITS 32]
LABEL_SEG_CODE32:
mov ax,SelectorVideo
mov gs,ax
mov ax,SelectorData
mov ds,ax
mov ax,SelectorStack
mov ss,ax
mov esp,TopOfStack
mov ah,0ch
xor esi,esi
xor edi,edi
mov esi,OffsetPMMessage
mov edi,(80*11+0)*2
cld
.loopPmMessage:
lodsb
test al,al
jz .end
mov [gs:edi],ax
add edi,2
jmp .loopPmMessage
.end: ;
call DispReturn
mov ax,SelectorTSS
ltr ax
push SelectorStack3
push TopOfStack3
push SelectorCodeRing3
push 0
xchg bx,bx
retf
xchg bx,bx
call SelectorCallGateTest:0
;Load LDT
mov ax,SelectorLDT
lldt ax
jmp SelectorCodeA:0
;function: read and print 8 byte from es:0
TestRead:
xor esi,esi
mov ecx,8
.loopForEightBype:
mov al,[es:esi]
call DispAL
inc esi
loop .loopForEightBype
call DispReturn
ret; be sure of this
;funtion:
;write 8byte to es:OffsetStrTest
;input:es
TestWrite:
push esi
push edi
xor esi,esi
xor edi,edi
mov esi,OffsetStrTest
cld
.loopForEightBype:
lodsb ;ds:si->al
test al,al
jz .end
mov [es:edi],al
inc edi
jmp .loopForEightBype
.end:
pop edi
pop esi
ret
;funtion DispAL
; display the number in AL
;input: AL-the number
; edi-the position to display
;modified:ax,edi
DispAL:
push ecx
push edx
mov ah,0ch
mov dl,al
shr al,4
mov ecx,2
.begin:
and al,01111b
cmp al,9
ja .moreThanNine
add al,'0'
jmp .end
.moreThanNine:
sub al,0ah
add al,'A'
.end:
mov [gs:edi],ax
add edi,2
mov al,dl
loop .begin
add edi,2
pop edx
pop ecx
ret
;DispAL
;function DispReturn
;if edi=(a*80 + b)*2
;then edi=(a*80 + 80)*2
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
;end for function DispReturn
SegCode32Len equ $-LABEL_SEG_CODE32
;end of section .s32
;---------------------section s16code,before return to real-----------
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
;return to real model
mov ax,SelectorNormal
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
mov eax,cr0
and al,11111110b
mov cr0,eax
LABEL_GO_BACK_TO_REAL:
jmp 0:LABEL_REAL_ENTRY;
Code16Len equ $-LABEL_SEG_CODE16
;end of section s16code
;---------------------section:sdest---------------------------------
[section .sdest]
[bits 32]
LABEL_SEG_CODE_DEST:
mov ax,SelectorVideo
mov gs,ax
mov edi,(80*12+0)*2
mov ah,0ch
mov al,'C'
mov [gs:edi],ax
;load LDT
mov ax,SelectorLDT
lldt ax
jmp SelectorCodeA:0
SegCodeDestLen equ $-LABEL_SEG_CODE_DEST
;-------------------section stack ring3-----------------------------
[section .s3]
align 32
[bits 32]
LABEL_SEG_STACK3:
times 512 db 0
TopOfStack3 equ $-LABEL_SEG_STACK3 - 1
;----------------------section CodeRing3-----------------------------
[section .CodeRing3]
[bits 32]
LABEL_SEG_CODE_RING3:
mov ax,SelectorVideo
mov gs,ax
mov edi,(80*14+0)*2
mov ah,0ch
mov al,'3'
mov [gs:edi],ax
call SelectorCallGateTest:0
jmp $
SegCodeRing3Len equ $ - LABEL_SEG_CODE_RING3
;------------------------section TSS-------------------------------
[section .tss]
[bits 32]
LABEL_SEG_TSS:
dd 0
dd TopOfStack
dd SelectorStack
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dd 0
dw 0
dw $ - LABEL_SEG_TSS +2
db 0ffh
TSSLen equ $ - LABEL_SEG_TSS