Compel
Compel is a utility to execute arbitrary code in a context of a foreign process. Compel is part of CRIU, and its sources are available from the criu-dev branch of CRIU repo, subdirectory compel.
The code to be executed is called parasite code. Once compiled with compel flags and packed, it can be executed in other task's context. Note the code is run in environment without glibc, thus it can not call the usual stdio/stdlib/etc. functions.
A set of compel plugins are provided for your convenience. Plugins get linked to the parasite binary during the pack stage.
Writing parasite code
Execution of parasite code always starts with a function in compel std plugin that should be linked with parasite binary (see below). From the parasite code these symbols should be available for libcompel to work
parasite_trap_cmd(int cmd, void *arg);
- This routine gets called by
compel_run_in_thread()
parasite_daemon_cmd(int cmd, void *arg);
- This routine gets called by
compel_rpc_call()
andcompel_rpc_call_sync()
. Thearg
points to the memory with arguments, see the #Arguments passing section below.
parasite_cleanup(void);
- This gets called on parasite unload by
compel_cure()
Compiling and packing
Compile the source of your parasite code with compel flags:
$ gcc -c foo1.c -o foo1.o $(compel cflags)
Don't forget to put compel/include/uapi/
directory into include paths.
Then link the parasite binary. Include all the .o files needed and compel std plugin by using compel linker script.
$ ld foo1.o foo2.o compel/plugins/std.built-in.o -T compel/arch/$ARCH/scripts/compel-pack.lds.S -o parasite.po
The .po blob can now be loaded as parasite.
Loading blob
Using CLI
This functionality is in plans and not implemented yet.
Using libcompel.a library
In libcompel.a there's currently only one way to load the blob into victim task, it's called 'c-header'. So first you should make a header out of you .po file
compel hgen -f parasite.po -v parasite_relocs -p parasite_sym -s parasite_blob -r parasite_nr_gotpcrel -u compel/include/uapi/ -o parasite.h
The -f
option tells which binary to turn into header and -o
where to put the result. The rest args are used when filling the blob descriptor, see the #Infecting section below. After this the parasite.h
file should be included into the infecting program and compiled with it.
Running parasite code
So, in order to infect a task with parasite one must.
- Stop the task with
compel_stop_task(int pid)
call and keep the return value if it's positive (it contains the task state) - Prepare the infection handler with
compel_prepare(int pid)
call. The return value is an opaque pointer tostruct parasite_ctl
- Run the remote code
- Just execute a system call with
compel_syscall(ctl, int syscall_nr, long *ret, int arg ... (6 of them))
- Infect victim with parasite blob with
compel_infect(ctl, nr_thread, size_of_args_area)
- Just execute a system call with
- Cure the victim with
compel_cure(ctl)
and stop using the ctl pointer as it's freed by the call - Resume the task with
compel_resume_task(pid, orig_state, state)
with the saved state value
Infecting
Infecting the victim with parasite blob needs some special treatment.
First, the ctl should be configured with the blob information. Currently there's only one type of blobs, generated by compel hgen
. To put this info into ctl one should call compel_parasite_blob_desc(ctl)
to get a pointer on struct parasite_blob_desc
and fill in the fields of this strucure
.parasite_type
should be set toCOMPEL_BLOB_CHEADER
.hdr.mem
should be set to theparasite_blob
of hgen.hdr.bsize
should be set to the sizeof(this symbol).hdr.nr_gotpcrel
should be set to theparasite_nr_gotpcrel
of hgen- Three offsets below should be set to respective offsets generated with the
parasite_sym
value.hdr.parasite_ip_off
toCOMPEL_H_PARASITE_HEAD(parasite_sym)
.hdr.addr_cmd_off
toCOMPEL_H_PARASITE_CMD(parasite_sym)
.hdr.addr_arg_off
toCOMPEL_H_PARASITE_ARGS(paraste_sym)
.hdr.relocs
should be set toparasite_relocs
argument (it's an array).hdr.nr_relocs
should be set to the number of elements in this array (sizeof(arr)/sizeof(arr[0])
)
Second, when infecting a parasite one should specify the amount of threads it will mess with (1, if only the thread leader will be accessed) and the maximum size of the memory area shared between infecting task and parasite used for arguments/result passing.
Arguments passing
To pass arguments between the infecting code and parasite one may call compel_parasite_args(ctl, type)
and get the pointer where it can put data. Subsequent calls to compel_rpc_call[_sync]()
would result int this data visible in void *arg
address of the parasite_daemon_cmd()
.