Showing posts with label linux. Show all posts
Showing posts with label linux. Show all posts

March 1, 2014

How to translate virtual to physical addresses through /proc/pid/pagemap

I currently work on a project where I need to make translations for virtual addresses of user-level application to physical addresses in Linux. I implemented my own system call to do that, but had hard times with verifying the results I'm getting.

Later I found out that in newer kernels there is a really nice virtual file in the /proc file system to get this information. I tried to cat it, doing cat /proc/self/pagemap, and got terrible binary output in my console.

So, it looks like working with this file is not such a pleasant experience. It's a binary file with all that it implies. I found couple of scripts that access this file and provide you with a nice text result, but unfortunately those were written in perl and ruby, and I needed to run it on very minimalistic embedded system. I needed something that fits into a single binary.

Long story short, I decided to bite the bullet and write a tool in C. My contribution might be helpful for someone, that's why I'm sharing this code.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <stdint.h>

#define PAGEMAP_ENTRY 8
#define GET_BIT(X,Y) (X & ((uint64_t)1<<Y)) >> Y
#define GET_PFN(X) X & 0x7FFFFFFFFFFFFF

const int __endian_bit = 1;
#define is_bigendian() ( (*(char*)&__endian_bit) == 0 )

int i, c, pid, status;
unsigned long virt_addr; 
uint64_t read_val, file_offset;
char path_buf [0x100] = {};
FILE * f;
char *end;

int read_pagemap(char * path_buf, unsigned long virt_addr);

int main(int argc, char ** argv){
   //printf("%lu\n", GET_BIT(0xA680000000000000, 63));
   //return 0;
   if(argc!=3){
      printf("Argument number is not correct!\n pagemap PID VIRTUAL_ADDRESS\n");
      return -1;
   }
   if(!memcmp(argv[1],"self",sizeof("self"))){
      sprintf(path_buf, "/proc/self/pagemap");
      pid = -1;
   }
   else{
         pid = strtol(argv[1],&end, 10);
         if (end == argv[1] || *end != '\0' || pid<=0){ 
            printf("PID must be a positive number or 'self'\n");
            return -1;
            }
       }
   virt_addr = strtol(argv[2], NULL, 16);
   if(pid!=-1)
      sprintf(path_buf, "/proc/%u/pagemap", pid);
   
   read_pagemap(path_buf, virt_addr);
   return 0;
}

int read_pagemap(char * path_buf, unsigned long virt_addr){
   printf("Big endian? %d\n", is_bigendian());
   f = fopen(path_buf, "rb");
   if(!f){
      printf("Error! Cannot open %s\n", path_buf);
      return -1;
   }
   
   //Shifting by virt-addr-offset number of bytes
   //and multiplying by the size of an address (the size of an entry in pagemap file)
   file_offset = virt_addr / getpagesize() * PAGEMAP_ENTRY;
   printf("Vaddr: 0x%lx, Page_size: %d, Entry_size: %d\n", virt_addr, getpagesize(), PAGEMAP_ENTRY);
   printf("Reading %s at 0x%llx\n", path_buf, (unsigned long long) file_offset);
   status = fseek(f, file_offset, SEEK_SET);
   if(status){
      perror("Failed to do fseek!");
      return -1;
   }
   errno = 0;
   read_val = 0;
   unsigned char c_buf[PAGEMAP_ENTRY];
   for(i=0; i < PAGEMAP_ENTRY; i++){
      c = getc(f);
      if(c==EOF){
         printf("\nReached end of the file\n");
         return 0;
      }
      if(is_bigendian())
           c_buf[i] = c;
      else
           c_buf[PAGEMAP_ENTRY - i - 1] = c;
      printf("[%d]0x%x ", i, c);
   }
   for(i=0; i < PAGEMAP_ENTRY; i++){
      //printf("%d ",c_buf[i]);
      read_val = (read_val << 8) + c_buf[i];
   }
   printf("\n");
   printf("Result: 0x%llx\n", (unsigned long long) read_val);
   //if(GET_BIT(read_val, 63))
   if(GET_BIT(read_val, 63))
      printf("PFN: 0x%llx\n",(unsigned long long) GET_PFN(read_val));
   else
      printf("Page not present\n");
   if(GET_BIT(read_val, 62))
      printf("Page swapped\n");
   fclose(f);
   return 0;
}
And now how you use it. It's very simple. Of course you need to compile it. Then you need to find out what mapping your target process does have. You can do that by reading /proc/pid/maps file. Fortunately that file is human readable.
When you know a valid virtual address, you can pass it to our tool to get actual value from pagemap, including physical frame number. Here is an example:

$ #let's find get virtual address of a page
$ cat /proc/self/maps 
00400000-0040b000 r-xp 00000000 08:02 1177367                            /bin/cat
0060a000-0060b000 r--p 0000a000 08:02 1177367                            /bin/cat
0060b000-0060c000 rw-p 0000b000 08:02 1177367                            /bin/cat
0223a000-0225b000 rw-p 00000000 00:00 0                                  [heap]
7fe7e15e1000-7fe7e1cc3000 r--p 00000000 08:02 1577390                    /usr/lib/locale/locale-archive
7fe7e1cc3000-7fe7e1e80000 r-xp 00000000 08:02 527324                     /lib/x86_64-linux-gnu/libc-2.17.so
7fe7e1e80000-7fe7e2080000 ---p 001bd000 08:02 527324                     /lib/x86_64-linux-gnu/libc-2.17.so
7fe7e2080000-7fe7e2084000 r--p 001bd000 08:02 527324                     /lib/x86_64-linux-gnu/libc-2.17.so
7fe7e2084000-7fe7e2086000 rw-p 001c1000 08:02 527324                     /lib/x86_64-linux-gnu/libc-2.17.so
7fe7e2086000-7fe7e208b000 rw-p 00000000 00:00 0 
7fe7e208b000-7fe7e20ae000 r-xp 00000000 08:02 527300                     /lib/x86_64-linux-gnu/ld-2.17.so
7fe7e228d000-7fe7e2290000 rw-p 00000000 00:00 0 
7fe7e22ab000-7fe7e22ad000 rw-p 00000000 00:00 0 
7fe7e22ad000-7fe7e22ae000 r--p 00022000 08:02 527300                     /lib/x86_64-linux-gnu/ld-2.17.so
7fe7e22ae000-7fe7e22b0000 rw-p 00023000 08:02 527300                     /lib/x86_64-linux-gnu/ld-2.17.so
7fffce6b6000-7fffce6d7000 rw-p 00000000 00:00 0                          [stack]
7fffce722000-7fffce724000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
$ #don't forget alsr, normally only /bin/cat will remain same
$ #so let's pick 0x00400000. Now we run our program. 
$ #First argument is pid, "self" is a legal option too, the second is virtual address
$ ./pagemap self 0x00400000
Reading /proc/self/pagemap at 0x2000
Result: 0a60000000008c445

We got  0x0a60000000008c445 as a result. There are some bits showing that the page is valid, along with the size of the page. You can reed more in Linux documentation: https://www.kernel.org/doc/Documentation/vm/pagemap.txt. Basically, the physical page number is 0x8c445.

Note, that in different kernel versions bits 56-60 have different meaning. In most current versions, they are forced to zero, however in kernel version 3.11.0 they represent page size.

UPDATE!
The original version of the code worked only on x86-64 machines. It used to read sizeof(unsigned int) amount of bytes from the binary file, but it must read 64 bits no matter what word size target machine has. There was another issue, on OpenRISC simulator the fread() function always failed, that's why I changed the code to use cget() instead, that way it works with all architectures I tested so far.

I plan to expand the functionality of my program, for example include support of range lookups in the /proc/*/pagemap files. If someone is interested in some functionality like that, leave a comment below.

January 8, 2013

Where to start: TPM on Linux

It is extremely hard to find any useful examples of using TPM. There is a lot of theoretical stuff, like discussion is it good or bad, but really few code examples that one can use to play with.

The last few days I was looking at the security issues with TPM, and found out that this topic is not addressed very well. Actually, I had hard times finding a good entry point to the world of trusted platforms. I thought, for someone things that I found might be useful, so here we go:

1. First go to the BIOS and enable your TPM there (if you don't have such option, probably you don't have the TPM).

2. Then, check if your TPM module is loaded:

$ lsmod | grep tpm

You can also check your dmesg, if your TPM is active, you should see something like that:
$ dmesg | grep tpm
[    9.132580] tpm_tis 00:09: 1.2 TPM (device-id 0x0, rev-id 78)
3. Here is a nice web-page showing how to use your TPM. Probably, you need to take ownership on the TPM to use it (tpm_takeownership comand).

4. But we want something really simple, right? I think, generating a random number is a nice thing to do first.

There is a package rng-tools, which allows us to use the TPM as a hardware random number generator, but for some reason it didn't work for me, you can find more details here. Then I found the other really nice page showing exactly what we want. Unfortunately, there is no description how to compile the code. So, here is what I did:

First install the trousers-dbg package to get required header files:
$ sudo apt-get install trousers-dbg
You need tspi library to compile the program. So, try compile to compile it as following:
 $ gcc -o getrand tpm-getrand.c -ltspi
If compiles without any errors run it as it was described in the original page:
$ ./getrand | hexdump -C
00000000  97 37 73 96 98 ad 07 08  f3 bc 14 90 c6 10 73 aa  |.7s...........s.|
00000010
Congratulations! These are your true-random bytes.