Can you access another program's stack/heap if you know the address?

Viewed 463

Just a miscellaneous question. I have a program in C++:

#include <stdio.h>

int main() 
{
    while (1) {
        int value1 = 1;
        printf("%p\n", (void *)&value1);
        void* address = (void *)&value1;
        int value2 = *(int*)address;
        printf("%d\n",value2);
    }
    return 0;
}

What this does is get the address of value1, store it in address, and then get the variable in address and store it in value2. This works just fine, however, I would like to access the variable from another C++ program. I have tried this (two methods in this one file):

#include<iostream>
#include <cstdint>

int main() {
    std::cout << "before seg fault" << std::flush;
    uintptr_t p = <address>;
    int value = *reinterpret_cast<int *>(p);
    int value2 = *(int*)<address>;
    std::cout << "after seg fault"<< std::flush;
    std::cout << value;
    return 0;
}

This causes a segmentation fault when trying to access the value, which is probably because the OS doesn't want me accessing this value or because it doesn't exist in this instance.

However unpractical and stupid this may seem, is there a way to overcome this? Or is it impossible? As a side note, Why should / why shouldn't I do this?

Edit: I have approved Chris's answer, as it works just fine when you run it with root on linux. Would it be too much to ask for something that works on Mac and Windows too? I have tried running the poke program on Mac system and it gives error: Can't access pid 26112:: No such file or directory, invoked by line: fprintf(stderr, "usage: %s pid address value\n", av[0]);

3 Answers

Each process has it's own address space and addresses in a program refer to that address space, independent of the addresses in any other address space. So when you put an explicit value into a pointer like that and use it, you'll get whatever is at that address in this process (which is probably invalid, so you get a SEGFAULT or similar error), rather than accessing the other process.

On most OSes, there are ways to access the address space of another process, subject to permissions. For example, on Linux, this poke program can modify another process's address space:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int main(int ac, char **av) {
    char    name[64];
    int     fd;
    if (ac != 4) {
        fprintf(stderr, "usage: %s pid address value\n", av[0]);
        exit(1); }
    sprintf(name, "/proc/%.10s/mem", av[1]);
    if ((fd = open(name, O_WRONLY)) < 0) {
        fprintf(stderr, "Can't access pid %s", av[1]);
        perror(":");
        exit(1); }
    lseek(fd, strtol(av[2], 0, 0), SEEK_SET);
    if (write(fd, av[3], strlen(av[3]) + 1) < 0)
        perror("write");
    return 0;
}

If you then run this program:

#include <stdio.h>
#include <unistd.h>

int main() {
    char data[16] = "test";
    while (1) {
        printf("pid = %d, &data = %p, data = %s\n", getpid(), &data, data);
        sleep(2);
    }
}

It will print lines looking something like:

pid = 6376, &data = 0x7ffe255f3190, data = test
pid = 6376, &data = 0x7ffe255f3190, data = test

and if you run ./poke 6376 0x7ffe255f3190 Hello in another window/terminal, it will change to

pid = 6376, &data = 0x7ffe255f3190, data = Hello
pid = 6376, &data = 0x7ffe255f3190, data = Hello

This causes a segmentation fault when trying to access the value, which is probably because the OS doesn't want me accessing this value or because it doesn't exist in this instance.

The latter. The virtual memory address of one process doesn't have a meaning to the other process. Unless the process attempting to read the arbitrary address incidentally had allocated memory in the same virtual address, the operating system notices that it's doing non-sensical things, and terminates it in order to prevent the process from doing non-sensical things that may have bad consequences.

Can you access another program's stack/heap if you know the address?

There is no standard way to do that in C++. The language lacks the concept of multiple processes.

A co-operative way for processes to communicate is to use shared memory. There is no standard way to allocate shared memory in C++.

Reading memory of another process without co-operation may also be possible, although operating systems typically prevent regular users from doing that by default for obvious reasons - unless the processes belong to the same group. There's no standard way to do this in C++ either.

Why should / why shouldn't I do this?

The co-operating shared memory is useful for inter-process communication. Note that there are other alternatives too, such as pipes and sockets (but none of the alternatives are in standard C++).

The non-co-operative reading of another process is rarely useful in user space programs. I suppose that debuggers doing this is one of such rare use cases.

This causes a segmentation fault when trying to access the value, which is probably because the OS doesn't want me accessing this value or because it doesn't exist in this instance.

Sort of both: it doesn't exist in your process's address space because the OS doesn't want you accessing other processes' memory, and therefore instructed the memory hardware not to map it into your process's address space.

In some old, single-user operating systems, you could access all memory, because there was no real concept of process isolation, or users, or the virtual memory hardware to enforce any separation.

The reason this isn't done any more is because it scales really badly. Once you have a few background tasks, things start to break if they don't get scheduled regularly (or if they take too long about yielding the processor). Once you have a few processes, things start to break if they accidentally trample each other's memory.

So now we have self-contained processes which can only communicate by mutual agreement (such as by both mapping some shared memory), but cannot trample over each other's variables.

However unpractical and stupid this may seem, is there a way to overcome this? Or is it impossible? As a side note, Why should / why shouldn't I do this?

You shouldn't do this (that is, exactly what you're asking) for the same reason the OS is preventing you: it causes impossible-to-diagnose bugs and security leaks in random parts of your system.

If you want to share memory between processes, you can use shared memory. It's very platform-specific, so you need to look up the API for a specific platform, or use Boost.Interprocess.

Note however that the same memory may be mapped at different addresses in each process using it, so you can't usefully store pointers inside it (but offsets are OK), and it's a lot of work to implement things like node-based containers in shared memory (but Boost.Interprocess has ones you can use).

Related