使用PTRACE_SINGLESTEP仅截获系统调用

使用PTRACE_SINGLESTEP仅截获系统调用,c,ptrace,C,Ptrace,我们有一个学校项目,我们需要重新编码“strace” 我们只能截获系统调用,比如写和读,但不能使用PTRACE\u系统调用。我正在寻找一种使用PTRACE\u SINGLESTEP实现这一点的方法,我已经编写了一种打印系统调用的方法,当我使用PTRACE\u syscall时,它工作得很好,但是当我使用PTRACE\u SINGLESTEP时,我找不到只打印系统调用的方法 这是我使用的代码,也许有人能帮我找出它的错误: pid_t child; long orig_eax; user_regs_

我们有一个学校项目,我们需要重新编码“strace”

我们只能截获系统调用,比如写和读,但不能使用
PTRACE\u系统调用
。我正在寻找一种使用
PTRACE\u SINGLESTEP
实现这一点的方法,我已经编写了一种打印系统调用的方法,当我使用
PTRACE\u syscall
时,它工作得很好,但是当我使用
PTRACE\u SINGLESTEP
时,我找不到只打印系统调用的方法

这是我使用的代码,也许有人能帮我找出它的错误:

pid_t child;
long orig_eax;
user_regs_struct regs;

child = fork();
if (child == 0) {
    ptrace(PTRACE_TRACEME, 0, 0, 0);
    execve("/home/architek/a.out", {"/home/architek/a.out", NULL}, envp);
} else {
    waitpid(child, &status, 0);
    while (WIFSTOPPED(status)) {
        orig_eax = ptrace(PTRACE_PEEKUSER, child, 8 * ORIG_RAX, NULL);
        ptrace(PTRACE_GETREGS, child, NULL, &regs);
        call_printer(&regs, child);
        ptrace(PTRACE_SINGLESTEP, child, 0, 0);
        waitpid(child, &status, 0);
    }
}

如果无法在系统调用之前/之后使用
PTRACE\u SYSCALL
停止子系统,则必须手动检测何时即将发生。我怀疑检查
strace
的源代码是否有帮助,因为
strace
很可能使用
PTRACE\u SYSCALL
,没有理由手动解码指令

假设您正在使用x86-64,以下是您可以使用的方法:

  • 使用
    PTRACE\u SINGLESTEP
    ,一次只执行一条指令
  • 对于每条指令,使用
    PTRACE\u peek text
    获取指令指针指向的下一条指令
  • 通过将字节与两个字节进行比较,检查指令是否为
    syscall
    指令:
    0x0f 0x05
    。由于x86是小端,这意味着检查
    ptrace(ptrace_PEEKDATA,…)
    的返回值是否将两个最低有效字节设置为
    0x050f
  • 注意:如果您在另一个体系结构上,或者如果您也想检测32位系统调用,只需在步骤3中检查不同/更多的值即可。在Linux x86-64上,有多种方法使用不同的操作码发出系统调用。例如,Linux上的32位系统调用是通过
    int0x80
    (操作码
    0xcd0x80
    )完成的。查看列表

    下面是一个例子:

    #包括
    长操作码;
    // ...
    waitpid(子级和状态,0);
    while(WIFSTOPPED(状态)){
    ptrace(ptrace_GETREGS、child、NULL和regs);
    errno=0;
    操作码=ptrace(ptrace_PEEKTEXT,child,regs.rip,0);
    if(操作码==-1&&errno!=0){
    perror(“ptrace(ptrace_PEEK_数据)失败”);
    出口(1);
    }
    if((无符号长)操作码&0xffff==0x050f){
    //将要执行syscall指令的子级,
    //查看注册表以了解更多。。。
    }
    ptrace(ptrace_SINGLESTEP,child,0,0);
    waitpid(子级和状态,0);
    }
    
    如果无法在系统调用之前/之后使用
    PTRACE\u SYSCALL
    停止子系统,则必须手动检测何时即将发生。我怀疑检查
    strace
    的源代码是否有帮助,因为
    strace
    很可能使用
    PTRACE\u SYSCALL
    ,没有理由手动解码指令

    假设您正在使用x86-64,以下是您可以使用的方法:

  • 使用
    PTRACE\u SINGLESTEP
    ,一次只执行一条指令
  • 对于每条指令,使用
    PTRACE\u peek text
    获取指令指针指向的下一条指令
  • 通过将字节与两个字节进行比较,检查指令是否为
    syscall
    指令:
    0x0f 0x05
    。由于x86是小端,这意味着检查
    ptrace(ptrace_PEEKDATA,…)
    的返回值是否将两个最低有效字节设置为
    0x050f
  • 注意:如果您在另一个体系结构上,或者如果您也想检测32位系统调用,只需在步骤3中检查不同/更多的值即可。在Linux x86-64上,有多种方法使用不同的操作码发出系统调用。例如,Linux上的32位系统调用是通过
    int0x80
    (操作码
    0xcd0x80
    )完成的。查看列表

    下面是一个例子:

    #包括
    长操作码;
    // ...
    waitpid(子级和状态,0);
    while(WIFSTOPPED(状态)){
    ptrace(ptrace_GETREGS、child、NULL和regs);
    errno=0;
    操作码=ptrace(ptrace_PEEKTEXT,child,regs.rip,0);
    if(操作码==-1&&errno!=0){
    perror(“ptrace(ptrace_PEEK_数据)失败”);
    出口(1);
    }
    if((无符号长)操作码&0xffff==0x050f){
    //将要执行syscall指令的子级,
    //查看注册表以了解更多。。。
    }
    ptrace(ptrace_SINGLESTEP,child,0,0);
    waitpid(子级和状态,0);
    }
    
    您是否阅读了的所有(神秘的)文档?另见和。顺便说一句,
    strace
    是开源的,你可以研究它的源代码。请提供一些您可能需要查看的内容
    /usr/include/x86_64-linux-gnu/sys/user.h
    我已经阅读了ptrace(2),但我没有发现任何可能有用的请求,或者可能我没有看到它如何有用,我已经找到了“ptrace_SYSEMU”,但这个主题也禁止它,我已经阅读了一些strace源代码,还将阅读更多,也许我会发现一些有用的东西;至于
    user.h
    中的结构,我找不到任何东西可以帮助我确定tracee是否在系统调用时停止了。您是否阅读了所有的(神秘的)文档?另见和。顺便说一句,
    strace
    是开源的,你可以研究它的源代码。请提供一些您可能需要查看的内容
    /usr/include/x86_64-linux-gnu/sys/user.h
    我已经阅读了ptrace(2),但我没有发现任何可能有用的请求,或者可能我没有看到它如何有用,我已经找到了“ptrace_SYSEMU”,但这个主题也禁止它,我已经阅读了一些strace源代码,还将阅读更多,也许我会发现一些有用的东西;至于
    user.h
    中的结构,我找不到任何东西可以帮助我判断tracee是否在系统调用时停止了。谢谢你的帮助,在系统调用之前停止tracee是正常的(我只是不得不使用一个长字符和字符[8]的联合来获取操作码,因为&不起作用),这是捕获aft的相同方法吗