【问题标题】:Trap all accesses to an address range (Linux)捕获对地址范围的所有访问(Linux)
【发布时间】:2014-01-30 20:47:29
【问题描述】:

背景

我正在编写一个框架,以实现在模拟器和未修改的主机软件中运行的 RTL 的联合模拟。主机软件是为控制实际硬件而编写的,通常以以下两种方式之一工作:

  1. 通过驱动程序读取/写入调用
  2. 使用 mmap 进行内存映射访问

前一种情况非常简单 - 编写一个库来实现与驱动程序相同的读/写调用,并在运行模拟时链接到它。这一切都非常有效,我可以运行未经修改的生产软件作为 RTL 模拟的激励。

事实证明,第二种情况比第一种要困难得多……

陷印mmap

最初我以为我可以使用LD_PRELOAD 来拦截 mmap 调用。在我的mmap 实现中,我会分配一些页面对齐的内存,然后分配mprotect 它并设置一个信号处理程序来捕获SIGSEGV

这种方法存在很多问题:

读取与写入

我可以从siginfo_t->si_addr 确定访问的地址,但不能确定访问是读取还是写入。

捕获重复访问

在信号处理程序中,我需要取消保护内存区域,否则一旦我的处理程序退出并且主机代码将永远无法继续,我将重复SIGSEGVs。但是,如果我取消对该区域的保护,那么我的信号处理程序将不会捕获后续访问。

信号处理程序的麻烦

在模拟器驱动 RTL 并返回结果时阻塞信号处理程序违反了各种编程规则 - 特别是考虑到模拟器可以触发各种其他事件并在从该访问返回结果之前执行任意代码。

其他方法

我想知道是否可以创建一个类似于磁盘的文件对象,而不是在缓冲区上使用mprotect。我没有发现任何信息表明这是可行的。

问题

是否可以捕获对 mmap 区域的所有访问以及如何捕获?

  • 访问需要阻塞一段不确定的时间(模拟器运行时)
  • 读取访问需要检索我的陷阱放置的新值

假设LD_PRELOADmprotect 是最佳路线:

  • 我能否确定访问是读取调用还是写入调用?
  • 由于必须取消mprotect 该区域,如何捕获后续访问?

相关问题

How to write a signal handler to catch SIGSEGV?

Possible to trap write to address (x86 - linux)

【问题讨论】:

    标签: c linux system-calls mmap signal-handling


    【解决方案1】:

    在 X86 上,您可以为调用者的上下文设置 Trap 标志,以便在一条指令后获取 SIGTRAP(此标志通常用于单步执行)。也就是说,当遇到 SIGSEGV 时,您在调用者的 EFLAGS 中设置 TF(请参阅ucontext.h),使用mprotect 启用读取并返回。如果 SIGSEGV 立即使用相同的 IP 重复,则启用写入(如果您想区分 read-modify-write 和只写访问,则可以选择禁用读取)。如果您从同一个 IP 获得 SIGSEGV 用于只读和只写保护,请启用读写。

    无论何时获得 SIGTRAP,您都可以分析写入的值(如果是写访问),还可以重新保护页面以捕获未来的访问。

    更正:如果读写都可能有副作用,请先尝试只写保护,然后应用读取副作用并尝试只读保护,然后在最终的SIGTRAP中启用写入并处理写入的副作用处理程序。

    更新:我在推荐假设的只写保护时犯了致命的错误,但事实证明大多数架构都不存在。幸运的是,有一种更直接的方法可以知道失败的操作是否尝试读取内存,至少在 x86 上是这样:

    页面错误异常将错误代码推送到堆栈,该代码在 Linux SIGSEGV 处理程序中作为sigcontext 结构的err 成员可用。 Bit 1 of the error code 对于 write 错误为 1,否则为 0。对于 read-modify-write 操作,初始值为 0(在这里你可以模拟阅读,确切地知道它会发生)。

    【讨论】:

    • 感谢您的建议。请注意,X86 不支持只写保护 (stackoverflow.com/a/18829341/579887)。不幸的是,看起来我必须每隔SIGSEGV 对模拟器执行一次推测性读取,这对于写入效率低下(并且可能使建模行为与真实硬件不同)
    • @Chiggs,我不敢相信我愚蠢到发明了这种“只写”的废话。幸运的是,解决方案(也进入sigcontext->err 的异常错误代码包含 r/w 位)。请参阅答案中的更新。
    • @AntonKovalenko -- 您能否详细说明如何在调用者的 EFLAGS 中设置 TF?这是否在 SIGSEGV 处理程序中通过 (1) 调用 swapcontext() 以交换到 uc_link 上下文,(2) 设置 TF 标志(通过内联 asm),(3) 交换回原始 SIGSEGV 处理程序上下文,以及(4) 启用通过mprotect() 读取?我不清楚 SIGSEGV 处理程序中的“调用者上下文”是什么。
    • @cklin 在 linux 中,调用者的 EFLAGS 在...->uc_mcontext.gregs[REG_EFL] 中(相关定义见sys/ucontext.h)。 AFAIK 你不需要交换也不需要使用uc_link,只需在 gregs[REG_EFL] 中设置 TF。
    • @cklin 我的实验有一个(混乱的)示例here,但总而言之:ucontext_t *c = context; c->uc_mcontext.gregs[REG_EFL] |= X86_EFLAGS_TF; 为我工作,无需调用swapcontext(),因为它已传递给您的处理程序。跨度>
    猜你喜欢
    • 2021-11-23
    • 2016-04-09
    • 1970-01-01
    • 2010-12-05
    • 2018-10-08
    • 1970-01-01
    • 1970-01-01
    • 2021-07-17
    • 1970-01-01
    相关资源
    最近更新 更多