linux ptrace 图文详解(七) gdb、strace跟踪系统调用

news2025/4/28 0:13:50

目录

一、gdb/strace 跟踪程序系统调用

二、实现原理

三、代码实现

四、总结


(代码:linux 6.3.1,架构:arm64)

One look is worth a thousand words.  —— Tess Flanders

相关链接:

linux ptrace 图文详解(一)基础介绍

linux ptrace 图文详解(二) PTRACE_TRACEME 跟踪程序

linux ptrace 图文详解(三) PTRACE_ATTACH 跟踪程序

linux ptrace 图文详解(四) gdb设置软断点

linux ptrace 图文详解(五) gdb设置硬断点、观察点

linux ptrace 图文详解(六) gdb单步调试

一、gdb/strace 跟踪程序系统调用

        gdb、strace都能够跟踪被调试程序在执行过程中的所有系统调用,其底层依赖的就是ptrace(PTRACE_SYSCALL) 的能力。当使用 PTRACE_SYSCALL 时,被跟踪的进程会在每次系统调用的开始或结束时被暂停挂起,并通知父进程(gdb、strace)。这使得开发者能够详细地监控和分析程序的系统调用行为,对于调试和性能分析非常有用。

        下图是strace跟踪ls命令执行期间,用到的所有系统调用信息:

二、实现原理

        上图是gdb跟踪被调试程序执行过程中所有系统调用的原理:

        1)gdb通过ptrace(PTRACE_SYSCALL),为被调试程序的task置位TIF_SYSCALL_TRACE,然后返回;

        2)被调试程序执行系统调用陷入内核;

        3)在系统调用的入口,调用tracehook_report_syscall,判断当前task是否置位TIF_SYSCALL_TRACE;

        4)若置位,则将PTRACE_EVENTMSG_SYSCALL_ENTRY记录到被调试程序task的ptrace_message中;

        5)随后给父进程gdb发送SIGCHLD信号,并唤醒gdb的wait操作,同时设置父进程gdb wait操作的status值 ( (SIGTRAP | 0x80) << 8) | 0X70;其中,0x80代表被调试程序触发了syscall!

        6)被调试程序将自己挂起;

        7)gdb被唤醒后,检查wait的status返回值内容,发现置位了0x80,说明被调试程序执行了syscall;

        8)gdb通过ptrace(PTRACE_GETEVENTMSG) 获取被调试任务内核中的task->ptrace_message内容,来判断当前被调试程序是刚进入syscall、还是已经执行完毕syscall;

        9)gdb唤醒被调试程序继续运行,被调试程序被调度运行后,调用invoke_syscall执行真正的系统调用任务;

        10)当invoke_syscall执行完毕后,会再次调用tracehook_report_syscall,将自身挂起并通知gdb(这个流程与上述3~6步一致),唯一的区别是:此时设置到被调试任务task->ptrace_message中的字段是PTRACE_EVENTMSG_SYSCALL_EXIT;

        11)gdb被唤醒后,判断出被调试程序是因为syscall挂起的,通过ptrace(PTRACE_GETEVENTMSG)可以获取到被调试程序执行完毕系统调用的信息;

三、代码实现

1、gdb、strace 通过 ptrace(PTRACE_SYSCALL) 为被调试程序置位标志

ptrace_request
	case PTRACE_SYSCALL:
	return ptrace_resume(child, request, data) {
		if (request == PTRACE_SYSCALL)
			set_task_syscall_work(child, SYSCALL_TRACE) {
				set_ti_thread_flag(task_thread_info(t), TIF_SYSCALL_TRACE)
			}
	}

2、被调试程序进入系统调用前夕,将自己暂停下来并通知gdb

el0t_64_sync_handler {
	el0_svc
		do_el0_svc
			el0_svc_common {
				unsigned long flags = current_thread_info()->flags
				
				if (has_syscall_work(flags)) {
					syscall_trace_enter {
						if (flags & (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE))
							tracehook_report_syscall(struct pt_regs *regs        = regs,
						                             enum ptrace_syscall_dir dir = PTRACE_SYSCALL_ENTER) {
								regno = (is_compat_task() ? 12 : 7)
								saved_reg = regs->regs[regno]
								regs->regs[regno] = dir
								
								if (dir == PTRACE_SYSCALL_ENTER) {
									tracehook_report_syscall_entry(regs){
										ptrace_report_syscall(regs, unsigned long message = PTRACE_EVENTMSG_SYSCALL_ENTRY) {
/* 保存syscall entry/exit event */			current->ptrace_message = message		// 保存 PTRACE_EVENTMSG_SYSCALL_ENTRY 到ptrace_message, 之后gdb会调用ptrace来获取该信息
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>			ptrace_notify(int exit_code = SIGTRAP | 0x80) {
												ptrace_do_notify(int signr = SIGTRAP, int exit_code = exit_code, int why = CLD_TRAPPED) {
													ptrace_stop(exit_code, why, int clear_code = 1, &info) {	// 通知tracer, 并将自己挂起
														current->last_siginfo = info
														current->exit_code = exit_code
														
														do_notify_parent_cldstop(current, true, why = CLD_TRAPPED)
															info.si_signo  = SIGCHLD
															info.si_code   = why			// A.K.A: CLD_TRAPPED
															info.si_status = tsk->exit_code & 0x7f
															
															__group_send_sig_info(SIGCHLD, &info, parent)
																send_signal(sig, info, p, PIDTYPE_TGID)
																	__send_signal(sig, info, t, type, force)

															__wake_up_parent
														freezable_schedule
														current->last_siginfo = NULL	// after wake up by gdb, clear last_siginfo
													}
												}
											}
											
											if (current->exit_code) {
												send_sig(current->exit_code, current, 1)
												current->exit_code = 0
											}
											
											current->ptrace_message = 0
											return fatal_signal_pending(current)
										}//ptrace_report_syscall
									}//tracehook_report_syscall_entry
								}
								regs->regs[regno] = saved_reg
							}
						return regs->syscallno
					}//syscall_trace_enter
				}
				invoke_syscall
				...
			}
}

3、被调试程序系统调用执行完毕后,将自己暂停下来并通知gdb

el0t_64_sync_handler {
	el0_svc
		do_el0_svc
			el0_svc_common {
				unsigned long flags = current_thread_info()->flags
				
				if (has_syscall_work(flags)) {
					syscall_trace_enter
				}
				
				invoke_syscall
				
				syscall_trace_exit {
					unsigned long flags = READ_ONCE(current_thread_info()->flags)
					
					if (flags & (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP))
						tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT) {
							tracehook_report_syscall_exit(regs, step = 0) {
								ptrace_report_syscall(regs, unsigned long message = PTRACE_EVENTMSG_SYSCALL_EXIT) {
									current->ptrace_message = message
									ptrace_notify(exit_code = SIGTRAP | 0x80)
										ptrace_do_notify(SIGTRAP, exit_code, why = CLD_TRAPPED) {
											kernel_siginfo_t info
											info.si_signo = signr
											info.si_code  = exit_code  // A.K.A: SIGTRAP | 0x80
											
											ptrace_stop(exit_code, why, 1, &info) {
												current->last_siginfo = info
												current->exit_code = exit_code

												do_notify_parent_cldstop(current, true, why) {
													info.si_signo  = SIGCHLD
													info.si_code   = why			// A.K.A: CLD_TRAPPED
													info.si_status = tsk->exit_code & 0x7f
													
													__group_send_sig_info(SIGCHLD, &info, parent)
														send_signal(sig, info, p, PIDTYPE_TGID)
															__send_signal(sig, info, t, type, force)
												}
												
												current->last_siginfo = NULL	// after wake up by gdb, clear last_siginfo
											}
										}//ptrace_do_notify
								}//ptrace_report_syscall
							}//tracehook_report_syscall_exit
						}//tracehook_report_syscall	
				}//syscall_trace_exit
			}//el0_svc_common
}

四、总结

        gdb、strace监控被调试程序的系统调用,主要是依赖系统调用的路径上,根据被调试程序是否置位TIF_SYSCALL_TRACE,通过tracehook_report_syscall将自身暂停,并记录相应的信息(PTRACE_EVENTMSG_SYSCALL_ENTRY、PTRACE_EVENTMSG_SYSCALL_EXIT)到current->ptrace_message中供后续gdb、strace通过ptrace(PTRACE_GETEVENTMSG)获取,最后通知gdb。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2343123.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【前端】ES6 引入的异步编程解决方案Promise 详解

Promise 详解 1. 基本概念 定义&#xff1a;Promise 是 ES6 引入的异步编程解决方案&#xff0c;表示一个异步操作的最终完成&#xff08;或失败&#xff09;及其结果值。核心作用&#xff1a;替代回调函数&#xff0c;解决“回调地狱”问题&#xff0c;提供更清晰的异步流程控…

const(C++)

打印出来的结果是 a是12 *p是200 const修饰指针 const修饰引用

python21-循环小作业

课程&#xff1a;B站大学 记录python学习&#xff0c;直到学会基本的爬虫&#xff0c;使用python搭建接口自动化测试就算学会了&#xff0c;在进阶webui自动化&#xff0c;app自动化 循环语句小作业 for-in作业斐波那契 for 固定数值计算素数字符统计数字序列range 函数 水仙花…

小白电路设计-设计11-恒功率充电电路设计

介绍 作为电子信息工程的我&#xff0c;电路学习是一定要学习的&#xff0c;可惜目前作为EMC测试工程师&#xff0c;无法兼顾太多&#xff0c;索性不如直接将所学的知识进行运用&#xff0c;并且也可以作为契机&#xff0c;进行我本人的个人提升。祝大家与我一起进行提升。1.本…

Spring AI 快速入门:从环境搭建到核心组件集成

Spring AI 快速入门&#xff1a;从环境搭建到核心组件集成 一、前言&#xff1a;Java开发者的AI开发捷径 对于Java生态的开发者来说&#xff0c;将人工智能技术融入企业级应用往往面临技术栈割裂、依赖管理复杂、多模型适配困难等挑战。Spring AI的出现彻底改变了这一局面——…

http://noi.openjudge.cn/——2.5基本算法之搜索——200:Solitaire

文章目录 题目宽搜代码总结 题目 总时间限制: 5000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB 描述 Solitaire is a game played on a chessboard 8x8. The rows and columns of the chessboard are numbered from 1 to 8, from the top to the bottom and from left t…

架构师面试(三十六):广播消息

题目 在像 IM、短视频、游戏等实时在线类的业务系统中&#xff0c;一般会有【广播消息】业务&#xff0c;这类业务具有瞬时高流量的特点。 在对【广播消息】业务实现时通常需要同时写 “系统消息库” 和更新用户的 “联系人库” 的操作&#xff0c;用户的联系人表中会有未读数…

TortoiseGit使用图解

前言 记录GitTortoiseGit使用&#xff0c;记录下开发中常用命令&#xff0c;健忘时用到方知好。 TortoiseGit使用 图解 commit-提交代码 pull-拉取远程分支最新代码 push-将本地分支代码推送到远程分支 show log-查看分支提交记录 show log - 切换分支查看 show log - 远程分…

【时时三省】(C语言基础)循环程序举例

山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 例题: 用公式4/π≈1-3/1+5/1-7/1+...求π的近似值,直到发现某一项的绝对值小于10的-6次方为止(该项不累加)。 解题思路: 这是求值的近似方法中的一种。求π值可以用不同的近似方法。如下面的表达式都可以…

珍爱网:从降本增效到绿色低碳,数字化新基建价值凸显

2024年12月24日&#xff0c;法大大联合企业绿色发展研究院发布《2024签约减碳与低碳办公白皮书》&#xff0c;深入剖析电子签在推动企业绿色低碳转型中的关键作用&#xff0c;为企业实现环境、社会和治理&#xff08;ESG&#xff09;目标提供新思路。近期&#xff0c;法大大将陆…

电子电子架构 --- 主机厂视角下ECU开发流程

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…

PyQt6基础_QTableWidget

目录 描述&#xff1a; 代码 演示 描述&#xff1a; 1 单击选中一行 2 右键菜单 3 填充数据 4 提取行数据 5 删除行数据 代码 from PyQt6.QtCore import (Qt ) from PyQt6.QtGui import ( QAction ) from PyQt6.QtWidgets import (QApplication,QAbstractItemView,QL…

uniapp 上传二进制流图片

文章目录 场景&#x1f7e2;一、步骤1.1、选择图片1.2、 读取图片为二进制数据1.3、上传二进制数据到服务器 &#x1f7e2;二、项目案例2.1、替换头像案例2.1、uView u-upload 上传封面 &#x1f7e2; 三、关键注意事项3.1 二进制流与 FormData 区别3.2 性能优化3.3 跨平台适配…

赛灵思 XCKU115-2FLVB2104I Xilinx Kintex UltraScale FPGA

XCKU115-2FLVB2104I 是 AMD Xilinx Kintex UltraScale FPGA&#xff0c;基于 20 nm 先进工艺&#xff0c;提供高达 1 451 100 个逻辑单元&#xff08;Logic Cells&#xff09;&#xff0c;77 721 600 bit 的片上 RAM 资源&#xff0c;以及 5 520 个 DSP 切片&#xff08;DSP48E…

Unreal Niagara制作SubUV贴图翻页动画

SubUV翻页动画是游戏中的常见功能&#xff0c;通过对每一小块UV进行移动可以模拟动画效果&#xff0c;接下来对下图进行SubUV动画的制作。 (金币测试图下载地址&#xff1a;https://download.csdn.net/download/grayrail/90684422&#xff09; 最终效果如下&#xff1a; 1.…

「零配置陷阱」:现代全栈工具链的复杂度管控实践

一、工具链膨胀的「死亡螺旋」 2024年典型全栈项目的初始化噩梦&#xff1a; $ npm create vitelatest ✔ Project name: … demo ✔ Select a framework: › React ✔ Select a variant: › TypeScript SWC ✔ Install shadcn/ui? … Yes ✔ Add Storybook? … Yes ✔ Co…

金仓数据库KingbaseES技术实践类深度剖析与实战指南

一、语法兼容及迁移实战 &#xff08;一&#xff09;语法兼容的多元魅力 在当今多元化的数据库应用环境中&#xff0c;金仓数据库管理系统KingbaseES凭借其卓越的语法兼容能力脱颖而出。它采用的融合数据库架构&#xff0c;通过多语法体系一体化架构&#xff0c;实现了对Orac…

基于ssm的个人博客管理系统(源码+数据库+万字文档)

57基于ssm的个人博客管理系统&#xff1a;前端jsp、jquery、easyui&#xff0c;后端 spring、mybatis、maven&#xff0c;集成个人博客浏览、详情查看、博客发布、富文本编辑、评论等功能于一体的系统。 ## 功能介绍 ### 用户 - 首页&#xff1a;博客列表、博客详情、关键词…

综述 | GUI Agent:让AI学会「玩手机」的新革命

想象一下&#xff0c;你的手机里住着一个隐形助理&#xff1a;你说“把亮度调到50%”&#xff0c;它自动操作&#xff1b;你说“下载最新游戏”&#xff0c;它一键完成。这就是GUI智能体——一种能“看懂”屏幕并操作的AI。 论文&#xff1a;A Survey on (M)LLM-Based GUI Agen…

Canvas入门教程!!【Canvas篇二】

没有一朵花&#xff0c;从一开始就是花。 目录 translate() 方法&#xff1a;rotate() 方法&#xff1a;scale() 方法&#xff1a; translate() 方法&#xff1a; Canvas 2D API 的 CanvasRenderingContext2D.translate() 方法用于对当前网格添加平移变换。 translate() 方法通…