Câu trả lời:
Trên thực tế, trên hầu hết các nền tảng, nó chỉ bị lỗi, nhưng điều đó phụ thuộc vào kiến trúc phần cứng. Chắc chắn là không có gì đảm bảo rằng điều này là vô hại trừ khi bạn chạy lệnh với tư cách là người dùng không có quyền. Với người dùng không có đặc quyền, lệnh hoàn toàn vô hại vì bạn không thể mở /dev/mem
.
Khi bạn chạy một lệnh với quyền root, bạn phải biết bạn đang làm gì. Hạt nhân đôi khi sẽ ngăn bạn làm điều gì đó nguy hiểm, nhưng không phải lúc nào cũng vậy. /dev/mem
là một trong những điều nguy hiểm tiềm tàng nơi bạn thực sự cần phải biết những gì bạn đang làm.
Tôi sẽ tìm hiểu cách viết để /dev/mem
hoạt động trên Linux. Nguyên tắc chung sẽ giống nhau trên các Unice khác, nhưng những thứ như tùy chọn kernel hoàn toàn khác nhau.
Điều gì xảy ra khi một quá trình đọc hoặc ghi vào tệp thiết bị tùy thuộc vào kernel. Quyền truy cập vào tệp thiết bị chạy một số mã trong trình điều khiển xử lý tệp thiết bị này. Ví dụ, viết để /dev/mem
gọi hàm write_mem
trongdrivers/char/mem.c
. Hàm này có 4 đối số: cấu trúc dữ liệu đại diện cho tệp đang mở, con trỏ tới dữ liệu cần ghi, số byte cần ghi và vị trí hiện tại trong tệp.
Lưu ý rằng bạn chỉ đạt được điều đó nếu người gọi có quyền mở tệp ở vị trí đầu tiên. Tập tin thiết bị tuân theo quyền tập tin bình thường. Các quyền thông thường /dev/mem
được crw-r-----
sở hữu bởi root:kmem
, vì vậy nếu bạn cố mở nó để viết mà không cần root, bạn sẽ chỉ nhận được quyền của từ chối từ chối (EACCESS). Nhưng nếu bạn là root (hoặc nếu root đã thay đổi quyền của tệp này), thì việc mở sẽ diễn ra và sau đó bạn có thể thử ghi.
Mã trong write_mem
hàm tạo ra một số kiểm tra độ tỉnh táo, nhưng các kiểm tra này không đủ để bảo vệ chống lại mọi thứ xấu. Điều đầu tiên nó làm là chuyển đổi vị trí tệp hiện tại *ppos
thành một địa chỉ vật lý. Nếu điều đó không thành công (trong thực tế, vì bạn đang ở trên một nền tảng có địa chỉ vật lý 32 bit nhưng độ lệch tệp 64 bit và độ lệch tệp lớn hơn 2 ^ 32), thì lỗi ghi với EFBIG (tệp quá lớn). Kiểm tra tiếp theo là liệu phạm vi địa chỉ vật lý cần ghi có hợp lệ trên kiến trúc bộ xử lý cụ thể này hay không và có kết quả không thành công trong EFAULT (địa chỉ xấu).
Tiếp theo, trên Sparc và m68k, bất kỳ phần nào của bài viết trong trang vật lý đầu tiên đều âm thầm bỏ qua.
Bây giờ chúng ta đã đạt đến vòng lặp chính lặp lại dữ liệu trong các khối có thể vừa trong một trang MMU .
/dev/mem
truy cập bộ nhớ vật lý, không phải bộ nhớ ảo, nhưng bộ xử lý hướng dẫn tải và lưu trữ dữ liệu trong bộ nhớ sử dụng địa chỉ ảo, do đó mã cần sắp xếp để ánh xạ bộ nhớ vật lý tại một số địa chỉ ảo. Trên Linux, tùy thuộc vào kiến trúc bộ xử lý và cấu hình kernel, ánh xạ này tồn tại vĩnh viễn hoặc phải được thực hiện nhanh chóng; đó là công việc của xlate_dev_mem_ptr
(và unxlate_dev_mem_ptr
hoàn tác bất cứ điều gì xlate_dev_mem_ptr
). Sau đó, hàm copy_from_user
đọc từ bộ đệm được truyền đếnwrite
gọi hệ thống và chỉ ghi vào địa chỉ ảo nơi bộ nhớ vật lý hiện đang được ánh xạ. Mã này phát ra các hướng dẫn lưu trữ bộ nhớ thông thường và điều này có nghĩa là tùy thuộc vào phần cứng.
Trước khi tôi thảo luận về việc ghi vào địa chỉ thực, tôi sẽ thảo luận về kiểm tra xảy ra trước khi viết. Bên trong vòng lặp, các page_is_allowed
khối chức năng truy cập vào một số địa chỉ nhất định nếu tùy chọn cấu hình kernel CONFIG_STRICT_DEVMEM
được bật (đó là trường hợp theo mặc định): chỉ có thể truy cập các địa chỉ được phép devmem_is_allowed
thông qua /dev/mem
, đối với các địa chỉ khác thì không thể ghi bằng EPERM (không được phép thao tác). Mô tả của tùy chọn này nêu:
Nếu tùy chọn này được bật và IO_STRICT_DEVMEM = n, tệp / dev / mem chỉ cho phép không gian người dùng truy cập vào không gian PCI và mã vùng và dữ liệu BIOS. Điều này là đủ cho dosemu và X và tất cả người dùng phổ biến của / dev / mem.
Đây là mô tả rất x86 trung tâm. Trên thực tế, nói chung hơn, CONFIG_STRICT_DEVMEM
chặn truy cập vào các địa chỉ bộ nhớ vật lý ánh xạ tới RAM, nhưng cho phép truy cập vào các địa chỉ không ánh xạ tới RAM. Chi tiết về phạm vi địa chỉ vật lý nào được phép tùy thuộc vào kiến trúc bộ xử lý, nhưng tất cả chúng đều loại trừ RAM nơi lưu trữ dữ liệu của hạt nhân và quy trình đất của người dùng. Tùy chọn bổ sung CONFIG_IO_STRICT_DEVMEM
(bị vô hiệu hóa kể từ Ubuntu 18.04) chặn truy cập vào các địa chỉ vật lý được yêu cầu bởi trình điều khiển.
Bộ nhớ vật lý địa chỉ ánh xạ tới RAM . Vì vậy, có địa chỉ bộ nhớ vật lý không ánh xạ tới RAM? Đúng. Đó là cuộc thảo luận mà tôi đã hứa ở trên về ý nghĩa của việc viết vào một địa chỉ.
Một hướng dẫn lưu trữ bộ nhớ không nhất thiết phải ghi vào RAM. Bộ xử lý phân tách địa chỉ và quyết định thiết bị ngoại vi nào sẽ gửi cửa hàng tới. (Khi tôi nói bộ xử lý của bộ xử lý, tôi bao gồm các bộ điều khiển ngoại vi có thể không đến từ cùng một nhà sản xuất.) RAM chỉ là một trong những thiết bị ngoại vi đó. Làm thế nào công văn được thực hiện rất phụ thuộc vào kiến trúc bộ xử lý, nhưng các nguyên tắc cơ bản ít nhiều giống nhau trên tất cả các kiến trúc. Bộ xử lý về cơ bản phân tách các bit cao hơn của địa chỉ và tìm kiếm chúng trong một số bảng được điền dựa trên thông tin được mã hóa cứng, thông tin thu được bằng cách thăm dò một số xe buýt và thông tin được cấu hình bởi phần mềm. Rất nhiều bộ đệm và bộ đệm có thể được tham gia, nhưng tóm lại, sau khi phân tách này,xe buýt và sau đó đến các thiết bị ngoại vi để đối phó với nó. (Hoặc kết quả của việc tra cứu bảng có thể là không có thiết bị ngoại vi tại địa chỉ này, trong trường hợp đó, bộ xử lý sẽ chuyển sang trạng thái bẫy trong đó nó thực thi một số mã trong kernel thường dẫn đến SIGBUS cho quá trình gọi.)
Cửa hàng tới một địa chỉ ánh xạ tới RAM không phải là bất kỳ thứ gì khác ngoài ghi đè lên giá trị được lưu trữ trước đó tại địa chỉ này, với lời hứa rằng tải sau đó tại cùng địa chỉ sẽ trả lại giá trị được lưu trữ cuối cùng. Nhưng ngay cả RAM cũng có một vài địa chỉ không hoạt động theo cách này: nó có một vài thanh ghi có thể kiểm soát những thứ như tốc độ làm mới và điện áp.
Nói chung, việc đọc hoặc ghi vào thanh ghi phần cứng sẽ làm bất cứ điều gì phần cứng được lập trình để làm. Hầu hết các truy cập vào phần cứng đều hoạt động theo cách này: phần mềm (thông thường là mã hạt nhân) truy cập vào một địa chỉ vật lý nhất định, điều này đến được bus kết nối bộ xử lý với thiết bị ngoại vi và thiết bị ngoại vi thực hiện công việc của nó. Một số bộ xử lý (cụ thể là x86) cũng có các hướng dẫn CPU riêng biệt gây ra đọc / ghi vào các thiết bị ngoại vi khác với tải và lưu trữ bộ nhớ, nhưng ngay cả trên x86, nhiều thiết bị ngoại vi được truyền qua tải / lưu trữ.
Lệnh dd if=/dev/urandom of=/dev/mem
ghi dữ liệu ngẫu nhiên vào bất kỳ thiết bị ngoại vi nào được ánh xạ tại địa chỉ 0 (và các địa chỉ tiếp theo, miễn là ghi thành công). Trong thực tế, tôi hy vọng rằng trên nhiều kiến trúc, địa chỉ vật lý 0 không có bất kỳ ngoại vi nào được ánh xạ tới nó, hoặc có RAM, và do đó lần thử ghi đầu tiên đã thất bại. Nhưng nếu có một thiết bị ngoại vi được ánh xạ ở địa chỉ 0 hoặc nếu bạn thay đổi lệnh để ghi thành một địa chỉ khác, bạn sẽ kích hoạt một cái gì đó không thể đoán trước trong thiết bị ngoại vi. Với dữ liệu ngẫu nhiên khi tăng địa chỉ, không có khả năng làm điều gì đó thú vị, nhưng về nguyên tắc, nó có thể tắt máy tính (có thể có một địa chỉ thực tế này), ghi đè lên một số cài đặt BIOS khiến nó không thể khởi động hoặc thậm chí không chạm được lỗi ngoại vi theo cách làm hỏng nó.
alias Russian_roulette='dd if=/dev/urandom of=/dev/mem seek=$((4096*RANDOM+4096*32768*RANDOM))'
CONFIG_STRICT_DEVMEM
được bật.
Trên mỗi trang hướng dẫn mem (4) :
/ dev / mem là một tệp thiết bị ký tự là hình ảnh của bộ nhớ chính của máy tính. Nó có thể được sử dụng, ví dụ, để kiểm tra (và thậm chí vá) hệ thống.
Vì vậy, về mặt lý thuyết, dd if=/dev/urandom of=/dev/mem
nên ghi đè lên toàn bộ không gian địa chỉ của bộ nhớ vật lý mà bạn đã cài đặt và vì kernel và các chương trình khác chạy từ bộ nhớ nên điều này sẽ làm sập hệ thống một cách hiệu quả. Trong thực tế, có giới hạn. Từ cùng một trang người đàn ông:
Kể từ Linux 2.6.26 và tùy thuộc vào kiến trúc, tùy chọn cấu hình hạt nhân CONFIG_STRICT_DEVMEM giới hạn các khu vực có thể được truy cập thông qua tệp này.
Thử điều này trên máy ảo Ubuntu 18.04, nó trả về lỗi dd: writing to '/dev/mem': Operation not permitted
ngay cả khi có sudo
và mặc dù có quyền cho root crw-r-----
. Từ Ubuntu Wiki :
/ dev / mem bảo vệ
Một số ứng dụng (Xorg) cần truy cập trực tiếp vào bộ nhớ vật lý từ không gian người dùng. Các tập tin / dev / mem đặc biệt tồn tại để cung cấp quyền truy cập này. Trước đây, có thể xem và thay đổi bộ nhớ kernel từ tệp này nếu kẻ tấn công có quyền truy cập root. Tùy chọn kernel CONFIG_STRICT_DEVMEM được giới thiệu để chặn truy cập bộ nhớ không phải thiết bị (tên ban đầu là CONFIG_NONPROMISC_DEVMEM).
Về mặt kỹ thuật, không có gì là không an toàn (vì nó sẽ làm sập hệ thống) và nếu tùy chọn kernel CONFIG_STRICT_DEVMEM
bị vô hiệu hóa thì đó là một lỗ hổng bảo mật, nhưng từ đó tôi thấy lệnh này sẽ không chạy nếu tùy chọn đó được bật. Theo trùng lặp chéo trang , khởi động lại sẽ khắc phục mọi sự cố với nó, nhưng tất nhiên dữ liệu trong RAM tại thời điểm đó sẽ bị mất và không bị xóa vào đĩa (nếu có).
Có một phương pháp được đề xuất trên bản sao được liên kết trước đó bằng cách sử dụng busybox devmem
vì vậy nếu bạn quyết tâm làm hỏng RAM, có thể có một cách sau tất cả.
CONFIG_STRICT_DEVMEM
, bạn có thể truy cập các vùng bộ nhớ nơi ánh xạ ngoại vi, đó là toàn bộ điểm có /dev/mem
. Nếu bạn viết công cụ ngẫu nhiên cho các thiết bị ngoại vi, bất cứ điều gì có thể xảy ra. Bạn không được phép hoạt động trên mạng nếu bạn cố truy cập vào một địa chỉ không được ánh xạ và lệnh bắt đầu tại địa chỉ 0. Việc địa chỉ 0 có ánh xạ tới thứ gì đó xấu hay không phụ thuộc vào kiến trúc phần cứng. Đối với tất cả tôi biết nó có thể không bao giờ ánh xạ tới bất cứ thứ gì trên PC, nhưng nói chung nó không an toàn.
head -c 1024 </dev/mem | od -tx1
), nhưng tôi không biết liệu chúng có được sử dụng khi bộ xử lý không ở chế độ thực (chế độ 8088). Tôi không nghĩ rằng chúng có thể được sử dụng ở chế độ 64 bit: sau tất cả, các vectơ ngắt 8088 chỉ có 32 bit cho địa chỉ. Và bằng cách này có thể truy cập được với CONFIG_STRICT_DEVMEM
tập hợp, vì vậy tôi đoán Linux không sử dụng nó.