#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef WIN32
#define lstrcmpi strcasecmp
#define lstrcmp strcmp
#define BYTE unsigned char
#define BOOL unsigned int
#define TRUE 1
#define FALSE 0
#else
#include <windows.h>
#include "aggressiveoptimize.h"
#endif
char key[256];
using namespace std;

#define ESTF_SIZE 32


void usage( void )
{
	fprintf( stderr, "ihpcrack v0.0.1 \nUsage:\n"
					 "ihpcrack (-i infile) \n"
					 "tries to crack a firmware binary using brute force\n"
					 "\n"
					);
	exit( -1 );
};

FILE * openinfile( const char * filename )
{
	FILE * F = fopen( filename, "rb" );
	if( F == NULL )
	{
		fprintf( stderr, "Couldn't open input file %s\n", filename );
		perror( "Error was " );
		exit( -1 );
	};
	return F;
};



void getfileargs( int argc, char * argv[], FILE ** infile, FILE ** outfile)
{
	*infile = stdin;
	*outfile = stdout;
 
	for( ; argc; --argc, ++argv )
	{
		if( lstrcmpi( *argv, "-i" ) == 0 )
		{
			if( *infile != stdin )
				usage();
			if( --argc )
				*infile = openinfile( *(++argv) );
			else
				usage();
		}
		else
			usage();
	};
	return;
};

void PrintHexLine(unsigned int off,BYTE *pD, int length)
{
	printf("0x%08x | ",off);

	for(int j=0;j<16;j++)
	{
		if (j<length)
			printf("%02x ",pD[j]);
		else
			printf("   ");

	}

	printf("| ");

	for(int j=0;j<length;j++)
	{
		if (j<length)
		{
			if (pD[j]>32 && pD[j]<128) printf("%c",pD[j]);
			else printf(".");
		}
		else
		{
			printf(" ");
		}
	}
	printf("\n");

}

void PrintHex(unsigned int off,BYTE *pD, int length)
{
	int l = length / 16;
	int m = length - l*16;

	for(int i=0;i<l;i++)
	{
		PrintHexLine(off,pD, 16);
		off+=16;
		pD+=16;
	}

	if (m>0) PrintHexLine(off,pD, m);
}

// returns 1 if the char is [A-B][a-b][0-9]
inline char IsLowerCase(unsigned char c)
{
	if ((c>='a') && (c<='z')) return 1;
	return 0;
}

inline char IsLowerCaseVowel(unsigned char c)
{
	if ((c=='a') || (c=='e') || (c=='i') || (c=='o') || (c=='u')) return 1;
	return 0;
}

inline char IsUpperCaseVowel(unsigned char c)
{
	if ((c=='A') || (c=='E') || (c=='I') || (c=='O') || (c=='U')) return 1;
	return 0;
}

inline char IsUpperCase(unsigned char c)
{
	if ((c>=65) && (c<=90)) return 1;
	return 0;
}

inline char IsDigit(unsigned char c)
{
	if ( (c>='0') && (c<='9')) return 1;
	return 0;
}

//
// returns the length of the alpha word found in the bynary string
// for example
// "Hello@#$%@#$%&" will return 5
// "HelLo@#$%@#$%&" will return 3
// note that strings with more that 3 consononat in a row will be rejected

inline int AlphaFromHere(unsigned char *pD,int len)
{
	int cnt=0;
	int consonants=0;
	int i=0;

	if (*pD>123) return 0;

	if (IsUpperCase(*pD))
	{
		cnt++;
		i=1;
		if (IsUpperCaseVowel(*pD)==0) 
        {
            consonants++;
        }
	}

	for(;i<len;i++)
	{
		if (IsLowerCase(pD[i]))
		{
            if (IsLowerCaseVowel(pD[i])==1)
            {
               consonants=0;                             
            }
            else
            {
                consonants++;
                //if there are more that 4 or more consonants in a row then bail out, 
                // and return 0 to make sure we skip this string"
                if (consonants>3) return 0;
            }
			cnt++;
		}
		else if ( pD[i]==' ')
		{
			cnt++;
		}
		else if (pD[i]==0)
		{
			cnt++;
			break;
		}
		else break;
	}

	return cnt;

}


// gets as input a bynary string and returns the size of the longest word found
// 
// it will print the valid words with more that 6 caracters
//

int LongestWordLength(unsigned char *pD,int len)
{
	int max=0;

	for(int i=0;i<len;i++)
	{
		int l=AlphaFromHere(&pD[i],len-i);
		if (l>6)
		{
			for(int j=0;j<l;j++)
			{
				printf ("%c",pD[j+i]);
			}
			printf ("\n");

			return l;

		}
	}

	return 0;
}

void ApplyKey(unsigned char *out,unsigned char *xorkey,unsigned char *pD, int len)
{
	for(int i=0;i<len;i++)
	{
		out[i] = xorkey[i] ^ pD[i];
	}
}

//
// apply key candidate to all the file in blocks of 16 bytes
//
void Try(unsigned char *pData, int lenread, unsigned char *candi, int len)
{
	//unsigned char candi[256];
	unsigned char outdata[256];

	for (int i=0;i<lenread;i++)
	{
		//decode
		ApplyKey(outdata,candi,&pData[i], len);
		if (outdata[0]=='f') continue;

		//found anything
		int a=LongestWordLength(outdata,len);
	}
}

int crack( int argc, char * argv[])
{
	FILE * infile = NULL;      
	FILE * outfile = NULL;      
	BYTE *pData;
	signed long lenread;

	getfileargs( argc, argv, &infile, &outfile);

	pData = (BYTE*)malloc(10*1024*1024);

	lenread = fread( pData, 1, 10*1024*1024, infile );

	int block = lenread/16;

	//unsigned char text[] = "SCAN MUSIC FILE";
	unsigned char text[] = "firmware upgrade";

	unsigned char candi[256];
	unsigned char outdata[256];


	for (int i=0;i<lenread;i++)
	{
		ApplyKey(candi,text,&pData[i], 16);

		if ((i&0xf)==0) printf ("%i\n",i);
		Try(pData, lenread,candi, 16);
	}


    return 1;

}


int main( int argc, char * argv[] )
{
//f( argc < 2 ) usage();

	return crack( argc-1,argv+1);

	return 0;
}
