问题:

  如何在不同特权级的代码段之间跳转执行?

 

一种新的描述符:门描述符(Gate Descriptor)

  通过门描述符在不同的特权级的代码间进行跳转

  根据应用场景的不同,门描述符分为:

    调用门(Call Gate)

    中断门(Interrupt  Gate)

    陷阱门(Trap Gate)

    任务门(Task Gate)

门描述符的一个功能就是可以在不同特权级的代码间进行跳转。

 

门描述符的内存结构:

  每一个门描述符占用8字节内存

  不同类型门描述符的内存含义不同

 

 

 

汇编小贴士:

  汇编语言中的跳转方式

    段内跳转(近跳转):call , jmp,call调用还可以返回回来,jmp是无条件跳转,无法返回来

      参数为相对地址,函数调用时只需要保存当前偏移地址

      近跳转就是段内的跳转,例如在段内定义了一个函数,就可以直接使用call来调用这个函数了,只保存当前偏移地址,例如,跳转到离当前10个字节的地址处执行,就只需将10保存下来,然后跳转执行,执行完之后将10拿出来,向回退10个字节就可以了。

    段间跳转(远跳转):call far, jmp  far

      参数为选择子和偏移地址

      函数调用时需要同时保存段基地址和偏移地址

          远跳转就是从当前段跳转到另一个段

      例如:段间远跳转的call far,将当前段的基地址即选择子保存下来,将当前段的偏移地址也保存下来。就是保存CS和IP的值。

 

调用门实验:

inc.asm

 1 ; Segment Attribute
 2 DA_32    equ    0x4000
 3 DA_DR    equ    0x90
 4 DA_DRW   equ    0x92
 5 DA_DRWA  equ    0x93
 6 DA_C     equ    0x98
 7 DA_CR    equ    0x9A
 8 DA_CCO   equ    0x9C
 9 DA_CCOR  equ    0x9E
10 
11 ; Segment Privilege
12 DA_DPL0  equ   0x00        ; DPL = 0
13 DA_DPL1  equ   0x20        ; DPL = 1
14 DA_DPL2  equ   0x40        ; DPL = 2
15 DA_DPL3  equ   0x60        ; DPL = 3
16 
17 ; Special Attribute
18 DA_LDT             equ        0x82
19 DA_TaskGate        equ        0x85    ; 
20 DA_386TSS        equ        0x89    ;
21 DA_386CGate        equ        0x8C    ;
22 DA_386IGate        equ        0x8E    ;
23 DA_386tgATE        equ        0x8F    ;
24 
25 ; Selector Attribute
26 SA_RPL0    equ    0
27 SA_RPL1    equ    1
28 SA_RPL2    equ    2
29 SA_RPL3    equ    3
30 
31 SA_TIG    equ    0
32 SA_TIL    equ    4
33 
34 ; 描述符
35 ; usage: Descriptor Base, Limit, Attr
36 ;        Base:  dd
37 ;        Limit: dd (low 20 bits available)
38 ;        Attr:  dw (lower 4 bits of higher byte are always 0)
39 %macro Descriptor 3                              ; 段基址, 段界限, 段属性
40     dw    %2 & 0xFFFF                         ; 段界限1
41     dw    %1 & 0xFFFF                         ; 段基址1
42     db    (%1 >> 16) & 0xFF                   ; 段基址2
43     dw    ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF) ; 属性1 + 段界限2 + 属性2
44     db    (%1 >> 24) & 0xFF                   ; 段基址3
45 %endmacro                                     ; 共 8 字节
46 
47 ;Gate
48 ; usage : Gate Selector, Offset, DCount, Attr
49 ;         Selector : dw
50 ;          Offset   : dd
51 ;          DCount   : db
52 ;          Attr       : db
53 %macro Gate 4
54     dw        (%2 & 0xFFFF)                        ; pianyidizhi1
55     dw        %1                                    ; xuanzezi
56     dw        (%3 & 0x1F) | ((%4 << 8) & 0xFF00)    ; shu xing
57     dw        ((%2 >> 16) & 0xFFFF)                ; pianyidizhi2
58 %endmacro

17-23行和53-58行是我们新添加的门相关的定义。

loader.asm如下:

  1 %include "inc.asm"
  2 
  3 org 0x9000
  4 
  5 jmp ENTRY_SEGMENT
  6 
  7 [section .gdt]
  8 ; GDT definition
  9 ;                                 段基址,       段界限,       段属性
 10 GDT_ENTRY       :     Descriptor    0,            0,           0
 11 CODE32_DESC     :     Descriptor    0,    Code32SegLen  - 1,   DA_C + DA_32
 12 VIDEO_DESC      :     Descriptor 0xB8000,       0x07FFF,       DA_DRWA + DA_32
 13 STACK32_DESC    :     Descriptor    0,      TopOfStack32,      DA_DRW + DA_32 
 14 FUNCTION_DESC    :     Descriptor    0,      FunctionSegLen - 1, DA_C + DA_32
 15 ; Gate Descriptor 
 16 ; Call Gate                       xuanzezi            pianyi    canshugeshu      shuxing
 17 FUNC_CG_ADD_DESC  :   Gate        FunctionSelector,      CG_Add,         0,           DA_386CGate    
 18 FUNC_CG_SUB_DESC  :   Gate        FunctionSelector,      CG_Sub,         0,           DA_386CGate
 19 ; GDT end
 20 
 21 GdtLen    equ   $ - GDT_ENTRY
 22 
 23 GdtPtr:
 24           dw   GdtLen - 1
 25           dd   0
 26           
 27           
 28 ; GDT Selector
 29 
 30 Code32Selector        equ (0x0001 << 3) + SA_TIG + SA_RPL0
 31 VideoSelector         equ (0x0002 << 3) + SA_TIG + SA_RPL0
 32 Stack32Selector       equ (0x0003 << 3) + SA_TIG + SA_RPL0
 33 FunctionSelector      equ (0x0004 << 3) + SA_TIG + SA_RPL0
 34 FuncCGAddSelector      equ (0x0005 << 3) + SA_TIG + SA_RPL0
 35 FuncCGSubSelector      equ (0x0006 << 3) + SA_TIG + SA_RPL0
 36 
 37 ; end of [section .gdt]
 38 
 39 TopOfStack16    equ  0x7c00
 40 
 41 [section .s16]
 42 [bits 16]
 43 ENTRY_SEGMENT:
 44     mov ax, cs
 45     mov ds, ax
 46     mov es, ax
 47     mov ss, ax
 48     mov sp, TopOfStack16
 49     
 50     ; initialize GDT for 32 bits code segment
 51     mov esi, CODE32_SEGMENT
 52     mov edi, CODE32_DESC
 53     
 54     call InitDescItem
 55     
 56     mov esi, STACK32_SEGMENT
 57     mov edi, STACK32_DESC
 58     
 59     call InitDescItem
 60     
 61     mov esi, FUNCTION_SEGMENT
 62     mov edi, FUNCTION_DESC
 63     
 64     call InitDescItem
 65     
 66     ; initialize GDT pointer struct
 67     mov eax, 0
 68     mov ax, ds
 69     shl eax, 4
 70     add eax, GDT_ENTRY
 71     mov dword [GdtPtr + 2], eax
 72 
 73     ; 1. load GDT
 74     lgdt [GdtPtr]
 75     
 76     ; 2. close interrupt
 77     cli 
 78     
 79     ; 3. open A20
 80     in al, 0x92
 81     or al, 00000010b
 82     out 0x92, al
 83     
 84     ; 4. enter protect mode
 85     mov eax, cr0
 86     or eax, 0x01
 87     mov cr0, eax
 88     
 89     ; 5. jump to 32 bits code
 90     jmp dword Code32Selector : 0
 91 
 92     
 93 ; esi    --> code segment label
 94 ; edi    --> descriptor label
 95 InitDescItem:
 96     push eax
 97     
 98     mov eax, 0
 99     mov ax, cs
100     shl eax, 4
101     add eax, esi
102     mov word [edi + 2], ax
103     shr eax, 16
104     mov byte [edi + 4], al
105     mov byte [edi + 7], ah
106     
107     pop eax
108     
109     ret
110      
111 [section .s32]
112 [bits 32]
113 CODE32_SEGMENT:
114     mov ax, VideoSelector
115     mov gs, ax
116     
117     mov ax, Stack32Selector
118     mov ss, ax
119     
120     mov eax, TopOfStack32
121     mov esp, eax
122     
123     mov ax, 2
124     mov bx, 1
125     
126     call FuncCGAddSelector : 0
127     
128     jmp $
129 
130 Code32SegLen    equ    $ - CODE32_SEGMENT
131 
132 [section .func]
133 [bits 32]
134 FUNCTION_SEGMENT:
135 
136 ; ax  -->  a
137 ; bx  -->  b
138 ; 
139 ; return 
140 ;      cx  --> a + b
141 AddFunc:
142     mov cx, ax
143     add cx, bx
144     retf
145 
146 CG_Add        equ        AddFunc - $$
147 
148 ; ax  -->  a
149 ; bx  -->  b
150 ; 
151 ; return 
152 ;      cx  --> a - b
153 SubFunc:
154     mov cx, ax
155     sub cx, bx
156     retf
157     
158 CG_Sub        equ        SubFunc - $$
159 
160 FunctionSegLen   equ  $ - FUNCTION_SEGMENT
161 
162 [section .gs]
163 [bits 32]
164 STACK32_SEGMENT:
165     times 1024 * 4 db 0
166     
167 Stack32SegLen    equ $ - STACK32_SEGMENT
168 TopOfStack32    equ Stack32SegLen - 1

我们新定义了一个func段,在里面定义了AddFunc和SubFunc函数。第14行将func段的描述符加入了全局段描述符表。

17、18行将两个门描述符加入了全局段描述符表。

33-35行分别是这几个新添加的段描述符的选择子。

下面我们单步执行:

首先使用ndisasm -o 0x9000  loader > loader.txt将可执行程序进行反汇编,结果如下:

我们重点关注跳转第58行的跳转这里。

打断点执行:

 

将ax,bx赋值准备跳转,这时的cx值如下:

 

执行函数,cx的值有变化:

因为只是加的最低位,所以高位的那个9我们不用关心。

 

通过以下的调用方式我们可以得到同样的结果:

126、127行直接使用段基址加段内偏移的方式调用。

将使用调用门的方式截图如下:

126、127现在是使用调用门的方式,冒号后面的0在这里没有什么意义,只是为了语法需要,如果去掉冒号和0,编译器会认为这是一个段内的函数调用。

使用调用门选择子时,程序会根据选择子找到描述符,然后根据描述符中的选择子和偏移再去调用函数。这个调用门选择子在这里相当于一个函数指针。

 

实验结论:

  门描述符是一种特殊的描述符,需要注册于段描述符表

  调用门可以看做一个函数指针(保存具体函数的入口地址)

  通过调用门选择子对相应的函数进行远调用(call far)

  可以直接使用  选择子:偏移地址  的方式调用其他段的函数

  使用调用门时偏移地址无意义,仅仅是语法需要

历史遗留问题:

  保护模式下的不同段之间如何进行代码复用(如:调用同一个函数)?

解决方案:

  将不同代码段中需要复用的函数定义到独立的段中(retf, f是far的意思)

  计算每一个可复用函数的偏移量(FuncName - $$)

  通过   段选择子:偏移地址   的方式对目标函数进行远调用

 

对程序进行重构:

  1 %include "inc.asm"
  2 
  3 org 0x9000
  4 
  5 jmp ENTRY_SEGMENT
  6 
  7 [section .gdt]
  8 ; GDT definition
  9 ;                                 段基址,       段界限,       段属性
 10 GDT_ENTRY       :     Descriptor    0,            0,           0
 11 CODE32_DESC     :     Descriptor    0,    Code32SegLen  - 1,   DA_C + DA_32
 12 VIDEO_DESC      :     Descriptor 0xB8000,       0x07FFF,       DA_DRWA + DA_32
 13 DATA32_DESC     :     Descriptor    0,    Data32SegLen  - 1,   DA_DR + DA_32
 14 STACK32_DESC    :     Descriptor    0,      TopOfStack32,      DA_DRW + DA_32    
 15 CODE16_DESC     :     Descriptor    0,           0xFFFF,       DA_C
 16 UPDATE_DESC     :     Descriptor    0,           0xFFFF,       DA_DRW
 17 TASK_A_LDT_DESC :     Descriptor    0,       TaskALdtLen - 1,  DA_LDT
 18 FUNCTION_DESC    :      Descriptor    0,         FunctionSegLen - 1, DA_C + DA_32
 19 ; GDT end
 20 
 21 GdtLen    equ   $ - GDT_ENTRY
 22 
 23 GdtPtr:
 24           dw   GdtLen - 1
 25           dd   0
 26           
 27           
 28 ; GDT Selector
 29 
 30 Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL0
 31 VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL0
 32 Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
 33 Stack32Selector   equ (0x0004 << 3) + SA_TIG + SA_RPL0
 34 Code16Selector    equ (0x0005 << 3) + SA_TIG + SA_RPL0
 35 UpdateSelector    equ (0x0006 << 3) + SA_TIG + SA_RPL0
 36 TaskALdtSelector  equ (0x0007 << 3) + SA_TIG + SA_RPL0
 37 FunctionSelector  equ (0x0008 << 3) + SA_TIG + SA_RPL0
 38 ; end of [section .gdt]
 39 
 40 TopOfStack16    equ  0x7c00
 41 
 42 [section .dat]
 43 [bits 32]
 44 DATA32_SEGMENT:
 45     DTOS                 db    "D.T.OS!", 0
 46     DTOS_OFFSET          equ   DTOS - $$
 47     HELLO_WORLD          db    "Hello World!", 0
 48     HELLO_WORLD_OFFSET   equ  HELLO_WORLD - $$
 49 
 50 Data32SegLen  equ $ - DATA32_SEGMENT
 51 
 52 [section .s16]
 53 [bits 16]
 54 ENTRY_SEGMENT:
 55     mov ax, cs
 56     mov ds, ax
 57     mov es, ax
 58     mov ss, ax
 59     mov sp, TopOfStack16
 60     
 61     mov [BACK_TO_REAL_MODE + 3], ax 
 62     
 63     ; initialize GDT for 32 bits code segment
 64     mov esi, CODE32_SEGMENT
 65     mov edi, CODE32_DESC
 66     
 67     call InitDescItem
 68     
 69     mov esi, DATA32_SEGMENT
 70     mov edi, DATA32_DESC
 71     
 72     call InitDescItem
 73     
 74     mov esi, DATA32_SEGMENT
 75     mov edi, STACK32_DESC
 76     
 77     call InitDescItem
 78     
 79     mov esi, CODE16_SEGMENT
 80     mov edi, CODE16_DESC
 81     
 82     call InitDescItem
 83     
 84     mov esi, TASK_A_LDT_ENTRY
 85     mov edi, TASK_A_LDT_DESC
 86     
 87     call InitDescItem
 88     
 89     mov esi, TASK_A_CODE32_SEGMENT
 90     mov edi, TASK_A_CODE32_DESC
 91     
 92     call InitDescItem
 93     
 94     mov esi, TASK_A_DATA32_SEGMENT
 95     mov edi, TASK_A_DATA32_DESC
 96     
 97     call InitDescItem
 98     
 99     mov esi, TASK_A_STACK32_SEGMENT
100     mov edi, TASK_A_STACK32_DESC
101     
102     call InitDescItem
103     
104     mov esi, FUNCTION_SEGMENT
105     mov edi, FUNCTION_DESC
106     
107     call InitDescItem
108     
109     ; initialize GDT pointer struct
110     mov eax, 0
111     mov ax, ds
112     shl eax, 4
113     add eax, GDT_ENTRY
114     mov dword [GdtPtr + 2], eax
115 
116     ; 1. load GDT
117     lgdt [GdtPtr]
118     
119     ; 2. close interrupt
120     cli 
121     
122     ; 3. open A20
123     in al, 0x92
124     or al, 00000010b
125     out 0x92, al
126     
127     ; 4. enter protect mode
128     mov eax, cr0
129     or eax, 0x01
130     mov cr0, eax
131     
132     ; 5. jump to 32 bits code
133     jmp dword Code32Selector : 0
134 
135 BACK_ENTRY_SEGMENT:
136         mov ax, cs
137         mov ds, ax
138         mov es, ax
139         mov ss, ax
140         mov sp, TopOfStack16
141         
142         in al, 0x92
143         and al, 11111101b
144         out 0x92, al
145         
146         sti 
147         
148         mov bp, HELLO_WORLD
149         mov cx, 12
150         mov dx, 0
151         mov ax, 0x1301
152         mov bx, 0x0007
153         int 0x10
154         
155         jmp $
156 
157 ; esi    --> code segment label
158 ; edi    --> descriptor label
159 InitDescItem:
160     push eax
161     
162     mov eax, 0
163     mov ax, cs
164     shl eax, 4
165     add eax, esi
166     mov word [edi + 2], ax
167     shr eax, 16
168     mov byte [edi + 4], al
169     mov byte [edi + 7], ah
170     
171     pop eax
172     
173     ret
174     
175 
176 [section .16]
177 [bits 16]
178 CODE16_SEGMENT:
179     mov ax, UpdateSelector
180     mov ds, ax
181     mov es, ax
182     mov fs, ax
183     mov gs, ax
184     mov ss, ax
185     
186     mov eax, cr0
187     and al, 11111110b
188     mov cr0, eax
189     
190 BACK_TO_REAL_MODE:
191     jmp 0 : BACK_ENTRY_SEGMENT
192     
193 Code16SegLen    equ $ - CODE16_SEGMENT
194 
195 [section .func]
196 [bits 32]
197 FUNCTION_SEGMENT:
198 
199 ; ds:ebp   --> string address
200 ; bx       --> attribute
201 ; dx       --> dh : row, dl : col
202 PrintStringFunc:
203     push ebp
204     push eax
205     push edi 
206     push cx
207     push dx
208     
209 print:
210     mov cl, [ds:ebp]
211     cmp cl, 0
212     je end
213     mov eax, 80
214     mul dh
215     add al, dl
216     shl eax, 1
217     mov edi, eax
218     mov ah, bl
219     mov al, cl
220     mov [gs:edi], ax
221     inc ebp
222     inc dl
223     jmp print
224     
225 end:
226     pop dx
227     pop cx
228     pop edi
229     pop eax
230     pop ebp
231     
232     retf
233     
234 PrintString        equ        PrintStringFunc - $$
235 
236 FunctionSegLen        equ        $ - FUNCTION_SEGMENT
237     
238 [section .s32]
239 [bits 32]
240 CODE32_SEGMENT:
241     mov ax, VideoSelector
242     mov gs, ax
243     
244     mov ax, Stack32Selector
245     mov ss, ax
246     
247     mov eax, TopOfStack32
248     mov esp, eax
249     
250     mov ax, Data32Selector
251     mov ds, ax
252     
253     mov ebp, DTOS_OFFSET
254     mov bx, 0x0C
255     mov dh, 12
256     mov dl, 33
257     
258     call FunctionSelector : PrintString
259     
260     mov ebp, HELLO_WORLD_OFFSET
261     mov bx, 0x0C
262     mov dh, 13
263     mov dl, 30
264     
265     call FunctionSelector : PrintString
266     
267     mov ax, TaskALdtSelector
268     
269     lldt ax
270     
271     jmp TaskACode32Selector : 0
272     
273     ;jmp Code16Selector : 0
274 
275     
276 Code32SegLen    equ    $ - CODE32_SEGMENT
277 
278 [section .gs]
279 [bits 32]
280 STACK32_SEGMENT:
281     times 1014 * 4 db 0
282     
283 Stack32SegLen    equ $ - STACK32_SEGMENT
284 TopOfStack32    equ Stack32SegLen - 1
285 
286 
287 ; ==================================
288 ;        
289 ;        Task A Code Segment
290 ;
291 ;===================================
292 
293 [section .task-a-ldt]
294 ; Task A LDT definition
295 ;                                        段基址                     段界限                段属性
296 TASK_A_LDT_ENTRY:
297 TASK_A_CODE32_DESC    :   Descriptor       0,               TaskACode32SegLen - 1,  DA_C + DA_32
298 TASK_A_DATA32_DESC    :   Descriptor       0,               TaskAData32SegLen - 1,  DA_DR + DA_32
299 TASK_A_STACK32_DESC   :   Descriptor       0,               TaskAStack32SegLen - 1, DA_DRW + DA_32
300 
301 TaskALdtLen        equ   $ - TASK_A_LDT_ENTRY
302 
303 ; Task A LDT  Selector
304 TaskACode32Selector        equ  (0x0000 << 3) + SA_TIL + SA_RPL0
305 TaskAData32Selector     equ     (0x0001 << 3) + SA_TIL + SA_RPL0
306 TaskAStack32Selector    equ  (0x0002 << 3) + SA_TIL + SA_RPL0
307 
308 [section .task-a-dat]
309 [bits 32]
310 TASK_A_DATA32_SEGMENT:
311     TASK_A_STRING        db   "This is Task A", 0
312     TASK_A_STRING_OFFSET    equ     TASK_A_STRING - $$
313     
314 TaskAData32SegLen    equ $ - TASK_A_DATA32_SEGMENT
315 
316 [section .task-a-gs]
317 [bits 32]
318 TASK_A_STACK32_SEGMENT:
319     times 1024 db 0
320     
321 TaskAStack32SegLen    equ     $ - TASK_A_STACK32_SEGMENT
322 TaskATopOfStack32    equ     TaskAStack32SegLen - 1
323 
324 [section .task-a-s32]
325 [bits 32]
326 TASK_A_CODE32_SEGMENT:
327 
328     mov ax, VideoSelector
329     mov gs, ax
330     
331     mov ax, TaskAStack32Selector
332     mov ss, ax
333     
334     mov eax, TaskATopOfStack32
335     mov esp, eax
336     
337     mov ax, TaskAData32Selector
338     mov ds, ax
339     
340     mov ebp, TASK_A_STRING_OFFSET
341     mov bx, 0x0c
342     mov dh, 14
343     mov dl, 29
344     
345     call FunctionSelector : PrintString
346     
347     jmp Code16Selector : 0
348         
349 TaskACode32SegLen     equ  $ - TASK_A_CODE32_SEGMENT

运行结果如下:

 

 

小结:

  门描述符是一种特殊的描述符,需要注册于段描述符表

  门描述符分为:调用门、中断门、陷阱门、任务门

  调用门可以看做一个函数指针(保存具体函数的入口地址)

  调用门选择子对应的函数调用方式为远调用(call  far)

 

posted on 2018-09-09 22:16  周伯通789  阅读(655)  评论(0编辑  收藏  举报