问题:
如何在不同特权级的代码段之间跳转执行?
一种新的描述符:门描述符(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)