Quantcast
Channel: Nikolay Igotti
Viewing all articles
Browse latest Browse all 30

Raw page table access

$
0
0
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;
}

Viewing all articles
Browse latest Browse all 30

Trending Articles