这一节我们来研究从核心代码特权级转移到应用代码特权级。
首先将boot.asm贴出来如下:
1 org 0x7c00 2 3 jmp short start 4 nop 5 6 define: 7 BaseOfStack equ 0x7c00 8 BaseOfLoader equ 0x9000 9 RootEntryOffset equ 19 10 RootEntryLength equ 14 11 EntryItemLength equ 32 12 FatEntryOffset equ 1 13 FatEntryLength equ 9 14 15 header: 16 BS_OEMName db "D.T.Soft" 17 BPB_BytsPerSec dw 512 18 BPB_SecPerClus db 1 19 BPB_RsvdSecCnt dw 1 20 BPB_NumFATs db 2 21 BPB_RootEntCnt dw 224 22 BPB_TotSec16 dw 2880 23 BPB_Media db 0xF0 24 BPB_FATSz16 dw 9 25 BPB_SecPerTrk dw 18 26 BPB_NumHeads dw 2 27 BPB_HiddSec dd 0 28 BPB_TotSec32 dd 0 29 BS_DrvNum db 0 30 BS_Reserved1 db 0 31 BS_BootSig db 0x29 32 BS_VolID dd 0 33 BS_VolLab db "D.T.OS-0.01" 34 BS_FileSysType db "FAT12 " 35 36 start: 37 mov ax, cs 38 mov ss, ax 39 mov ds, ax 40 mov es, ax 41 mov sp, BaseOfStack 42 43 mov ax, RootEntryOffset 44 mov cx, RootEntryLength 45 mov bx, Buf 46 47 call ReadSector 48 49 mov si, Target 50 mov cx, TarLen 51 mov dx, 0 52 53 call FindEntry 54 55 cmp dx, 0 56 jz output 57 58 mov si, bx 59 mov di, EntryItem 60 mov cx, EntryItemLength 61 62 call MemCpy 63 64 mov ax, FatEntryLength 65 mov cx, [BPB_BytsPerSec] 66 mul cx 67 mov bx, BaseOfLoader 68 sub bx, ax 69 70 mov ax, FatEntryOffset 71 mov cx, FatEntryLength 72 73 call ReadSector 74 75 mov dx, [EntryItem + 0x1A] 76 mov si, BaseOfLoader 77 78 loading: 79 mov ax, dx 80 add ax, 31 81 mov cx, 1 82 push dx 83 push bx 84 mov bx, si 85 call ReadSector 86 pop bx 87 pop cx 88 call FatVec 89 cmp dx, 0xFF7 90 jnb BaseOfLoader 91 add si, 512 92 jmp loading 93 94 output: 95 mov bp, MsgStr 96 mov cx, MsgLen 97 call Print 98 99 last: 100 hlt 101 jmp last 102 103 104 ; cx --> index 105 ; bx --> fat table address 106 ; 107 ; return: 108 ; dx --> fat[index] 109 FatVec: 110 mov ax, cx 111 mov cl, 2 112 div cl 113 114 push ax 115 116 mov ah, 0 117 mov cx, 3 118 mul cx 119 mov cx, ax 120 121 pop ax 122 123 cmp ah, 0 124 jz even 125 jmp odd 126 127 even: ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i]; 128 mov dx, cx 129 add dx, 1 130 add dx, bx 131 mov bp, dx 132 mov dl, byte [bp] 133 and dl, 0x0F 134 shl dx, 8 135 add cx, bx 136 mov bp, cx 137 or dl, byte [bp] 138 jmp return 139 140 odd: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F ); 141 mov dx, cx 142 add dx, 2 143 add dx, bx 144 mov bp, dx 145 mov dl, byte [bp] 146 mov dh, 0 147 shl dx, 4 148 add cx, 1 149 add cx, bx 150 mov bp, cx 151 mov cl, byte [bp] 152 shr cl, 4 153 and cl, 0x0F 154 mov ch, 0 155 or dx, cx 156 157 return: 158 ret 159 160 ; ds:si --> source 161 ; es:di --> destination 162 ; cx --> length 163 MemCpy: 164 165 cmp si, di 166 167 ja btoe 168 169 add si, cx 170 add di, cx 171 dec si 172 dec di 173 174 jmp etob 175 176 btoe: 177 cmp cx, 0 178 jz done 179 mov al, [si] 180 mov byte [di], al 181 inc si 182 inc di 183 dec cx 184 jmp btoe 185 186 etob: 187 cmp cx, 0 188 jz done 189 mov al, [si] 190 mov byte [di], al 191 dec si 192 dec di 193 dec cx 194 jmp etob 195 196 done: 197 ret 198 199 ; es:bx --> root entry offset address 200 ; ds:si --> target string 201 ; cx --> target length 202 ; 203 ; return: 204 ; (dx !=0 ) ? exist : noexist 205 ; exist --> bx is the target entry 206 FindEntry: 207 push cx 208 209 mov dx, [BPB_RootEntCnt] 210 mov bp, sp 211 212 find: 213 cmp dx, 0 214 jz noexist 215 mov di, bx 216 mov cx, [bp] 217 push si 218 call MemCmp 219 pop si 220 cmp cx, 0 221 jz exist 222 add bx, 32 223 dec dx 224 jmp find 225 226 exist: 227 noexist: 228 pop cx 229 230 ret 231 232 ; ds:si --> source 233 ; es:di --> destination 234 ; cx --> length 235 ; 236 ; return: 237 ; (cx == 0) ? equal : noequal 238 MemCmp: 239 240 compare: 241 cmp cx, 0 242 jz equal 243 mov al, [si] 244 cmp al, byte [di] 245 jz goon 246 jmp noequal 247 goon: 248 inc si 249 inc di 250 dec cx 251 jmp compare 252 253 equal: 254 noequal: 255 256 ret 257 258 ; es:bp --> string address 259 ; cx --> string length 260 Print: 261 mov dx, 0 262 mov ax, 0x1301 263 mov bx, 0x0007 264 int 0x10 265 ret 266 267 ; no parameter 268 ResetFloppy: 269 270 mov ah, 0x00 271 mov dl, [BS_DrvNum] 272 int 0x13 273 274 ret 275 276 ; ax --> logic sector number 277 ; cx --> number of sector 278 ; es:bx --> target address 279 ReadSector: 280 281 call ResetFloppy 282 283 push bx 284 push cx 285 286 mov bl, [BPB_SecPerTrk] 287 div bl 288 mov cl, ah 289 add cl, 1 290 mov ch, al 291 shr ch, 1 292 mov dh, al 293 and dh, 1 294 mov dl, [BS_DrvNum] 295 296 pop ax 297 pop bx 298 299 mov ah, 0x02 300 301 read: 302 int 0x13 303 jc read 304 305 ret 306 307 MsgStr db "No LOADER ..." 308 MsgLen equ ($-MsgStr) 309 Target db "LOADER " 310 TarLen equ ($-Target) 311 EntryItem times EntryItemLength db 0x00 312 Buf: 313 times 510-($-$$) db 0x00 314 db 0x55, 0xaa
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
18-2节的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 + DA_DPL0 12 VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32 + DA_DPL0 13 DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DR + DA_32 + DA_DPL0 14 STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32 + DA_DPL0 15 FUNCTION_DESC : Descriptor 0, FunctionSegLen - 1, DA_C + DA_32 + DA_DPL0 16 TASK_A_LDT_DESC : Descriptor 0, TaskALdtLen - 1, DA_LDT + DA_DPL0 17 ; GDT end 18 19 GdtLen equ $ - GDT_ENTRY 20 21 GdtPtr: 22 dw GdtLen - 1 23 dd 0 24 25 26 ; GDT Selector 27 28 Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0 29 VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0 30 Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0 31 Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0 32 FunctionSelector equ (0x0005 << 3) + SA_TIG + SA_RPL0 33 TaskALdtSelector equ (0x0006 << 3) + SA_TIG + SA_RPL0 34 ; end of [section .gdt] 35 36 TopOfStack16 equ 0x7c00 37 38 [section .s16] 39 [bits 16] 40 ENTRY_SEGMENT: 41 mov ax, cs 42 mov ds, ax 43 mov es, ax 44 mov ss, ax 45 mov sp, TopOfStack16 46 47 ; initialize GDT for 32 bits code segment 48 mov esi, CODE32_SEGMENT 49 mov edi, CODE32_DESC 50 51 call InitDescItem 52 53 mov esi, DATA32_SEGMENT 54 mov edi, DATA32_DESC 55 56 call InitDescItem 57 58 mov esi, STACK32_SEGMENT 59 mov edi, STACK32_DESC 60 61 call InitDescItem 62 63 mov esi, FUNCTION_SEGMENT 64 mov edi, FUNCTION_DESC 65 66 call InitDescItem 67 68 mov esi, TASK_A_LDT_ENTRY 69 mov edi, TASK_A_LDT_DESC 70 71 call InitDescItem 72 73 mov esi, TASK_A_DATA32_SEGMENT 74 mov edi, TASK_A_DATA32_DESC 75 76 call InitDescItem 77 78 mov esi, TASK_A_CODE32_SEGMENT 79 mov edi, TASK_A_CODE32_DESC 80 81 call InitDescItem 82 83 mov esi, TASK_A_STACK32_SEGMENT 84 mov edi, TASK_A_STACK32_DESC 85 86 call InitDescItem 87 88 ; initialize GDT pointer struct 89 mov eax, 0 90 mov ax, ds 91 shl eax, 4 92 add eax, GDT_ENTRY 93 mov dword [GdtPtr + 2], eax 94 95 ; 1. load GDT 96 lgdt [GdtPtr] 97 98 ; 2. close interrupt 99 cli 100 101 ; 3. open A20 102 in al, 0x92 103 or al, 00000010b 104 out 0x92, al 105 106 ; 4. enter protect mode 107 mov eax, cr0 108 or eax, 0x01 109 mov cr0, eax 110 111 ; 5. jump to 32 bits code 112 jmp dword Code32Selector : 0 113 114 115 ; esi --> code segment label 116 ; edi --> descriptor label 117 InitDescItem: 118 push eax 119 120 mov eax, 0 121 mov ax, cs 122 shl eax, 4 123 add eax, esi 124 mov word [edi + 2], ax 125 shr eax, 16 126 mov byte [edi + 4], al 127 mov byte [edi + 7], ah 128 129 pop eax 130 131 ret 132 133 [section .dat] 134 [bits 32] 135 DATA32_SEGMENT: 136 DTOS db "D.T.OS!", 0 137 DTOS_OFFSET equ DTOS - $$ 138 139 Data32SegLen equ $ - DATA32_SEGMENT 140 141 142 [section .s32] 143 [bits 32] 144 CODE32_SEGMENT: 145 mov ax, VideoSelector 146 mov gs, ax 147 148 mov ax, Data32Selector 149 mov ds, ax 150 151 mov ax, Stack32Selector 152 mov ss, ax 153 154 mov eax, TopOfStack32 155 mov esp, eax 156 157 mov ebp, DTOS_OFFSET 158 mov bx, 0x0c 159 mov dh, 12 160 mov dl, 33 161 162 call FunctionSelector : PrintString 163 164 mov ax, TaskALdtSelector 165 166 lldt ax 167 168 push TaskAStack32Selector 169 push TaskATopOfStack32 170 push TaskACode32Selector 171 push 0 172 retf 173 174 jmp $ 175 176 Code32SegLen equ $ - CODE32_SEGMENT 177 178 [section .gs] 179 [bits 32] 180 STACK32_SEGMENT: 181 times 1024 * 4 db 0 182 183 Stack32SegLen equ $ - STACK32_SEGMENT 184 TopOfStack32 equ Stack32SegLen - 1 185 186 187 ; ====================================== 188 ; 189 ; Global Function Segment 190 ; 191 ; ====================================== 192 193 [section .func] 194 [bits 32] 195 FUNCTION_SEGMENT: 196 197 ; ds:ebp --> string address 198 ; bx --> attribute 199 ; dx --> dh : row, dl : col 200 PrintStringFunc: 201 push ebp 202 push eax 203 push edi 204 push cx 205 push dx 206 207 print: 208 mov cl, [ds:ebp] 209 cmp cl, 0 210 je end 211 mov eax, 80 212 mul dh 213 add al, dl 214 shl eax, 1 215 mov edi, eax 216 mov ah, bl 217 mov al, cl 218 mov [gs:edi], ax 219 inc ebp 220 inc dl 221 jmp print 222 223 end: 224 pop dx 225 pop cx 226 pop edi 227 pop eax 228 pop ebp 229 230 retf 231 232 PrintString equ PrintStringFunc - $$ 233 234 FunctionSegLen equ $ - FUNCTION_SEGMENT 235 236 ; ================================== 237 ; 238 ; Task A Code Segment 239 ; 240 ;=================================== 241 [section .task-a-ldt] 242 ; Task A LDT definition 243 ; ¶Î»ùÖ· ¶ÎœçÏÞ ¶ÎÊôÐÔ 244 245 TASK_A_LDT_ENTRY: 246 TASK_A_CODE32_DESC : Descriptor 0, TaskACode32SegLen - 1, DA_C + DA_32 + DA_DPL3 247 TASK_A_DATA32_DESC : Descriptor 0, TaskAData32SegLen - 1, DA_DR + DA_32 + DA_DPL3 248 TASK_A_STACK32_DESC : Descriptor 0, TaskAStack32SegLen - 1, DA_DRW + DA_32 + DA_DPL3 249 250 TaskALdtLen equ $ - TASK_A_LDT_ENTRY 251 252 ; Task A LDT Selector 253 TaskACode32Selector equ (0x0000 << 3) + SA_TIL + SA_RPL3 254 TaskAData32Selector equ (0x0001 << 3) + SA_TIL + SA_RPL3 255 TaskAStack32Selector equ (0x0002 << 3) + SA_TIL + SA_RPL3 256 257 [section .task-a-dat] 258 [bits 32] 259 TASK_A_DATA32_SEGMENT: 260 TASK_A_STRING db "This is Task A", 0 261 TASK_A_STRING_OFFSET equ TASK_A_STRING - $$ 262 263 TaskAData32SegLen equ $ - TASK_A_DATA32_SEGMENT 264 265 [section .task-a-gs] 266 [bits 32] 267 TASK_A_STACK32_SEGMENT: 268 times 1024 db 0 269 270 TaskAStack32SegLen equ $ - TASK_A_STACK32_SEGMENT 271 TaskATopOfStack32 equ TaskAStack32SegLen - 1 272 273 [section .task-a-s32] 274 [bits 32] 275 TASK_A_CODE32_SEGMENT: 276 mov ax, TaskAData32Selector 277 mov ds, ax 278 279 jmp $ 280 281 282 TaskACode32SegLen equ $ - TASK_A_CODE32_SEGMENT
第10-16行我们定义的段描述符特权级都是0,处理器从实模式跳转到保护模式后进入的是核心特权级0,进入32位的保护模式后首先执行的是144行的程序,我们首先将一些段选择子放在段寄存器中,157-162行调用了函数进行打印,这个FUNCTION_SEGMENT段也是在特权级0,所以这时候的打印不涉及特权级的转移。
164-166行将任务段的选择子加载进CPU的特殊寄存器中,168-170将任务段的栈段选择子、栈顶地址、任务段的代码段选择子压入高特权级的栈中,第171行压入的0指的是偏移地址,第172行的retf远跳转返回的作用是使处理器从核心特权级0跳转到普通特权级3。跳转的时候处理器会自动的在核心态的栈中加载我们刚刚存进去的值到相应的寄存器中,然后程序就到了275行的任务段中的代码段执行。
为了单步执行我们先使用如下命令对loader可执行程序进行反编译:
ndisasm -b 32 -o 0x9000 loader > loader.txt
-b 32是以32位的方式进行反编译。
在反编译得到文件中我们找到lldt所在的位置,断点就打在这里:
启动bochs,使用break 0x915D打上断点。
输入c执行到断点处,结果如下:
可以看到下一条指令就是 lldt ax,继续单步执行:
单步执行并使用sreg查看寄存器信息,可以看到cs中的值是0x0008,可知最后两位是00,这时处于核心特权级0。
继续单步执行:
retf执行完之后再次使用retf查看寄存器的值,可以看到cs的之变成了0x0007,最后两位是11,这说明特权级变为了3。
继续单步执行:
程序转移到了276行的地方。
继续进行实验,我们我特权级3模拟一个内核函数调用,这就涉及到了低特权级到高特权级的转移,初步给出程序如下:
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 + DA_DPL0 12 VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32 + DA_DPL0 13 DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DR + DA_32 + DA_DPL0 14 STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32 + DA_DPL0 15 FUNCTION_DESC : Descriptor 0, FunctionSegLen - 1, DA_C + DA_32 + DA_DPL0 16 TASK_A_LDT_DESC : Descriptor 0, TaskALdtLen - 1, DA_LDT + DA_DPL0 17 ; call Gate 18 ; selector offset canshugeshu attr 19 FUNC_PRINTSTRING_DESC : Gate FunctionSelector, PrintString, 0, DA_386CGate + DA_DPL3 20 ; GDT end 21 22 GdtLen equ $ - GDT_ENTRY 23 24 GdtPtr: 25 dw GdtLen - 1 26 dd 0 27 28 29 ; GDT Selector 30 31 Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0 32 VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0 33 Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0 34 Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0 35 FunctionSelector equ (0x0005 << 3) + SA_TIG + SA_RPL0 36 TaskALdtSelector equ (0x0006 << 3) + SA_TIG + SA_RPL0 37 38 ; Gate selector 39 FuncPrintStringSelector equ (0x0007 << 3) + SA_TIG + SA_RPL0 40 41 ; end of [section .gdt] 42 43 TopOfStack16 equ 0x7c00 44 45 [section .s16] 46 [bits 16] 47 ENTRY_SEGMENT: 48 mov ax, cs 49 mov ds, ax 50 mov es, ax 51 mov ss, ax 52 mov sp, TopOfStack16 53 54 ; initialize GDT for 32 bits code segment 55 mov esi, CODE32_SEGMENT 56 mov edi, CODE32_DESC 57 58 call InitDescItem 59 60 mov esi, DATA32_SEGMENT 61 mov edi, DATA32_DESC 62 63 call InitDescItem 64 65 mov esi, STACK32_SEGMENT 66 mov edi, STACK32_DESC 67 68 call InitDescItem 69 70 mov esi, FUNCTION_SEGMENT 71 mov edi, FUNCTION_DESC 72 73 call InitDescItem 74 75 mov esi, TASK_A_LDT_ENTRY 76 mov edi, TASK_A_LDT_DESC 77 78 call InitDescItem 79 80 mov esi, TASK_A_DATA32_SEGMENT 81 mov edi, TASK_A_DATA32_DESC 82 83 call InitDescItem 84 85 mov esi, TASK_A_CODE32_SEGMENT 86 mov edi, TASK_A_CODE32_DESC 87 88 call InitDescItem 89 90 mov esi, TASK_A_STACK32_SEGMENT 91 mov edi, TASK_A_STACK32_DESC 92 93 call InitDescItem 94 95 ; initialize GDT pointer struct 96 mov eax, 0 97 mov ax, ds 98 shl eax, 4 99 add eax, GDT_ENTRY 100 mov dword [GdtPtr + 2], eax 101 102 ; 1. load GDT 103 lgdt [GdtPtr] 104 105 ; 2. close interrupt 106 cli 107 108 ; 3. open A20 109 in al, 0x92 110 or al, 00000010b 111 out 0x92, al 112 113 ; 4. enter protect mode 114 mov eax, cr0 115 or eax, 0x01 116 mov cr0, eax 117 118 ; 5. jump to 32 bits code 119 jmp dword Code32Selector : 0 120 121 122 ; esi --> code segment label 123 ; edi --> descriptor label 124 InitDescItem: 125 push eax 126 127 mov eax, 0 128 mov ax, cs 129 shl eax, 4 130 add eax, esi 131 mov word [edi + 2], ax 132 shr eax, 16 133 mov byte [edi + 4], al 134 mov byte [edi + 7], ah 135 136 pop eax 137 138 ret 139 140 [section .dat] 141 [bits 32] 142 DATA32_SEGMENT: 143 DTOS db "D.T.OS!", 0 144 DTOS_OFFSET equ DTOS - $$ 145 146 Data32SegLen equ $ - DATA32_SEGMENT 147 148 149 [section .s32] 150 [bits 32] 151 CODE32_SEGMENT: 152 mov ax, VideoSelector 153 mov gs, ax 154 155 mov ax, Data32Selector 156 mov ds, ax 157 158 mov ax, Stack32Selector 159 mov ss, ax 160 161 mov eax, TopOfStack32 162 mov esp, eax 163 164 mov ebp, DTOS_OFFSET 165 mov bx, 0x0c 166 mov dh, 12 167 mov dl, 33 168 169 call FunctionSelector : PrintString 170 171 mov ax, TaskALdtSelector 172 173 lldt ax 174 175 push TaskAStack32Selector 176 push TaskATopOfStack32 177 push TaskACode32Selector 178 push 0 179 retf 180 181 Code32SegLen equ $ - CODE32_SEGMENT 182 183 [section .gs] 184 [bits 32] 185 STACK32_SEGMENT: 186 times 1024 * 4 db 0 187 188 Stack32SegLen equ $ - STACK32_SEGMENT 189 TopOfStack32 equ Stack32SegLen - 1 190 191 192 ; ====================================== 193 ; 194 ; Global Function Segment 195 ; 196 ; ====================================== 197 198 [section .func] 199 [bits 32] 200 FUNCTION_SEGMENT: 201 202 ; ds:ebp --> string address 203 ; bx --> attribute 204 ; dx --> dh : row, dl : col 205 PrintStringFunc: 206 push ebp 207 push eax 208 push edi 209 push cx 210 push dx 211 212 print: 213 mov cl, [ds:ebp] 214 cmp cl, 0 215 je end 216 mov eax, 80 217 mul dh 218 add al, dl 219 shl eax, 1 220 mov edi, eax 221 mov ah, bl 222 mov al, cl 223 mov [gs:edi], ax 224 inc ebp 225 inc dl 226 jmp print 227 228 end: 229 pop dx 230 pop cx 231 pop edi 232 pop eax 233 pop ebp 234 235 retf 236 237 PrintString equ PrintStringFunc - $$ 238 239 FunctionSegLen equ $ - FUNCTION_SEGMENT 240 241 ; ================================== 242 ; 243 ; Task A Code Segment 244 ; 245 ;=================================== 246 [section .task-a-ldt] 247 ; Task A LDT definition 248 ; ¶Î»ùÖ· ¶ÎœçÏÞ ¶ÎÊôÐÔ 249 250 TASK_A_LDT_ENTRY: 251 TASK_A_CODE32_DESC : Descriptor 0, TaskACode32SegLen - 1, DA_C + DA_32 + DA_DPL3 252 TASK_A_DATA32_DESC : Descriptor 0, TaskAData32SegLen - 1, DA_DR + DA_32 + DA_DPL3 253 TASK_A_STACK32_DESC : Descriptor 0, TaskAStack32SegLen - 1, DA_DRW + DA_32 + DA_DPL3 254 255 TaskALdtLen equ $ - TASK_A_LDT_ENTRY 256 257 ; Task A LDT Selector 258 TaskACode32Selector equ (0x0000 << 3) + SA_TIL + SA_RPL3 259 TaskAData32Selector equ (0x0001 << 3) + SA_TIL + SA_RPL3 260 TaskAStack32Selector equ (0x0002 << 3) + SA_TIL + SA_RPL3 261 262 [section .task-a-dat] 263 [bits 32] 264 TASK_A_DATA32_SEGMENT: 265 TASK_A_STRING db "This is Task A", 0 266 TASK_A_STRING_OFFSET equ TASK_A_STRING - $$ 267 268 TaskAData32SegLen equ $ - TASK_A_DATA32_SEGMENT 269 270 [section .task-a-gs] 271 [bits 32] 272 TASK_A_STACK32_SEGMENT: 273 times 1024 db 0 274 275 TaskAStack32SegLen equ $ - TASK_A_STACK32_SEGMENT 276 TaskATopOfStack32 equ TaskAStack32SegLen - 1 277 278 [section .task-a-s32] 279 [bits 32] 280 TASK_A_CODE32_SEGMENT: 281 mov ax, TaskAData32Selector 282 mov ds, ax 283 284 mov ebp, TASK_A_STRING_OFFSET 285 mov bx, 0x0c 286 mov dh, 14 287 mov dl, 29 288 289 call FuncPrintStringSelector : 0 290 291 jmp $ 292 293 294 TaskACode32SegLen equ $ - TASK_A_CODE32_SEGMENT
第19行添加了调用门的描述符,偏移位置是PrintString,第39行添加了调用门的选择子。
然后我们在TASKA任务段模拟内核函数的调用,也就是在第281行这里开始,先将任务段中的数据段加载进数据段寄存器ds。
第284行将字符串的偏移放到ebp寄存器里面,这相当于先在用户空间把参数准备好,第289行即通过调用门执行了这个调用,但是实验结果显示,程序崩溃了。
这是为什么呢?
特权级转移的时候栈会变化。
因为从低特权级到高特权级的跳转需要将栈切换到高特权级的栈,这个高特权级的栈去哪里找呢?去TSS段里面去找,这个段里面保存了栈的一些信息,但是现在我们的TSS并没有准备好,所以出错了,对程序进行改进。
TSS段本质也是一段内存。
改进的程序如下:
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 + DA_DPL0 12 VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32 + DA_DPL0 13 DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DR + DA_32 + DA_DPL0 14 STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32 + DA_DPL0 15 FUNCTION_DESC : Descriptor 0, FunctionSegLen - 1, DA_C + DA_32 + DA_DPL0 16 TASK_A_LDT_DESC : Descriptor 0, TaskALdtLen - 1, DA_LDT + DA_DPL0 17 TSS_DESC : Descriptor 0, TSSLen - 1, DA_386TSS + DA_DPL0 18 ; call Gate 19 ; selector offset canshugeshu attr 20 FUNC_PRINTSTRING_DESC : Gate FunctionSelector, PrintString, 0, DA_386CGate + DA_DPL3 21 ; GDT end 22 23 GdtLen equ $ - GDT_ENTRY 24 25 GdtPtr: 26 dw GdtLen - 1 27 dd 0 28 29 30 ; GDT Selector 31 32 Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0 33 VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0 34 Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0 35 Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0 36 FunctionSelector equ (0x0005 << 3) + SA_TIG + SA_RPL0 37 TaskALdtSelector equ (0x0006 << 3) + SA_TIG + SA_RPL0 38 TSSSelector equ (0x0007 << 3) + SA_TIG + SA_RPL0 39 ; Gate selector 40 FuncPrintStringSelector equ (0x0008 << 3) + SA_TIG + SA_RPL3 41 42 ; end of [section .gdt] 43 44 [section .tss] 45 [bits 32] 46 TSS_SEGMENT: 47 dd 0 48 dd TopOfStack32 ; 0 49 dd Stack32Selector ; 50 dd 0 ; 1 51 dd 0 ; 52 dd 0 ; 2 53 dd 0 ; 54 times 4*18 dd 0 55 dw 0 56 dw $ - TSS_SEGMENT + 2 57 db 0xFF 58 59 TSSLen equ $ - TSS_SEGMENT 60 61 TopOfStack16 equ 0x7c00 62 63 [section .s16] 64 [bits 16] 65 ENTRY_SEGMENT: 66 mov ax, cs 67 mov ds, ax 68 mov es, ax 69 mov ss, ax 70 mov sp, TopOfStack16 71 72 ; initialize GDT for 32 bits code segment 73 mov esi, CODE32_SEGMENT 74 mov edi, CODE32_DESC 75 76 call InitDescItem 77 78 mov esi, DATA32_SEGMENT 79 mov edi, DATA32_DESC 80 81 call InitDescItem 82 83 mov esi, STACK32_SEGMENT 84 mov edi, STACK32_DESC 85 86 call InitDescItem 87 88 mov esi, FUNCTION_SEGMENT 89 mov edi, FUNCTION_DESC 90 91 call InitDescItem 92 93 mov esi, TASK_A_LDT_ENTRY 94 mov edi, TASK_A_LDT_DESC 95 96 call InitDescItem 97 98 mov esi, TASK_A_DATA32_SEGMENT 99 mov edi, TASK_A_DATA32_DESC 100 101 call InitDescItem 102 103 mov esi, TASK_A_CODE32_SEGMENT 104 mov edi, TASK_A_CODE32_DESC 105 106 call InitDescItem 107 108 mov esi, TASK_A_STACK32_SEGMENT 109 mov edi, TASK_A_STACK32_DESC 110 111 call InitDescItem 112 113 mov esi, TSS_SEGMENT 114 mov edi, TSS_DESC 115 116 call InitDescItem 117 118 ; initialize GDT pointer struct 119 mov eax, 0 120 mov ax, ds 121 shl eax, 4 122 add eax, GDT_ENTRY 123 mov dword [GdtPtr + 2], eax 124 125 ; 1. load GDT 126 lgdt [GdtPtr] 127 128 ; 2. close interrupt 129 cli 130 131 ; 3. open A20 132 in al, 0x92 133 or al, 00000010b 134 out 0x92, al 135 136 ; 4. enter protect mode 137 mov eax, cr0 138 or eax, 0x01 139 mov cr0, eax 140 141 ; 5. jump to 32 bits code 142 jmp dword Code32Selector : 0 143 144 145 ; esi --> code segment label 146 ; edi --> descriptor label 147 InitDescItem: 148 push eax 149 150 mov eax, 0 151 mov ax, cs 152 shl eax, 4 153 add eax, esi 154 mov word [edi + 2], ax 155 shr eax, 16 156 mov byte [edi + 4], al 157 mov byte [edi + 7], ah 158 159 pop eax 160 161 ret 162 163 [section .dat] 164 [bits 32] 165 DATA32_SEGMENT: 166 DTOS db "D.T.OS!", 0 167 DTOS_OFFSET equ DTOS - $$ 168 169 Data32SegLen equ $ - DATA32_SEGMENT 170 171 172 [section .s32] 173 [bits 32] 174 CODE32_SEGMENT: 175 mov ax, VideoSelector 176 mov gs, ax 177 178 mov ax, Data32Selector 179 mov ds, ax 180 181 mov ax, Stack32Selector 182 mov ss, ax 183 184 mov eax, TopOfStack32 185 mov esp, eax 186 187 mov ebp, DTOS_OFFSET 188 mov bx, 0x0c 189 mov dh, 12 190 mov dl, 33 191 192 call FunctionSelector : PrintString 193 194 mov ax, TSSSelector 195 196 ltr ax 197 198 mov ax, TaskALdtSelector 199 200 lldt ax 201 202 push TaskAStack32Selector 203 push TaskATopOfStack32 204 push TaskACode32Selector 205 push 0 206 retf 207 208 Code32SegLen equ $ - CODE32_SEGMENT 209 210 [section .gs] 211 [bits 32] 212 STACK32_SEGMENT: 213 times 1024 * 4 db 0 214 215 Stack32SegLen equ $ - STACK32_SEGMENT 216 TopOfStack32 equ Stack32SegLen - 1 217 218 219 ; ====================================== 220 ; 221 ; Global Function Segment 222 ; 223 ; ====================================== 224 225 [section .func] 226 [bits 32] 227 FUNCTION_SEGMENT: 228 229 ; ds:ebp --> string address 230 ; bx --> attribute 231 ; dx --> dh : row, dl : col 232 PrintStringFunc: 233 push ebp 234 push eax 235 push edi 236 push cx 237 push dx 238 239 print: 240 mov cl, [ds:ebp] 241 cmp cl, 0 242 je end 243 mov eax, 80 244 mul dh 245 add al, dl 246 shl eax, 1 247 mov edi, eax 248 mov ah, bl 249 mov al, cl 250 mov [gs:edi], ax 251 inc ebp 252 inc dl 253 jmp print 254 255 end: 256 pop dx 257 pop cx 258 pop edi 259 pop eax 260 pop ebp 261 262 retf 263 264 PrintString equ PrintStringFunc - $$ 265 266 FunctionSegLen equ $ - FUNCTION_SEGMENT 267 268 ; ================================== 269 ; 270 ; Task A Code Segment 271 ; 272 ;=================================== 273 [section .task-a-ldt] 274 ; Task A LDT definition 275 ; ¶Î»ùÖ· ¶ÎœçÏÞ ¶ÎÊôÐÔ 276 277 TASK_A_LDT_ENTRY: 278 TASK_A_CODE32_DESC : Descriptor 0, TaskACode32SegLen - 1, DA_C + DA_32 + DA_DPL3 279 TASK_A_DATA32_DESC : Descriptor 0, TaskAData32SegLen - 1, DA_DR + DA_32 + DA_DPL3 280 TASK_A_STACK32_DESC : Descriptor 0, TaskAStack32SegLen - 1, DA_DRW + DA_32 + DA_DPL3 281 282 TaskALdtLen equ $ - TASK_A_LDT_ENTRY 283 284 ; Task A LDT Selector 285 TaskACode32Selector equ (0x0000 << 3) + SA_TIL + SA_RPL3 286 TaskAData32Selector equ (0x0001 << 3) + SA_TIL + SA_RPL3 287 TaskAStack32Selector equ (0x0002 << 3) + SA_TIL + SA_RPL3 288 289 [section .task-a-dat] 290 [bits 32] 291 TASK_A_DATA32_SEGMENT: 292 TASK_A_STRING db "This is Task A", 0 293 TASK_A_STRING_OFFSET equ TASK_A_STRING - $$ 294 295 TaskAData32SegLen equ $ - TASK_A_DATA32_SEGMENT 296 297 [section .task-a-gs] 298 [bits 32] 299 TASK_A_STACK32_SEGMENT: 300 times 1024 db 0 301 302 TaskAStack32SegLen equ $ - TASK_A_STACK32_SEGMENT 303 TaskATopOfStack32 equ TaskAStack32SegLen - 1 304 305 [section .task-a-s32] 306 [bits 32] 307 TASK_A_CODE32_SEGMENT: 308 mov ax, TaskAData32Selector 309 mov ds, ax 310 311 mov ebp, TASK_A_STRING_OFFSET 312 mov bx, 0x0c 313 mov dh, 14 314 mov dl, 29 315 316 call FuncPrintStringSelector : 0 317 318 jmp $ 319 320 321 TaskACode32SegLen equ $ - TASK_A_CODE32_SEGMENT
TSS段本质也是一段内存,因此在44-59行我们定义了这个段,在这个段中,我们只定义了特权级0的栈的信息,特权级1和2的栈的信息赋值为0。
第17行定义了这个段的段描述符,第38行定义了这个段的选择子。
此外第20行的调用门的描述符的DPL要定义成3。
第40行的调用门的选择子的RPL也要定义成3。
在程序运行的时候不要忘了TSS段的段基址的初始化,这是在113-116行完成的。
在程序还在核心态特权级0的时候,还要将TSS加载进处理器内部的特殊寄存器中,194-196正是完成了这个操作,用到的指令时ltr。
进行完这些修改之后,我们再次执行程序,发现程序还是崩溃,进行单步执行操作:
首先使用如下指令进行反汇编:
ndisasm -b 32 -o 0x9000 loader > loader.txt
找到lldt后面的那个retf所在的位置:
位置为0x92DB。
打断点再次单步执行:
上图中马上要经过调用门了,继续单步执行:
通过调用门调用之后可以看到cs的值为0x0028,最后两位是00,到了核心态特权级0。
调用门之后程序到了232行的PrintStringFunc处,继续单步执行:
在第250行的程序之前程序还没有出错,继续单步执行第250行:
这时候程序崩溃了,产生了上图中的错误。
第250行的程序的本质是内存赋值,即从ax指向的内存复制到gs代表的内存,ax代表的内存由DS寄存器获得,上图中可以看到DS的rpl为3,而GS的rpl为0,这相当于从用户态向内核态赋值数据,这是一个很危险的操作,错误就出在了这里。
为了解决这个错误,我们将Vedio段描述符的dpl改为3:
选择子的rpl也改为3:
再次执行程序结果如下:
程序可以正常的执行了。
RPL究竟是什么?有什么用?
RPL的意义:
位置意义:
选择子或者段寄存器的最低两位。
请求意义:
资源请求的特权级(不同于当前特权级CPL)。
请求特权级表明了,请求这个资源的时候所使用的特权级是什么。
如果当前执行的代码段的CPL为0,那么是不是就可以访问任意的内存了呢?
这时候还取决于资源的请求特权级也即RPL,即使这时候CPL为0,但是如果RPL很低也是不能请求对应的资源的。
资源请求时:
CPL、RPL、DPL共同确定请求是否合法。
当前代码(CPL)-> 选择子(RPL) -> 数据段(DPL)。
小结:
TSS是通过调用门转移到高特权级的关键。
TSS是处理器的硬件数据结构,用于实现多任务。
TSS的结构遵循保护模式下的内存使用规则。
RPL在请求资源时是合法性判断的依据之一。
处理器使用CPL、RPL、DPL共同确定合法性。
参考:狄泰软件学院唐佐林操作系统教程