Linux 監測內存訪問的方法彙總【轉】

轉自:https://blog.csdn.net/dianzichongchong/article/details/120133833

1. hw break point
它是 linux kernel 自帶的,監測一段內存訪問信息的方法。它可以檢測一段內存是否被讀或寫。

具體可見 linux 自帶例子:linux/samples/hw_breakpoint/data_breakpoint.c

static int __init hw_break_module_init(void)
{
int ret;
struct perf_event_attr attr;

hw_breakpoint_init(&attr);
attr.bp_addr = kallsyms_lookup_name(ksym_name);
attr.bp_len = HW_BREAKPOINT_LEN_4;
attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;

sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler, NULL);
if (IS_ERR((void __force *)sample_hbp)) {
ret = PTR_ERR((void __force *)sample_hbp);
goto fail;
}

printk(KERN_INFO "HW Breakpoint for %s write installed\n", ksym_name);

return 0;

fail:
printk(KERN_INFO "Breakpoint registration failed\n");

return ret;
}

static void __exit hw_break_module_exit(void)
{
unregister_wide_hw_breakpoint(sample_hbp);
printk(KERN_INFO "HW Breakpoint for %s write uninstalled\n", ksym_name);
}
register_wide_hw_breakpoint 用來向所有 CPU 註冊監測的地址和大小以及訪問類型,它們存放在 perf_event_attr 中。

地址是 kernel 虛擬地址,支持監測內存的大小和訪問類型詳見:linux/include/uapi/linux/hw_breakpoint.h

enum {
HW_BREAKPOINT_LEN_1 = 1,
HW_BREAKPOINT_LEN_2 = 2,
HW_BREAKPOINT_LEN_4 = 4,
HW_BREAKPOINT_LEN_8 = 8,
};

enum {
HW_BREAKPOINT_EMPTY = 0,
HW_BREAKPOINT_R = 1,
HW_BREAKPOINT_W = 2,
HW_BREAKPOINT_RW = HW_BREAKPOINT_R | HW_BREAKPOINT_W,
HW_BREAKPOINT_X = 4,
HW_BREAKPOINT_INVALID = HW_BREAKPOINT_RW | HW_BREAKPOINT_X,
};
當不需要監測該段內存時,調用 unregister_wide_hw_breakpoint 即可。

當監測到指定內存被訪問時,會在 dmesg 中打印如下信息:

[ 2295.708755] Call trace:
[ 2295.711234] dump_backtrace+0x0/0x1a0
[ 2295.714929] show_stack+0x14/0x20
[ 2295.718279] dump_stack+0x94/0xb4
[ 2295.721633] sample_hbp_handler+0xc/0x18 [hbp_drv]
[ 2295.725853] __perf_event_overflow+0x54/0x100
[ 2295.730248] perf_swevent_overflow+0x54/0x88
[ 2295.734556] perf_swevent_event+0xc0/0xc8
2. mprotect
mpotect 是 linux 的 API,用來修改一塊內存的訪問權限。可以用於監測內存的非法訪問。

可見如下例子:

#include <sys/mman.h>
#include <unistd.h>

#include <stdint.h>
#include <stdio.h>

static int protect_memory(void *addr, uint64_t len, uint32_t type) {
int ret = 0;

uint64_t start = (uint64_t) addr;
uint64_t end = start + len;

uint64_t page_size = getpagesize();

start = start- start % page_size;

while (start < end) {
ret = mprotect((void *) start, page_size, type);

printf("---0x%llx---0x%llx, type: %d, ret: %d\n",
start, page_size, type, ret);
start += page_size;
}

return ret;
}
調用 mprotect 傳入虛擬地址、大小(不能超過PAGE_SIZE),以及新的屬性。屬性只能取如下4個,可以用 | 複合多個:

1)PROT_READ:表示內存段內的內容可讀;

2)PROT_WRITE:表示內存段內的內容可寫;

3)PROT_EXEC:表示內存段中的內容可執行;

4)PROT_NONE:表示內存段中的內容根本沒法訪問

需要監測內存被非法寫入時,將內存屬性設爲只讀,然後等待 log 即可,如下所示:

int main() {
int buffer[20] = { 0 };

protect_memory(buffer, 20 * sizeof(int), PROT_READ);
buffer[10] = 1;

return 0;
}
在內存被試圖寫入時,可以在 kernel log 中看到如下信息:

signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0xffb94228
Cause: stack pointer is not in a rw map; likely due to stack overflow.
r0 00000000 r1 00001000 r2 00000001 r3 00000000
r4 00001000 r5 00000000 r6 ffb94000 r7 00000000
r8 ffb94288 r9 eed55124 r10 00000000 r11 00000001
ip 00000000 sp ffb94228 lr 00a4d477 pc 00a4d476
backtrace:
#00 pc 00001476 /system/bin/mprotect_demo (main+130) (BuildId: ...)
參考:C語言之 mprotect - Max_hhg - 博客園
————————————————
版權聲明:本文爲CSDN博主「孤獨的小魚&lt;---<」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/dianzichongchong/article/details/120133833

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章