Linux x86 64内核终止D状态的进程

在上一篇文章《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

原文地址:https://www.cnblogs.com/ksiwnhiwhs/p/10390509.html

时间: 2024-08-23 14:18:45

Linux x86 64内核终止D状态的进程的相关文章

栈溢出攻击系列:shellcode在linux x86 64位攻击获得root权限(二)shellcode

shellcode 是一组指令opcode, 是可以被程序运行,因为shellcode是要直接操作寄存器和函数,所以opcode 必须是十六进制的形式. 既然是攻击,那shellcode 主要的目的是调用系统函数,而在x86下 在linux下有两种方式. 第一种是通过直接调用中断 int 0x80进入内核态,从而达到调用目的. 第二种是通过调用libc里syscall(64位)和sysenter(32位) 而目前在64位linux中推荐使用syscall,因为opcode是16进制的指令集合,可

栈溢出攻击系列:shellcode在linux x86 64位攻击获得root权限(一)函数如何执行

栈溢出网上已经有很多的例子了,但是很少会涉及到在64位的和操作系统linux相关的,而最近刚好在一直研究这个,所以写着一系列博文,一来是为了帮助自己记忆,二来也是为了更多的大家互相探讨. 寄存器 X86-64有16个64位寄存器,分别是:%rax,%rbx,%rcx,%rdx,%esi,%edi,%rbp,%rsp,%r8,%r9,%r10,%r11,%r12,%r13,%r14,%r15.其中: %rax 作为函数返回值使用 %rsp 栈指针寄存器,指向栈顶 %rdi,%rsi,%rdx,%r

栈溢出攻击系列:shellcode在linux x86 64位攻击获得root权限(七)利用寄存器攻击

在(六)中我们提到了使用固定栈地址的攻击方式,但在实际中,系统默认的参数不会为0 cat /proc/sys/kernel/randomize_va_space 那么在系列中的六失去攻击的意义,但是任何事情都会有漏洞,我们来讲另一个基于寄存器的攻击 漏洞代码 vulnerableret2reg.c #include <stdio.h> #include <string.h> void evilfunction(char* input) { char buffer[1000]; st

Socket与系统调用深度分析 ——X86 64环境下Linux5.0以上的内核中

1.Socket与系统调用——概述 Socket API编程接口之上可以编写基于不同网络协议的应用程序: Socket接口在用户态通过系统调用机制进入内核: 内核中将系统调用作为一个特殊的中断来处理,以socket相关系统调用为例进行分析: socket相关系统调用的内核处理函数内部通过“多态机制”对不同的网络协议进行的封装方法: 下面会将Socket API编程接口.系统调用机制及内核中系统调用相关源代码. socket相关系统调用的内核处理函数结合起来分析,并在X86 64环境下Linux5

Linux 5.3内核系列已终止支持 建议用户升级至Linux Kernel 5.4

上周,Linux 内核开发人员 Greg Kroah-Hartman 宣布了 Linux 5.3 内核系列的第 18 个维护更新(5.3.18).该版本共更改了 59 个文件,插入 369 项 / 移除 329 项.此外开发者指出,这将是 Linux 5.3 内核系列的最后一个维护更新.随着 Linux Kernel 5.3 抵达 EoL,官方建议用户及时更新至 Linux Kernel 5.4,以获得全面的支持与保障. Greg Kroah-Hartman 在<a href="http:

Linux 第20天: (09月12日) Linux启动和内核管理

本章内容 CentOS 5和6的启动流程服务管理Grub管理自制Linux启动排错编译安装内核 Linux组成Linux: kernel+rootfskernel: 进程管理.内存管理.网络管理.驱动程序.文件系统.安全功能rootfs:程序和glibc库:函数集合, function, 调用接口(头文件负责描述)过程调用:procedure,无返回值函数调用:function程序:二进制执行文件内核设计流派:单内核(monolithic kernel):Linux把所有功能集成于同一个程序微内

【读书笔记】《Linux内核设计与实现》进程管理与调度

大学跟老师做嵌入式项目,写过I2C的设备驱动,但对Linux内核的了解也仅限于此.Android系统许多导致root的漏洞都是内核中的,研究起来很有趣,但看相关的分析文章总感觉隔着一层窗户纸,不能完全理会.所以打算系统的学习一下Linux内核.买了两本书<Linux内核设计与实现(第3版)>和<深入理解Linux内核(第3版)> 0x00 一些废话 面向对象思想. Linux内核虽然是C和汇编语言写的,没有使用面向对象的语言,但里面却包含了大量面向对象的设计.比如可以把内核中的进程

Linux命令:kill命令 终止进程

Linux中的kill命令用来终止指定的进程(terminate a process)的运行 通常,终止一个前台进程可以使用Ctrl+C键,但是,对于一个后台进程就须用kill命令来终止,我们就需要先使用ps/pidof/pstree/top等工具获取进程PID,然后使用kill命令来杀掉该进程.kill命令是通过向进程发送指定的信号来结束相应进程的.在默认情况下,采用编号为15的TERM信号.TERM信号将终止所有不能捕获该信号的进程.对于那些可以捕获该信号的进程就要用编号为9的kill信号,

【转载】LINUX 和 WINDOWS 内核的区别

LINUX 和 WINDOWS 内核的区别 [声明:欢迎转载,转载请注明出自CU ACCESSORY http://linux.chinaunix.net/bbs/thread-1153868-1-1.html] 关于LINUX和WINDOWS的口水站已经很多了.本文企图从技术角度来比较下2个主流操作系统的异同.偏重于内核部分. 一.动机: 我最早是 WINDOWS 阵营的.在WINDOWS下写过2年多的驱动程序.后来由于学习需要,转投LINUX,一晃也快2年了.期间经历了很多曲折,也学到了很多