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
Mark the memory region read-only with
VirtualProtect()
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
How to Pipe Output to a File When Running as a Systemd Service
Remove Occurrences of String in Text File
Determine the Os Version, Linux and Windows from Powershell
Switch from 32Bit Mode to 64 Bit (Long Mode) on 64Bit Linux
Calculating Rounded Percentage in Shell Script Without Using "Bc"
Checkpoint/Restart Using Core Dump in Linux
Let Non-Root User Write to Linux Host in Docker
Installing G++ on Windows Subsystem for Linux
Assigning Output of a Command to a Variable(Bash)
How to Detect a Buffer Over Run on Serial Port in Linux Using C++
Is Clock_Gettime() Adequate for Submicrosecond Timing
Ansible Conditional Based on Stdout of Result
How to Calculate the Total Size of Certain Files Only, Recursive, in Linux
Change System Date Time in Docker Containers Without Impacting Host