您可以更改 运行 进程的页面权限吗?
Can you change the page permissions of a running process?
我想写一个程序集存根来调用 mprotect
页面地址,但是我需要 运行 这个存根在进程的相同地址 space 中,我不知道该怎么做。还有其他方法吗?
是的,您可以,只要有适当的权限。根据情况,您可能需要 CAP_SYS_PTRACE
功能。请参阅 this documentation page 关于 /proc/sys/kernel/yama/ptrace_scope
以了解更多信息。
使用 GDB 的简单解决方案
最简单的方法是使用调试器,例如GDB:
运行目标进程并获取其PID。您可以使用 ps
、htop
、pidof
和类似命令找到进程的 PID。
打开终端并使用 gdb --pid PID
.
将 GDB 附加到进程
在 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
,因为不幸的是后者没有显示权限。
调用mprotect()
libc 函数更改您想要的页面的权限。例如这里我将单个页面更改为 RWX:
(gdb) call (long)mprotect(0x555555577000, 0x1000, 0x7)
= 0 <-- return value, 0 == success
现在 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
系统调用。您可以编写一个执行以下操作的程序:
- 通过
fork
+ execve
(或者只是 运行 在另一个终端中生成目标进程)。
PTRACE_ATTACH
就可以了。
- 使用
PTRACE_GETREGS
(保存初始寄存器状态以备后用)、PTRACE_PEEKDATA
(检查内存)等查看
- 操纵被跟踪者的记忆来编写一个简单的存根,为您调用
mprotect
(PTRACE_POKEDATA
)。
- 强制被跟踪者执行那个小存根。
- 将一切重置为正常(恢复注册,可能还删除存根等)。
- 从tracee分离并让它继续执行。
这当然比基于调试器的方法难得多,但试验起来会很有趣。您基本上是在编写自己的非常专业的调试器。
查看我在 上的这个回答,了解更多信息和代码示例。
这里还有 a more complex example on GitHub:在这种情况下,程序员希望通过 ptrace
.
关闭已经 运行ning 进程的任意文件描述符
我想写一个程序集存根来调用 mprotect
页面地址,但是我需要 运行 这个存根在进程的相同地址 space 中,我不知道该怎么做。还有其他方法吗?
是的,您可以,只要有适当的权限。根据情况,您可能需要 CAP_SYS_PTRACE
功能。请参阅 this documentation page 关于 /proc/sys/kernel/yama/ptrace_scope
以了解更多信息。
使用 GDB 的简单解决方案
最简单的方法是使用调试器,例如GDB:
运行目标进程并获取其PID。您可以使用
ps
、htop
、pidof
和类似命令找到进程的 PID。打开终端并使用
将 GDB 附加到进程gdb --pid PID
.在 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
,因为不幸的是后者没有显示权限。调用
mprotect()
libc 函数更改您想要的页面的权限。例如这里我将单个页面更改为 RWX:(gdb) call (long)mprotect(0x555555577000, 0x1000, 0x7) = 0 <-- return value, 0 == success
现在
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
系统调用。您可以编写一个执行以下操作的程序:
- 通过
fork
+execve
(或者只是 运行 在另一个终端中生成目标进程)。 PTRACE_ATTACH
就可以了。- 使用
PTRACE_GETREGS
(保存初始寄存器状态以备后用)、PTRACE_PEEKDATA
(检查内存)等查看 - 操纵被跟踪者的记忆来编写一个简单的存根,为您调用
mprotect
(PTRACE_POKEDATA
)。 - 强制被跟踪者执行那个小存根。
- 将一切重置为正常(恢复注册,可能还删除存根等)。
- 从tracee分离并让它继续执行。
这当然比基于调试器的方法难得多,但试验起来会很有趣。您基本上是在编写自己的非常专业的调试器。
查看我在
这里还有 a more complex example on GitHub:在这种情况下,程序员希望通过 ptrace
.