/* cinac prod pour projet7.org v0.4              */
/* transforme un fichier executable en shellcode */
/* desassemble grace a gdb (donc il le requiere) */
/*              Coding Is Not A Crime            */

/* remerciements a un groupe qui monte et dont on peut etre fier. */
/* remerciements aussi aux squatteurs d'un channel d'entraide ;)  */


/* Pour vous expliquer un peu mieux comment le programme marche je 
 * vais vous donner un exemple concret. J'espere qu'il sera 
 * suffisement clair.
 *
 * [root@batmobile exeshell]# cat projet7.c
 *
 * void main()
 * {
 *   __asm__("
 *              mov $0x4, %eax    #  /
 *              mov $0x0, %ebx    # |
 *              pushl $0x0a377465 # |
 *              pushl $0x6a6f7270 # |  printf("projet7\n");
 *              movl %esp, %ecx   # |
 *              mov $0x8, %edx    # |
 *              int $0x80         #  \
 *              mov $0x1, %eax   #  /
 *              mov $0x0, %ebx   # |  exit(0);
 *              int $0x80        #  \
 *   ");
 * };
 * [root@batmobile exeshell]# cc projet7.c -o projet7
 * projet7.c: In function `main':
 * projet7.c:3: warning: return type of `main' is not `int'
 * [root@batmobile exeshell]# ./projet7
 * projet7
 * [root@batmobile exeshell]# ./exeshell -q -s projet7 -d exemple.c -D -c -z
 * Traitement en cours...
 *  Dump of assembler code for function main:
 * 0x8048430 <main>:	push   %ebp
 * 0x8048431 <main+1>:	mov    %esp,%ebp
 * 0x8048433 <main+3>:	mov    $0x4,%eax
 * 0x8048438 <main+8>:	mov    $0x0,%ebx
 * 0x804843d <main+13>:	push   $0xa377465
 * 0x8048442 <main+18>:	push   $0x6a6f7270
 * 0x8048447 <main+23>:	mov    %esp,%ecx
 * 0x8048449 <main+25>:	mov    $0x8,%edx
 * 0x804844e <main+30>:	int    $0x80
 * 0x8048450 <main+32>:	mov    $0x1,%eax
 * 0x8048455 <main+37>:	mov    $0x0,%ebx
 * 0x804845a <main+42>:	int    $0x80
 * 0x804845c <main+44>:	pop    %ebp
 * 0x804845d <main+45>:	ret    
 * 0x804845e <main+46>:	mov    %esi,%esi
 * End of assembler dump.
 * (gdb)
 * Les instructions qui contiennent des opcodes \x00 sont les suivantes:
 *
 * 0x8048433 <main+3>:	mov    $0x4,%eax
 * 0x8048438 <main+8>:	mov    $0x0,%ebx
 * 0x8048449 <main+25>:	mov    $0x8,%edx
 * 0x8048450 <main+32>:	mov    $0x1,%eax
 * 0x8048455 <main+37>:	mov    $0x0,%ebx
 * Traitement termine. Shellcode genere dans le fichier exemple.c
 * [root@batmobile exeshell]# cat exemple.c
 * char shellcode[] = "\xb8\x04\x00\x00\x00\xbb\x00\x00\x00\x00\x68\x65\x74
 * \x37\x0a\x68\x70\x72\x6f\x6a\x89\xe1\xba\x08\x00\x00\x00\xcd\x80\xb8\x01
 * \x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\x5d\xc3";
 *
 * void main()
 * {
 *   int *ret;
 *
 *   ret = (int* )&ret + 2;
 *   (*ret) = (int)shellcode;
 * }
 * [root@batmobile exeshell]# cc exemple.c -o exemple
 * exemple.c: In function `main':
 * exemple.c:4: warning: return type of `main' is not `int'
 * [root@batmobile exeshell]# ./exemple
 * projet7
 * [root@batmobile exeshell]#
 *
 * Voila j'espere que ca aura ete clair. Cet exemple ne montre bien
 * sur pas toutes les possibilities du programme. Cette version ne
 * traite pas encore les appels aux syscalls tels qu'un 
 * "call 0x804831c <execve>" mais le fera dans la v0.5. Il est
 * fortement conseille d'ecrire l'executable que vous voulez 
 * transformer en shellcode de la maniere decrite dans l'exemple car,
 * etant base sur gdb, il ne peut utiliser que les fonctionnalites ce
 * dernier. Les fonctions du genre malloc(), si elles sont utilises
 * dans votre executable, ne donneront pas un shellcode susseptible de
 * fonctionner.
 *
 * Il faut voir dans ce petit utilitaire un moyen de gagner du temps
 * dans la realisation de shellcodes et non pas l'ultime outil. J'ai
 * cree d'autre outils plus ou moins du meme genre ayant comme sujet
 * les shellcodes, si cela vous interresse mailez moi,
 * cinac@projet7.org
 *
 */


#include <stdio.h>
#include <unistd.h>
#include <netdb.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#define UTOPISTE "bien sur, pas toi? dommage :("


int algohash(char * phrase, unsigned int debut, char * mot);
int nbreopcode(char * sectext);
void erreur(char * progname);


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

  char c;
  int tube[2], tube2[2];
  int i, j, k, l, increm, fin, transtart, debut, memlen, nbre, total, bed, nbrph, deja;
  int options, optim, opcoded, opcodef, disas, zero, about;
  char filebuffer[2];
  unsigned char buffer[50];
  int * tabled, * tablef, * tablem;
  char * point1, * point2, * point3;
  char * srcfile, * destfile;
  char * argvo[4];
  FILE * fd;


  i = increm = fin = transtart = debut = opterr = optim = disas = zero = about = 0;
  opcoded = opcodef = deja = -2;
  srcfile = destfile = NULL;

  while((options = getopt(argc, argv, "co:O:Dzqs:d:")) != -1)
  {
    switch(options)
    {
      case 'c':
        optim = 1;
        break;
      case 'o':
        opcoded = atoi(optarg);
        if (opcoded < 0) erreur(argv[0]);
        break;
      case 'O':
        opcodef = atoi(optarg);
        if (opcodef < 1) erreur(argv[0]);
        break;
      case 'D':
        disas = 1;
        break;
      case 'z':
        zero = 1;
        break;
      case 'q':
        about = 1;
        break;
      case 's':
        srcfile = optarg; 
        break;
      case 'd':
        destfile = optarg;
        break;
      case '?':
        erreur(argv[0]);
        break;
    }
  }

  if (srcfile == NULL || destfile == NULL) erreur(argv[0]);

  if (pipe(tube) != 0)
  {
    fprintf(stderr, "\E[31mErreur\E[0m: l'appel a tube() a echoue\n");
    exit(-1);
  } 

  if (pipe(tube2) != 0)
  {
    fprintf(stderr, "\E[31mErreur\E[0m: l'appel a tube() a echoue\n");
    exit(-1);
  }

  if ((fd = fopen(srcfile, "r")) == NULL)
  {
    fprintf(stderr, "\E[31mErreur\E[0m: Binaire introuvable\n");
    exit(-1);
  }
  else
  {
    fclose(fd);
  }

  if (about != 1)
  {
    fprintf(stdout, "\E[32m\E[1mExeshell\E[0m Cinac Prod. pour projet7.org (version 0.4)\n");
    fprintf(stdout, "Bug report: cinac@projet7.org\n");
  }
  fprintf(stdout, "Traitement en cours... (il peut prendre plusieurs minutes)\n");

  switch (fork())
  {
    case 1:
      fprintf(stderr, "\E[31mErreur\E[0m: l'appel a fork() a echoue");
      exit(-1);

    case 0:
      dup2(tube[1], STDOUT_FILENO);
      dup2(tube2[0], STDIN_FILENO);
      sprintf(buffer, "gdb %s", srcfile);
      argvo[0] = "sh";
      argvo[1] = "-c";
      argvo[2] = (char *)buffer;
      argvo[3] = 0;
      execve("/bin/sh", argvo, NULL);
      break;

    default:
      sleep(1);
      while(1)
      {
        read(tube[0], &c, 1);
        if (i == 0)
        {
           point1 = (char *)malloc(1);
           if (increm == 2) transtart ++;
        }
        else
        {
          point1 = (char *)realloc(point1, i+1);
        }
        if (point1 == NULL)
        {
          fprintf(stderr, "\E[31mErreur\E[0m: manque de memoire\n");
          exit(-1);
        }
        point1[i] = c;
        i++;

        if (algohash(point1, 0, "was configured") != -1 && algohash(point1, 0, "(gdb)") != -1)
        {
          fflush(stdout);
          strcpy(buffer, "disassemble main\n");
          for (j = 0; j < strlen(buffer); j++)
          {
            write(tube2[1], &buffer[j], 1); 
          }
          i = 0;
        }

        if (algohash(point1, 0, "End of assembler dump.") != -1 && algohash(point1, 0, "(gdb)") != -1)
        {
          if (zero == 1)
          {
            point3 = (char *)malloc(strlen(point1)+1);
            if (point3 == NULL)
            {
              fprintf(stderr, "\E[31mErreur\E[0m: manque de memoire\n");
              exit(-1);
            }
            for (j = 0; j < strlen(point1)+1; j++) point3[j] = point1[j];
          }
          if (disas == 1)
          {
            fprintf(stdout,"%s\n", point1);
          }
          fflush(stdout);
          if (opcoded != -2)
          {
            memlen = opcoded-1;  /* 3 */
          }
          else
          {
            memlen = 2;
          }
          increm = 2;
          strcpy(buffer, "x/bx main+3\n");
          for (j = 0; j < strlen(buffer); j++)
          {
            write(tube2[1], &buffer[j], 1);
          }
          nbre = nbreopcode(point1);
          if (opcoded > nbre-1)
          {
            fprintf(stderr, "\E[31mErreur\E[0m: l'opcode de depart est plus grand que l'opcode maximum donne par gdb\n");
            exit(-1); 
          }
          if (opcodef != -2)
          {
            if (opcodef > nbre-1) 
            {
              fprintf(stderr, "\E[31mErreur\E[0m: l'opcode de fin est plus grand que l'opcode maximum donne par gdb\n");
              fprintf(stderr, "le traitement continu quand meme mais avec comme opcode de fin <main+%d>\n", nbre-1);
            }
            else
            {
              nbre = opcodef+1;
            }
            if (memlen+2 > nbre)
            {
              fprintf(stderr, "\E[31mErreur\E[0m: l'opcode de debut(%d) est superieur ou egal a l'opcode de fin(%d). Commande sans interet\n", memlen+1, nbre);
              exit(-1);
            }
          }
        }

        if (algohash(point1, 0, "(gdb)") != -1 && increm == 2)
        {
          fflush(stdout);
          if (transtart > 1)
          {
            if (transtart == 2)
            {
              point2 = (char *)malloc(strlen(point1));
              if (point2 == NULL)
              {
                fprintf(stderr, "\E[31mErreur\E[0m: manque de memoire\n");
                exit(-1); 
              }
              strcpy(point2, point1);
            }
            else
            {
              point2 = (char *)realloc(point2, strlen(point2) + strlen(point1) + 1);
              if (point2 == NULL)
              {
                fprintf(stderr, "\E[31mErreur\E[0m: manque de memoire\n");
                exit(-1);
              }
              strcat(point2, point1);
            }
          }
          memlen++;
          sprintf(buffer, "x/bx main+%d\n", memlen); 
          increm = 2;
          for (j = 0; j < strlen(buffer); j++)
          {
            write(tube2[1], &buffer[j], 1);
          }
          i = 0;
          memset(point1, 0, strlen(point1));
          free(point1);
          point1 = NULL;
          if (memlen > nbre) 
          { 
            strcpy(buffer, "quit\n");
            for (j = 0; j < strlen(buffer); j++)
            {
              write(tube2[1], &buffer[j], 1);
            }
            close(tube[0]);
            close(tube[1]);
            close(tube2[0]);
            close(tube2[1]);
            break;
          } 
        }
      }       /* /while  */
  }           /* /switch */

  fd = fopen(destfile, "w");

  if (optim == 1) fprintf(fd, "char shellcode[] = ");
  fprintf(fd, "\"");

  i = j = 0;

  if (zero == 1)
  {
    for (k = 0, nbrph = 0; k < strlen(point3); k++)
    {
      if (point3[k] == '\n') nbrph++;
    }
  
    tabled = calloc(nbrph, 4);
    tablef = calloc(nbrph, 4);
    tablem = calloc(nbrph, 4);
    if (tabled == NULL || tablef == NULL || tablem == NULL)
    {
      fprintf(stderr, "\E[31mErreur\E[0m: manque de memoire\n");
      exit(-1);
    }

    for (k = 0, l = 0, nbrph = 0; k < strlen(point3); k++)
    {
      if (point3[k] == '\n')
      {
        tabled[nbrph] = l;
        tablef[nbrph] = k;
        if (point3[tabled[nbrph]+16] == '>') tablem[nbrph] = 0;
        if (point3[tabled[nbrph]+16] == '+')
        {
          for (bed = tabled[nbrph]+16, total = 0;;bed++)
          {
            if (point3[bed] == '>')
            {
              tablem[nbrph] = total;
              break;
            }
            filebuffer[0] = point3[bed];
            filebuffer[1] = '\0';
            total = (total * 10) + atoi(filebuffer);
          }
        }
        l = k;
        nbrph++;
      }
    }
    memset(filebuffer, 0, 2);
  }

  for(increm = 0; increm < strlen(point2); increm++)
  {
    filebuffer[0] = point2[increm];
    if (filebuffer[0] == ':' ) debut = 1;
    if (debut == 1)
    {
      if (i == 0)
      {
        point1 = (char *)malloc(2);
      }
      else
      {
        point1 = (char *)realloc(point1, i+2);
      }
      if (point1 == NULL)
      {
        fprintf(stderr, "\E[31mErreur\E[0m: manque de memoire\n");
        exit(-1);
      }
      point1[i] = filebuffer[0];
      point1[i+1] = '\0';
      i++;
    }
    if (i != 0 && filebuffer[0] == '(')
    {
      j++;
      i = 0;
      debut = 0;
      fprintf(fd, "\\%c%c%c", point1[3], point1[4], point1[5]);
      if (zero == 1)
      {
        if (point1[4] == '0' && point1[5] == '0')
        {
          for(k = 1;; k++)
          {
            if (j+2 >= tablem[k] && j+2 < tablem[k+1])
            {
              if (deja == -2) fprintf(stdout, "\nLes instructions qui contiennent des opcodes \\x00 sont les suivantes:");
              if (deja != tablem[k]) for (l = tabled[k]; l < tablef[k]; l++) fprintf(stdout, "%c", point3[l]);
              deja = tablem[k];
              break;
            }
          }
        }
      }
      free(point1);
      point1 = NULL;
    }

    if (filebuffer[0] == '\0') break;
    memset(filebuffer, 0, 2);
  }
  if (zero == 1)
  {
    free(point3);
    point3 = NULL;
  }
  fprintf(fd, "\"");
  if (optim == 1) fprintf(fd, ";\n\nvoid main()\n{\n  int *ret;\n\n  ret = (int* )&ret + 2;\n  (*ret) = (int)shellcode;\n\n}\n");

  fclose(fd);
  free(point2);
  point2 = NULL;
  
  if (zero == 1) fprintf(stdout, "\n");
  fprintf(stdout, "Traitement termine. Shellcode genere dans le fichier %s\n", destfile);
  exit(0);
}





int algohash(char * phrase, unsigned int debut, char * mot)
{
  int i = 0, j, k;

  if (strlen(phrase) == 0) return(-1);
  for (j = debut; j < strlen(phrase); j++)
  {
    k = 0;
    debt:
    if (phrase[j+k] == mot[k])
    {
      if (k == strlen(mot)-1)
      {
        i = 1;
        break;
      }
      else
      {
        k++;
        goto debt;
        /* mais non c pas lame un goto
        c'est rapide et ca evite de se
        prendre la tete :) */
      }
    }
  }
  
  if (i != 1)
  {
    return(-1);
  }
  else
  {
    return(j + strlen(mot));
  }
}




int nbreopcode(char * sectext)
{
  int i;
  int j = 0;
  int lentotal;
  int debutdeligne;
  char * point1;
  char * point2;

  for (i = 0; i < strlen(sectext); i++)
  {
    if (sectext[i] == '\n')
    {
      j++;
      lentotal = j;
    }
  } 
  j = 0;
  for (i = 0; i < strlen(sectext); i++)
  {
    if (sectext[i] == '\n')
    {
      j++;
      if (j == (lentotal - 2)) debutdeligne = i;
    }
  }
 
  point1 = (char *)malloc(strlen(sectext)-debutdeligne);
  if (point1 == NULL)
  {
    fprintf(stderr, "\E[31mErreur\E[0m: manque de memoire\n");
    exit(-1);
  }
  for (i = debutdeligne; i < strlen(sectext); i++) point1[i-debutdeligne] = sectext[i];

  i = algohash(point1, 0, "<main+");
  j = algohash(point1, i, ">");  

  lentotal = 0;
  for (; i < j-1; i++)
  {
    if (lentotal == 0)
    {
      point2 = (char *)malloc(2);
    }
    else
    {
      point2 = (char *)realloc(point2, lentotal+2);
    }
    if (point2 == NULL)
    {
      fprintf(stderr, "\E[31mErreur\E[0m: manque de memoire\n");
      exit(-1);
    }
    point2[lentotal] = point1[i];
    lentotal++;
  }
  point2[lentotal] = '\0';
  lentotal = atoi(point2);
  
  free(point1);
  point1 = NULL;
  free(point2);
  point2 = NULL;

  return(lentotal);
}





void erreur(char * progname)
{
  fprintf(stdout, "\n\E[32m\E[1mUsage:\E[0m %s [options] -s <programme_a_transformer_en_shellcode> -d <fichier_destine_a_contenir_le_shellcode>\n\n", progname);
  fprintf(stdout, " \E[31m\E[1moptions:\E[0m\n  -c : ecrit un code en C overflowant un buffer avec le shellcode\n       nouvellement cree pour qu'une fois compile vous puissiez\n       tester directement la validite du shellcode\n");
  fprintf(stdout, "  -o : choix de l'adresse ou doit commencer le shellcode on ne\n       specifie que le nombre d'octet depuis le main (par defaut\n       3, <main+3>)\n");
  fprintf(stdout, "  -O : choix de l'adresse ou doit finir le shellcode specifie que\n       le nombre d'octet depuis le main. Si la valeur est\n       superieure a la valeur maximale que donne gdb, la valeur\n       que donne gdb sera prefere a la votre\n");
  fprintf(stdout, "  -D : affiche le binaire en assembleur (fonction 'disassemble\n       main' de gdb)\n");
  fprintf(stdout, "  -z : affiche les instructions qui, une fois traduite en opcodes\n       contiennent des \\x00\n");
  fprintf(stdout, "  -q : permet de ne pas afficher les infos relatives au code\n");
  fprintf(stdout, "  -s : fichier binaire source que vous voulez transformer en\n       shellcode\n");
  fprintf(stdout, "  -d : fichier contenant, apres traitement, le shellcode\n\n");
  exit(0);
}
