使用ptrace提取系统调用名称和参数

时间:2015-10-30 08:53:15

标签: c unix system-calls strace ptrace

我正在进行一项任务,我必须使用strace实现ptrace类功能。到目前为止,我已经找到了如何提取系统调用号和返回值,如下所示:

//In parent process
struct user_regs_struct regs;
ptrace( PTRACE_GETREGS, child_pid, 0, &regs ); 
//child_pid is the pid of child process executing the required program
//or system call passed as command line arguments
syscall_num = regs.orig_rax;
syscall_retval = regs.rax;

但我还没有找到如何提取系统调用名称和参数。 任何人都可以建议吗?

2 个答案:

答案 0 :(得分:8)

要获取系统调用的参数,您必须逐个读取寄存器。为此,您需要知道哪些寄存器将存储系统调用的参数。几个月前我自己写了一个这样的程序。基本上每个寄存器存储的是:

  

regs.rdi - 存储第一个参数

     

regs.rsi - 存储第二个参数

     

regs.rdx - 存储第三个参数

     

regs.r10 - 存储第四个参数

     

regs.r8 - 存储第五个参数

     

regs.r9 - 存储第六个参数

这个table提供了更详细的描述(请注意它特定于x86-64架构)。

现在,您必须通过每个系统调用的文档来分别理解和编码它们。有不同的方法来阅读不同的论点。

让我们通过read()系统调用来证明这一点。

我们将看到不同类型的参数,并了解如何访问它们。

  

第一个参数(int fd)

由于它是一个数字,它将直接保存在寄存器regs.rdi中。

在我的代码中,我需要获取fd所指向的文件,因此我使用了以下代码。

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)ptr到输入缓冲区

要阅读此内容,您需要使用PTRACE_PEEKDATA / PTRACE_PEEKTEXT请求(ptrace()的第一个参数)来读取字节。由于ptrace()一次只读取8个字节,因此需要使用long变量迭代执行此操作。应该有另一个char *指向内存的开头,稍后将用于读取字符串。

我使用的代码如下。

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);

这就是我所能说的。我猜你几乎可以通过这种方式读取大多数系统调用的所有参数。

至于系统调用的名称。我是操作系统映射并制作了一个手动switch案例来获取系统调用的名称。

希望这会有所帮助。 :)

答案 1 :(得分:0)

我会将 regs.orig_rax 映射到系统调用的 const 列表,您可以从 /usr/include/x86_64-linux-gnu/asm/unistd_64 获取该列表。

如果您想知道指向注册表地址的内容,我建议您阅读 https://groogroot.eu/the-ptrace-system-call/ 方法 read_addr_into_buff,当检测到 __NR_write SC 时,我确实使用它来读取 regs.rsi。