Difference between revisions of "Parasite code"

From CRIU
Jump to navigation Jump to search
 
(13 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
== Overview ==
 
== Overview ==
 +
Parasite code is a binary blob of code built in [http://en.wikipedia.org/wiki/Position-independent_code PIE] format for execution inside another process address space. The main and only purpose of the parasite code is to execute CRIU service routines inside dumpee tasks address space.
  
Parasite code is a binary blob of code built in [http://en.wikipedia.org/wiki/Position-independent_code PIE] format for execution inside another process address space. As result in a sake of simplicity parasite code utilize native system calls only.
+
== Using the parasite ==
  
=== Running the parasite ===
+
All architecture independent code calling for parasite service routines is sitting in <code>parasite-syscall.c</code> file. When we need to run parasite code inside some dumpee task we:
 +
 +
# Move task into that named seized state with <code>ptrace(PTRACE_SEIZE, …)</code> helper (thus task get stopped but does not notice that someone outside is trying to manipulate it).
 +
# Inject and execute mmap syscall inside dumpee address space with help of <code>ptrace</code> system call, because we need to allocate a shared memory area which will be used for parasite stack and parameters exchange between CRIU and dumpee.
 +
# Open local copy of shared memory space from ''/proc/$PID/map_files/'', where '''$PID''' is process identificator of a dumpee.
  
Injection of a parasite code may be spitted into two phases
+
All these actions are gathered in <code>parasite_infect_seized()</code> helper. Once parasite is prepared and placed into dumpee address space, CRIU can call for parasite service routines.
  
# preparation of a victim task
+
There are two modes the parasite can operate in:
# injection itself
 
  
During preparation stage we move a victim into that named ''seized'' state with help of '''prctl''' system call (in this state the victim does not recognize that it is being manipulated by someone). Once seized we substitute current CS:IP code with '''mmap''' system call allocating shared memory space needed to carry parasite blob.
+
# Trap mode
 +
# Daemon mode
  
Parasite code injection is simple: because we have a shared memory slab allocated inside victim space we can scan <code>/proc/$pid/map_files/</code> directory and open this slab inside CRIU address space. Once opened we simply copy parasite code there with '''memcpy'''.
+
In trap mode parasite simply executes one command and yields cpu trap instruction which CRIU intercepts. This is like one command at a time mode.
  
At this moment we can run parasite code adjusting CS:IP of the victim and call '''prctl''' again. After that parasite is spinning listening the socket for commands from outside world.
+
In daemon mode (as name implies) parasite behaves like a unix daemon - it opens a unix socket and start listening for commands on it. Once a command is received, it is handled and the daemon returns the result back via a socket packet. The daemon continues listening for the subsequent commands to execute. All currently known commands are assembled as <code>PARASITE_CMD_…</code> enum in <code>parasite.h</code> header.
 +
 
 +
== Parasite internal structure ==
 +
 
 +
Internally parasite might be represented as following blocks
 +
 
 +
[[File:Parasite-layout.svg|thumb|upright=1|center]]
 +
 
 +
Parasite bootstrap code written in assembly language, which is specific for every architecture (x86, arm, arm64), in turn parasite daemon is common for all architectures and written in C language. We consider only x86 architecture here but other architectures have the similar approach.
 +
 
 +
While all blocks on the image above are self descriptive the sigframe (signal frame) block requires some comments. Its main purpose is to handle <code>rt_sigreturn()</code> system call which we use to restore victim’s execution context (registers and etc). It should be prepared by caller setting up original registers values and etc the victim has at the moment of parasite injection.
 +
 
 +
=== Parasite bootstrap ===
 +
 
 +
Parasite bootstrap lives in <code>parasite-head.S</code> file and simply adjusts own stack and literally call the daemon entry point. Right after the call there is trapping instruction placed which triggers the notification to a caller that parasite has finished its work if been running in trap mode. When parasite is running in daemon mode the notifications are a bit more complex and will be considered later.
 +
 
 +
=== Parasite daemon ===
 +
 
 +
Parasite daemon code lives in <code>pie/parasite.c</code> file. Its entry point is <code>parasite_daemon()</code>. Upon enter it opens command socket which is used to communicate with the caller. Once socket is opened the daemon comes to sleep waiting for command to appear.
 +
 
 +
[[File:Parasite-daemon.svg|thumb|upright=3|center]]
 +
 
 +
Because the whole parasite memory block is a shared memory slab data exchange between CRIU and dumpee is regular read/write operations into arguments area while commands are sent as network packets.
 +
 
 +
== Curing dumpee from parasite code ==
 +
 
 +
Once everything is done and we no longer need parasite it is removed from the dumpee's address space by performing the following steps:
 +
 
 +
# CRIU starts tracing the syscalls parasite is executing with help of <code>ptrace</code>
 +
# CRIU sends <code>PARASITE_CMD_FINI</code> to the parasite via the control socket
 +
# Parasite receives the command, then closes control socket and executes <code>rt_sigreturn()</code> system call
 +
# CRIU intercepts exit from this syscall and unmaps parasite memory area, thus reverting the dumpee back to the state it was in before parasite injection
 +
 
 +
== See also ==
 +
* [[Code blobs]]
 +
* [[Compel]]
  
 
[[Category: Under the hood]]
 
[[Category: Under the hood]]
 +
[[Category: Editor help needed]]

Latest revision as of 08:42, 25 April 2018

Overview[edit]

Parasite code is a binary blob of code built in PIE format for execution inside another process address space. The main and only purpose of the parasite code is to execute CRIU service routines inside dumpee tasks address space.

Using the parasite[edit]

All architecture independent code calling for parasite service routines is sitting in parasite-syscall.c file. When we need to run parasite code inside some dumpee task we:

  1. Move task into that named seized state with ptrace(PTRACE_SEIZE, …) helper (thus task get stopped but does not notice that someone outside is trying to manipulate it).
  2. Inject and execute mmap syscall inside dumpee address space with help of ptrace system call, because we need to allocate a shared memory area which will be used for parasite stack and parameters exchange between CRIU and dumpee.
  3. Open local copy of shared memory space from /proc/$PID/map_files/, where $PID is process identificator of a dumpee.

All these actions are gathered in parasite_infect_seized() helper. Once parasite is prepared and placed into dumpee address space, CRIU can call for parasite service routines.

There are two modes the parasite can operate in:

  1. Trap mode
  2. Daemon mode

In trap mode parasite simply executes one command and yields cpu trap instruction which CRIU intercepts. This is like one command at a time mode.

In daemon mode (as name implies) parasite behaves like a unix daemon - it opens a unix socket and start listening for commands on it. Once a command is received, it is handled and the daemon returns the result back via a socket packet. The daemon continues listening for the subsequent commands to execute. All currently known commands are assembled as PARASITE_CMD_… enum in parasite.h header.

Parasite internal structure[edit]

Internally parasite might be represented as following blocks

Parasite-layout.svg

Parasite bootstrap code written in assembly language, which is specific for every architecture (x86, arm, arm64), in turn parasite daemon is common for all architectures and written in C language. We consider only x86 architecture here but other architectures have the similar approach.

While all blocks on the image above are self descriptive the sigframe (signal frame) block requires some comments. Its main purpose is to handle rt_sigreturn() system call which we use to restore victim’s execution context (registers and etc). It should be prepared by caller setting up original registers values and etc the victim has at the moment of parasite injection.

Parasite bootstrap[edit]

Parasite bootstrap lives in parasite-head.S file and simply adjusts own stack and literally call the daemon entry point. Right after the call there is trapping instruction placed which triggers the notification to a caller that parasite has finished its work if been running in trap mode. When parasite is running in daemon mode the notifications are a bit more complex and will be considered later.

Parasite daemon[edit]

Parasite daemon code lives in pie/parasite.c file. Its entry point is parasite_daemon(). Upon enter it opens command socket which is used to communicate with the caller. Once socket is opened the daemon comes to sleep waiting for command to appear.

Parasite-daemon.svg

Because the whole parasite memory block is a shared memory slab data exchange between CRIU and dumpee is regular read/write operations into arguments area while commands are sent as network packets.

Curing dumpee from parasite code[edit]

Once everything is done and we no longer need parasite it is removed from the dumpee's address space by performing the following steps:

  1. CRIU starts tracing the syscalls parasite is executing with help of ptrace
  2. CRIU sends PARASITE_CMD_FINI to the parasite via the control socket
  3. Parasite receives the command, then closes control socket and executes rt_sigreturn() system call
  4. CRIU intercepts exit from this syscall and unmaps parasite memory area, thus reverting the dumpee back to the state it was in before parasite injection

See also[edit]