成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

截獲Linux操作系統異常處理

系統 Linux
在某些情況下,我們可能需要去截獲Linux操作系統的一些異常處理,比如截獲page fault異常處理。如果我們能夠修改內核,那么截獲page fault異常處理就會非常簡單。但是有些情況下,我們不能直接修改內核代碼,需要在已經編譯好的內核上完成截獲功能。

  在某些情況下,我們可能需要去截獲Linux操作系統的一些異常處理,比如截獲page fault異常處理。

  可以修改內核的情況下:

  如果我們能夠修改內核,那么截獲page fault異常處理就會非常簡單。以linux 3.8.0內核為例,系統中發生page fault之后,會進入page fault異常處理,調用do_page_fault函數。do_page_fault的代碼如下:

  1. dotraplinkage void __kprobes 
  2. do_page_fault(struct pt_regs *regs, unsigned long error_code) 
  3. exception_enter(regs); 
  4. __do_page_fault(regs, error_code); 
  5. exception_exit(regs); 

  我們把do_page_fault函數的內容提取出來,寫成一個新的函數default_do_page_fault。再增加一個函數指針do_page_fault_handler,初始化為default_do_page_fault。將原來的do_page_fault內部改為調用函數指針do_page_fault_handler。修改之后的代碼如下:

  1. void 
  2. default_do_page_fault(struct pt_regs *regs, unsigned long error_code) 
  3. exception_enter(regs); 
  4. __do_page_fault(regs, error_code); 
  5. exception_exit(regs); 
  6. EXPORT_SYMBOL(default_do_page_fault); 
  7. typedef void (*do_page_fault_handler_t)(struct pt_regs *, unsigned long); 
  8. do_page_fault_handler_t do_page_fault_handler = default_do_page_fault
  9. EXPORT_SYMBOL(do_page_fault_handler); 
  10. dotraplinkage void __kprobes 
  11. do_page_fault(struct pt_regs *regs, unsigned long error_code){ 
  12. do_page_fault_handler(regs, error_code); 

  由于do_page_fault_handler被EXPORT_SYMBOL導出,我們在內核模塊中可以很方便地訪問它。只要將do_page_fault_handler的值設置為自定義的page fault異常處理函數,就能完成截獲功能。如果想要恢復原來的異常處理函數,只需要再次把do_page_fault_handler設置為default_do_page_fault即可。

  不能修改內核的情況下:

  但是有些情況下,我們不能直接修改內核代碼,需要在已經編譯好的內核上完成截獲功能。

  開始的時候,我考慮在do_page_fault函數開始處插入跳轉代碼,跳轉到自定義的page fault處理函數中。但是實踐的時候發現,內核不允許直接修改do_page_fault的代碼。

  經過一番調查,又想到一個新的辦法,即通過更改IDT表的方式來截獲page fault。

  內核原有的IDT表肯定是不能直接寫的,所以我申請了一個頁,將原來的IDT表復制過來,再更改頁面異常對應的ISR(Interrupt Service Routine)。page fault的ISR名稱為page_fault,它將寄存器壓棧,將error number壓棧,然后調用do_page_fault,待do_page_fault返回之后再恢復寄存器,退出異常處理。

  在Linux內核中,ISR是用匯編寫的。例如,x86_64 Linux的ISR源碼位于內核源碼arch/x86/kernel/entry_64.S中,X86_32的位于arch/x86/kernel/entry_32.S中。如果去讀entry_64.S或者entry_32.S,你會發現這兩個文件非常復雜,利用了很多的匯編宏和宏定義,無法方便地基于它們寫一個自定義的ISR出來。

  我的解決辦法是將內核編譯出來,反匯編vmlinux.o,然后查找page_fault,找到其匯編代碼。下面的匯編代碼就是linux-3.8.0 X86_64內核的:

  1. ffffffff8136f6f0 <page_fault>
  2. ffffffff8136f6f0:       66 66 90                data32 xchg %ax,%ax 
  3. ffffffff8136f6f3:       ff 15 07 0a 2b 00       callq  *0x2b0a07(%rip)        # ffffffff81620100 <pv_irq_ops+0x30> 
  4. ffffffff8136f6f9:       48 83 ec 78             sub    $0x78,%rsp 
  5. ffffffff8136f6fd:       e8 ae 01 00 00          callq  ffffffff8136f8b0 <error_entry> 
  6. ffffffff8136f702:       48 89 e7                mov    %rsp,%rdi 
  7. ffffffff8136f705:       48 8b 74 24 78          mov    0x78(%rsp),%rsi 
  8. ffffffff8136f70a:       48 c7 44 24 78 ff ff    movq   $0xffffffffffffffff,0x78(%rsp) 
  9. ffffffff8136f711:       ff ff 
  10. ffffffff8136f713:       e8 1f 2e 00 00          callq  ffffffff81372537 <do_page_fault> 
  11. 11 ffffffff8136f718:       e9 33 02 00 00          jmpq   ffffffff8136f950 <error_exit> 
  12. 12 ffffffff8136f71d:       0f 1f 00                nopl   (%rax) 

  我仿照著寫了一個,名為my_page_fault:

  1. asmlinkage void my_page_fault(void); 
  2. asm("   .text"); 
  3. asm("   .type my_page_fault,@function"); 
  4. asm("my_page_fault:"); 
  5. //the first 3 bytes of the routine basically do nothing, 
  6. //but I decide to keep them because kernel may rely on them for some special purpose 
  7. asm("   .byte 0x66"); 
  8. asm("   xchg %ax, %ax"); 
  9. asm("   callq *addr_adjust_exception_frame"); 
  10. asm("   sub $0x78, %rsp"); 
  11. asm("   callq *addr_error_entry"); 
  12. asm("   mov %rsp, %rdi"); 
  13. asm("   mov 0x78(%rsp), %rsi"); 
  14. asm("   movq $0xffffffffffffffff, 0x78(%rsp)"); 
  15. asm("   callq my_do_page_fault"); 
  16. asm("   jmpq *addr_error_exit"); 
  17. asm("   nopl (%rax)"); 

  其中第9行addr_adjust_exception_frame是(pv_irq_ops+0x30)地址處存儲的值;第11行addr_error_entry是error_entry的地址;第16行addr_error_exit是error_exit的地址。這幾個值需要從System.map文件中查詢,然后用內核模塊參數的形式傳入。而my_do_page_fault則是我們自己定義的page fault處理函數。

  如果需要截獲X86_32的page fault,可以參考這個C文件。不過需要注意的是,新版內核有所變動,這里的代碼需要根據自己的情況做一些調整。

  有了自定義的ISR之后,就可以將這個ISR填到IDT中,加載新的IDT表之后,自定義的page fault處理函數就開始發揮作用了。這個過程主要有以下幾個步驟:

  • 用store_idt(&default_idtr)保存現有的IDT寄存器值
  • 從default_idtr中讀出IDT表首地址和表的大小
  • 申請一個頁面
  • 將原來的idt表拷貝到新申請的頁面中
  • 利用pack_gate將my_page_fault(注意不是my_do_page_fault)填入到對應的IDT項中
  • 在idtr中填寫新的IDT表地址和大小,用load_idt(&idtr)加載新的IDT表到當前CPU
  • 利用smp_call_function,將新的IDT表加載到其他CPU上。

  如果想恢復原來的IDT表,則用load(&default_idtr)和smp_call_function加載原來的IDT表,釋放申請的頁面。

  讀完文章之后,可以參考我的github中的代碼:https://github.com/RichardUSTC/intercept-page-fault-handler

責任編輯:黃丹 來源: 博客
相關推薦

2009-12-09 17:25:19

Linux操作系統

2010-04-08 17:56:42

Unix操作系統

2020-12-29 16:39:01

Linux代碼命令

2009-12-14 17:46:40

Linux桌面操作系統

2010-04-29 14:08:38

Unix操作系統

2013-12-12 17:03:57

Lua腳本語言

2009-12-23 10:11:49

Linux操作系統

2011-01-10 16:34:13

linux安裝

2014-07-28 17:25:25

國產Linux

2009-12-16 09:43:12

Linux操作系統

2009-12-22 13:44:33

Linux操作系統

2014-09-10 09:54:43

2010-02-26 14:13:51

Linux操作系統

2009-12-17 14:10:37

Linux操作系統

2009-12-21 17:18:45

Linux操作系統

2009-12-09 16:34:31

2009-12-10 16:45:39

Linux操作系統

2009-12-10 17:45:46

Linux操作系統

2010-01-05 13:23:18

Linux操作系統

2014-06-05 09:49:33

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人精品鲁一区一区二区 | 在线电影日韩 | 91久久久久久久久久久久久 | 午夜精品一区二区三区在线视频 | 午夜精品久久久久久久久久久久 | 91精品免费视频 | 久久久999国产精品 中文字幕在线精品 | 国产精品中文在线 | 国产免费一区 | 国产精品久久午夜夜伦鲁鲁 | 国产99热 | 亚洲+变态+欧美+另类+精品 | 国产亚洲一区二区三区 | 中文字幕一区二区三区乱码图片 | 日韩av一区二区在线观看 | 日本午夜一区二区三区 | 久久大| 亚洲视频免费在线看 | 一区二区三区日韩 | 欧美在线a| 久久久精品一区二区三区 | 亚洲欧美在线观看 | www.国产精品 | 九九热在线视频观看这里只有精品 | 国产www在线 | 日本不卡一区二区三区在线观看 | 91在线视频观看免费 | 麻豆国产一区二区三区四区 | 免费成人高清 | 色婷婷综合久久久久中文一区二区 | 久久91精品国产一区二区 | 91免费在线 | 日韩在线精品视频 | av网站免费观看 | 亚洲视频在线播放 | 操人网站 | 99久久久国产精品 | 亚洲精品成人网 | 欧美综合国产精品久久丁香 | 日韩福利 | 亚洲精品乱码8久久久久久日本 |