读Kernel感悟-伪装现场-内核线程

Linux kernel 分析之十一:信号通信 信号是进程之间通信的一种方式。它包括3部分操作: 1.设置信号处理函数。系统调用signal。内核调用sys_signal(),设置当前进程对某信号的处理函数。 2.发送信号.系统调用kill。内核调用sys_kill()。向目标进程发送信号。3.接收并处理信号。目标进程调用do_signal()处理信号。 从用户态的角度看,目标进程在执行用户态的代码时突然“中断”,转而去执行对应的信号处理 阅读详情

文章来源:http://www.top-e.org/jiaoshi/class/

众所周知,内核中创建一个内核线程是通过kernel_thread实现的。声明如下:

int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);

我们知道,用户态创建线程调用clone(),如果要在内核态创建线程,首先想到的是在内核态调用clone()。这是可以的。比如在init内核线程中就直接在内核态调用execve,参数为/sbin/init等等。但是还是要小心翼翼。因为系统调用里会有很多参数要求是用户态的(一般在声明前有__user ),在调用一些内核函数时也会检查参数的界限,严格要求参数在用户态。一旦发现参数是在内核态,就立即返回出错。

所以kernel_thread采用了另外一种办法。

由于不是从用户态进入内核的,它需要制造一种现场,好像它是通过clone系统调用进入内核一样。方法是手动生成并设置一个struct pt_regs,然后调用do_fork()。但是怎样把线程的函数指针fn,参数arg传进去呢?和flags不同,flags可以作为do_fork()的参数。但是fn正常情况下应该是在clone()结束后才执行的。此外,线程总不能长生不老吧,所以执行完fn()还要执行exit()。

所以,我们希望内核线程在创建后,回到内核态(普通情况下是用户态)后,去调用fn(arg),最后调用exit()。而要去“遥控”内核线程在创建以后的事,只能通过设置pt_regs来实现了。

看kernel_thread的实现:

355         regs.ebx = (unsigned long) fn;

356         regs.edx = (unsigned long) arg;

这里设置了参数fn,arg,当内核线程在创建以后,ebx中放的是fn,edx中放的是arg

358         regs.xds = __USER_DS;

359         regs.xes = __USER_DS;

360         regs.orig_eax = -1;

361         regs.eip = (unsigned long) kernel_thread_helper;

当内核线程在创建以后,执行的是 kernel_thread_helper函数

362         regs.xcs = __KERNEL_CS;

当内核线程在创建以后,cs寄存器的值表明当前仍然处于内核态。

363         regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;

364 

365         /* Ok, create the new process.. */

366         return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);

看来kernel_thread_helper就是我们想要的东西了。

336 __asm__(".section .text/n"

337         ".align 4/n"

338         "kernel_thread_helper:/n/t"

339         "movl %edx,%eax/n/t"

340         "pushl %edx/n/t"

341         "call *%ebx/n/t"

342         "pushl %eax/n/t"

343         "call do_exit/n"

344         ".previous");

首先把edx保存到eax(不明白为什么这么做,因为调用fn后返回值就把eax覆盖掉了)把edx(其实就是参数arg)压入堆栈,然后调用ebx(也就是fn)。最后调用do_exit。kernel_thread_helper是不返回的。

这里,内核通过巧妙设置pt_regs,在没有用户进程的情况下,在内核态创建了线程。

读核感悟-伪装现场-信号通信

信号是进程之间通信的一种方式。它包括3部分操作:

1.设置信号处理函数。系统调用signal。内核调用sys_signal(),设置当前进程对某信号的处理函数。

2.发送信号.系统调用kill。内核调用sys_kill()。向目标进程发送信号。

3.接收并处理信号。目标进程调用do_signal()处理信号。

从用户态的角度看,目标进程在执行用户态的代码时突然“中断”,转而去执行对应的信号处理函数(同样在用户态)。等到信号处理函数执行完后,又从原来被中断的代码开始执行。

如何达到这样的效果呢?由前面的几种内核的伪装现场的手段,我们可以猜出它这次使用的手段。比如,要让目标进程执行信号处理函数,在内核态中当然不可能直接调用,但是可以通过设置pt_regs中的eip来达到这种效果。但是,要使目标进程在执行完信号处理函数后,又恢复到被中断的现场继续执行,那得花些技巧。不过,不外乎设置堆栈。这一次还包括了用户态堆栈。由于恢复的任务比较艰巨,系统干脆提供了一个系统调用sigreturn 。

既然内核希望用户在执行完信号处理函数后,调用sigreturn。接下去的思路就比较简单了。就是先把用户态的eip设置为signal_handler(通过修改pt_regs中的eip来实现),然后把堆栈中的返回地址改成调用sigreturn的一段代码的入口(当然原来的返回地址也还是要保存的)并且把相关参数“压入”用户态堆栈。

这样,在源进程发送信号后不久,目标进程被调度到,然后执行到do_signal。对信号一一作处理。调用顺序:

do_signal()->handle_signal()->setup_rt_frame()

用来设置用户态堆栈。

我们看看这个函数做了些啥?

447         frame = get_sigframe(ka, regs, sizeof(*frame));

struct rt_sigframe __user *frame是内核在用户态的堆栈上新分配的一个数据结构。

011 struct rt_sigframe

012 {

013         char *pretcode;

014         int sig;

015         struct siginfo *pinfo;

016         void *puc;

017         struct siginfo info;

018         struct ucontext uc;

019         struct _fpstate fpstate;

020         char retcode[8];

021 };

图示:

高地址

-------------- 用户进程原堆栈底部

-------------- 用户进程原堆栈顶部

frame->retcode frame底部

frame->pretcode 返回地址:从signal handler返回后跳转的地址

-------------- frame顶部:用户进程新堆栈顶部

低地址

里面保存了大量用户态进程的上下文信息。尤其是 pretcode,现在位于用户进程的新堆栈的顶部。

接下去开始设置frame

458         err |= __put_user(usig, &frame->sig);

459         err |= __put_user(&frame->info, &frame->pinfo);

460         err |= __put_user(&frame->uc, &frame->puc);

461         err |= copy_siginfo_to_user(&frame->info, info);

462         if (err)

463                 goto give_sigsegv;

464 

465         /* Create the ucontext.  */

466         err |= __put_user(0, &frame->uc.uc_flags);

467         err |= __put_user(0, &frame->uc.uc_link);

468         err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);

469         err |= __put_user(sas_ss_flags(regs->esp),

470                           &frame->uc.uc_stack.ss_flags);

471         err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);

472         err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,

473                                 regs, set->sig[0]);

474         err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));

当用户进程根据pt_regs中设置好的eip执行signal handler。执行完毕后就会把frame->pretcode作为返回地址。(这一点2.6.13的内核与2.4的不同,后者是把指针指向retcode,其实仍然是调用sigreturn的代码。)

478         /* Set up to return from userspace.  */

479         restorer = &__kernel_rt_sigreturn;

480         if (ka->sa.sa_flags & SA_RESTORER)

481                 restorer = ka->sa.sa_restorer;

482         err |= __put_user(restorer, &frame->pretcode);

这里的&__kernel_rt_sigreturn就是内核设置的负责信号处理“善后”工作的代码入口。定义在arch/i386/kernel/vsyscall-sigreturn.S

nm /usr/src/linux/vmlinux|grep _kernel_rt_sigreturn

得,它的值是_kernel_rt_sigreturn

&__kernel_rt_sigreturn的值应该是内核态的。

问题来了。frame->pretcode是作为进程在用户态执行的代码(事实上,从执行signal handler开始,进程一直处于用户态)。它怎么能访问内核态的代码呢?这不是会段错误么?

这里涉及到PIII中用sysenter来代替系统调用的int 0x80的问题。大概就是内核允许一部分代码给用户态进程访问。

例如:

cat /proc/$pid/maps可以看到:

ffffe000-fffff000 ---p 00000000 00:00 0          [vdso]

ldd 一个应用程序也可以看到:

linux-gate.so.1 =>  (0xffffe000)

在arch/i386/kernel/中可以看到两个文件:

vsyscall-sysenter.so

vsyscall-int80.so

也就是说,__kernel_rt_sigreturn这段代码是链接在两个动态链接文件中。而不是vmlinux这个内核文件中。

具体是如何做到的,就不展开说了。

总之,在执行完signal handler后,进程将跳转到__kernel_rt_sigreturn。

021 __kernel_sigreturn:

022 .LSTART_sigreturn:

023         popl %eax               /* XXX does this mean it needs unwind info? */

024         movl $__NR_sigreturn, %eax

025         int $0x80

实际上调用的是sigreturn系统调用。该系统调用会根据frame里的信息,把堆栈恢复到处理信号之前的状态。所以这段代码是不返回的。然后,用户进程就像什么事也没发生,继续照常运行。

这里,内核通过设置用户态堆栈的手段,达到了打断用户态进程的运行,转而调用signal handler的目的。手段不可谓不高明。

文章来源:http://www.top-e.org/jiaoshi/class/

 

伪装线程进程 可以伪装现成进程 立即下载

相关推荐

PackML工业标准解析:统一机器状态管理与数据交换的实践指南

在工业自动化与智能制造领域,实现设备间的互联互通与高效协同是核心挑战。这背后依赖于一套标准化的机器行为与数据交换规范,其原理在于通过定义统一的状态模型与接口协议,消除不同厂商设备间的“语言”隔阂。这项技术的核心价值在于大幅降低系统集成复杂度、提升运维效率与生产透明度,是构建数字化工厂的基石。其典型应用场景包括包装机械、离散制造产线等需要多设备协同的领域。本文聚焦的PackML(包装机械语言)标准,正是这一理念的杰出实践,它通过一套严谨的**状态机模型**和**数据标签(PackTags)** 体系,为机器提

weixin_30070339的博客 401

linux信号处理

linux在构造信号处理过程中面临内核态触发用户态代码的问题。当信号到达内核,内核不可能直接执行用户态的代码。所以,内核利用的方式是将用户态进程的栈帧扩展,然后直接返回用户态,利用伪造的栈信息执行信号量。 这是内核给用户态的程序提供的一个接口。 arch/arm64/kernel/vdso/vdso.lds.S ENTRY(__kernel_rt_sigreturn) .cfi_startproc .cfi_signal_frame .cfi_def_cfa

inquisiter 2168

Ubuntu 20.04下IgH EtherCAT主站部署实战

本文详细介绍了在Ubuntu 20.04系统上部署IgH EtherCAT主站的完整流程,包括环境准备、源码编译、内核模块配置、网络接口设置及实时性优化等关键步骤。通过实战案例和常见问题排查,帮助开发者快速掌握EtherCAT主站部署技巧,提升工业自动化系统的稳定性和实时性。

weixin_30596343的博客 1071

Linux kernel signal原理(下)- aarch64架构sigreturn流程

aarch64 sigreturn

weixin_43412488的博客 1230

拆解维修指南:当你的大扭矩电动扳手‘罢工’,如何自己动手排查行星齿轮与谐波传动故障?

本文详细解析了大扭矩电动扳手行星齿轮与谐波传动故障的排查与修复方法。从故障初判、拆解工艺到行星齿轮系统检测、谐波传动维护,提供了一套完整的维修指南,帮助用户快速诊断和解决常见问题,延长工具使用寿命。

weixin_42525582的博客 436

基于java实现的弹幕视频网站

开发语言:Java框架:ssm技术:JSPJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:Maven3.3.9系统展示弹幕视频网站,在弹幕视频网站可以查看首页、视频信息、商品信息、论坛信息、我的、跳转到后台、购物车、客服等内容前台首页界面图登录,通过登录填写账号、密码等信息进行登录操作登录界面图视频信息界面图。

DC5621的博客 1333

Linux Signal handling(信号处理)Arm64

本文基于Arm64架构详述Linux的信号处理过程,Linux把信号处理分为俩个部分,第一部分是信号发送,第二部分是信号处理。信号发送部分主要是设置相应进程的信号阻塞位;信号处理则是接收到信号的进程调用信号处理函数。 信号发送 在内核代码中: kernel/signal.c static int send_signal(int sig, struct siginfo *in

firefox_1980的专栏 2988

触摸开关原理图_反激式开关电源PCB设计要点

由于最近工作中经常用到反激开关电源,就顺便做个有关反激电源PCB Layout方面的简单总结。关于变压器和补偿环路的设计原理和方法,可以参考之前的文章,链接如下:电子小白菜:反激电源变压器设计篇之详细设计步骤​zhuanlan.zhihu.com电子小白菜:反激电源变压器设计篇之基础原理​zhuanlan.zhihu.com电子小白菜:【干货分享】开关电源环路补偿设计步骤讲解​zhuanlan.z...

weixin_39574868的博客 1159

libuv 编译使用,打印调用堆栈

libuv 编译选项: CFLAGS='-g -O0 -funwind-tables' ./configure --disable-silent-rules --disable-udev --enable-debug-log make -j8 V=1 其中 -funwind-tables 可以打印详细调用堆栈。 写程序使用:t1.cc #include <ux.h> #include <unistd.h> #include <signal.h> #inclu

huihuiwith的博客 1171

Autosar配置——导入DBC后配置SecOC同步报文和同步请求报文

在Autosar中,安全性是一个重要的考虑因素,而SecOC(Secure On-Board Communication)是一种用于保护通信数据完整性和安全性的安全机制。在上述示例中,我们配置了同步报文和同步请求报文的属性。接下来,我们需要在Autosar中配置同步报文和同步请求报文。同步报文用于在通信双方之间同步SecOC状态,而同步请求报文用于请求SecOC的状态信息。通过以上配置,我们成功地导入了DBC文件,并在Autosar系统中配置了SecOC同步报文和同步请求报文。希望以上信息对您有所帮助!

CyberSphinx的博客 649

Unity之如何实现TTS文本转语音(真人发音)

微软Azure TTS 文本转语音 服务 将文本转换为逼真的语音的语音服务功能 支持个各国语言,各种中文各种方言,支持不同音色男音女音 支持离线和在线版本

TxNet 5480

内核中劫持线程注入DLL并隐藏

在驱动程序中,您可以通过调用PsSetCreateThreadNotifyRoutine或PsSetLoadImageNotifyRoutine等API来获取进程线程的通知。这时,您可以暂停线程并修改其上下文,以便在恢复时执行您的代码。为了实现这些功能,您需要学习Windows内核编程和驱动开发。在此过程中,您可能需要使用Windows Driver Kit(WDK)和Visual Studio。编写好驱动程序后,您需要在目标系统上加载和卸载它。在劫持线程后,您需要将目标DLL加载到目标进程的内存中。

人生苦短,我用python 2102

麒麟操作系统提示“默认密钥环已上锁”的解决办法

麒麟操作系统提示“默认密钥环已上锁”的解决办法

太极淘的博客 1万+

滴水逆向——Win32_创建线程

进程是正在运行的程序的实例。一个进程至少有一个线程,一个进程中可以并发多个线程,每条线程同时执行不同的任务。不知道为什么两个线程的输出顺序一直改变,好像输出的"+“比”-"要慢。好像vs2019的线程有点多。

tscg_的博客 429

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

windows黑客编程技术之隐藏技术(进程伪装,傀儡进程,进程隐藏)

windows黑客编程技术之隐藏技术(进程伪装,傀儡进程,进程隐藏)

weixin_44891742的博客 8281

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

CentOS4.7 kernel_thread_helper+0x5/0xb

看到一篇文章,“惠普建议增加启动选项“acpi=off”并启动安装内核”,执行linux acp=off果然安装成功。光盘启动后可以进入到安装界面,敲回车后出现新问题,屏幕显示了很多信息,最后一行是:[] kernel_thread_helper+0x5/0xb 死机。我实际操作为在install界面,编辑install对应命令行,命令行后加acpi=off。(试了RHEL AS4.7的光盘,错误提示一样,应该不是光盘的问题),里面的办法解决不了问题,再说那也不是办法。

weixin_42342523的博客 284

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

查看进程是否被注入线程(不在系统模块)

有些进程被注入了某些恶意的线程(也可能不是恶意的) 首先我们拿到该进程的进程模块,用来对比进程中线程的模块是否在这里面 把这些模块放入到vector里面 便利该进程的所有线程获取模块的起始地址和大小,很快就可以得到伪装线程。 #pragma region 依赖 typedef enum _THREADINFOCLASS{ ThreadBasicInformation, ThreadTimes, ThreadPriority, ThreadBasePriori..

henuyl的博客 2054

xenomai内核解析--信号signal(一)---Linux信号机制

文章目录1. Linux信号1.1注册信号处理函数1.2 信号的发送1.3 信号的处理2. linux 线程信号 1. Linux信号 涉及硬件底层,本文以X86平台讲解。 信号是事件发生时对进程的通知机制,是操作系统提供的一种软件中断。信号提供了一种异步处理事件的方法,信号与硬件中断的相似之处在于打断了程序执行的正常流程,例如,中断用户键入中断键(Ctrl+C),会通过信号机制停止应用程序。 信号的几种产生方式: 按键产生 当用户按某些按键时,引发终端产生的信号。 ctrl+C产生 SIGINT

wsg1100 1508

驱动开发:内核RIP劫持实现DLL注入

本章将探索内核级DLL模块注入实现原理,DLL模块注入在应用层中通常会使用`CreateRemoteThread`直接开启远程线程执行即可,驱动级别的注入有多种实现原理,而其中最简单的一种实现方式则是通过劫持EIP的方式实现,其实现原理可总结为,挂起目标进程,停止目标进程EIP的变换,在目标进程开启空间,并把相关的指令机器码和数据拷贝到里面去,然后直接修改目标进程EIP使其强行跳转到我们拷贝进去的相关机器码位置,执行相关代码后,然后再次跳转回来执行原始指令集。

微软技术分享 9590

线程劫持技术

windows 线程劫持

weixin_43799752的博客 1488

Linux kernel 分析之十:内核线程

众所周知,内核中创建一个内核线程是通过kernel_thread实现的。声明如下:         int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);       我们知道,用户态创建线程调用clone(),如果要在内核态创建线程,首先想到的是在内核态调用clone()。这是可以的。比如在init内核线程

vanileo的专栏 1887

终极指南:Windows DLL注入技术从入门到精通——探索Xenos工具的强大功能

在Windows系统开发与调试领域,DLL注入技术一直扮演着至关重要的角色。Xenos作为一款基于Blackbone库开发的专业Windows DLL注入工具,为开发者和安全研究人员提供了高效、灵活的进程内存操作解决方案。本文将深入剖析Windows DLL注入技术的演进历程,从传统方法到现代替代方案,全面展示Xenos工具的核心功能与应用场景。 ## 一、DLL注入技术基础:揭开进程内存操作的

gitblog_01124的博客 231

Kernel 内核线程

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34096182的博客 460

深入理解操作系统-内核线程篇

内核线程(Kernel Thread)是操作系统内核中创建和管理的线程,它们是操作系统核心的一部分。与用户线程不同,内核线程的创建、管理和调度完全由操作系统内核负责。内核线程通常用于执行操作系统的核心任务,如进程调度、硬件中断处理、文件系统操作等。内核线程是操作系统的核心组成部分,它们负责执行操作系统的核心任务,如进程调度、硬件中断处理、文件系统操作等。了解内核线程的特性、创建、销毁、调度、同步、互斥、实现、应用等方面的知识对于操作系统开发和理解操作系统工作原理非常重要。

Bright20040513的博客 2005
上一篇: 读Kernel感悟-伪装现场-系统调用参数
下一篇: 读Kernel感悟-伪装现场-fork()系统调用
TopEmbedded
博客等级 码龄18年 58粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值