Raw page table access

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;
  }
  static char\* 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};


int main() {
  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;
}
Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

nike

Search

Categories
Archives
« July 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  
       
Today