Writing Memory of the Traced Process

Writing memory of the traced process.

You are opening the file in read-only mode (O_RDONLY). I'd suggest trying again with O_RDWR instead:

  mem_fd = open(mem_file_name, O_RDWR);

However, from man proc it's not clear this will work:

   /proc/[pid]/mem
This file can be used to access the pages of a process's memory
through open(2), read(2), and lseek(2).

EDIT:

I was curious too, so I put together this example using just ptrace() directly:

#include <sys/ptrace.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define SHOW(call) ({ int _ret = (int)(call); printf("%s -> %d\n", #call, _ret); if (_ret < 0) { perror(NULL); }})

char changeme[] = "This is a test";

int main (void)
{
pid_t pid = fork();
int ret;
int i;
union {
char cdata[8];
int64_t data;
} u = { "Hijacked" };

switch (pid) {
case 0: /* child */
sleep(1);
printf("Message: %s\n", changeme);
exit(0);

case -1:
perror("fork");
exit(1);
break;

default: /* parent */
SHOW(ptrace(PTRACE_ATTACH, pid, 0, 0));
SHOW(ptrace(PTRACE_POKEDATA, pid, changeme, u.data));
SHOW(ptrace(PTRACE_CONT, pid, 0, 0));
wait(NULL);
break;
}

return 0;
}

how to trace memory used by a child process after the process finished in C

The VmData field appears to only exist for running processes. I don't know why you decided that it does not. Just run grep VmData /proc/$$/status and you'll see it for your current shell process.

There are three interesting points in time in your question. The first is while your child process is running. The second is when it is no longer running, but exists as a zombie, and the third is after you have reaped it, and it no longer exists. You seem to have missed out on point #2.

If you want to catch your process while it is in zombie state (i.e. - it has already exited, but not yet reaped) you will need to find a way to get notified that it has finished without calling wait, which will make it disappear. My recommended way is to register a SIGCHLD handler. It will get called when your process exists, but will not remove it from the system. At that point in time, /proc/pid/status will still exist.

Unfortunately for you, VmData does not exist for zombie processes. Your options are either to periodically sample VmData, or to stop the process when it is about to terminate, but have not yet done so in practice.

There is a race free method of doing this, but it, unfortunately, requires the use of the somewhat black magic ptrace syscall. Full explanation of how to use ptrace is beyond the scope of this answer. In a nutshell, however, your child process, before calling execve, calls ptrace with the PTRACE_TRACEME argument. Your parent process then becomes your child process' debugger. It gets notified with wait any time your child process is about to do something special.

This would take some playing with the call, but this would allow you to know when your child is about to exit, but has not yet actually done so.

Trace page table access of a Linux process

Ok, so you don't really need an "index", but just some unique identifier to distinguish different pages in the virtual address of a process.

In such case, then you can just do address >> PAGE_SHIFT. In x86 with 4KB pages PAGE_SHIFT is 12, so you can do:

page_id = address >> 12

Then if address1 and address2 correspond to the same page the page_id will be the same for both addresses.

Alternatively, to achieve the same result, you could do address & PAGE_MASK, where PAGE_MASK is just 0xfffffffffffff000 (that is ~((1UL << PAGE_SHIFT) - 1)).

How does ptrace POKETEXT works when modifying program text?

Looking at the kernel sources, x86 uses the generic (as opposed to arch-specific) ptrace request functions.

The actual changes are done by mm/memory.c:__access_remote_vm(), which uses mm/gup.c:get_user_pages_remote() to obtain the kernel mapping for the target page, followed by kmap(page), copy_to_user_pages(), set_page_dirty_lock(), kunmap(page), and put_page(page).

The simple description of what is actually done, is that the target process memory containing the code is accessed (modified) thorough the kernel mapping — the virtual memory "window" or "barrier" between the target process and the kernel — and not through the mappings visible to user-space processes.

Based on the above, we can answer the stated questions:

Does PTRACE_POKETEXT bypass read only page permissions of the traced process?

Yes. The kernel does not use the page protection mechanisms visible to userspace processes for this; it uses its own internal mappings.

Or does it need to change permission temporarily to be writable?

No, it does not.

Note that except for the changed data in the userspace memory (and possibly whether the pages are backed by an executable file or not), and for any kernel or hardware bugs there might be, when and how the kernel uses its own mappings is invisible and undetectable to userspace processes.

Restricting a process from writing to it's own memory


  1. Mark the memory region read-only with VirtualProtect()

  2. Attach a "debugger" that traps the first-chance exception and steps around the writes to the memory.

Or do it the right way and track down the camera updater that you're fighting with and zap it (replace a critical bit with nop so it does nothing interesting, preferably using as little CPU as possible).



Related Topics



Leave a reply



Submit