/* /bin/cd: Changes the working directory of the caller.
 *
 * v. 0.0.1
 *
 * 2006-05-18, Copyright Paul Sladen <debian@paul.sladen.org>, 
 * Do with this code whatever you want (since you will anyway...!)
 *
 * $ gcc -o cd cd.c ; gcc -o cd-test cd-test.c
 * $ ./cd-test
 *
 * TODO: 
 * - work on things other than i386
 * - write the syscall injector code in C rather than asm.
 * - do something nicer using the stack for execution in the target
 *   process.  (Eg. fake a dlopen() by following the link_map).
 * - take the result of %eax (syscall return value) and return that
 *   as the exit() result.
 * - fetch $HOME from the environment and use that when no argument
 *   is passed.
 * - stat() the destination file to make sure that it is accessible.
 * 
 * NOTES:
 * - if you run 'cd' directly, it will confuse the hell out of your
 *   bash shell as you rip the carpet from underneath it.
 * - gdb/strace doesn't work for debugging a program that _relies_
 *   on ptrace as part of its execution...
 *
 * Written for Scott James Remnant, blame him for the evilness.
 *
 * Helpful pointers from 'man 2 ptrace' and Phrack, of all places.
 */

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <linux/user.h>
#include <signal.h>
#include <syscall.h>

static char *target = "..";

static void ptrace_setup(pid_t pid)
{
  if( ptrace(PTRACE_ATTACH, pid, 0, 0) == -1)
    perror("ptrace(PTRACE_ATTACH)"), exit(1);

  waitpid(pid, 0, 0);
}

static void ptrace_push(pid_t pid, struct user_regs_struct* regs, unsigned long x)
{
  regs->esp -= sizeof(x);
  ptrace(PTRACE_POKETEXT, pid, regs->esp, x);
}

static void ptrace_copyto(pid_t pid, const void* where, const void* what, int length)
{
  for(length = (length + 3) & ~3; length >= 0; length -= 4)
    if (ptrace(PTRACE_POKETEXT, pid, where+length, *(unsigned long *)(what+length)) == -1)
      perror("ptrace(PTRACE_POKETEXT)"), exit(2);
}

/* pusha, pushf */
#define LOAD_OFFSET (4 + 8*4 + 4 + 4)

static void ptrace_inject(pid_t pid, const void* code, int length)
{
  struct user_regs_struct regs;
  unsigned long start;

  if( ptrace(PTRACE_GETREGS, pid, 0, &regs) == -1)
    perror("ptrace(PTRACE_GETREGS)"), exit(3);

#if 0  
  printf("eip:\t%p\n"
	 "esp:\t%p\n",
	 regs.eip, regs.esp);
#endif

#define PUSH(x) ptrace_push(pid, &regs, x)

  PUSH(regs.eip); // Return Address

  PUSH(regs.eax); // Eight lines equivalent to 'pusha'
  PUSH(regs.ecx); 
  PUSH(regs.edx);
  PUSH(regs.ebx);
  PUSH(regs.esp); // %esp is ignored, so doesn't matter it being "wrong"
  PUSH(regs.ebp);
  PUSH(regs.esi);
  PUSH(regs.edi);
  PUSH(regs.eflags); // Equivalent to 'pushf'

  /* start, syscall, directory, null, rootkit, padding */
  start = regs.esp - (4 + 4 + 4 + strlen(target) + 1 + length + 8);
  
  PUSH(start);
  PUSH(SYS_chdir);

#undef PUSH

  ptrace_copyto(pid, (void *)start, target, strlen(target) + 1);
  start += strlen(target) + 1;
  ptrace_copyto(pid, (void *)start, code, length);
  
  regs.eip = start + 2;  /* nop, nop in case of sys_call restart */
  if( ptrace(PTRACE_SETREGS, pid, &regs, &regs) == -1 )
    perror("ptrace(PTRACE_SETREGS)"), exit(4);
}

void ptrace_run(pid_t pid)
{
  if (ptrace(PTRACE_SYSCALL, pid, 0, 0) == -1)
    perror("ptrace(PTRACE_DETACH)"), exit(5);;
  waitpid(pid, 0, 0);

  /* Syscall entry */
  if (ptrace(PTRACE_SYSCALL, pid, 0, 0) == -1)
    perror("ptrace(PTRACE_DETACH)"), exit(5);;
  waitpid(pid, 0, 0);

  /* Syscall exit (to catch errors).  Then leave it alone. */
  if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1)
    perror("ptrace(PTRACE_DETACH)"), exit(5);;
}

extern void trojan_chdir(void), trojan_chdir_end(void);

int main(int argc, char **argv)
{
  int length = trojan_chdir_end - trojan_chdir;
  pid_t pid = getppid();

  ptrace_setup(pid);
  ptrace_inject(pid, trojan_chdir, length);
  ptrace_run(pid);

  return 0;
}

__asm__(
	".section .rodata\n"
	".globl trojan_chdir\n"
	"trojan_chdir:\n"
	"	nop		/* incase Linux tries to restart a syscall() */\n"
	"	nop\n"
	"	pop %eax	/* syscall number */\n"
	"	pop %ebx	/* directory */\n"
	"	int $0x80\n"
	"	popf\n"
	"	popa\n"
	"//	int $0x3\n"
	"	ret\n"
	".globl trojan_chdir_end\n"
	"trojan_chdir_end:\n");
