#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include "procfs.h"
int procfs_open(pid_t pid) {
char path[2048];
sprintf(path, "/proc/%d/mem", pid);
return open(path, O_RDWR);
}
ssize_t procfs_maps(pid_t pid, struct procfs_map **maps) {
char path[2048];
sprintf(path, "/proc/%d/maps", pid);
FILE *f = fopen(path, "r");
if (!f) return -1;
int n = 0;
int capacity = 2;
if (*maps) {
free(*maps);
}
*maps = calloc(capacity, sizeof(**maps));
if (!*maps) return -1;
while (1) {
void *base, *max;
char prot_str[4];
char name[4096] = {0};
void *unused;
int matches = fscanf(
f, "%p-%p %4c %p %p:%p %d",
&base, &max, &prot_str, &unused, &unused, &unused, &unused
);
if (matches == EOF) goto eof;
if (matches < 7) goto err;
int c = ' ';
while (c == ' ') {
c = fgetc(f);
}
if (c == EOF) goto err;
ungetc(c, f);
if (fgets(name, 4096, f) == NULL) goto err;
name[strlen(name) - 1] = '\0'; // remove newline
int prot = 0;
for (int i = 0; i < 4; i++) {
switch (prot_str[i]) {
case 'r':
prot |= PROT_READ;
break;
case 'w':
prot |= PROT_WRITE;
break;
case 'x':
prot |= PROT_EXEC;
break;
default:
break;
}
}
if (prot == 0) prot = PROT_NONE;
if (n > 0) {
procfs_map *prev = *maps + (n - 1);
if ((strlen(name) == 0 || strncmp(prev->name, name, 1024) == 0) &&
prev->max == (uintptr_t)base) {
prev->max = (uintptr_t)max;
continue;
}
}
n++;
if (n > capacity) {
capacity <<= 1;
struct procfs_map *r = reallocarray(*maps, capacity, sizeof(*r));
if (!r) goto err;
*maps = r;
}
procfs_map *new = *maps + (n - 1);
new->base = (uintptr_t)base;
new->max = (uintptr_t)max;
new->prot = prot;
strncpy(new->name, name, sizeof(new->name) - 1);
}
eof:
if (ferror(f)) goto err;
fclose(f);
if (n == 0) {
free(*maps);
*maps = NULL;
}
return n;
err:
fclose(f);
free(*maps);
*maps = NULL;
return -1;
}
// mmap does not work on /proc/pid/mem, so we issue reads/writes instead
int read_mem(int fd, uintptr_t addr, uint8_t *data, size_t size) {
if (lseek(fd, addr, SEEK_SET) == -1) return -1;
size_t n = 0;
while (n < size) {
ssize_t result = read(fd, data + n, size - n);
if (result <= 0) return -1;
n += result;
}
return 0;
}
int write_mem(int fd, uintptr_t addr, uint8_t *data, size_t size) {
if (lseek(fd, addr, SEEK_SET) == -1) return -1;
size_t n = 0;
while (n < size) {
ssize_t result = write(fd, data + n, size - n);
if (result <= 0) return -1;
n += result;
}
return 0;
}