-
-
Save FergusInLondon/fec6aebabc3c9e61e284983618f40730 to your computer and use it in GitHub Desktop.
/** | |
* snoopy.c - Snoop on another tasks memory. | |
* Fergus In London <[email protected]> | |
* | |
* This is a pretty basic demo of the process_vm_readv syscall, a syscall which | |
* provides a nicer interface than ptrace for accessing memory used by another | |
* task. | |
* | |
* To play with simply use `ps` to get a PID for the process you'd like to snoop | |
* on, and `pmap` for the relevant memory address. | |
* | |
* Wrote whilst eating a bagel during breakfast.. probably not the nicest code. | |
* | |
* Requires a Linux Kernel > 3.2. | |
* https://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html | |
*/ | |
#define _GNU_SOURCE | |
#include <sys/uio.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <errno.h> | |
int main(int argc, char **argv) { | |
if (argc < 3) { | |
printf("usage: %s <pid> <mem address> [len]\n", argv[0]); | |
printf(" <pid> - PID of process to target\n"); | |
printf(" <mem> - Memory address to target\n"); | |
printf(" [len] - Length (in bytes) to dump\n"); | |
return -1; | |
} | |
// PARSE CLI ARGS | |
pid_t pid = strtol(argv[1], NULL, 10); | |
printf(" * Launching with a target PID of: %zd\n", pid); | |
void *remotePtr = (void *)strtol(argv[2], NULL, 0); | |
printf(" * Launching with a target address of 0x%llx\n", remotePtr); | |
size_t bufferLength = (argc > 3) ? strtol(argv[3], NULL, 10) : 20; | |
printf(" * Launching with a buffer size of %d bytes.\n", bufferLength); | |
// Build iovec structs | |
struct iovec local[1]; | |
local[0].iov_base = calloc(bufferLength, sizeof(char)); | |
local[0].iov_len = bufferLength; | |
struct iovec remote[1]; | |
remote[0].iov_base = remotePtr; | |
remote[0].iov_len = bufferLength; | |
// Call process_vm_readv - handle any error codes | |
ssize_t nread = process_vm_readv(pid, local, 2, remote, 1, 0); | |
if (nread < 0) { | |
switch (errno) { | |
case EINVAL: | |
printf("ERROR: INVALID ARGUMENTS.\n"); | |
break; | |
case EFAULT: | |
printf("ERROR: UNABLE TO ACCESS TARGET MEMORY ADDRESS.\n"); | |
break; | |
case ENOMEM: | |
printf("ERROR: UNABLE TO ALLOCATE MEMORY.\n"); | |
break; | |
case EPERM: | |
printf("ERROR: INSUFFICIENT PRIVILEGES TO TARGET PROCESS.\n"); | |
break; | |
case ESRCH: | |
printf("ERROR: PROCESS DOES NOT EXIST.\n"); | |
break; | |
default: | |
printf("ERROR: AN UNKNOWN ERROR HAS OCCURRED.\n"); | |
} | |
return -1; | |
} | |
printf(" * Executed process_vm_ready, read %zd bytes.\n", nread); | |
printf("%s\n", local[0].iov_base); | |
return 0; | |
} |
To bypass permission checking you can exec
sudo sysctl kernel.yama.ptrace_scope=0
in bash
Hey I'm having permission issues, but this line seems to not do anything for me, it gives me:
sysctl: cannot stat /proc/sys/kernel/yama/ptrace_scope: No such file or directory
Is this a kernel config or something that i am missing or perhaps something else?
To bypass permission checking you can exec
sudo sysctl kernel.yama.ptrace_scope=0
in bashHey I'm having permission issues, but this line seems to not do anything for me, it gives me:
sysctl: cannot stat /proc/sys/kernel/yama/ptrace_scope: No such file or directory
Is this a kernel config or something that i am missing or perhaps something else?
@SpieringsAE
What's linux is it? I tested this on x84_64 Ubuntu
I'm running debian 11 on an arm64 embedded linux system
i found this page but I turned the process_vm_readv and process_vm_writev into a python library and when i try
:~# setcap cap_sys_ptrace=eip /usr/moduline/python/read_test.py
Failed to set capabilities on file `/usr/moduline/python/read_test.py' (Operation not supported)
The value of the capability argument is not permitted for a file. Or the file is not a regular (non-symlink) file
Okay I compiled snoopy, and that seems to work. so there seems to be an issue where my python module/shared library is not getting the right permissions
Sorry to bother you. There is one thing in the code that I don't quite understand: why is the value of liovcnt
equal to 2 when calling process_vm_readv
? Shouldn't it be 1?
The program is reading your whole range as you can see by the "* Executed process_vm_ready, read 134496 bytes.", it isn't printing it all out though because there is a null byte. Basically how printf knows the end of a string is because they are terminated with a null byte or \x00 so it will only print out all the data it reads up to the first null byte as it thinks that is the end of the string.