ptrace doesnt show the same as objdump

你离开我真会死。 提交于 2020-05-17 03:40:29

问题


I am writing a C program thats shows instructions using ptrace. This is the code:

#include<stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <string.h>

void run_target()
{
    ptrace(PTRACE_TRACEME, 0, 0, 0);
    execl("./test", "test", NULL);
}

void debugger(pid_t pid)
{
    int status;
    wait(&status);

    while(WIFSTOPPED(status))
    {
        struct user_regs_struct regs;
        ptrace(PTRACE_GETREGS, pid, 0, &regs);
        long instruction = ptrace(PTRACE_PEEKTEXT, pid, regs.rip, 0);
        ptrace(PTRACE_SINGLESTEP, pid, 0, 0);

        //EDITED SO IT PRINTS ONLY LINES I WANT
        if(((regs.rip >> (8*5)) & 0xFF) != 0x7f)    //i noticed all the junk lines that shouldnt be there, their regs.rip began with 7f
            printf("%llx %16lx\n", regs.rip, instruction);

        wait(&status);
    }
}

int main()
{
    pid_t pid;
    pid = fork();

    if(pid == 0)
    {
        run_target();
    }
    else
    {
        debugger(pid);
    }

    return 0;
}

But the output looks like this

...
7f3bf1487308 8348da7426fa8348
7f3bf148730c d47408fa8348da74
7f3bf148730e 8d48d47408fa8348
7f3bf1487312 16ad50d8d48d474
7f3bf14872e8 18c0834810508b48
7f3bf14872ec 48f2014c18c08348
7f3bf14872f0 8948c33948f2014c
7f3bf14872f3 860f118948c33948
7f3bf14872f6 fff670860f118948
7f3bf14872f9 508bfffff670860f

And the objdump -d looks like this:

000000000000064a <main>:
64a:    55                      push   %rbp
64b:    48 89 e5                mov    %rsp,%rbp
64e:    48 8d 3d af 00 00 00    lea    0xaf(%rip),%rdi        # 704 <_IO_stdin_used+0x4>
655:    b8 00 00 00 00          mov    $0x0,%eax
65a:    e8 c1 fe ff ff          callq  520 <printf@plt>
65f:    48 8d 3d a5 00 00 00    lea    0xa5(%rip),%rdi        # 70b <_IO_stdin_used+0xb>
666:    b8 00 00 00 00          mov    $0x0,%eax
66b:    e8 b0 fe ff ff          callq  520 <printf@plt>
670:    b8 00 00 00 00          mov    $0x0,%eax
675:    5d                      pop    %rbp
676:    c3                      retq   
677:    66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
67e:    00 00 

And running ./program | grep "64e" for example shows this

7f46f3bb64e2 89483e8b48f90148
7f46f3bb64e5 8b483989483e8b48
7f46f3bb64e8 48087e8b48398948
7f46f3bb64eb 48c70148087e8b48
7f46f3bb64ef 4808798948c70148
7f46f3bb7318 f64ee8ef894c9174
7f46f3bb731a f64ee8ef894c
7f46f3bb731d 4887eb0000f64ee8
7f46f3800b70 4864e889481f8948
7f46f3800b73 2504334864e88948
55dfb208464e b8000000af3d8d48
7f46f38564e2 89440af883481476
7f46f38f2a11 4334864e8458b48
7f46f380505a e264e8ff894c0874

with this one actually being correct:

55dfb208464e b8000000af3d8d48

So is there something im missing in objdump or is there something missing in my code that it shows more of the rip than it should?

EDIT: added whole code

EDIT: edited the printf, now it prints the lines its supposed to, but it still adds random hex to the beginning what it should be

64a: 55

what it is

56160a60a64a 9f3d8d48e5894855

I understand why the instruction isnt the same, that is NOT my problem, my problem is why the regs.rip isnt 64a, but is 64a. And the random hex at the start is different everytime i rerun the program.


回答1:


Your program works, you just have to look at the right place in the right order in your result.

Having that test definition :

#include <stdio.h>
int main()
{
  puts("hello");
  puts("world");
  return 0;
}

objdump -d test produces among other things :

0000000000400526 <main>:
  400526:   55                      push   %rbp
  400527:   48 89 e5                mov    %rsp,%rbp
  40052a:   bf d4 05 40 00          mov    $0x4005d4,%edi
  40052f:   e8 cc fe ff ff          callq  400400 <puts@plt>
  400534:   bf da 05 40 00          mov    $0x4005da,%edi
  400539:   e8 c2 fe ff ff          callq  400400 <puts@plt>
  40053e:   b8 00 00 00 00          mov    $0x0,%eax
  400543:   5d                      pop    %rbp
  400544:   c3                      retq   
  400545:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40054c:   00 00 00 
  40054f:   90                      nop

if I run your program among the result I found the code with the same addresses of objdump and the same instructions codes, so the init section etc then main (from the line 81208 !) :

400526 4005d4bfe5894855
400527   4005d4bfe58948
40052a fecce8004005d4bf
40052f  5dabffffffecce8
400400   6800200c1225ff
400406 ffe0e90000000068
40040b  a25ffffffffe0e9
4003f0 25ff00200c1235ff
4003f6 1f0f00200c1425ff
...

I run on an Intel i7, the instruction bytes have to be read in the reverse order than shown by objdump, for instance the first instruction is 55, then 48 89 e5 then bf d4 05 40 00 then e8 cc fe ff ff etc.

Of course in your case it is like you do 'step' rather than 'next' in a debugger, so on a callq to puts you enter in puts, while objdump disassemble. There are 60084 lines after the address 40052f before to reach address 400534, so 60084 instructions are necessary to do puts("hello"); !


Of course it is possible to print only from the beginning of main, for instance with a lazy way using objdump :

void debugger(pid_t pid)
{
  FILE * fp = popen("objdump -d ./test | grep \"<main>:\"", "r");

  if (fp == NULL) {
    puts("cannot get main address");
  }
  else {
    char line[256];

    if (fgets(line, sizeof(line), fp) == NULL) {
      puts("no address !");
      pclose(fp);
    }
    else {
      unsigned long long main_addr;

      pclose(fp);
      errno = 0;
      main_addr = strtoull(line, NULL, 16);

      if (errno != 0)
        puts("invalid address");
      else {
        int found = 0;
        int status;

        while(wait(&status), WIFSTOPPED(status))
        {
          struct user_regs_struct regs;

          ptrace(PTRACE_GETREGS, pid, 0, &regs);

          if (found |= (regs.rip == main_addr)) {
            long instruction = ptrace(PTRACE_PEEKTEXT, pid, regs.rip, 0);

            printf("%llx %16lx\n", regs.rip, instruction);
          }

          ptrace(PTRACE_SINGLESTEP, pid, 0, 0);
        }
      }
    }
  }
}

[edit from your remark]

ptrace does not know if you want to read a data or instructions, if you want to separate each instructions as objdump you have to know the code of each and the corresponding length, else you can use the difference of addresses from one to the next even that does not work in case of call/jmp/conditional jmp/return :

void debugger(pid_t pid)
{
  FILE * fp = popen("objdump -d ./test | grep \"<main>:\"", "r");

  if (fp == NULL) {
    puts("cannot get main address");
  }
  else {
    char line[256];

    if (fgets(line, sizeof(line), fp) == NULL) {
      puts("no address !");
      pclose(fp);
    }
    else {
      unsigned long long main_addr;

      pclose(fp);
      errno = 0;
      main_addr = strtoull(line, NULL, 16);

      if (errno != 0)
        puts("invalid address");
      else {
        int found = 0;
        int status;
        unsigned long prev_instr;
        long long prev_addr = -1;

        while(wait(&status), WIFSTOPPED(status))
        {
          struct user_regs_struct regs;

          ptrace(PTRACE_GETREGS, pid, 0, &regs);

          if (found |= (regs.rip == main_addr)) {
            unsigned long instruction =
              (unsigned long) ptrace(PTRACE_PEEKTEXT, pid, regs.rip, 0);

            if (prev_addr != -1) {
              /* longest instruction has 15 bytes on x86 */
              int len = ((regs.rip > prev_addr) && ((regs.rip - prev_addr) <= 15))
                ? regs.rip - prev_addr : 100;

              printf("%llx ", prev_addr);
              while (prev_instr && len--) {
                printf("%02x ", (unsigned) (prev_instr & 0xff));
                prev_instr /= 256;
              }
              if (len > 15)
                puts(" (?)");
              else
                putchar('\n');
            }
            prev_instr = instruction;
            prev_addr = regs.rip;
          }

          ptrace(PTRACE_SINGLESTEP, pid, 0, 0);
        }
      }
    }
  }
}

now the output is :

400526 55 
400527 48 89 e5 
40052a bf d4 05 40 00 
40052f e8 cc fe ff ff bf da 05  (?)
400400 ff 25 12 0c 20 00 
400406 68 00 00 00 00 
40040b e9 e0 ff ff ff ff 25 0a  (?)
4003f0 ff 35 12 0c 20 00 
4003f6 ff 25 14 0c 20 00 0f 1f  (?)
7fe20cc1fe10 53 
7fe20cc1fe11 48 89 e3 
7fe20cc1fe14 48 83 e4 c0 
7fe20cc1fe18 48 2b 25 31 df 20 00 
7fe20cc1fe1f 48 89 04 24 
7fe20cc1fe23 48 89 4c 24 08 
...

to do more you have to know the code and length of each instruction call/jmp/conditional jmp/return.Note a code operation can be on 1 or 2 bytes.



来源:https://stackoverflow.com/questions/61531930/ptrace-doesnt-show-the-same-as-objdump

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!