系统调用,ptrace,go语言syscall

系统调用

内核实现了很多系统调用函数,为每个函数提供一个标识,也就是系统调用号,在不同的架构中可能不同。
通过syscall(2) man page可以看到完整的系统调用列表

系统调用的执行流程:

  1. 用户程序调用C库或者直接通过自身的汇编指令进行系统调用,需要传递的变量及系统的调用编号保存在CPU寄存器中;
  2. 进程进入内核态,通过寄存器保存的系统调用编号识别系统函数,并执行系统调用;
  3. 系统调用结束后,结果、返回值和参数保存在寄存器,用户程序获取结果

go提供了syscall包直接通过汇编代码进行系统调用。

ptrace

定义

long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
  • pid:要跟踪的进程ID
  • request:使用的ptrace请求

Go - syscall

数据结构

寄存器数据:syscall.PtraceRegs
状态数据:syscall.WaitStatus

封装的系统调用

PTRACE_ATTACH:func PtraceAttach(pid int) (err error)
PTRACE_DETACH:func PtraceDetach(pid int) (err error)
PTRACE_SYSCALL:func PtraceSyscall(pid int, signal int) (err error)
PTRACE_GETREGS:func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error)
等待状态改变:func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error)

存储位置

参考:https://man7.org/linux/man-pages/man2/syscall.2.html

x86_64

Orig_rax:系统调用时系统调用号
Rax:返回时返回值
regs.rdi - Stores the first argument
regs.rsi - Stores the second argument
regs.rdx - Stores the third argument
regs.r10 - Stores the fourth argument
regs.r8 - Stores the fifth argument
regs.r9 - Stores the sixth argument

arm/EABI

调用参数r0-r6
返回值r0,r1
调用号r7

读取方式

文件描述符int fd,通过/proc/pid/fd/%llu来读取filepath

sprintf(fdpath,"/proc/%u/fd/%llu",proc,regs.rdi);
size = readlink(fdpath, filepath, 256);  //this gives the filepath for a particular fd
filepath[size] = '\0';
printf("File-%s-\n", filepath);

指向缓冲区的指针void *buf,通过PTRACE_PEEKDATA / PTRACE_PEEKTEXT读取,ptrace一次返回8字节,所以要迭代读取数据,同时需要一个指针指向内存的开始位置,用于后续读取字符串。比如read参数第二个参数指示缓冲区位置,第三个参数指示长度

char message[1000];
char* temp_char2 = message;
int j = 0;
long temp_long;

while( j < (regs.rdx/8) ) //regs.rdx stores the size of the input buffer
{
    temp_long = ptrace(PTRACE_PEEKDATA, proc, regs.rsi + (j*8) , NULL);
    memcpy(temp_char2, &temp_long, 8);
    temp_char2 += sizeof(long);
    ++j;
}
message[regs.rdx] = '\0';
printf("Message-%s-\n\n", message);

参考链接

https://github.com/eaburns/ptrace
LinuGo - 用Go看到进程中发生的系统调用
linux 系统调用号表
ptrace手册
Linux System Call Table for x86 64
Extracting system call name and arguments using ptrace
strace 源码分析 (快速分析源码,半小时搞懂)
ptrace函数深入分析
strace 是如何工作的

go语言类似strace的项目

jfrabaute/libtrace
jfrabaute/gotrace
eaburns/ptrace
agis/gtrace(看起来还可以)
maebashi/go-strace
hugelgupf/go-strace(这个库很不错)
hugelgupf/go-strace/strace 文档

在 Unix 系统上,可以通过调用 `ptrace` 系统调用来实现资源追踪功能。`ptrace` 可以用来对指定进程进行跟踪、修改、读取进程的寄存器和内存等操作。 下面是一个简单的 C 语言示例,演示如何使用 `ptrace` 打印指定进程的寄存器值: ```c #include <stdio.h> #include <unistd.h> #include <sys/ptrace.h> #include <sys/reg.h> #include <sys/wait.h> int main(int argc, char **argv) { pid_t child_pid; long orig_eax, eax; int status; child_pid = fork(); if (child_pid == 0) { /* 在子进程中执行 */ ptrace(PTRACE_TRACEME, 0, NULL, NULL); execl("/bin/ls", "ls", NULL); } else { /* 在父进程中执行 */ wait(&status); while (WIFSTOPPED(status)) { orig_eax = ptrace(PTRACE_PEEKUSER, child_pid, 4 * ORIG_EAX, NULL); printf("syscall %ld\n", orig_eax); ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL); wait(&status); } } return 0; } ``` 在上面的代码中,我们首先使用 `fork` 创建一个子进程,并在子进程中使用 `ptrace(PTRACE_TRACEME, 0, NULL, NULL)` 让子进程开始被跟踪。然后子进程通过 `execl` 调用 `/bin/ls` 命令,开始执行。 在父进程中,我们使用 `wait` 等待子进程停止,并使用 `PTRACE_PEEKUSER` 读取子进程的寄存器值(`ORIG_EAX` 表示系统调用号),并打印该系统调用号。然后使用 `PTRACE_SYSCALL` 继续执行子进程,并等待子进程再次停止。 这样,我们就可以通过 `ptrace` 系统调用实现对指定进程的跟踪,并在需要的时候读取和修改其寄存器和内存等信息,从而实现资源追踪功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏打呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值