2015-06-18 14 views
7

Làm thế nào để tôi lừa Linux vào suy nghĩ đọc/ghi bộ nhớ đã thành công? Tôi đang viết một thư viện C++ sao cho tất cả các lần đọc/ghi được chuyển hướng và xử lý một cách minh bạch đến người dùng cuối. Bất cứ khi nào một biến được viết hoặc đọc từ, thư viện sẽ cần phải nắm bắt yêu cầu đó và bắn nó ra một mô phỏng phần cứng sẽ xử lý dữ liệu từ đó.Làm thế nào để bẫy bộ nhớ đọc và viết bằng sigsegv?

Lưu ý rằng thư viện của tôi là nền tảng phụ thuộc vào:

Linux ubuntu 3.16.0-39-generiC# 53 ~ 14.04.1-Ubuntu SMP x86_64 GNU/Linux

gcc (Ubuntu 4.8. 2-19ubuntu1) 4.8.2

hiện cách tiếp cận: catch SIGSEGV và increment REG_RIP

cur My phương pháp thuê bao gồm việc nhận vùng nhớ bằng cách sử dụng mmap() và tắt quyền truy cập bằng cách sử dụng mprotect(). Tôi có một trình xử lý SIGSEGV để lấy thông tin chứa địa chỉ bộ nhớ, xuất đọc/ghi ở nơi khác, sau đó tăng ngữ cảnh REG_RIP.

void handle_sigsegv(int code, siginfo_t *info, void *ctx) 
{ 
    void *addr = info->si_addr; 
    ucontext_t *u = (ucontext_t *)ctx; 
    int err = u->uc_mcontext.gregs[REG_ERR]; 
    bool is_write = (err & 0x2); 
    // send data read/write to simulation... 
    // then continue execution of program by incrementing RIP 
    u->uc_mcontext.gregs[REG_RIP] += 6; 
} 

này hoạt động đối với trường hợp rất đơn giản, chẳng hạn như:

int *num_ptr = (int *)nullptr; 
*num_ptr = 10;       // write segfault 

Nhưng đối với bất cứ điều gì, ngay cả một chút phức tạp hơn, tôi nhận được một SIGABRT:

30729 hướng dẫn bất hợp pháp (core dumped) ./$target

Sử dụng mprotect() trong Trình xử lý SIGSEGV

Nếu tôi không tăng REG_RIP, handle_sigsegv() sẽ được gọi lại nhiều lần bởi hạt nhân cho đến khi vùng bộ nhớ khả dụng để đọc hoặc ghi. Tôi có thể chạy mprotect() cho địa chỉ cụ thể, nhưng có nhiều hãy cẩn thận:

  • Tiếp theo truy cập bộ nhớ sẽ không kích hoạt SIGSEGV do khu vực bộ nhớ bây giờ có khả năng PROT_WRITE. Tôi đã cố gắng tạo một chuỗi liên tục đánh dấu khu vực là PROT_NONE, nhưng điều đó không bỏ qua điểm tiếp theo:
  • mprotect() sẽ, vào cuối ngày, thực hiện đọc hoặc ghi vào bộ nhớ, làm mất hiệu lực trường hợp sử dụng của thư viện của tôi.

Viết một trình điều khiển thiết bị

Tôi cũng đã cố gắng để viết một mô-đun thiết bị như vậy mà thư viện có thể gọi mmap() trên thiết bị char, nơi người lái xe sẽ xử lý đọc và viết từ đó. Điều này có ý nghĩa về lý thuyết, nhưng tôi đã không thể (hoặc không có kiến ​​thức để) nắm bắt mọi tải/lưu trữ các vấn đề xử lý cho thiết bị. Tôi đã cố gắng ghi đè lên ánh xạ vm_operations_struct và/hoặc cấu trúc address_space_operations của inode, nhưng điều đó sẽ chỉ gọi số lần đọc/ghi khi trang bị lỗi hoặc trang bị xóa vào cửa hàng sao lưu.

Có lẽ tôi có thể sử dụng mmap()mprotect(), như đã giải thích ở trên, trên thiết bị ghi dữ liệu hư không (tương tự /dev/null), sau đó có quy trình nhận dạng đọc/ghi và định tuyến dữ liệu từ đó (?).

Sử dụng syscall() và cung cấp một chức năng lắp ráp phục chế

Sau đây được kéo ra khỏi dự án segvcatch có thể chuyển đổi segfaults vào trường hợp ngoại lệ.

#define RESTORE(name, syscall) RESTORE2(name, syscall) 
#define RESTORE2(name, syscall)\ 
asm(\ 
    ".text\n"\ 
    ".byte 0\n"\ 
    ".align 16\n"\ 
    "__" #name ":\n"\ 
    " movq $" #syscall ", %rax\n"\ 
    " syscall\n"\ 
); 
RESTORE(restore_rt, __NR_rt_sigreturn) 
void restore_rt(void) asm("__restore_rt") __attribute__ 
((visibility("hidden"))); 

extern "C" { 
    struct kernel_sigaction { 
     void (*k_sa_sigaction)(int, siginfo_t *, void *); 
     unsigned long k_sa_flags; 
     void (*k_sa_restorer)(void); 
     sigset_t k_sa_mask; 
    }; 
} 

// then within main ... 
struct kernel_sigaction act; 
act.k_sa_sigaction = handle_sigegv; 
sigemptyset(&act.k_sa_mask); 
act.k_sa_flags = SA_SIGINFO|0x4000000; 
act.k_sa_restorer = restore_rt; 
syscall(SYS_rt_sigaction, SIGSEGV, &act, NULL, _NSIG/8); 

Nhưng điều này kết thúc hoạt động không khác với cấu hình thông thường sigaction(). Nếu tôi không đặt chức năng khôi phục, trình xử lý tín hiệu không được gọi nhiều hơn một lần, ngay cả khi vùng bộ nhớ vẫn không khả dụng. Có lẽ có một số trickery khác tôi có thể làm với tín hiệu hạt nhân ở đây.


Một lần nữa, toàn bộ mục tiêu của thư viện là xử lý minh bạch lần đọc và ghi vào bộ nhớ. Có lẽ có cách làm tốt hơn nhiều, có thể với ptrace() hoặc thậm chí cập nhật mã hạt nhân tạo tín hiệu segfault, nhưng phần quan trọng là mã của người dùng cuối không yêu cầu thay đổi. Tôi đã nhìn thấy các ví dụ sử dụng setjmp()longjmp() để tiếp tục sau khi phân đoạn, nhưng điều đó sẽ yêu cầu thêm các cuộc gọi đó vào mọi truy cập bộ nhớ. Cũng vậy với việc chuyển đổi một segfault thành try/catch.


segvcatch project

+0

Tại sao '+ = 6'? Thứ hai, một đọc sẽ (nói) thiết lập một giá trị đăng ký. Tôi không thấy bất cứ điều gì về điều đó trong mã của bạn, bạn đang làm điều đó? Bạn nói rằng bạn 'gửi dữ liệu đọc/ghi để mô phỏng', nhưng không có gì về việc lấy lại dữ liệu, đó là dự định? – Yakk

+0

'+ = 6' có vẻ là chiều rộng lệnh cho x86_64, ít nhất là đối với trường hợp đơn giản nhất. Thật không may tôi không thể cung cấp nguồn kiến ​​thức này, nhưng nó dường như hoạt động. Khi tôi nói 'gửi dữ liệu đọc/ghi ...', tôi có nghĩa là tôi đưa ra một hướng dẫn tùy chỉnh (với dữ liệu đính kèm) để mô phỏng phần cứng của tôi (tức là tải/lưu trữ). Bạn có nói rằng tôi cần phải xác định đăng ký bộ xử lý cần dữ liệu và tải nó lên? Điều đó sẽ có ý nghĩa ... – Will

+0

Bọc phần cứng của bạn trong một lớp trừu tượng và chỉ thực hiện cuộc gọi đến lớp trừu tượng. Là phần thưởng thêm khi bạn chuyển phần mềm sang phần cứng mới, bạn chỉ cần cập nhật lớp trừu tượng. (Và đối phó với bất kỳ lỗi trong mã của bạn tiếp xúc bởi thời gian mới và có lẽ là một trình biên dịch mới) – user4581301

Trả lời

3

Bạn có thể sử dụng mprotect và tránh những vấn đề đầu tiên bạn lưu ý bởi cũng có handler SIGSEGV đặt cờ T trong cờ đăng ký. Sau đó, bạn thêm một trình xử lý SIGTRAP để khôi phục bộ nhớ được bảo vệ và xóa cờ T.

Cờ T khiến bộ xử lý thực hiện một bước, vì vậy khi trình xử lý SEGV trả về nó sẽ thực thi lệnh đơn đó, và sau đó ngay lập tức TRAP.

Điều này vẫn khiến bạn gặp vấn đề thứ hai - hướng dẫn đọc/ghi sẽ thực sự xảy ra. Bạn có thể giải quyết vấn đề đó bằng cách sửa đổi cẩn thận bộ nhớ trước và/hoặc sau lệnh trong hai bộ xử lý tín hiệu ...

+0

Cảm ơn thông tin về cài đặt cờ bẫy. Tôi nên chú ý hơn đến câu hỏi này [link] (http://stackoverflow.com/questions/21068714/trap-all-accesses-to-an-address-range-linux). Có lẽ tôi có thể sử dụng chuỗi tín hiệu SIGSEGV + SIGTRAP với một vùng bộ nhớ nhỏ hoạt động như một sự trao đổi sắp xếp để đạt được chức năng dự định của tôi. – Will

+0

Tôi đã có thể xây dựng bộ giả lập "bộ nhớ cache bộ xử lý" hoạt động ở mức độ chi tiết khối bằng SIGSEGV, SIGTRAP và mprotect. Thật không may bẫy mỗi đọc/ghi tại một địa chỉ gây ra những hạn chế hiệu suất rất lớn, nhưng việc thực hiện khái niệm là hợp lệ. Cảm ơn một lần nữa, và xin lỗi vì sự chấp nhận chậm trễ. – Will

Các vấn đề liên quan