目標:用戶程序調用whoami,一個字符串「lizhijun」放在內核中 (系統引導時載入),利用系統調用打印該字符串安全
首先不能隨意jmp到內核代碼或取用內核中的字符串。函數
由於要防止操做系統數據泄露。操做系統中的數據可能有:操作系統
那麼如何實現這種限制呢?設計
經過區分程序執行在用戶態仍是內核態,隔離內核態和用戶態的代碼和數據。3d
這種限制經過爲段設置特權級實現。指針
具體來講,訪問時,經過硬件比較CPL(當前的特權級)和DPL(目標段的特權級)code
DPL:標識一個段的特權級blog
CPL:標識正在執行的程序的特權級文檔
用戶程序(代碼+數據)位於用戶段,執行在用戶態(CPL=3 , DPL=3)字符串
內核程序位於內核段,執行在內核態(CPL=0 ,DPL=0)
用戶程序位於用戶段(CPL=3),調用系統調用進入內核(CPL=0),系統調用的具體代碼位於內核段。
CS的最低兩位(0,1,2,3)表示當前的特權級(0內核態/3用戶態)
系統初始化時(即head.s執行的時候),會創建GDT表項,表項全部的DPL都是0,這些表項對應的段是內核段。
只有中斷能進入內核,並且不是全部中斷都能進入內核
//一、應用程序調用的printf printf(格式化輸入){ //C庫函數printf,負責把參數轉換爲庫函數write()須要的格式 printf(write須要格式的參數); } //二、C庫函數printf printf(write須要格式的參數){ //writeC庫函數 write(write須要格式的參數); } //三、C庫函數write write(write須要格式的參數){ //中斷代碼,int 0x80 int 0x80 } //四、int 0x80調用特定中斷處理程序,即系統調用write() //系統調用write,位於內核區 write(){ ... }
write.c中的_syscall3()按照unistd.h中定義的格式,將參數一次填入表示爲
int write(int fd,const char *buf,off_t count)
但注意只有這個宏定義只適用於3個參數的。
「int 0x80」這一句表示嵌入的彙編代碼
**「=a」(__res)** 這一句表示彙編向C的輸出,其中a爲eax,這句的意思爲,將eax置給__res。因爲eax存放的是返回值,因此表示返回值置給 _res
**""(__NR_##name) **這一句表示C向彙編的輸入,「」若是裏邊沒有東西,則代表默認和輸出時選擇的同樣(eax)。__NR_##name中將name替換爲write。這一句表示將__NR_write輸入到eax,。
"b"((long)(a)) 同上,「b」"c"d"分別表示eax,ebx,ecx。((long)(a))中的a表示第一個參數。
總結:
「」內的東西是有關彙編的。()裏的東西是有關C語言的
__NR_write這個值表明的是系統調用號,用它找到系統調用write函數(做爲中斷處理函數),系統調用write()纔算進入內核。以後返回int 0x80後的語句,從內核返回用戶態。
[](https://img2018.cnblogs.com/blog/1735814/201910/1735814-20191029200013610-1343595035.png
sched_init(void)是系統初始化執行的函數
set_system_gate(0x80,&system_call);用於設置中斷處理門(IDT中的每一個表項就對應一箇中斷處理門),將中斷0x80交給system_call()處理
具體實現分析:
**_set_gate(&idt[n],15,3,addr)**經過宏定義
**#define _set_gate(gate_addr,type,dp1,addr)**展開
gate_addr=&idt[n]表示idt是中斷向量表基址(是個全局變量)
idt[n]找到IDT中0x80對應的表項
type=15表示
dpl=3表示中斷向量表
「a」(0x0008 0000)的做用是最後截取0x0008 0000的高16位放入段選擇符,即段選擇符爲8。
addr爲中斷處理函數入口的偏移地址
"movl %%eax,%1\n\t"中%1表示C向彙編輸入的第2個變量即*((char*)(gate_addr))。完成了將addr的低4位放入eax。
同理"movl %%edx,%2"將高4位放入edx。剩下的細節不講,最終實現將addr組裝至IDT表中
總結:
中斷處理函數system_call
movl $0x10,%edx mov %dx,%ds mov %dx,%es 用於將ds和es都置爲0x10,將數據段的段選擇符置爲0x10,數據段也在內核態中
**call _sys_call_table(,%eax,4)** 這句進入系統調用處理函數。
這句中,%eax內是__NR_write。
**_sys_call_table+4*%eax**就是相應的系統調用處理函數(sys_wirte)的入口 。__NR_write至關於系統調用的編號。*4是由於每一個系統調用佔4個字節。
_sys_call_table是一個函數指針表。
總結:
程序用戶態不能直接訪問內核態。
printf的過程
庫函數:printf。包含int 0x80調用中斷。
中斷處理函數:system_call。調用系統調用處理函數sys_write。
系統調用處理函數:sys_write。執行真正的對內核數據的操做。
實現用戶態進入內核態的關鍵
查IDT調用中斷處理程序時,經過IDT中DPL=3,且跳轉到段選擇符爲8的內核段,讓程序得以從用戶態進入內核態。