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

#include <signal.h>

/*
  Brute force password cracker for the Edimax ES-5224R+, 24 port 10/100, 2 port gigabit
  switch, with serial console.

  If you suspect you know some letters of the password (ie. based on a permutatation
  of a known password) then this program has some chance of finding the password.
  The strcpy() in init() below provides a list of characters, and the function 
  check4bad() provides some way of trimming the search space by skipping known 
  impossible combinations. In my case it was repeated characters except the : 
  character, which could appear twice.

  For the search time to be realistic, the list of characters needs to be short. The
  list of 11 characters below, and the non-repeating policy of check4bad() would still
  take around 4 years to completely check the search space. If you had average luck,
  you would find the password in four years.


  Theory of operation:

  Implemented as a state machine that identifies key words in the switch output, leading
  to this program sending a username(admin) and password as response. The state machine
  expects the un/pw to fail, and so will stop when the response is different to a 
  failure.

  Example serial port code was nicked from a man page.
 */


#define BAUDRATE B9600
#define MODEMDEVICE "/dev/ttyS0"
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1

void ignore(int status)
 {
   fprintf(stderr,"Signaled\n");
 }

struct passgen
 {
   char chars[128];
   char out[9];
   int length, l, maxlength;
   int idx[8];
   int maxc;
   int optim[256], skip;
 } pg;

void init(struct passgen *pg)
 {
   int l;
   pg->maxlength=8;
   pg->length=1;
   for(l=0;l<pg->maxlength;l++)
      pg->idx[l]=0;
   pg->idx[0]=-1;
   strcpy(pg->chars,"0123456789a");
   strcpy(pg->chars,"DJcjIOio257:");
   strcpy(pg->chars,"12345");
   pg->maxc=strlen(pg->chars);
   pg->skip=0;
 }

int check4bad(struct passgen *pg)
 {
   int l;
   pg->skip=0;
   
   /* no repeats, taking account of case - but repeating : is okay */
   for(l=0;l<pg->maxc;l++)
      pg->optim[ tolower(pg->chars[l]) ]=0;
   for(l=0;l<pg->length;l++)
      pg->optim[ tolower(pg->chars[ pg->idx[l] ]) ]++;
   for(l=0;l<pg->maxc;l++)
      if( (pg->optim[ tolower(pg->chars[l]) ] >1 && pg->chars[l]!=':') || 
          (pg->optim[ tolower(pg->chars[l]) ] >2 && pg->chars[l]==':') )
        { pg->skip=1; return(1); }
   
   return(0);
 }

inc2next(struct passgen *pg)
 {
   int l;
   for(l=0;l<pg->length;l++)
    { 
      if( ++pg->idx[l]>=pg->maxc )
         pg->idx[l]=0;
      else
         break;
    }
   if( l==pg->length )
      pg->length++;
   if( pg->length>pg->maxlength )
    { pg->skip=1; return(0); }
   
   return(1);
 }

makenext(struct passgen *pg)
 {
   int l;
   for(l=0;l<pg->length;l++)
    {
      pg->out[l]=pg->chars[pg->idx[l]];
    }
   pg->out[pg->length]=0;
   //printf("pw=%s\n",pg->out);
 }    

int getnext(struct passgen *pg)
 {
   do {
      if( !inc2next(pg) )
         break;
   } while( check4bad(pg) );

   if( pg->skip )
      return(0);
		   
   makenext(pg);

   return(1);
 }


char *str="admin\015";

main()
 {
   int fd,c, res, i;
   struct termios oldtio,newtio;
   char buf[255];

   int silent;
   int state=-1;
   int datread=0;
   
   fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
   if (fd <0) {perror(MODEMDEVICE); exit(-1); }

   tcgetattr(fd,&oldtio); /* save current port settings */

   bzero(&newtio, sizeof(newtio));
   newtio.c_cflag = BAUDRATE /*| CRTSCTS*/ | CS8 | CLOCAL | CREAD;
   newtio.c_iflag = IGNPAR | IGNBRK;
   newtio.c_oflag = 0;

   /* set input mode (non-canonical, no echo,...) */
   newtio.c_lflag = 0;

   newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
   newtio.c_cc[VMIN]     = 1;   /* blocking read until 5 chars received */

   tcflush(fd, TCIFLUSH);
   tcsetattr(fd,TCSANOW,&newtio);

   /*signal( SIGINT, &ignore );
   signal( SIGHUP, &ignore );
   signal( SIGTERM, &ignore );
   signal( SIGQUIT, &ignore );*/
   signal( SIGALRM, &ignore );
 
   fprintf(stderr,"Sending init\n");
   write(fd,"\377\375\03\377\375\01\377\373\0",9);

   sleep(1);
   
   fprintf(stderr,"Pressing return\n");
   write(fd,"\015",1);
   
   init(&pg);
   
   fprintf(stderr,"Waiting for input\n");
   silent=0;
   do /* loop for input */
    {

      fd_set rfds;
      struct timeval tv;
      int retval;

      /* Watch fd to see when it has input. */
      FD_ZERO(&rfds);
      FD_SET(fd, &rfds);
      /* Wait up to five seconds. */
      tv.tv_sec = 5;
      tv.tv_usec = 0;

      retval = select(fd+1, &rfds, NULL, NULL, &tv);
      /* Don't rely on the value of tv now! */

      
      if( retval>=0 && FD_ISSET(fd, &rfds) )
       {
         res = read(fd,buf,255);   /* returns after 255 chars have been input */
         if( res>0 )
          {
            /*printf(" %d ",res );*/
            for( i=0;i<res;i++)
             {
               if( buf[i]==27 ) silent=1;
   	       if( silent==0 )
		{
                  printf("%c",buf[i]);
		  datread++;
		  if( buf[i]==' ' ) state=0;
		  if( state==0 && buf[i]=='u' )state=1;
		  if( state==1 && buf[i]==':' )state=2;
		  if( state==2 && buf[i]=='p' )state=3;
		  if( state==3 && buf[i]==':' )state=4;
		}
	       if( buf[i]=='H' )silent=0;
               /*if( buf[i]=='\r' )
                  printf("\n");*/
             }
            fflush(stdout);
            /*printf("\n");*/
          }
       }
      else
       {
	 int i;
         printf("No data within five seconds, best do something to wake it up, datread=%d.\n",datread);
	 tcsetattr(fd,TCSANOW,&oldtio);
	 exit(1);
       }
      if( state==4 ) {
	 printf("\nenter pw state reached, having read %d bytes\n",datread);
	 if( datread==140 || datread==150 ) usleep(1000000);
	 else usleep(100);  // 10k
	 if( datread!=163+pg.length )
	  {
		  FILE *fp=fopen("crackedimax.out","a");
		  fprintf(fp,"poss hit with %s, datread=%d\n",pg.out, datread);
		  fclose(fp);
	    printf("poss hit with %s\n",pg.out);
	  }
	 datread=0;
	 for(i=0;i<strlen(str);i++)
            write(fd,&str[i],1), usleep(10);
	 if( getnext(&pg) )
	  {
	    for(i=0;pg.out[i];i++)
               write(fd,&pg.out[i],1), usleep(10);  // 9000
	    char nl=015;
            write(fd,&nl,1), usleep(10);
	    printf("Testing %s\n",pg.out);
	  }
	 else
	  {
  	    res=0;
	  }
	 state=-1;
       }
    } while( res>0 );

   tcsetattr(fd,TCSANOW,&oldtio);

 } /* main() */


