A personal summary of the privilege level in the IA32 segmentation mechanism:
In IA32 's segmented mechanism, it is divided into 4 privilege levels (RING0~RING3): Level0 High (inner layer)
L e v E l 1
L e v E l 2
L e v E l 3 Low (outer)
The difference between the privilege levels is the restriction of the instruction (mainly the limitation of the system instruction, such as Lgdt,lldt, etc.). It is very easy to understand that the higher the privilege level, the more commands are allowed to execute. With this restriction, ordinary applications cannot use special system directives (which are not normally required).
If you need to implement functions that must be implemented through privileged directives, you can do so through the services provided by the operating system. At this point, a change in the level of privilege is involved. Assuming that the normal program is running in Ring3, when it calls a system service with a privileged level of 0, the privilege is promoted to RING0 when the execution is given to the system service (at which point the privilege command can be used to complete a particular function), and when the service is completed, the execution is returned to the normal program. Execution is reduced to ring3 (at this time in a restricted state, only ordinary instructions can be executed).
With regard to the validation and transformation of privilege levels, IA32 has a complex set of mechanisms. For simplicity, here's a simple introduction to the area involved.
First of all, 3 noun concepts need to be recognized: CPL, DPL, RPL.
Current privileged-level CPL (Privilege levels):
CPL represents the privilege level at which the current program is running, stored in CS and SS registers. In general, the CPL is the same as the descriptor of the code snippet currently in place (except when the current code snippet is a consistent code snippet, as it is not yet covered, and is not considered here).
Descriptor Privilege Level DPL (descriptor Privilege levels):
DPL, as part of a descriptor (GDT, LDT, etc.) attribute, is stored in the descriptor's data structure body. When a program selects a data segment or snippet defined by a child using or invoking a descriptor, it uses the CPL value that the program has (typically its privileged level equals that of the calling code snippet in the descriptor) compared to DPL in the descriptor, which generally requires a CPL whose value is less than or equal to the callee's DPL, In other words, privileged levels can be called with low privilege levels, and the privilege level (CPL) will only decrease or remain constant as the code segments are called each other. If you need to increase the privilege level (execute a high-privilege code snippet), you need to use a call gate or other means.
Request Privilege level RPL (requested Privilege levels):
RPL as one of the attributes of the selector is stored in the first and second bits of the data structure of the selector. When the program by selecting a sub-use or call the descriptor of a data segment or code, in addition to the CPL and DPL compared to the RPL will also be compared with DPL, if the CPL privilege level is higher than or equal to DPL but the RPL privilege level is lower than DPL, also can not be successfully invoked.
As mentioned earlier, if you call directly, the level of privilege (CPL) will only get lower, want to increase the level of privilege, can be through the task gate and other means. Here's a look at some of the things that use the task gate.
First look at the case of the invocation gate in the descriptor: most notably, the door descriptor contains a selector, which, by the last experiment, can be used to move to the code snippet that the selector contains in the door task. A program calls the task door, must follow the above mentioned rules, that is, can not exceed the authority to invoke the task gate. The task gate also cannot contain arbitrary selectors to go to the code snippet of any privileged level, and must be a DPL of the task gate greater than or equal to the code snippet pointed to. In other words, with the task gate, the privilege level will only increase or remain unchanged.
You typically use JMP or call to invoke the calling gate. There is also a limit to the use of jmp, and JMP can invoke only those call gates that have the same DPL as the code snippet that they point to. Summary: You can temporarily use the call door to increase the privilege level to execute some code and then return to the low-privileged code snippet (using call), if you want to use JMP to use the calling gate (may be gone forever), you must ensure that the use of the call gate after the privilege level remains unchanged.
Let's go back to the experiment. The experiment was to experience the use of task gates to increase privilege levels at low privilege levels. However, previous experiments have been run at the default highest privilege level RONG0. Therefore, you need to lower the operational privilege level.
The method used is:
Pretend that you are called by a code snippet with a low privilege level (press SS,ESP,CS,EIP into the stack used by the current snippet of the stack), and then use the RETF directive to return. The selection Child (ss) of the stack segment used by push to drop to the code snippet
The stack segment stack top pointer (ESP) that the push needs to drop to the code snippet used
Push needs to drop to the code snippet of the selector (CS)
Number of instruction strips (EIP) executed to the code snippet to which push needs to be dropped
Retf
This method lets you jump to a code snippet that has a lower privileged level. At the same time we need to provide two different stack segments for two different privileged levels of code snippets. However, because of the boot sector space limitation, it is no longer possible for the code to declare a space to be used as a stack, but rather to specify the unused memory as a stack using the macro directly. #define stackspace 0x7c00 //stack address
#define stacktop 0x4f //Stack Top
#define STACKSPACE3 0x7a00 //stack address
#define stacktop3 0x4f //Stack top
Jump to a code snippet with a higher level of privilege, you have to tell the system what the stack segment is used at this privilege level and some other relevant information, so you need to prepare a task-state called TSS Segment) data structures, TSS is actually a set of data structures that store various registers into memory for later use. Also, because the boot sector space is limited, direct access to the macro to specify a piece of memory as TSS (see the code in pm.h).
The process for this experiment:
1. Jump to Protected Mode
(main function in pm32.c)
2. Reload the new GDT
3. Display string: Protect.
4. Initialize the address
5 for the GDT task function. Initializes the address
6 for a function with a privilege of 3 in the GDT. Initializes the TSS and loads
7 with the LTR instruction. Drop right jump to a privileged 3 code snippet (CodeRing3 function)
8. Display string: Ring 3.
9. Use the call invoke door to jump to the Codedest function (code snippet with the default privilege level of 0)
10. Display string: Gate.
11. Do not return, but set the base site of the LDT on the GDT and load the local descriptor (LDT) and jump to the local task.
12. Display string: Local.
13. Enter the dead loop.
The following is the experiment code:
CODE:RUN.C
File: Run.c//function: Compile experiment Code of operating system and create IMG, generate Bochs configuration file, run Bochs. Description: The experimental code consists of a 16-bit partial boot program and a 32-bit partial bootstrapper. The 16-bit partial bootstrapper is placed in the first half of the boot sector, the 0~79 byte//32-bit part of the bootstrapper is placed in the second half of the boot sector, the 80~509 byte//510, and 511 bytes of the bootloader end tag: 0x55, 0XAA//run: Use the YC09 compiler to compile the run, Click Enter to compile the run again//author: Miao//Time: 2010-1-13 #define FDISK_SIZE 1474560//Mirror size: 1.4MB//virtual machine set char *pmsrc = "megs:32/n" "Romimage: File=bios-bochs-latest, address=0xf0000/n "vgaromimage:vgabios-elpin-2.40/n" "Floppya:1_44=pm.img, status= inserted/n "" Boot:a/N "" log:pm.out/n "" mouse:enabled=0/n "" keyboard_mapping:enabled=1, map=x11-pc-us.map/n "; Compiles the specified code file and puts it in the mirror at the specified location//filename: The file name to compile Imgbuffer: The image buffer to be saved//startindex: Specify the starting position limitsize: Post-compilation program-qualified int compilefile ( Char *filename, byte *imgbuffer, int startIndex, int limitsize) {char *tempbuffer;//Save part of the bootstrapper temporary buffer//compile this part of the bootstrapper, and put the result in TEMPB uffer int length = Yc_compilecpp (&tempbuffer, fileName, 0, 0); if (length <= 0 | | length >= limitsize) {printf ("File:%s has some errors or file is too large (more than%d bytes):%d bytes/n", filename,limitsize,length); re Turn 1; } printf ("File:%s was successfully compiled with size:%d bytes. /n ", fileName, length); Place 1 This part of the bootstrapper into the mirrored boot sector buffer at the specified starting position memcpy (Imgbuffer + startIndex, tempbuffer, length); Free (tempbuffer); return 0; } int main (int argc, char **argv) {char * FilePath = argv[0];/////Current folder path char Filename[max_path];////For caching individual filenames//The full path to the executable file The path removes the file name, keeping the folder paths for (int i = strlen (FilePath); Filepath[i]! = '//'; i--) filepath[i] = '/0 '; byte *imgbuffer = new byte[fdisk_size];//Mirror buffer _start://Compile the 16-bit part bootstrapper and place it in the first half of the boot sector, 0~79 byte if (compilefile ("pm16.c", Imgbuffer, 0, ()) goto _restart; Compile the 32-bit part of the bootstrapper and place it in the second half of the boot sector, 80~509 byte if (compilefile ("pm32.c", Imgbuffer, N, 512-80-2)) goto _restart; 0000H-01FFH is a fat boot sector [NO. 0 sector] with a AA flag ending length of 200H (512) bytes imgbuffer[510] = 0x55; IMGBUFFER[511] = 0xaa;//flag Floppy boot end//Create operating system image pm.img if (Yc_writefile ("pm.img", Imgbuffer, fdisk_size)! = fdisk_size) { printf ("Write:%s" error occurred during file.) /r/n ", fileName); Goto _restart; } printf ("/n%s created successfully. /n ", fileName); Generate operating system virtual machine profile Pm.src yc_writefile ("Pm.src", Pmsrc, strlen (PMSRC)); Run virtual machine yc_winexec (strcat (strcPY (FileName, FilePath), "Bochs.exe"), "-q-f pm.src"); _restart:printf ("/n" click Enter to recompile the run.) /n/n/n "); while (GetChar ()! = '/n '); Goto _start; return 0; }
Code:pm.h
File: pm.h//function: PM16.C and pm32.c Common header file//run: Run.exe automatically compiles pm16.c and pm32.c then generates an IMG and calls Bochs to run the program// Hint: Please first compile run.c file with yc09, generate Run.exe Program//After modify PM16.C and pm32.c code, can run Run.exe view effect directly, click Enter again compile run//author: Miao//Time: 2010-2-8// Defines the GDT attribute #define DA_32 0x4000//32-bit segment #define DA_DRW 0x92//Existing read-write data segment property values #define DA_DRWA 0x93//presence of accessed read-write data segment type values #define Da_c R 0x9a//Existing executable readable code snippet property value #define Da_c 0x98//exist only execute Code Snippet property value//define LDT attribute #define DA_LDT 0x82//Local Descriptor Type value #define SA_TIL 0x4//Will T I position 1, which is the LDT selector//define Gate Properties #define DA_386CGATE 0x8c//386 call category #define DA_386TSS 0x89//Available 386 task status Segment Type value//privilege level #define DA_DPL0 0 X00//DPL = 0 #define DA_DPL3 0x60//DPL = 3//Select subtype #define SA_RPL3 3//RPL//In order to save space, TTS's structure does not request memory space//instead uses free memory space directly through memory address #define Tts_back 0x10000 #define Tts_topofstack 0x10004//0-stage stack #define TTS_SELECTORSTACK 0x10008 #define TTS_CR1 0x1000 c//Level 1 stack #define TTS_SS1 0x10010 #define TTS_CR2 0x10014//2-level stack #define TTS_SS2 0x10018 #define TTS_CR3 0x1001c #define Tts_eip 0x10020 #define TTS_EFLAGS 0x10024 #define TTS_EAX 0x10028 #deFine tts_ecx 0x1002c #define TTS_EDX 0x10030 #define TTS_EBX 0x10034 #define TTS_ESP 0x10038 #define TTS_EBP 0x1003c #defi NE tts_esi 0x10040 #define TTS_EDI 0x10044 #define Tts_es 0x10048 #define TTS_CS 0x1004c #define TTS_SS 0x10050 #define TT S_ds 0x10054 #define TTS_FS 0x10058 #define TTS_GS 0x1005c #define TTS_LDT 0x10060 #define TTS_DEBUGTRAPFLAG 0x10064//debug Trap Trap Flag #define TTS_IO 0x10066//i/o bitmap base #define Tts_end 0x10068//i/o bitmap end flag typedef unsigned int t_32; 4-byte typedef unsigned short t_16; 2-byte typedef unsigned char t_8; 1-byte typedef int T_BOOL;//4 byte typedef unsigned int T_PORT;//4 byte//Bucket Descriptor/System Segment Descriptor struct Descriptor//Total 8 bytes {T_16 limit_ Low Limit 2 bytes t_16 base_low; Base 2 byte t_8 base_mid; Base 1 byte t_8 attr1; P (1) DPL (2) DT (1) TYPE (4) 1 bytes t_8 limit_high_attr2; G (1) D (1) 0 (1) AVL (1) Limithigh (4) 1 bytes t_8 Base_high; Base 1 bytes}; #define DESCRIPTOR (BAS,LEN,ATTR) {/(len) & 0xFFFF,/(BAS) & 0xFFFF,/((BAS) >>16) &0xff,/(attr) & ; 0xFF,/((attr) >>8) &0xf0) + (((len) >>16) & 0x0f),/((BAS) >>) & 0xFF}/#define Gate (SLECTOR,OFFSET,DC OUNT,ATTR) {/(offset) & 0xFFFF,/Slector,/(DCount) &0x1f,/attr,/((offset) >>16) &0xff,/((OFFSE T) >> & 0xFF}/
Code:pm16.c
File: pm16.c//function: Switch to protected Mode, jump to 32-bit code snippet//Description: I tried to write the protection mode only in the boot sector, so I reduced the program a lot. It is only responsible for jumping to protected mode, and all other work is done under PM32.C. PM16.C only the first half of the boot sector 0~79 bytes. The pm32.c portion is loaded into the memory 0X7C50. Run: Run.exe automatically compiles pm16.c and pm32.c and then generates an IMG and calls Bochs run this program//hint: Please compile yc09 file with run.c First, generate Run.exe program// After modifying the code in the PM16.C and pm32.c, you can run the Run.exe to see the effect, click Enter again compile run//author: Miao//Time: 2010-1-30 #define YCBIT 16//Tell the compiler to compile the program in 16-bit format #define ycorg 0x7c00//Tell the compiler to load the program at 7C00 #include "pm.h"//gdt bounds, only responsible for jumping to protected mode, when loading new GDT descriptor label_gdt[] = {//segment base segment bounds attribute Des Criptor (0, 0, 0), descriptor (0X7C50, 0XFFFFF, DA_CR | DA_32),//32-bit code snippet (pm32.c), executable readable}; GDT Selector, set the offset value according to the GDT bounds #define SELECTORCODE32 8*1//points at 32-bit segment #pragma pack (1) struct GDT_PTR {unsigned short size; void *a Ddr }; #pragma pack () gdt_ptr gdtptr = {sizeof (LABEL_GDT), (char*) LABEL_GDT}; Segment bounds, base address asm Void Main () {MOV ax, CS mov ds, ax mov es, ax//clear screen mov ah, 06h//screen initialization or roll mov al, 00h//ah = 6, Al = 0h mov BX, 1110h//blue background mov cx, 0//upper left corner: (0, 0) mov dl, 4FH//No. 0 column mov dh, 1fh//No. 0 Line int 10h//display interrupt Lgdt GDTPTR//Load GDTR CLI//OFF interrupt//Open Address line A20 in Al, 92h or Al, 00000010b out 92h, AL//ready to switch to protected mode, place CR0 PE bit for 1 mov eax, CR0 or EAX, 1 mov cr0, EAX//Real into protected mode jmp DWORD selectorcode32:0x0}
Code:pm32.c
File: pm32.c//function: Protected mode 32-bit code snippet, function for loading a new GDT, loading TSS, lowering the right to switch to a privileged level of 3 code snippet CodeRing3 function,//////////////////////Use call Gate elevation to enter the privileged level 0 Code snippet Codedest function, initialize Jump into the LDT local task. Description: The 32-bit partial bootstrapper is placed in the second half of the mirrored boot sector, 80~509 bytes, the program size cannot exceed this limit//run: Run.exe automatically compiles pm16.c and pm32.c and then generates an IMG and calls Bochs to run the program// Hint: Please first compile run.c file with yc09, generate Run.exe Program//After modify PM16.C and pm32.c code, can run Run.exe view effect directly, click Enter again compile run//author: Miao//Time: 2010-2-8 #define Ycbit 32//Tell the compiler to compile the program in 32-bit format #define ycorg 0x0//This value generates an address base offset for variable functions such as at compile-time, simple, set to 0 #include "pm.h" #define RETF db 0XCB//For For YC09 does not recognize the RETF directive, so use a macro to define a RETF directive #define PROTECADDR 0X7C50//Enter protected mode after the program base of ASM Void CodeRing3 ();//test function with privilege Level 3 ASM void Ldtcode (); Local code snippet ASM void Codedest (); Gate Task test function asm void Dispstr (); To display a string, you need to set up ESI to point to the string address, EDI to the starting position of the string//ldt the selector, set the offset value according to the LDT bounds in the pm32.c #define SELECTORLDTCODEA 8*0+sa_til// Point to 32-bit segment local task at//ldt bounds Descriptor label_ldt[] = {//segment base segment bounds attribute descriptor (protecaddr, 0XFFFFF, DA_CR | DA_32),//32-bit code snippet (pm32.c), executable readable}; #define STACKSPACE 0X7C00//Stack address #define STACKTOP 0x4f//Stack top #define STACKSPACE3 0x7a00//Stack address #deFine STACKTOP3 0x4f//stack top//GDT selector, set offset value according to the GDT bounds in pm32.c #define SELECTORCODE32 8*1//points to a code snippet at 32-bit segment, executable readable #define SELECTORV Ideo 8*2//points to the first address of the memory #define SELECTORDATA32 8*3//points to 32-bit segments so that variables in the program can be read and written #define Selectorstack 8*4//points to the stack (stacks), 32 bits #def INE Selectorldt 8*5//point to the LDT, through this jump to the local task #define SELECTORCODEDEST 8*6//point to the test task provided for the door call #define SELECTORCODERING3 (8*7+sa_ RPL3)//point to a test function with a privileged level of 3 #define SELECTORSTACK3 (8*8+SA_RPL3)//point to a 3-level stack #define SELECTORTSS 8*9//to a stack (stack), From privileged level 3 to jump back to privilege level 0 to be used (recorded in TSS)//Gate selector #define SELECTORCALLGATETEST (8*10+SA_RPL3)//point gate Call test function//gdt bounds, note that Unlike the GDT in pm16.c, this new GDT is loaded immediately after jumping from pm16.c to descriptor label_gdt[] = {//segment base segment bounds attribute descriptor (0, 0, 0), descriptor (Proteca DDR, 0XFFFFF, DA_CR | DA_32),//32-bit code snippet (pm32.c), executable readable descriptor (0xb8000, 0xFFFF, DA_DRW | DA_DPL3),//memory address segment, readable writable descriptor (protecaddr, 0XFFFFF, DA_DRW | da_32 | DA_DPL3),//Make 32-bit code snippet (PM32.C) variable can read and write descriptor (Stackspace, stacktop, Da_drwa | DA_32),//stack (Stack), 32-bit descriptor (0, 0XFFFFF, Da_ldt),//local descriptor, segment base and 32-bitThe code snippet is the same, and the call needs to be offset descriptor (protecaddr, 0XFFFFF, DA_CR | DA_32),//test function provided for gate invocation, executable readable descriptor (0, 0XFFFFF, DA_CR | da_32 | DA_DPL3),//codering3 descriptor (StackSpace3, STACKTOP3, Da_drwa | da_32 | DA_DPL3),//stack3 descriptor (Tts_back, 0x68, DA_386TSS),//task status Segment TSS//select sub-offset parameter number of properties Gate (selectorcodedest, 0, 0, da_38 6CGate | DA_DPL3),//Use SELECTORDATA32 to select a child, so memory address: base (PROTECADDR) + function address offset}; #pragma pack (1) struct GDT_PTR {t_16 size; void *addr;} Gdtptr = {sizeof (LABEL_GDT), (char*) &label_gdt + protecaddr}; Segment bounds, base address #pragma pack () char msg1[] = "Protect."; Char msg2[] = "Ring 3."; Char msg3[] = "Gate."; Char msg4[] = "Local."; 32-bit code snippet. Jump from real mode into ASM void Main () {LGDT cs:gdtptr//Load new GDTR mov eax, selectorvideo mov gs, AX//Video Segment Selector (purpose) mov eax, SelectorData32 Make 32-bit code snippet variable (printplace) can read and write mov ds, ax mov ax, selectorstack mov ss, ax mov esp,stacktop//below display a string (display has reached protection mode information) mov es I, &MSG1//source data offset mov edi, (((80 * 0 + 0) * 2)//destination data offset. Screen No. 0, column No. 0. Call Dispstr//For GDT in-door task function initialize address XOR eax, eax mov eax, &codedest + PROTECADDR mov word label_gdt+8*6+2, ax shr eax, mov byte label_gdt+8*6+4, AL mov b Yte label_gdt+8*6+7, AH//For the function of the GDT privileged 3 initialization address XOR eax, EAX mov eax, &codering3 + PROTECADDR mov word label_gdt+8*7+2, ax shr eax, + mov byte label_gdt+8*7+4, AL mov byte label_gdt+8*7+7, AH//Initialize TSS mov DWORD [tts_topofstack-protecaddr], S Tacktop//Record privilege 0 Stack mov DWORD [tts_selectorstack-protecaddr], selectorstack//record privilege 0 when using the stack selector sub mov word [Tts_io-protec ADDR], tts_end//i/o bitmap base Address mov byte [tts_end-protecaddr], 0xFF//i/o bitmap End flag//Load TSS mov ax, selectortss ltr AX//For a privilege level of 3 Standby push SelectorStack3 push StackTop3 push SelectorCodeRing3 push 0 retf//RETF to reduce the power and switch to a privileged level of 3 test function _dead:jmp _dead}//Privileges Level 3 test function asm void CodeRing3 () {mov ax, selectorvideo//Because the privilege level is 3, so the video segment also has to be for levels 3 MOV GS, AX//Video Segment Selector (purpose)//below to display a string ( Display has reached protection mode information) mov esi, &MSG2//source data offset mov edi, (((80 * 1 + 0) * 2)//destination data offset. Screen 1th, column No. 0. Call Dispstr call Selectorcallgatetest:&codedest//PassDoor call, back to privileged Level 0 _dead://Will not execute here, just to detect whether it is out of bounds jmp _dead}//Gate test function asm void Codedest () {mov ax, selectorvideo mov gs, AX//below Shows a string (display has reached the door task information) mov esi, &MSG3//source data offset mov edi, (((80 * 2 + 0) * 2)//destination data offset. Screen 2nd, column No. 0. Call DISPSTR//Set the base address of the LDT on the GDT and then load the local descriptor (LDT) XOR eax, EAX mov eax, &label_ldt + PROTECADDR mov word label_gdt+select orldt+2, ax shr eax, mov byte label_gdt+selectorldt+4, AL mov byte label_gdt+selectorldt+7, ah mov ax, Selectorldt Lldt AX jmp selectorldtcodea:&ldtcode//Jump to local task _dead://Will not execute here, just to detect whether it is out of bounds jmp _dead}//Local code snippet ASM void Ldtcode () {mov AX, Selectorvideo mov gs, AX//below shows a string (display has reached protection mode information) mov esi, &MSG4//source data offset mov edi, (((80 * 3 + 0) * 2)//destination data offset. Screen 3rd, column No. 0. Call Dispstr _dead://Last here to stop jmp _dead}//display a string that requires the ESI to point to the string address first, EDI to the starting position of the string asm void Dispstr () {mov ah, 14h//Blue bottom Red character (ah = 14h)//Looping string output _dispstr:mov al, ds:[esi]//because it is readable, you can use CS to point to the current segment of the MSG String Inc ESI CMP al, '/0 '//To determine if the string ends JZ _stop mov g S:[edi], ax add EDI, 2 jmp _dispstr _stoP://Show complete RET}