Walking Down Memory Lane


Memory

Working memory, provided by a system’s RAM, is something that every process needs. Collectively its a shared resource that every process uses while talking to the CPU. Although there are boundaries to make sure no one accidently reads or writes into another one’s working area.

What process sees ?

Every process creates or maintains a memory map and the PCB structure is more or less the same for most OS. Thanks to the concept of virtual memory every process sees the RAM as an exclusive resource and creates its own address space. Just like an array, starting from zero, allocations start with text or instruction data then some space from data structure and then heap memory which is allocated or mapped on demand based on memory requirement. The last or the highest addresses are for stack memory or running function calls. Mapping means creating a link between virtual address space and physical address space and this virtual to physical address translation is done by the system. Similar to physical memory a process can map libraries (code) and other devices into its own virtual address space.

code used in this post can be found here

Memory maps

Every process can know its current mapping using its process id

pid_t pid = getpid();

cat /proc/pid/maps

Every line of this raw output is formatted as below

begin_addr end_addr permission <> device inode mapname

This much information is not much fun. Can be easily procured from the command line or can be parsed inside the process itself. What if a process, given the right permission, can get access to the complete RAM and see its content. That is what we are attempting to do here.

Memory device

Easiest way of doing this is using /dev/mem device and dumping the data into a hex file. But this can be done using a C program as well and more logic could be added to do memory walk and look for known structures and data in memory.

Individual pages can be mapped to processes virtual memory and then read.

code taken from devmem2

// iteratively move cur_addr to cover the region
map_base = mmap(0, walk_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, cur_addr & ~mask);

This mapped address can be used to read individual byte in the mapped region

virt_addr = map_base + (cur_addr & mask);
for (b = 0; b < walk_size; b++) {
    buffer[b] = *((unsigned char *) (virt_addr+b));
}

This buffer can then be written to a dump file, opened with “wb”

fwrite(buffer, walk_size, 1, dumpfd);

Dump files can be examined later using any hex reader tool, hexdump or a C program.

hexdump -C dump.bin | more