Today, I'd like to show an example of raw physical memory access, to demonstrate layout of x86 page tables.
This demo requires x86 Solaris with 32-bit kernel, and root privileges.
You can see how to access page table of arbitrary process. Code is somewhat long, but I believe this layout would allow interesting reader to experiment with low level stuff far beyond of what's shown here.
Let fun start:
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/stat.h> #include<sys/wait.h> #include<stdlib.h> #include<stdint.h> #include<fcntl.h>class PhysMem {int mem_fd; public: PhysMem() { mem_fd = open("/dev/mem", O_RDWR); } ~PhysMem() {if (mem_fd >= 0) { close(mem_fd); mem_fd = -1; } }bool int32_at(uint64_t pa, int32_t* rv) {if (lseek(mem_fd, pa, SEEK_SET) != (off_t)pa) {return false; }return (read(mem_fd, rv, 4) == 4); }bool uint32_at(uint64_t pa, uint32_t* rv) {return int32_at(pa, (int32_t*)rv); } };class ProcAccess { PhysMem* pm; pid_t pid; uint64_t pagetable;static uint64_t getcr3(pid_t pid) { pid_t mdb;char tmp[64];char buf[1024]; snprintf(tmp, sizeof tmp, "/tmp/%d.pt", pid); snprintf(buf, sizeof buf, "echo \"%x::pid2proc|::print proc_t p_as | ""::print struct as a_hat | ::print hat_t hat_htable | ""::print htable_t ht_pfn\" | /usr/bin/mdb -k > %s", pid, tmp);if ((mdb = fork()) == 0) { execl("/bin/sh", "/bin/sh", "-c", buf, NULL); exit(1); } else { waitpid(mdb, NULL, 0); } FILE* log = fopen(tmp, "r");if (log == NULL) {return 0; } uint64_t rv = 0; fscanf(log, "ht_pfn = %llx", &rv); fclose(log); remove(tmp);return rv << 12; }staticchar* pt_flags[];public: ProcAccess(PhysMem* pm, pid_t pid) : pm(pm), pid(pid) { pagetable = getcr3(pid); } ~ProcAccess() {}void dump_pt() { printf("Page directory\n");for (int i=0; i<1024; i++) { int32_t pde = 0;if (pm->int32_at(pagetable+i*4, &pde)) { printf("pde[%d]=%x\n", i, pde); } else { printf("cannot read pde %d\n", i); } } }// assume 32-bit kernel uint64_t pa_by_va(uint64_t va, int* ptflags) {int pdi = (va >> 22) & 0x3ff; uint32_t pde = 0;if (!pm->uint32_at(pagetable+pdi*4, &pde)) {return 0; } pde &= ~0x3ff;int pti = (va >> 12) & 0x3ff; uint32_t pte = 0;if (!pm->uint32_at(pde + pti*4, &pte)) {return 0; }if (ptflags != NULL) { *ptflags = pte & 0xfff; }return (va & 0xfff) | (pte & ~0xfff); }void dump_flags(int flags) { printf("page table flags: \n");for (int i=0; pt_flags[i]; i++) { printf(" %s%s\n", (flags & (1<<i)) ? "" : "no ", pt_flags[i]); } } };volatile int32_t var = 0x12345678;char* ProcAccess::pt_flags[] = {"VALID", "WRITEABLE", "USER", "WRITETHRU","NONCACHEABLE", "REFERENCED", "MODIFIED", "WRITE COMBINED","GLOBAL", "OS1", "OS2", NULL};intmain() { PhysMem pm; ProcAccess me(&pm, getpid());int flags = 0; uint64_t pa_of_var = me.pa_by_va((uint64_t)&var, &flags); int32_t var_val; pm.int32_at(pa_of_var, &var_val); printf("va=%p pa=%llx val at this pa=%x, var=%x\n",&var, pa_of_var, var_val, var); printf("\nDATA segment flags\n"); me.dump_flags(flags); printf("\nTEXT segment flags\n"); me.pa_by_va((uint64_t)&main, &flags); me.dump_flags(flags);return 0; }