【45】busybox devmem 访问所有地址(内存+MMIO)

  调试amd_iommu时想看一下iommu的device table中存的数据,通过busybox 访问amd_iommu的BAR的0地址,然后可以获取device table的地址(0x6800 01FF),但是device table是个全局的数组,其地址是内存中预留的,这个地址devmem是没有权限访问的,正如图中那样,访问该地址话,busybox提示operation not permitted。那么怎么可能访问到内存的地址呢?
在这里插入图片描述
  查看config文件中,CONFIG_STRICT_DEVMEM为,限制用户态通过dev/mem访问所有memroy地址。
If this option is disabled, you allow userspace (root) access to all of memory, including kernel and userspace memory. Accidental access to this is obviously disastrous, but specific access can be used by people debugging the kernel. Note that with PAT support enabled, even in this case there are restrictions on /dev/mem use due to the cache aliasing requirements.

If this option is switched on, the /dev/mem file only allows userspace access to PCI space and the BIOS code and data regions. This is sufficient for dosemu and X and all common users of /dev/mem.

If in doubt, say Y.
在这里插入图片描述
可以看到devmem访问时被限制的,需要重新编译内核打开限制
make menuconfig,然后输入/ 搜索STRICT_DEVMEM,可以看到在kernel hacking有对应配置
在这里插入图片描述
进入 kernel hacking把
[ ] Filter access to /dev/mem
[ ] Filter I/O access to /dev/mem (NEW)
前面的*点掉(上下键选中,然后敲空格)
在这里插入图片描述
vim .config查看CONFIG_STRICT_DEVMEM是没有set的,剩下的就是编译内核了
编译内核的方式
在这里插入图片描述
起来后访问内存还是不行,但是提示从没有权限变成了invalid argument
在这里插入图片描述
对应这个问题linux/lib/Kconfig.debug有说明
在这里插入图片描述
我们需要在配置kernel时,关闭CONFIG_X86_PAT=n,同时开启CONFIG_EXPERT
或者直接在cmdline加上nopat的配置 [X86] Disable PAT (page attribute table extension of pagetables) support.
在这里插入图片描述
重启系统后,可以正常访问内存的地址了
在这里插入图片描述
我们可以看到devmem读取的和直接hexdump /dev/mem是一样的,只是大小端显示不同。
在这里插入图片描述

  还有一种不用重编内核的简单方法,就是利用kretprobe把devmem_is_allowed函数强制返回1
range_is_allowed

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/ktime.h>

#define MODULE_NAME "devmem_allowed"

static char func_name[NAME_MAX] = "devmem_is_allowed";

/* 每个实例的私有数据结构 */
struct my_data {
    ktime_t entry_stamp;  // 记录函数进入的时间戳
};

/* 入口处理函数:记录函数进入的时间戳 */
static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    struct my_data *data;

    if (!current->mm)
        return 1;  // 跳过内核线程

    data = (struct my_data *)ri->data;
    data->entry_stamp = ktime_get();  // 获取当前时间戳
    return 0;
}

/* 返回处理函数:强制设置返回值为 1 */
static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    regs_set_return_value(regs, 1);  // 强制设置返回值为 1
    return 0;
}

/* 定义 kretprobe */
static struct kretprobe my_kretprobe = {
    .handler        = ret_handler,      // 返回时的处理函数
    .entry_handler  = entry_handler,    // 入口时的处理函数
    .data_size      = sizeof(struct my_data),  // 私有数据大小
    .maxactive      = 20,               // 最大并发实例数
};

/* 初始化 kretprobe */
static int kretprobe_init(void)
{
    int ret;

    my_kretprobe.kp.symbol_name = func_name;  // 要监控的函数名
    ret = register_kretprobe(&my_kretprobe);  // 注册 kretprobe
    if (ret < 0) {
        pr_err("register_kretprobe failed, returned %d\n", ret);
        return ret;
    }
    pr_info("Planted return probe at %s: %p\n",
            my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);
    return 0;
}

/* 卸载 kretprobe */
static void kretprobe_exit(void)
{
    unregister_kretprobe(&my_kretprobe);
    pr_info("kretprobe at %p unregistered\n", my_kretprobe.kp.addr);
}

/* 模块初始化函数 */
static int __init devmem_allowed_init(void)
{
    return kretprobe_init();
}

/* 模块退出函数 */
static void __exit devmem_allowed_exit(void)
{
    kretprobe_exit();
}

/* 模块信息 */
module_init(devmem_allowed_init);
module_exit(devmem_allowed_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Force devmem_is_allowed to return 1");
MODULE_AUTHOR("Your Name");

makefile

EXTRA_CFLAGS += -I$(PWD)

MODULE_NAME = devmem_is_allowed


ifeq ($(KERNELDIR), )
KERNELDIR = /lib/modules/$(shell uname -r)/build
endif

ifeq ($(Peek), y)
KDIR = /lib/modules/$(shell uname -r)/build
else
KDIR=$(KERNELDIR)
endif

EXTRA_CFLAGS += -Wno-date-time -g


obj-m += $(MODULE_NAME).o

all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	make -C $(KDIR) M=$(PWD) clean

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
dmesg相关打印

[2697892.742080] Planted return probe at devmem_is_allowed: 00000000ae19aa25
[2697911.150814] x86/PAT: busybox:1200663 map pfn RAM range req uncached-minus for [mem 0x00100000-0x00100fff], got write-back
[2697922.624486] x86/PAT: busybox:1200730 map pfn RAM range req uncached-minus for [mem 0x00100000-0x00100fff], got write-back
[2697924.020980] x86/PAT: busybox:1200743 map pfn RAM range req uncached-minus for [mem 0x00100000-0x00100fff], got write-back
[2698426.939187] kretprobe at 00000000ae19aa25 unregistered

参考
https://lwn.net/Articles/267427/
http://blog.sina.com.cn/s/blog_6f5b220601012xbc.html
https://blog.csdn.net/anzhuangguai/article/details/54316206
https://blog.csdn.net/linuxdrivers/article/details/8498204
https://cloud.tencent.com/developer/article/1543163
https://mp.weixin.qq.com/s?__biz=Mzg2OTc0ODAzMw==&mid=2247502373&idx=1&sn=29f55e365916eccda2e7d9b98b810889&source=41#wechat_redirect

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linjiasen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值