1. 程式人生 > >Linux x86 64內核終止D狀態的進程

Linux x86 64內核終止D狀態的進程

_for div 狀態 com state schedule set 教程 mpat

在上一篇文章《Linux x86內核終止D狀態的進程》中,我展示了32位x86系統中如何編碼殺死D進程。本文我將展示一種64位x86系統上的方法。
說實話,64位系統上做這樣的事是比較難的,因為你無法通過修改p->thread.ip來到達將進程拽出死循環的目的。要想知道64位系統上到底該怎麽把進程執行緒引出,我們得先看看”標準“的做法是什麽。

標準的做法就是fork時的行為,一個新進程剛剛被創建,它第一次進入運行狀態之前,並不是通過switch_to切出的,為了讓它”看起來像“是被切出而後被切入,就需要ret_from_fork來制造現場。問題是既然無法修改p->thread.ip,那又如何把執行緒引導到ret_from_fork裏。

答案在於,64位(這裏特指x86_64)系統是在switch_to中直接通過標誌位判斷跳轉的,其過程如下:

1.在copy_thread中設置TIF_FORK標誌

2.在switch_to中判斷TIF_FORK標誌是否存在,若存在則直接跳轉到ret_from_fork

因此ret_from_fork在64位系統中是硬編碼到switch_to中的,不像32位系統中那樣是可以隨意修改的。

到這裏,想通過修改堆棧上保存的PC寄存器來達到跳出循環的這條心也該死了。一個進程被切入,要麽循著被切出之前的路徑走,要麽進入ret_from_fork,只有這兩條路。如果循著之前的路,那還是在死循環裏面,那麽只能給D進程設置TIF_FORK標記,引導它進入ret_from_fork!
然而我們並不是真的希望它return from fork,而是因為這是不得已的辦法,它只能到ret_from_fork裏面。接下來怎麽辦?
接下來的技術涉及到inline hook,我們希望hook掉ret_from_fork這個entry!具體如何inline hook,本文不講,不然本文又要很長很長了。本文僅僅給出ret_from_fork被hook後的樣子:
ENTRY(ret_from_fork)
        DEFAULT_FRAME

        LOCK ; btr $TIF_FORK,TI_flags(%r8)

        push kernel_eflags(%rip)
        CFI_ADJUST_CFA_OFFSET 8
        popf                                    # reset kernel eflags
        CFI_ADJUST_CFA_OFFSET -8

        call schedule_tail                      # rdi: ‘prev‘ task parameter

        GET_THREAD_INFO(%rcx)
        
        testl $_TIF_D, TI_flags(%rcx)        # 這裏判斷是不是有新增的TIF_D標識,如果有,就直接do_exit
        jnz do_exit

        RESTORE_REST


        testl $3, CS-ARGOFFSET(%rsp)            # from kernel_thread?
        je   int_ret_from_sys_call

        testl $_TIF_IA32, TI_flags(%rcx)        # 32-bit compat task needs IRET
        jnz  int_ret_from_sys_call

        RESTORE_TOP_OF_STACK %rdi, -ARGOFFSET
        jmp ret_from_sys_call                   # go to the SYSRET fastpath

        CFI_ENDPROC
END(ret_from_fork)

然後,模塊裏非常簡單的設置TIF_FORK和TIF_D即可:
if (pid > 0) {
    for_each_process(p) {
        if (task_pid_vnr(p) == pid) {
            set_task_state(p, TASK_INTERRUPTIBLE);
            // 設置TIF_FORK,目的是執行流導入ret_from_fork
            set_tsk_thread_flag(p, TIF_FORK);
            // 設置TIF_D,目的是將執行流在hook後的ret_from_fork裏進行區分
            set_tsk_thread_flag(p, TIF_D);
            wake_up_process(p);
            break;
        }
    }
}

和32位系統的實驗方法一樣,D進程將被拉出死循環,然後死掉!

註意,用kprobe/jprobe技術進行hook事實上就是一種inline hook的應用,然而我們不方便用它來hook ret_from_fork,因為你既不能在ret_from_fork之前執行hook,也不能之後執行hook,而必須在其中間,即調用完schedule_tail之後去執行do_exit,因此,正確的做法是hook別的函數而不是hook ret_from_fork這個函數。仔細觀察ret_from_fork的匯編碼,就會發現在int_ret_from_sys_call,ret_from_sys_call的pre handler中進行TIF_D的判斷並且執行do_exit應該是正確的做法!

溫州皮鞋,下雨進水不會胖!


再分享一下我老師大神的人工智能教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智能的隊伍中來!https://blog.csdn.net/jiangjunshow

Linux x86 64內核終止D狀態的進程