2010-03-13 27 views
12

Tôi muốn tìm địa chỉ thực của một biến được xác định trong quy trình không gian người dùng? Có cách nào để làm điều đó bằng cách sử dụng đặc quyền root?Làm thế nào để tìm địa chỉ vật lý của một biến từ không gian người dùng trong Linux?

+0

Có lẽ với/dev/mem? – user2284570

+1

Một số thông tin có liên quan trong http://stackoverflow.com/questions/5748492/is-there-any-api-for-determining-the-physical-address-from-virtual-address-in-li –

Trả lời

2

Trước tiên, tại sao bạn muốn thực hiện việc này? Mục đích của các hệ thống VM hiện đại là loại bỏ trình lập trình ứng dụng khỏi sự phức tạp của bố cục bộ nhớ vật lý. Gving chúng mỗi không gian địa chỉ đồng phục của riêng mình để làm cho cuộc sống của họ dễ dàng hơn.

Nếu bạn muốn thực hiện việc này, bạn gần như chắc chắn sẽ cần sử dụng mô-đun hạt nhân. Nhận địa chỉ ảo của biến theo cách thông thường, sử dụng địa chỉ này để lập chỉ mục vào các bảng trang quy trình và đọc giá trị bạn tìm thấy (địa chỉ vật lý của khung). Sau đó, thêm trang bù đắp để có được địa chỉ vật lý hoàn chỉnh. Lưu ý rằng bạn sẽ không thể sử dụng địa chỉ này khi bật phân trang.

(Nếu may mắn bạn có thể để có được địa chỉ của khung của một vùng VM từ hệ thống tập tin/proc và do đó wouldnt yêu cầu viết một mô-đun hạt nhân của mình.)

+4

... và trừ khi bạn khóa trang vào bộ nhớ, địa chỉ vật lý đó có thể thay đổi bất cứ lúc nào. – caf

+1

Bạn không cần viết mô-đun hạt nhân: như các ví dụ khác giải thích, điều này đã được hiển thị thông qua '/ proc/$ pid/sơ đồ trang '. – poolie

+0

Có thể thú vị trong các kiến ​​trúc NUMA để biết địa chỉ vật lý của các biến số – horro

-5

(chỉnh sửa: Nếu theo "địa chỉ vật lý ", bạn có nghĩa là cấp độ" trong đó RAM là các bit của tôi được lưu trữ ", sau đó câu trả lời sau là không phù hợp.)

Bạn không cần quyền root để thực hiện việc này. Những gì bạn cần thay vào đó là một trình gỡ lỗi. Và ở đây chúng tôi đi (sử dụng hệ thống Linux trên x86_64):

Trước tiên, chúng tôi cần một chương trình nhỏ để chơi cùng. Điều này truy cập một biến toàn cầu và in nó hai lần liên tiếp. Nó có hai biến toàn cầu, mà chúng ta tìm thấy trong bộ nhớ sau này.

 
#include <stdio.h> 

int a, b = 0; 

int main(void) 
{ 
    printf("a: "); 
    if (fscanf("%d", &a) < 1) 
     return 0; 

    printf("a = %d\n", myglobal); 

    printf("b: "); 
    if (fscanf("%d", &b) < 1) 
     return 0; 

    printf("a = %d, b = %d\n", a, b); 

    return 0; 
} 

Bước 1: Biên dịch chương trình và xóa tất cả thông tin gỡ lỗi khỏi chương trình, vì vậy chúng tôi không nhận được bất kỳ gợi ý nào từ trình gỡ lỗi mà chúng tôi không gặp phải trong thực tế.

 
$ gcc -s -W -Wall -Os -o ab ab.c 

Bước 2: Chạy chương trình và nhập một trong hai số.

 
$ ./ab 
a: 123 
a = 123 
b: _ 

Bước 3: Tìm quy trình.

 
$ ps aux | grep ab 
roland 21601 0.0 0.0 3648 456 pts/11 S+ 15:17 0:00 ./ab 
roland 21665 0.0 0.0 5132 672 pts/12 S+ 15:18 0:00 grep ab 

Bước 4: Đính kèm trình gỡ lỗi cho quy trình (21601).

 
$ gdb 
... 
(gdb) attach 21601 
... 
(gdb) where 
#0 0x00007fdecfdd2970 in read() from /lib/libc.so.6 
#1 0x00007fdecfd80b40 in _IO_file_underflow() from /lib/libc.so.6 
#2 0x00007fdecfd8230e in _IO_default_uflow() from /lib/libc.so.6 
#3 0x00007fdecfd66903 in _IO_vfscanf() from /lib/libc.so.6 
#4 0x00007fdecfd7245c in scanf() from /lib/libc.so.6 
#5 0x0000000000400570 in ??() 
#6 0x00007fdecfd2f1a6 in __libc_start_main() from /lib/libc.so.6 
#7 0x0000000000400459 in ??() 
#8 0x00007fffd827da48 in ??() 
#9 0x000000000000001c in ??() 
#10 0x0000000000000001 in ??() 
#11 0x00007fffd827f9a2 in ??() 
#12 0x0000000000000000 in ??() 

Khung thú vị là số 5, vì nó là giữa một số mã gọi main chức năng và scanf chức năng, vì vậy nó phải là chức năng main của chúng tôi. Tiếp tục phiên gỡ lỗi:

 
(gdb) frame 5 
... 
(gdb) disassemble $pc $pc+50 
... 
0x0000000000400570 :  test %eax,%eax 
0x0000000000400572 :  jle 0x40058c <[email protected]+372> 
0x0000000000400574 :  mov 0x2003fe(%rip),%edx  # 0x600978 <[email protected]+2098528> 
0x000000000040057a :  mov 0x2003fc(%rip),%esi  # 0x60097c <[email protected]+2098532> 
0x0000000000400580 :  mov $0x40068f,%edi 
0x0000000000400585 :  xor %eax,%eax 
0x0000000000400587 :  callq 0x4003f8 <[email protected]> 
... 

Bây giờ chúng ta biết rằng chức năng printf sẽ nhận được ba thông số, và hai trong số đó là chỉ có bốn byte ra xa nhau. Đó là dấu hiệu tốt cho thấy hai biến này là các biến số ab. Vì vậy, địa chỉ của a là 0x600978 hoặc 0x60097c. Hãy tìm hiểu bằng cách cố gắng:

 
(gdb) x/w 0x60097c   
0x60097c <[email protected]+2098532>: 0x0000007b 
(gdb) x/w 0x600978 
0x600978 <[email protected]+2098528>: 0x00000000 

Vì vậy a, biến được đọc trong lần đầu tiên, là tại địa chỉ 0x60097c (vì 0x0000007B là đại diện thập lục phân cho 123, mà chúng tôi đã nhập), và b là 0x600978.

Vẫn còn trong trình gỡ lỗi, chúng tôi có thể sửa đổi biến a ngay bây giờ và sau đó tiếp tục chương trình.

 
(gdb) set *(int *)0x60097c = 1234567 
(gdb) continue 

Quay trở lại chương trình yêu cầu chúng tôi nhập vào hai số:

 
$ ./ab 
a: 123 
a = 123 
b: 5 
a = 1234567, b = 5 
$ 
+11

Điều này cho bạn địa chỉ ảo của bạn, chứ không phải là vật lý. –

16

Như một phần trả lời trước đó, các chương trình bình thường không cần phải lo lắng về các địa chỉ vật lý khi họ chạy trong một không gian địa chỉ ảo với tất cả các tiện nghi của nó. Hơn nữa, không phải mọi địa chỉ ảo đều có địa chỉ vật lý, có thể thuộc về các tệp được ánh xạ hoặc các trang được hoán đổi. Tuy nhiên, đôi khi nó có thể là thú vị để xem bản đồ này, ngay cả trong userland.

Vì mục đích này, hạt nhân Linux phơi bày ánh xạ tới vùng người dùng thông qua một tập hợp các tệp trong /proc. Các tài liệu có thể được tìm thấy here. Tóm tắt ngắn:

  1. /proc/$pid/maps cung cấp danh sách ánh xạ địa chỉ ảo cùng với thông tin bổ sung, chẳng hạn như tệp tương ứng cho tệp được ánh xạ.
  2. /proc/$pid/pagemap cung cấp thêm thông tin về từng trang được ánh xạ, bao gồm cả địa chỉ thực nếu nó tồn tại.

This website cung cấp chương trình C loại bỏ ánh xạ của tất cả các quy trình đang chạy bằng giao diện này và giải thích về nó.

+0

Ví dụ tối thiểu với các thử nghiệm: https://stackoverflow.com/questions/17021214/how-to-decode-proc-pid-pagemap-entries-in-linux/45126141#45126141 –

14
#include "stdio.h" 
#include "unistd.h" 
#include "inttypes.h" 

uintptr_t vtop(uintptr_t vaddr) { 
    FILE *pagemap; 
    intptr_t paddr = 0; 
    int offset = (vaddr/sysconf(_SC_PAGESIZE)) * sizeof(uint64_t); 
    uint64_t e; 

    // https://www.kernel.org/doc/Documentation/vm/pagemap.txt 
    if ((pagemap = fopen("/proc/self/pagemap", "r"))) { 
     if (lseek(fileno(pagemap), offset, SEEK_SET) == offset) { 
      if (fread(&e, sizeof(uint64_t), 1, pagemap)) { 
       if (e & (1ULL << 63)) { // page present ? 
        paddr = e & ((1ULL << 54) - 1); // pfn mask 
        paddr = paddr * sysconf(_SC_PAGESIZE); 
        // add offset within page 
        paddr = paddr | (vaddr & (sysconf(_SC_PAGESIZE) - 1)); 
       } 
      } 
     } 
     fclose(pagemap); 
    } 

    return paddr; 
} 
Các vấn đề liên quan