/*
 * siringe -- non-destructive ptrace() injector
 *            see http://www.secdev.org/
 *            for more informations
 *
 * Copyright (C) 2004  Philippe Biondi <phil@secdev.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>
#define __KERNEL__ /* macros ERESTART... */
#include <linux/errno.h>
#undef __KERNEL__

/* $Id$ */

#define STACKSIZE 512

#define ERROR(msg) do{ perror(msg); exit(-1); }while(0)
#define USAGE do { fprintf(stderr, "Usage: siringe [-f <shellcode] -p <pid>\n"); exit(-2); }while(0)

int main(int argc, char *argv[])

{
	int res, i, pid=0, shlen;	
	long start, old_eip;
	struct user_regs_struct regs;
	char shcode[32768];
	int fd = 1;
	char c;

	while (1) {
		c = getopt(argc, argv, "p:f:");
		if (c == -1) break;
		switch (c) {
		case 'p': 
			pid = atoi(optarg);
			break;
		case 'f':
			fd = open(optarg, O_RDONLY);	
			if (fd == -1) ERROR("open");
			break;
		}
	}

	if (!pid) USAGE;
	shlen = read(fd, shcode, sizeof(shcode));
	if (!shlen) USAGE;
	
	/**** let's attach ****/
	res = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
	if (res == -1) ERROR("ptrace attach");
	res = waitpid(pid, NULL, WUNTRACED);
	if (res != pid) ERROR("waitpid");

	/**** We gather the registers ****/
	res = ptrace(PTRACE_GETREGS, pid, NULL, &regs);
	if (res == -1) ERROR("ptrace getregs");

	/**** We inject the code ****/
	start = regs.esp-STACKSIZE-shlen;
	for (i=0; i < shlen; i+=4) {
		res = ptrace(PTRACE_POKEDATA, pid, start+i, *(int *)(shcode+i));
		if (res == -1) ERROR("ptrace pokedata");
	}
		
	/**** We hijack the exection flow ****/
	old_eip = regs.eip;
	regs.eip = start;
	if ( (regs.orig_eax >= 0) &&
	     (regs.eax == -ERESTARTNOHAND ||
	      regs.eax == -ERESTARTSYS ||
	      regs.eax == -ERESTARTNOINTR) ) {
		regs.eip += 2; 
		old_eip -= 2;
	}

	/**** We simulate a call (push eip) ****/
	regs.esp -= 4;
	res = ptrace(PTRACE_POKEDATA, pid, regs.esp, old_eip);
	if (res == -1) ERROR("ptrace pokedata old_eip");

	/**** We update the registers ****/
	res = ptrace(PTRACE_SETREGS, pid, NULL, &regs);
	if (res == -1) ERROR("ptrace setregs");
	
	/**** We detach and begin to run  ****/
	res = ptrace(PTRACE_DETACH, pid, NULL, NULL);
	if (res == -1) ERROR("ptrace detach");

	return 0;
}

