【问题标题】:How can I create a testing script with GDB如何使用 GDB 创建测试脚本
【发布时间】:2019-04-03 05:14:57
【问题描述】:

我有一组输入,我想用它们来测试我的程序,看看哪些输入会遇到断点。我想创建一个脚本来一一测试这些输入,如果它遇到断点,打印或将结果保存到文件中。 请让我知道它是否可能,如果是,我该怎么做。谢谢。

【问题讨论】:

    标签: linux debugging testing assembly gdb


    【解决方案1】:

    我不确定我是否完全理解您的要求。但如果我理解正确,你想写一个程序:

    • 启动另一个程序
    • 将一些预定义的输入传递给其他程序
    • 检查是否命中了其他程序中的某个断点

    我不知道这是否可以使用gdb,但可以编写自己的调试器:

    • 使用forkexec函数之一(如execlp)启动要测试的程序
    • exec 函数调用之前ptrace(PTRACE_TRACEME,0,0,0)
    • 致电waitpid;如果exec 成功,程序将立即停止。 waitpid 返回的“状态码”(第二个参数)将为 0x57F(假设是 x86 CPU)。
    • 如果waitpid 返回另一个退出代码,exec 失败,您无法继续。
    • 使用ptrace(PTRACE_PEEKTEXT,...)ptrace(PTRACE_POKETEXT,...) 修改程序:通过用“断点”指令替换该地址处的指令(在x86 CPU 上:int3,即字节@987654335 @)
      这意味着:
      您必须知道断点的地址(而不是行号),并使用ptrace()0xCC 写入每个地址。 因为PTRACE_POKETEXT一次只能修改4个字节(x86_32)或8个字节(x86_64),你首先要使用PTRACE_PEEKTEXT读取这4个或8个字节的旧值,修改4个或8个字节中的1个并写入所有4 或 8 个字节。
    • 如果您的程序并不总是加载到相同的地址(由于 ASLR 等),您可以读取程序计数器(使用 PTRACE_PEEKUSER):它应该是程序入口点的(实际)地址。
    • 调用ptrace(PTRACE_CONT,pid,0,0)启动正在测试的程序
    • 调用waitpid等待程序停止或退出
    • 如果waitpid 返回 0x57F 作为“状态码”,则您处于断点中。您现在可以使用kill(pid, SIGKILL) 来停止您的程序。
    • 您可以使用PTRACE_PEEKUSER 来检查程序计数器的值(x86-64 上的rip),以便知道哪个断点被命中。注意,程序计数器可能是断点的地址加1,所以如果0x12340000地址处的断点被命中,rip可能是0x12340001。
    • 如果waitpid返回低字节为0x7F的任何其他值,则程序引发异常。您应该使用kill(pid,SIGKILL) 最终阻止它。
    • 否则(如果waitpid返回的低字节不是0x7F),程序已经结束,没有引发异常,也没有遇到任何断点。

    这里有一些示例代码:

    int pid, code;
    long tmpLong;
    
    pid=fork();
    
    if(!pid)
    {
        ptrace(PTRACE_TRACEME,0,0,0);
        execlp("program_to_be_tested","program_to_be_tested",NULL);
        exit(123);
    }
    
    waitpid(pid,&code,0);
    
    if(code!=0x57F)
    {
        /* Starting the program failed ... */
    }
    else
    {
        /* Set breakpoints - here assuming x86-64 */
        tmpLong=ptrace(PTRACE_PEEKDATA,pid,(void *)(address&~7),0);
        ((char *)&tmpLong)[address&7]=0xCC;
        ptrace(PTRACE_POKEDATA,pid,(void *)(address&~7),(void *)tmpLong);
    
        /* Continue the program */
        ptrace(PTRACE_CONT,pid,0,0);
        waitpid(pid,&code,0);
        if((code&0xFF)!=0x7F)
        {
            /* Program did not hit a breakpoint
             * and did not cause an exception */
        }
        else if(code==0x57F)
        {
            /* Breakpoint hit */
            kill(pid,SIGKILL);
        }
        else
        {
            /* Program caused an exception */
            kill(pid,SIGKILL);
        }
    }
    

    要将输入传递给您的程序,您有两种可能的选择:

    • 多次运行调试器:

      echo "Input to be tested" | ./myDebugger
      

      因为您的调试器不从 STDIN 读取,所以输入将被传递给要测试的程序。

    • 创建子进程时使用pipedup2

      ...
      pipe(pipes);
      pid=fork();
      if(!pid)
      {
          dup2(pipes[0],0);
          close(pipes[0]);
          close(pipes[1]);
          ...
      }
      close(pipes[0]);
      write(pipes[1],"Input to be sent to program", ...);
      ...
      

    【讨论】:

    • 非常感谢。完美。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-06
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    • 2011-10-20
    • 1970-01-01
    相关资源
    最近更新 更多