17

I'm trying to read a child process's stack but with no luck. I know it is possible using ptrace, but ptrace's interface allows you to read only one word at a time, and I'm trying to scan a larger portions of the stack.

I've also tried reading the /proc/$pid/mem from the boundries of the stack as extracted from the /proc/$pid/maps file after first using ptrace to attach to it (as suggested here) but the read keeps failing (even when running as root) although the same code succeeds when tried reading from different parts of the process (e.g. heap).

What am I doing wrong? Is there any other option?

user4537
  • 271
  • Did you call waitpid between ptrace(PTRACE_ATTACH,…) and read (otherwise there's a possible race condition)? What error does read return? Is the child doing anything peculiar with its memory mapping — can you try your code with a simple child like sleep? – Gilles 'SO- stop being evil' Feb 20 '11 at 19:11
  • I did use wait after ptrace, and I've put a scanf in the child to force it to wait. – user4537 Feb 20 '11 at 21:51
  • Is this only on Linux? Solaris also has a /proc filesystem, but it's completely different from Linux, eve philosophically. Lots of "binary files". –  Feb 23 '11 at 12:55
  • just do a system("pstack pid") and parse the output .. – vrdhn Mar 02 '11 at 10:11
  • Please show us your code perl -e '$p=shift;open MAPS, "/proc/$p/maps";($m)=grep /\[stack\]/, <MAPS>;($a,$b)=map hex, $m =~ /[\da-f]+/g;open MEM, "/proc/$p/mem" or die "open mem: $!";seek MEM,$a,0 or die "seek: $!";read MEM, $c,$b-$a or die "read";print $c' "$$" | hd works for me. What do you get when you run it? – Stéphane Chazelas Jul 27 '16 at 09:33

5 Answers5

5

ptrace's interface allows you to read only one word at a time, and I'm trying to scan a larger portions of the stack

Well, just use a loop, then. I honestly don't see how that constitutes a problem with ptrace, I use it all the time to remotely access processes.

I use something like this:

static int memcpy_from_target(pid_t pid, char *dest, long src, size_t n)
{
    static int const align = sizeof(long) - 1;
while (n)
{
    size_t todo = MIN(n, sizeof(long) - (src &amp; align));
    long data = ptrace(PTRACE_PEEKTEXT, pid, src - (src &amp; align), 0);
    if (errno)
    {
        perror(&quot;ptrace_peektext (memcpy_from_target)&quot;);
        return -1;
    }
    memcpy(dest, (char *)&amp;data + (src &amp; align), todo);

    dest += todo; src += todo; n -= todo;
}

return 0;

}

  • Hi Sam, while your code will do (and actually that's what I'm currently doing), it has a large performance overhead. – user4537 Mar 13 '11 at 09:30
  • @user4536: I see. I have another strategy in mind that I'll post about when I have time writing it down. What are your typical stack sizes? – sam hocevar Mar 13 '11 at 11:37
  • It's hard to say really as my research does not assume a specific stack size, but for the sake of this argument let's say at least a couple of pages long

    Could you drop a hint regarding your strategy? Thanks for the help anyway!

    – user4537 Mar 13 '11 at 12:21
1

Another suggestion.

When/if it gets accepted in the main Linux kernel tree, you will be able to use Christopher Yeoh's Cross Memory Attach patch. See the documentation for process_vm_readv for instance.

1

You can easily read the stack of another process using the proc file system (You will need root access for this). Before arbitrarily reading from the /proc/pid/mem you need to consult the /proc/pid/maps. A simple read in this file shows a lot of entries. We are interested in the entry marked as stack. Once you get this you need to read the lower and upper bounds of the stack. Now simply open the /proc/pid/mem file, seek to the lower bound of the stack and read the correct size of data.

Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
  • 1
    Are you sure you mean mems and not maps? (I can't see any mems entries under my /proc filesystem.) The OP already mentioned reading the stack boundaries from /proc/$pid/maps -- what are you suggesting they do differently? – JigglyNaga Jul 26 '16 at 13:12
  • Edited the typo. I did exactly what I mentioned in my answer and it dumped 132 KBs of stack data. We need more info on what OP did wrong. Maybe OP can share the code he used for reading the stack boundaries. If he doesn't reply, I will share mine. – Ajay Brahmakshatriya Jul 27 '16 at 09:13
1

Here is another strategy that might need tweaking but should be more efficient with large chunks of data. The idea is to execute syscalls in the remote process in order to retrieve the stack contents. It will need specific architecture code but if you only target x86 / x86_64 it shouldn't be too much hassle.

  1. Create a named pipe such as "/tmp/fifo" in your calling process.
  2. Step into the traced process until it returns from a syscall, using PTRACE_SYSCALL to step, waitpid() to wait and PTRACE_GETREGS/PTRACE_PEEKTEXT to check the currently executed opcode.
  3. Backup the remote process’s registers and a small area of its stack.
  4. Execute syscalls on the remote process by overriding its stack with your own data: open("/tmp/fifo"), write() the stack contents, close() the descriptor.
  5. Restore the remote process’s state.
  6. Read the fifo data from your calling process.

There might be more elegant alternatives to the named pipe, but I can’t think of any right now. The reason I only use syscalls is because remote code injection is pretty unreliable on modern systems due to various security protections. The drawback is that it will hang until the remote process does a syscall (which can be a problem for some programs that mostly do computations).

You can see some free code implementing most of the work in this source file. Feedback on the code is welcome!

0

You could try lsstack. It uses ptrace, just like every other successful "read another process's stack" program. I could not get a program using /proc/$pid/mem reading to work. I believe that you can't do it that way, although, logically, you should.