linux之ptrace

经典博客:
Linux内存替换系列(包括实验 实验可行)  就是要注意下是64位还是32位的。。。。
https://blog.youkuaiyun.com/Dearggae/article/details/47379245

玩转ptrace:
http://www.cnblogs.com/wangkangluo1/archive/2012/06/05/2535484.html

ptrace系统调用
ptrace系统调从名字上看是用于进程跟踪的,它提供了父进程可以观察和控制其子进程执行的能力,并允许父进程检查和替换子进程的内核镜像(包括寄存器)的值。其基本原理是: 当使用了ptrace跟踪后,所有发送给被跟踪的子进程的信号(除了SIGKILL),都会被转发给父进程,而子进程则会被阻塞,这时子进程的状态就会被系统标注为TASK_TRACED。而父进程收到信号后,就可以对停止下来的子进程进行检查和修改,然后让子进程继续运行。
其原型为:
#include sys/ptrace.h
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
ptrace有四个参数:
1). enum __ptrace_request request:指示了ptrace要执行的命令。
2). pid_t pid: 指示ptrace要跟踪的进程。
3). void *addr: 指示要监控的内存地址。
4). void *data: 存放读取出的或者要写入的数据。
ptrace是如此的强大,以至于有很多大家所常用的工具都基于ptrace来实现,如strace和gdb。
来自 https://www.cnblogs.com/tangr206/articles/3094358.html

【描述】
提供父进程观察和控制另一个进程执行的机制,同时提供查询和修改另一进程的核心映像与寄存器的能力,主要用于执行断点调试和系统调用跟踪。
【功能】
Request参数决定了系统调用的功能:(第一个参数决定ptrace的行为也决定了接下来其它3个参数是怎样被使用的)
• PTRACE_TRACEME
本进程被其父进程所跟踪。其父进程应该希望跟踪子进程。
• PTRACE_PEEKTEXT, PTRACE_PEEKDATA
从内存地址中读取一个字节,内存地址由addr给出。
• PTRACE_PEEKUSR
从USER区域中读取一个字节,偏移量为addr。
• PTRACE_POKETEXT, PTRACE_POKEDATA
往内存地址中写入一个字节。内存地址由addr给出。
• PTRACE_POKEUSR
往USER区域中写入一个字节。偏移量为addr。
• PTRACE_SYSCALL, PTRACE_CONT
重新运行。
• PTRACE_KILL
杀掉子进程,使它退出。
• PTRACE_SINGLESTEP
设置单步执行标志
• PTRACE_ATTACH
跟踪指定pid 进程。
• PTRACE_DETACH
结束跟踪

Intel386特有:
• PTRACE_GETREGS
读取寄存器
• PTRACE_SETREGS
设置寄存器
• PTRACE_GETFPREGS
读取浮点寄存器
• PTRACE_SETFPREGS
设置浮点寄存器
init进程不可以使用此函数

来自 https://blog.youkuaiyun.com/Dearggae/article/details/47144393

将代码插入到自由空间
在前面的例子中我们将代码直接插入到了正在执行的指令流中,然而,调试器可能会被这种行为弄糊涂,所以我们决定把指令插入到进程中的自由空间中去。通过察看/proc/pid/maps可以知道这个进程中自由空间的分布。

小结:
ptrace函数可能会让人们觉得很奇特,因为它居然可以检测和修改一个运行中的程序。这种技术主要是在调试器和系统调用跟踪程序中使用。它使程序员可以在用户级别做更多有意思的事情。已经有过很多在用户级别下扩展操作系统得尝试,比如UFO,一个用户级别的文件系统扩展,它使用ptrace来实现一些安全机制。

来自 http://www.cnblogs.com/wangkangluo1/archive/2012/06/05/2535484.html

断点的实现原理,就是在指定的位置插入断点指令,当被调试的程序运行到断点的时候,产生SIGTRAP信号。

来自 https://www.cnblogs.com/tangr206/articles/3094358.html

<think>我们正在讨论Linuxptrace系统调用的使用教程和示例代码。ptrace是一个强大的系统调用,主要用于进程跟踪和调试,允许一个进程(tracer)观察和控制另一个进程(tracee)的执行,包括读写其内存和寄存器等。根据引用[3],ptrace的函数原型为:```c#include<sys/ptrace.h>longptrace(enum__ptrace_requestrequest,pid_tpid,void*addr,void*data);```其中,`request`参数指定要执行的操作,`pid`是目标进程的ID,`addr`和`data`的含义取决于`request`。引用[4]提到,ptrace可以用于拦截系统调用、修改系统调用参数、实现调试器的功能等。下面我们通过几个示例来展示ptrace的基本用法。###示例1:跟踪子进程执行以下示例展示如何使用ptrace跟踪子进程,并在子进程执行系统调用时中断。```c#include<stdio.h>#include<sys/ptrace.h>#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>#include<sys/user.h>intmain(){pid_tchild;child=fork();//创建子进程if(child==0){//子进程:请求被跟踪ptrace(PTRACE_TRACEME,0,NULL,NULL);execl("/bin/ls","ls",NULL);//执行ls命令}else{//父进程:跟踪子进程intstatus;wait(&status);//等待子进程状态改变while(WIFSTOPPED(status)){//当子进程被停止时structuser_regs_structregs;//获取寄存器状态ptrace(PTRACE_GETREGS,child,NULL,&regs);//在x86_64架构下,系统调用号存放在orig_rax寄存器中printf("系统调用号:%lld\n",regs.orig_rax);//继续执行,并在下一次系统调用入口停止ptrace(PTRACE_SYSCALL,child,NULL,NULL);wait(&status);//等待子进程状态改变}}return0;}```在这个例子中,子进程通过`PTRACE_TRACEME`请求被父进程跟踪。父进程使用`PTRACE_SYSCALL`让子进程继续运行,并在每次进入和退出系统调用时停止。然后父进程通过`PTRACE_GETREGS`读取子进程的寄存器,获取系统调用号。###示例2:附加到运行中的进程ptrace还可以附加到一个已经运行的进程上。以下示例展示如何附加到一个进程并读取其内存。```c#include<stdio.h>#include<sys/ptrace.h>#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>#include<sys/user.h>intmain(intargc,char*argv[]){if(argc!=2){printf("用法:%s<进程ID>\n",argv[0]);return1;}pid_tpid=atoi(argv[1]);//目标进程IDlongret=ptrace(PTRACE_ATTACH,pid,NULL,NULL);if(ret==-1){perror("ptraceattach失败");return1;}intstatus;waitpid(pid,&status,0);//等待目标进程停止//读取目标进程的内存longword=ptrace(PTRACE_PEEKTEXT,pid,(void*)0x400000,NULL);printf("地址0x400000处的值:%lx\n",word);//分离目标进程ptrace(PTRACE_DETACH,pid,NULL,NULL);return0;}```这个例子中,我们使用`PTRACE_ATTACH`附加到指定进程,然后使用`PTRACE_PEEKTEXT`读取目标进程内存中指定地址的值,最后使用`PTRACE_DETACH`分离。###示例3:代码注入引用[5]提到了使用ptrace进行代码注入。以下是一个简化的代码注入示例,将一段代码(payload)注入到目标进程中执行。```c#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/ptrace.h>#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>#include<sys/user.h>#include<sys/mman.h>#include<dlfcn.h>//用于注入的shellcodeconstcharshellcode[]="\x48\x31\xff\xb0\x3c\x48\x31\xf6\x48\x31\xd2\x0f\x05";//退出系统调用的shellcode:exit(0)intmain(intargc,char*argv[]){if(argc!=2){fprintf(stderr,"用法:%s<进程ID>\n",argv[0]);return1;}pid_tpid=atoi(argv[1]);//附加到目标进程if(ptrace(PTRACE_ATTACH,pid,NULL,NULL)==-1){perror("附加失败");return1;}intstatus;waitpid(pid,&status,0);//等待目标进程停止//获取目标进程的寄存器structuser_regs_structregs;ptrace(PTRACE_GETREGS,pid,NULL,&regs);//在目标进程中分配内存void*remote_mem=(void*)ptrace(PTRACE_PEEKTEXT,pid,(void*)regs.rip,NULL);//实际上,分配内存需要使用目标进程调用mmap,这里简化处理,假设已经有一块可写内存//更完整的做法是调用目标进程的mmap函数,但比较复杂,这里不展开。//将shellcode写入目标进程的内存for(inti=0;i<sizeof(shellcode);i++){//每次写入一个字节longdata=ptrace(PTRACE_PEEKDATA,pid,remote_mem+i,NULL);//修改低字节为shellcode[i]data=(data&~0xFF)|shellcode[i];ptrace(PTRACE_POKEDATA,pid,remote_mem+i,data);}//修改目标进程的指令指针指向注入的代码regs.rip=(unsignedlonglong)remote_mem;ptrace(PTRACE_SETREGS,pid,NULL,&regs);//继续执行目标进程,执行我们的shellcodeptrace(PTRACE_DETACH,pid,NULL,NULL);return0;}```注意:这个代码注入示例非常简化,实际应用中需要考虑更多因素,比如内存权限、避免破坏原有进程等。完整的代码注入通常需要调用目标进程的mmap来分配内存,并设置内存为可执行。###使用注意事项1.权限:需要具有相应权限(通常是root或目标进程的所有者)才能使用ptrace。2.多线程:跟踪多线程进程时,ptrace会跟踪所有线程,但需要分别处理每个线程。3.安全性:ptrace功能强大,但使用不当可能导致目标进程崩溃。以上示例和说明可以帮助你开始使用ptrace。更多详细内容可以参考Linux的man手册(`manptrace`)以及相关的项目示例(如引用[1][2][5]中的项目)。§§相关问题§§1.ptrace如何用于实现一个简单的调试器?2.如何用ptrace修改目标进程的系统调用参数?3.ptrace在安全领域有哪些应用?4.ptrace和strace、gdb等工具有什么关系?</think>### Linux ptrace 使用教程与示例代码 `ptrace` 是 Linux 的核心调试工具,允许一个进程(tracer)监控和控制另一个进程(tracee)的执行。它广泛应用于调试器、代码注入、系统调用拦截等场景[^3][^4]。 #### 核心功能 ```c #include <sys/ptrace.h> long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); ``` - **request**:操作类型(如 `PTRACE_TRACEME`, `PTRACE_ATTACH`) - **pid**:目标进程ID - **addr/data**:操作参数(如内存地址/寄存器值) --- ### 基础使用示例 #### 示例 1:跟踪子进程(基础调试) ```c #include <sys/ptrace.h> #include <sys/wait.h> #include <unistd.h> int main() { pid_t child = fork(); if (child == 0) { ptrace(PTRACE_TRACEME, 0, NULL, NULL); // 子进程请求被跟踪 execl("/bin/ls", "ls", NULL); // 执行目标程序 } else { wait(NULL); // 等待子进程停止 ptrace(PTRACE_CONT, child, NULL, NULL); // 继续执行子进程 wait(NULL); // 等待子进程结束 } return 0; } ``` **功能**:父进程跟踪子进程执行 `ls` 命令[^3][^4]。 --- #### 示例 2:拦截系统调用 ```c #include <sys/ptrace.h> #include <sys/user.h> // ... void trace_syscalls(pid_t child) { while (1) { // 在系统调用入口暂停 ptrace(PTRACE_SYSCALL, child, NULL, NULL); wait(NULL); struct user_regs_struct regs; ptrace(PTRACE_GETREGS, child, NULL, &regs); printf("系统调用号: %llu\n", regs.orig_rax); // 打印系统调用号 // 在系统调用退出暂停 ptrace(PTRACE_SYSCALL, child, NULL, NULL); wait(NULL); } } ``` **功能**:捕获并打印所有系统调用(类似 `strace` 基础功能)[^4]。 --- ### 进阶应用:代码注入 #### 示例 3:注入 Shellcode 到运行进程 ```c #include <sys/mman.h> // ... void inject_code(pid_t target_pid, void *shellcode, size_t size) { // 附加到目标进程 ptrace(PTRACE_ATTACH, target_pid, NULL, NULL); wait(NULL); // 在目标进程分配内存 long remote_addr = ptrace(PTRACE_PEEKTEXT, target_pid, mmap(NULL, size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0), NULL); // 写入 Shellcode for (int i = 0; i < size; i += sizeof(long)) { long data = *(long *)((char *)shellcode + i); ptrace(PTRACE_POKETEXT, target_pid, remote_addr + i, data); } // 修改指令指针执行 Shellcode struct user_regs_struct regs; ptrace(PTRACE_GETREGS, target_pid, NULL, &regs); regs.rip = remote_addr; // x86_64 指令指针 ptrace(PTRACE_SETREGS, target_pid, NULL, &regs); ptrace(PTRACE_DETACH, target_pid, NULL, NULL); } ``` **关键步骤**: 1. 附加目标进程 2. 分配可执行内存 3. 写入机器码 4. 劫持控制流 5. 恢复执行[^5] > **安全警告**:代码注入可能破坏进程稳定性,仅用于学习目的。 --- ### 实用技巧 1. **权限要求**: - `ptrace` 需要 `CAP_SYS_PTRACE` 能力或目标进程属主身份 - `/proc/sys/kernel/yama/ptrace_scope` 控制权限级别 2. **调试多线程**: - 使用 `PTRACE_ATTACH` 附加线程需指定线程ID - `PTRACE_SETOPTIONS` 设置 `PTRACE_O_TRACECLONE` 跟踪子线程 3. **错误处理**: ```c if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) { perror("ptrace attach failed"); exit(EXIT_FAILURE); } ``` --- ### 学习资源推荐 1. **官方文档**: - `man 2 ptrace`(Linux 手册页) - [Linux 内核源码](https://elixir.bootlin.com/linux/latest/source/kernel/ptrace.c) 2. **实战项目**: - [ptrace-examples](https://gitcode.com/gh_mirrors/pt/ptrace-examples):基础操作示例[^1] - [ptrace-call-userspace](https://gitcode.com/gh_mirrors/pt/ptrace-call-userspace):用户态函数调用[^2] 3. **调试工具源码**: - `strace`(系统调用跟踪) - `gdb`(GNU 调试器)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值