Nikolay Igotti

pageicon Thursday Jul 05, 2007

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

« March 2008
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

Feeds

Search this blog

Links

Weblog menu

Today's referrers

Today's Page Hits: 150

Stats