I thought about writing an assembly stub to call mprotect
with the page address, but then I would need to run this stub in the same address space of the process, and I do not know how to do that. Is there another method?
CodePudding user response:
Yes, you can, given the appropriate privileges. Depending on the situation, you may need CAP_SYS_PTRACE
capabilities. See this documentation page about /proc/sys/kernel/yama/ptrace_scope
to know more.
Easy-peasy solution using GDB
The simplest way is to use a debugger, for example GDB:
Run the target process and get its PID. You can find the PID of the process with
ps
,htop
,pidof
and similar commands.Open a terminal and attach GDB to the process with
gdb --pid PID
.At the GDB prompt, check the memory mappings:
(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] ...
Note: I use
!cat /proc/PID/maps
instead ofinfo proc mappings
here because the latter does not show the permissions unfortunately.Call the
mprotect()
libc function to change the permissions of the pages you want. For example here I change a single page to RWX:(gdb) call (long)mprotect(0x555555577000, 0x1000, 0x7) $1 = 0 <-- return value, 0 == success
Now
detach
and let the process run with the modified permissions.
You can also write a GDB script to do this for you automatically. For example, if you know that at some point RDI will contain an address in the page you want to touch, you could do something like this:
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
Then in your terminal:
$ gdb -x yourscript.txt
Advanced manual solution using ptrace
Alternatively, you can leverage the same low level tool that GDB uses under the hood to write a program that does this for you automatically: the ptrace
syscall. You can write a program which does the following:
- Spawn the target process through
fork
execve
(or just run it in another terminal). PTRACE_ATTACH
to it.- Peek around with
PTRACE_GETREGS
(save the initial register state for later),PTRACE_PEEKDATA
(inspect memory), etc. - Manipulate the tracee's memory to write a simple stub that calls
mprotect
for you (PTRACE_POKEDATA
). - Force the tracee to execute that little stub.
- Reset everything back to normal (restore regs, possibly also remove the stub, etc).
- Detach from the tracee and let it continue executing.
This is of course a lot harder than the debugger based approach, but it can be fun to experiment with. You are basically writing your own very specialized debugger.
See this answer of mine on "Writing the simplest assembly debugger" for more information and a code example of what it could look like.
Here's also a more complex example on GitHub: in this case the programmer wants to close an arbitrary file descriptor of an already running process through ptrace
.