Home > Net >  Can you change the page permissions of a running process?
Can you change the page permissions of a running process?

Time:12-01

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:

  1. Run the target process and get its PID. You can find the PID of the process with ps, htop, pidof and similar commands.

  2. Open a terminal and attach GDB to the process with gdb --pid PID.

  3. 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 of info proc mappings here because the latter does not show the permissions unfortunately.

  4. 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
    
  5. 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:

  1. Spawn the target process through fork execve (or just run it in another terminal).
  2. PTRACE_ATTACH to it.
  3. Peek around with PTRACE_GETREGS (save the initial register state for later), PTRACE_PEEKDATA (inspect memory), etc.
  4. Manipulate the tracee's memory to write a simple stub that calls mprotect for you (PTRACE_POKEDATA).
  5. Force the tracee to execute that little stub.
  6. Reset everything back to normal (restore regs, possibly also remove the stub, etc).
  7. 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.

  • Related