【问题标题】:Can you change the page permissions of a running process?您可以更改正在运行的进程的页面权限吗?
【发布时间】:2021-11-29 17:35:07
【问题描述】:

我曾想过编写一个程序集存根来调用带有页面地址的mprotect,但是我需要在进程的同一地址空间中运行这个存根,我不知道该怎么做。还有其他方法吗?

【问题讨论】:

  • 您可以拦截内核中的页面错误并更改正在运行的进程的 pte 页位。非常难,非常危险。风险自负。
  • ptrace 可以对另一个进程的内存进行操作 不必 mprotect。例如ptrace(PTRACE_POKETEXT)。你也可以使用 ptrace 在另一个进程中运行类似 mprotect 的代码,但是当你使用像 print foo() 这样的 GDB 命令时,GDB 会这样做,其中 foo 是被调试程序中的一个函数。

标签: linux memory linux-kernel x86-64 paging


【解决方案1】:

是的,只要有适当的权限,您就可以。根据具体情况,您可能需要CAP_SYS_PTRACE 功能。请参阅this documentation page 关于/proc/sys/kernel/yama/ptrace_scope 了解更多信息。

使用 GDB 的简单解决方案

最简单的方法是使用调试器,例如GDB

  1. 运行目标进程并获取其 PID。您可以使用pshtoppidof 和类似命令找到进程的 PID。

  2. 打开终端并使用gdb --pid PID 将 GDB 附加到进程。

  3. 在 GDB 提示符下,检查内存映射:

    (gdb) info inferiors
      Num  Description       Connection           Executable
    * 1    process 19433     2 (native)           /usr/bin/ls
    (gdb) !cat /proc/19433/maps
    555555554000-555555558000 r--p 00000000 00:18 5154601                    /usr/bin/ls
    555555558000-55555556d000 r-xp 00004000 00:18 5154601                    /usr/bin/ls
    55555556d000-555555576000 r--p 00019000 00:18 5154601                    /usr/bin/ls
    555555577000-555555579000 rw-p 00022000 00:18 5154601                    /usr/bin/ls
    555555579000-55555557a000 rw-p 00000000 00:00 0                          [heap]
    7ffff7fcc000-7ffff7fd0000 r--p 00000000 00:00 0                          [vvar]
    ...
    

    注意:我在这里使用!cat /proc/PID/maps 而不是info proc mappings,因为不幸的是后者没有显示权限。

  4. 调用mprotect() libc 函数来改变你想要的页面的权限。比如这里我把单页改成RWX:

    (gdb) call (long)mprotect(0x555555577000, 0x1000, 0x7)
    $1 = 0  <-- return value, 0 == success
    
  5. 现在detach 让进程以修改后的权限运行。

您也可以写一个GDB script 来自动为您执行此操作。例如,如果您知道 RDI 会在某个时间点包含您想要触摸的页面中的地址,您可以执行以下操作:

file path/to/your/elf
# or alternatively `attach PID`

# Set a breakpoint to some known address (assuming your ELF is not position independent).
# You could also do `break some_symbol+offset` if there are symbols.

break *0x123450
command 1
    set $page = $rdi & ~0xfff
    call (long)mprotect($page, 0x1000, 0x7)
    detach
end

run

然后在你的终端中:

$ gdb -x yourscript.txt

使用ptrace的高级手动解决方案

或者,您可以利用 GDB 在后台使用的相同低级工具来编写一个自动为您执行此操作的程序:ptrace 系统调用。您可以编写一个执行以下操作的程序:

  1. 通过fork + execve 生成目标进程(或在另一个终端中运行)。
  2. PTRACE_ATTACH 给它。
  3. 查看PTRACE_GETREGS(保存初始寄存器状态以备后用)、PTRACE_PEEKDATA(检查内存)等。
  4. 操纵被跟踪者的内存以编写一个简单的存根,为您调用mprotect (PTRACE_POKEDATA)。
  5. 强制被跟踪者执行那个小存根。
  6. 将所有内容重置为正常状态(恢复 regs,也可能删除存根等)。
  7. 与被跟踪者分离并让它继续执行。

这当然比基于调试器的方法要困难得多,但尝试起来会很有趣。您基本上是在编写自己的非常专业的调试器。

请在"Writing the simplest assembly debugger" 上查看我的这个答案,了解更多信息和代码示例。

这里还有a more complex example on GitHub:在这种情况下,程序员想要通过ptrace关闭已经运行的进程的任意文件描述符。

【讨论】:

  • 太好了,谢谢我不知道你可以在你正在调试的进程的上下文中调用 gdb 中的函数,
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-29
  • 2011-01-25
  • 1970-01-01
  • 2012-11-28
  • 1970-01-01
  • 2011-05-19
  • 2012-01-08
相关资源
最近更新 更多