[5/5] compel: Simple infection test

Submitted by Pavel Emelianov on Nov. 25, 2016, 1:21 p.m.

Details

Message ID 58383AF0.3030109@virtuozzo.com
State Accepted
Headers show

Commit Message

Pavel Emelianov Nov. 25, 2016, 1:21 p.m.
And, at the same time, an example of how to work with compel.
Based on titanic preliminary work of Cyrill :)

Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
---
 compel/test/infect/.gitignore |   4 +
 compel/test/infect/Makefile   |  40 +++++++++
 compel/test/infect/parasite.c |  37 +++++++++
 compel/test/infect/spy.c      | 183 ++++++++++++++++++++++++++++++++++++++++++
 compel/test/infect/victim.c   |  15 ++++
 5 files changed, 279 insertions(+)
 create mode 100644 compel/test/infect/.gitignore
 create mode 100644 compel/test/infect/Makefile
 create mode 100644 compel/test/infect/parasite.c
 create mode 100644 compel/test/infect/spy.c
 create mode 100644 compel/test/infect/victim.c

Patch hide | download patch | download mbox

diff --git a/compel/test/infect/.gitignore b/compel/test/infect/.gitignore
new file mode 100644
index 0000000..0a55475
--- /dev/null
+++ b/compel/test/infect/.gitignore
@@ -0,0 +1,4 @@ 
+parasite.h
+parasite.po
+spy
+victim
diff --git a/compel/test/infect/Makefile b/compel/test/infect/Makefile
new file mode 100644
index 0000000..fa05b65
--- /dev/null
+++ b/compel/test/infect/Makefile
@@ -0,0 +1,40 @@ 
+ifeq ($(ARCH),)
+        ARCH := x86
+endif
+
+# FIXME -- generate common symlink in compel/uapi
+COMMON_IDIR	:= ../../../include
+COMPEL		:= ../../../compel/compel-host
+COMPEL_IDIR	:= ../../../compel/include/uapi
+COMPEL_PACK_LDS	:= ../../../compel/arch/$(ARCH)/scripts/compel-pack.lds.S
+COMPEL_PLUGINS	:= ../../../compel/plugins
+COMPEL_LIBRARY	:= ../../../compel/libcompel.a
+
+clean:
+	rm -f victim
+	rm -f spy
+	rm -f parasite.h
+	rm -f parasite.po
+	rm -f parasite.o
+
+victim: victim.c
+	gcc -o $@ $^
+
+spy: spy.c parasite.h $(COMPEL_LIBRARY)
+	gcc -Werror -I$(COMPEL_IDIR) -I$(COMMON_IDIR) -o $@ $^
+
+parasite.h: parasite.po
+	$(COMPEL) hgen -f $^ -l 4		\
+                -v parasite_relocs		\
+		-p parasite_sym			\
+		-s parasite_blob		\
+		-r parasite_nr_gotpcrel		\
+		-u $(COMPEL_IDIR)		\
+		-o $@
+
+# FIXME -- fds.plugin.o isn't generated automatically
+parasite.po: parasite.o $(COMPEL_PLUGINS)/std.built-in.o $(COMPEL_PLUGINS)/fds.built-in.o
+	ld -r -T $(COMPEL_PACK_LDS) -o $@ $^
+
+parasite.o: parasite.c
+	gcc -c $(shell $(COMPEL) --arch=$(ARCH) cflags) -I$(COMPEL_IDIR) -o $@ $^
diff --git a/compel/test/infect/parasite.c b/compel/test/infect/parasite.c
new file mode 100644
index 0000000..baecc0e
--- /dev/null
+++ b/compel/test/infect/parasite.c
@@ -0,0 +1,37 @@ 
+#include <errno.h>
+
+#include <compel/plugins/std/syscall.h>
+#include <compel/plugins/std/string.h>
+#include <compel/plugins/std/log.h>
+
+#include <compel/infect-rpc.h>
+
+/*
+ * Stubs for std compel plugin.
+ */
+int compel_main(void *arg_p, unsigned int arg_s) { return 0; }
+int parasite_trap_cmd(int cmd, void *args) { return 0; }
+void parasite_cleanup(void) { }
+
+#define PARASITE_CMD_INC	PARASITE_USER_CMDS
+#define PARASITE_CMD_DEC	PARASITE_USER_CMDS + 1
+
+int parasite_daemon_cmd(int cmd, void *args)
+{
+	int v;
+
+	switch (cmd) {
+	case PARASITE_CMD_INC:
+		v = (*(int *)args) + 1;
+		break;
+	case PARASITE_CMD_DEC:
+		v = (*(int *)args) - 1;
+		break;
+	default:
+		v = -1;
+		break;
+	}
+
+	sys_write(1, &v, sizeof(int));
+	return 0;
+}
diff --git a/compel/test/infect/spy.c b/compel/test/infect/spy.c
new file mode 100644
index 0000000..90428b1
--- /dev/null
+++ b/compel/test/infect/spy.c
@@ -0,0 +1,183 @@ 
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include <compel/compel.h>
+#include "parasite.h"
+
+#define PARASITE_CMD_INC	PARASITE_USER_CMDS
+#define PARASITE_CMD_DEC	PARASITE_USER_CMDS + 1
+
+static void print_vmsg(unsigned int lvl, const char *fmt, va_list parms)
+{
+	printf("\tLC%u: ", lvl);
+	vprintf(fmt, parms);
+}
+
+static int do_infection(int pid)
+{
+#define err_and_ret(msg) do { fprintf(stderr, msg); return -1; } while (0)
+
+	int state;
+	struct parasite_ctl *ctl;
+	struct infect_ctx *ictx;
+	struct parasite_blob_desc *pbd;
+	int *arg;
+
+	compel_log_init(print_vmsg, LOG_DEBUG);
+
+	printf("Stopping task\n");
+	state = compel_stop_task(pid);
+	if (state < 0)
+		err_and_ret("Can't stop task");
+
+	printf("Preparing parasite ctl\n");
+	ctl = compel_prepare(pid);
+	if (!ctl)
+		err_and_ret("Can't prepare for infection");
+
+	printf("Configuring contexts\n");
+
+	/*
+	 * First -- the infection context. Most of the stuff
+	 * is already filled by compel_prepare(), just set the
+	 * log descriptor for parasite side, library cannot
+	 * live w/o it.
+	 */
+	ictx = compel_infect_ctx(ctl);
+	ictx->log_fd = STDERR_FILENO;
+
+	/*
+	 * Next the blob descriptor. We've requested for hgen
+	 * in Makefile, so prepare this type of blob with the
+	 * values from parasite.h.
+	 */
+	pbd = compel_parasite_blob_desc(ctl);
+	pbd->parasite_type	= COMPEL_BLOB_CHEADER;
+	pbd->hdr.mem		= parasite_blob;
+	pbd->hdr.bsize		= sizeof(parasite_blob);
+	pbd->hdr.nr_gotpcrel	= parasite_nr_gotpcrel;
+	pbd->hdr.parasite_ip_off	= COMPEL_H_PARASITE_HEAD(parasite_sym);
+	pbd->hdr.addr_cmd_off	= COMPEL_H_PARASITE_CMD(parasite_sym);
+	pbd->hdr.addr_arg_off	= COMPEL_H_PARASITE_ARGS(parasite_sym);
+	pbd->hdr.relocs		= parasite_relocs;
+	pbd->hdr.nr_relocs		= sizeof(parasite_relocs) / sizeof(parasite_relocs[0]);
+
+	printf("Infecting\n");
+	if (compel_infect(ctl, 1, sizeof(int)))
+		err_and_ret("Can't infect victim");
+
+	/*
+	 * Now get the area with arguments and run two
+	 * commands one by one.
+	 */
+	arg = compel_parasite_args(ctl, int);
+
+	printf("Running cmd 1\n");
+	*arg = 137;
+	if (compel_rpc_call_sync(PARASITE_CMD_INC, ctl))
+		err_and_ret("Can't run parasite command 1");
+
+	printf("Running cmd 2\n");
+	*arg = 404;
+	if (compel_rpc_call_sync(PARASITE_CMD_DEC, ctl))
+		err_and_ret("Can't run parasite command 2");
+
+	/*
+	 * Done. Cure and resume the task.
+	 */
+	printf("Curing\n");
+	if (compel_cure(ctl))
+		err_and_ret("Can't cure victim");
+
+	if (compel_resume_task(pid, state, state))
+		err_and_ret("Can't unseize task");
+
+	printf("Done\n");
+	return 0;
+}
+
+static inline int chk(int fd, int val)
+{
+	int v = 0;
+
+	read(fd, &v, sizeof(v));
+	printf("%d, want %d\n", v, val);
+	return v == val;
+}
+
+int main(int argc, char **argv)
+{
+	int p_in[2], p_out[2], p_err[2], pid, i, pass = 1;
+
+	/*
+	 * Prepare IO-s and fork the victim binary
+	 */
+	if (pipe(p_in) || pipe(p_out) || pipe(p_err)) {
+		perror("Can't make pipe");
+		return -1;
+	}
+
+	pid = vfork();
+	if (pid == 0) {
+		close(p_in[1]);  dup2(p_in[0], 0);  close(p_in[0]);
+		close(p_out[0]); dup2(p_out[1], 1); close(p_out[1]);
+		close(p_err[0]); dup2(p_err[1], 2); close(p_err[1]);
+		execl("./victim", "victim", NULL);
+		exit(1);
+	}
+
+	close(p_in[0]); close(p_out[1]); close(p_err[1]);
+
+	/*
+	 * Tell the little guy some numbers
+	 */
+	i = 1;  write(p_in[1], &i, sizeof(i));
+	i = 42; write(p_in[1], &i, sizeof(i));
+
+	printf("Checking the victim alive\n");
+	pass = chk(p_out[0], 1);
+	pass = chk(p_out[0], 42);
+	if (!pass)
+		return 1;
+
+	/*
+	 * Now do the infection with parasite.c
+	 */
+
+	printf("Infecting the victim\n");
+	if (do_infection(pid))
+		return 1;
+
+	/*
+	 * Tell the victim some more stuff to check it's alive
+	 */
+	i = 1234; write(p_in[1], &i, sizeof(i));
+	i = 4096; write(p_in[1], &i, sizeof(i));
+
+	/*
+	 * Stop the victim and check the infection went well
+	 */
+	printf("Closing victim stdin\n");
+	close(p_in[1]);
+	printf("Waiting for victim to die\n");
+	wait(NULL);
+
+	printf("Checking the result\n");
+
+	/* These two came from parasite */
+	pass = chk(p_out[0], 138);
+	pass = chk(p_out[0], 403);
+
+	/* These two came from post-infect */
+	pass = chk(p_out[0], 1234);
+	pass = chk(p_out[0], 4096);
+
+	if (pass)
+		printf("All OK\n");
+	else
+		printf("Something went WRONG\n");
+
+	return 0;
+}
diff --git a/compel/test/infect/victim.c b/compel/test/infect/victim.c
new file mode 100644
index 0000000..b37688b
--- /dev/null
+++ b/compel/test/infect/victim.c
@@ -0,0 +1,15 @@ 
+#include <unistd.h>
+
+int main(int argc, char **argv)
+{
+	int i;
+
+	while (1) {
+		if (read(0, &i, sizeof(i)) != sizeof(i))
+			break;
+
+		write(1, &i, sizeof(i));
+	}
+
+	return 0;
+}