CMU Binary Bomb

blevy

2018/08/27

Categories: reverse-engineering

Binary

bomb

Reversed source code

bomb.c

FILE *infile;
int num_input_strings = 0;
char input_strings[...][80];

void initialize_bomb(void)
{
	signal(SIGINT, sig_handler);
}

void sig_handler(void)
{
	printf("So you think you can stop the bomb with ctrl-c, do you?\n");
	sleep(3);
	printf("Well...");
	fflush(stdout);
	sleep(1);
	print("OK. :-)\n");
	exit(16);
}

// Returns whether a string has only space characters
int blank_line(char *input)
{
	if (input[0] == '\0') {
		return 1;
	}
	for (;;) {
		int c = *input;
		input++;
		if (isspace(c)) {
			if (*input == '\0') {
				return 1;
			}
		} else {
			return 0;
		}
	}
}

// Can return NULL
char *skip(void)
{
	do {
		char *input_string = input_strings[num_input_strings];
		char *input = fgets(input_string, 80, infile);
		if (input == NULL) {
			break;
		}
	} while (blank_line(input));
	return input;
}

void explode_bomb(void)
{
	printf("\nBOOM!!!\n");
	printf("The bomb has blown up.\n");
	exit(8);
}

char *read_line(void)
{
	char *line;
	
	line = skip();
	if (line == NULL) {
		if (infile == stdin) {
			printf("Error: Premature EOF on stdin\n");
			explode_bomb();
		}
		char *env = getenv("GRADE_BOMB");
		if (env != NULL) {
			exit(0);
		}
		infile = stdin;
		line = skip();
		if (line == NULL) {
			printf("Error: Premature EOF on stdin\n");
			explode_bomb();
		}
	}
	size_t len = strlen(line) + 1;
	if (len >= 0x4f) {
		printf("Error: Input line too long\n");
		explode_bomb();
	}
	input_strings[num_input_strings][len] = '\0';
	num_input_strings++;
}

// Equivalent to strlen()
size_t string_length(char *s)
{
	if (*s == '\0') {
		return 0;
	}
	size_t len = 0;
	do {
		s++;
		len++;
	} while (*s != '\0');
	return len;
}

// Equivalent to strcmp()
bool strings_not_equal(char *s1, char *s2)
{
	// STUB
}

// "Public speaking is very easy."
void phase_1(char *line)
{
	if (strings_not_equal(line, "Public speaking is very easy.")) {
		explode_bomb();
	}
}

void read_six_numbers(char *line, int *nums)
{
	int n_nums_read = sscanf(line, "%d %d %d %d %d %d", nums[0], nums[1],
			nums[2], nums[3], nums[4], nums[5]);
	if (n_nums_read != 6) {
		explode_bomb();
	}
}

// "1 2 6 24 120 720"
void phase_2(char *line)
{
	int nums[6];

	read_six_numbers(line, nums);
	if (nums[0] != 1) {
		explode_bomb();
	}
	int i = 1;
	do {
		int prod = i + 1;
		prod *= nums[i - 1];
		if (nums[i] != prod) {
			explode_bomb();
		}
		i++;
	} while (i <= 5);
}

// "0 q 777"
void phase_3(char *line)
{
	int n1, c, n2, c_chk;

	int nread = sscanf(line, "%d %c %d", n1, c, n2);
	if (nread != 3) {
		explode_bomb();
	}
	if (n1 > 7) {
		explode_bomb();
	}
	switch (n1) {
	case 0:
		c_chk = 0x71;
		if (n2 != 0x309) {
			expolde_bomb();
		}
		break;
	case 1:
		c_chk = 0x62;
		if (n2 != 0xd6) {
			expolde_bomb();
		}
		break;
	case 2:
		c_chk = 0x62;
		if (n2 != 0x2f3) {
			expolde_bomb();
		}
		break;
	case 3:
		c_chk = 0x6b;
		if (n2 != 0xfb) {
			expolde_bomb();
		}
		break;
	case 4:
		c_chk = 0x6f;
		if (n2 != 0xa0) {
			expolde_bomb();
		}
		break;
	case 5:
		c_chk = 0x74;
		if (n2 != 0x1ca) {
			expolde_bomb();
		}
		break;
	case 6:
		c_chk = 0x76;
		if (n2 != 0x30c) {
			expolde_bomb();
		}
		break;
	case 7:
		c_chk = 0x62;
		if (n2 != 0x20c) {
			expolde_bomb();
		}
		break;
	default:
		explode_bomb();
	}
	if (c != c_chk) {
		explode_bomb();
	}
}

int fibo(int num)
{
	if (num <= 1) {
		return 1;
	}
	return fibo(num - 1) + fibo(num - 2);
}

// "9"
void phase_4(char *line)
{
	int num;
	int nread = sscanf(line, "%d", num);
	if (nread != 1) {
		explode_bomb();
	}
	if (num <= 0) {
		explode_bomb();
	}
	if (fibo(num) != 55) {
		explode_bomb();
	}
}

// "? %+-!"
void phase_5(char *line)
{
	char buf[7];

	if (string_length(line) != 6) {
		explode_bomb();
	}
	int i = 0;
	do {
		buf[i] = "isrveawhobpnutfg\xb0\x01"[line[i] & 0xf];
		i++;
	} while (i <= 5);
	buf[6] = '\0';
	if (strings_not_equal(buf, "giants")) {
		explode_bomb();
	}
}

struct list_node {
	uint32_t data;
	uint32_t list_node_num;
	struct list_node *next;
};

struct list_node node6 = {
	0x1b0,
	6,
	NULL
};
struct list_node node5 = {
	0xd4,
	5,
	&node6
};
struct list_node node4 = {
	0x3e5,
	4,
	&node5
};
struct list_node node3 = {
	0x12d,
	3,
	&node4
};
struct list_node node2 = {
	0x2d5,
	2,
	&node3
};
struct list_node node1 = {
	0xfd,
	1,
	&node2
};

// "4 2 6 3 1 5"
void phase_6(char *line)
{
	// [gb]
	// Get 6 numbers, and check that they are unique and all 5 or less
	int nums[6];
	struct list_node *node = &node1;
	// Input line is 6 numbers
	read_six_numbers(line, nums);
	int i = 0;
	// [gd]
	do {
		// [gd]
		// All numbers must be 5 or less
		if (nums[i] > 6 && nums[i] <= 0) {
			// [gf]
			explode_bomb();
		}
		// [gc]
		// All numbers unique
		if (i <= 4) {
			// [gh]
			int j = i + 1;
			// [gj]
			do {
				if (nums[i] == nums[j]) {
					// [gk]
					explode_bomb();
				}
				// [gi]
				j++;
			} while (j <= 5);
		}
		// [gg]
		i++;
	} while (i <= 5);

	// [gl]
	i = 0;
	struct list_node *ptee;
	struct list_node *nodes[6];
	nodes[0] = &ptee;
	// [gn]
	// Order nodes in an array
	do {
		if (nums[i] > 1) {
			// [go]
			int j = i;
			do {
				node = node->next;
				j++;
			} while (j < num[j]);
		}
		// [gm]
		nodes[i] = node;
		i++;
	} while (i <= 5);
	// [gq]
	node = ptee;
	i = 1;
	// [gr]
	// Order list based on array of nodes
	do {
		node->next = ptee[i];
		node = ptee[i];
		i++;
	} while (i <= 5);
	// [gs]
	node->next = NULL;
	node = ptee;
	i = 0;
	// [gu]
	// Ensure that data is in descending order
	do {
		// List must be in descending order?
		if (node->data < node->next->data) {
			// [gv]
			explode_bomb();
		}
		// [gt]
		node = node->next;
		i++;
	} while (i <= 4);
}

struct tree_node {
	int val;
	struct tree_node *next1;
	struct tree_node *next2;
	char pad[5 * 4];
};

struct tree_node n1 = {
	0x24,
	&n21,
	&n22
};

struct tree_node n21 = {
	0x8,
	&n31,
	&n32
};

struct tree_node n31 = {
	0x6,
	&n41,
	&n42
};

struct tree_node n41 = {
	0x1,
	NULL,
	NULL
};

struct tree_node n42 = {
	0x7,
	NULL,
	NULL
};

struct tree_node n32 = {
	0x16,
	&n43,
	&n44
};

struct tree_node n43 = {
	0x14,
	NULL,
	NULL
};

struct tree_node n44 = {
	0x23,
	NULL,
	NULL
};

struct tree_node n22 = {
	0x32,
	&n33,
	&n34
};

struct tree_node n33 = {
	0x2d,
	&n45,
	&n46
};

struct tree_node n45 = {
	0x28,
	NULL,
	NULL
};

struct tree_node n46 = {
	0x2f,
	NULL,
	NULL
};

struct tree_node n34 = {
	0x6b,
	&n47,
	&n48
};

struct tree_node n47 = {
	0x63,
	NULL,
	NULL
};

struct tree_node n48 = {
	0x3e9,
	NULL,
	NULL
};

// 1st run: tree_node = &n1, return 7
// 2nd run: tree_node = &n22, return 3
// 3nd run: tree_node = &n34, return 1
// 4th run: tree_node = &n48, return 0
int fun7(struct tree_node *tree_node, int num)
{
	// Shuould never be true
	if (tree_node == NULL) {
		return -1;
	}
	if (num >= tree_node->val) {
		// [ge]
		// 4th run num must be == 0x3e9
		if (num == tree_node->val) {
			return 0;
		}
		// [gh]
		// 1st run num must be > 0x24
		// 2nd run num must be > 0x32
		// 3rd run num must be > 0x6b
		return fun7(tree_node->next2, num) * 2 + 1;
	} else {
		// [gf]
		return fun7(tree_node->next1, num) * 2;
	}
}

void secret_phase(void)
{
	char *line = read_line();
	int num = strtol(line, NULL, 10);
	// 0 <= num <= 0x3e9
	if (num > 0x3e9 || num < 0) {
		explode_bomb();
	}
	if (fun7(n1, num) != 7) {
		// [gi]
		explode_bomb();
	}
	printf("Wow! You've defused the secret stage!\n");
	phase_defused();
}

void phase_defused(void)
{
	if (num_input_strings != 6) {
		return;
	}
	int num;
	char *str;
	if (sscanf((char *) 0x804b770, "%d %s", num, str) != 2) {
		goto defused;
	}
	if (strings_not_equal(str, "austinpowers")) {
		goto defused;
	}
	printf("Curses, you've found the secret phase!\n");
	printf("But finding it and solving it are quite different...\n");
	secret_phase();
defused:
	printf("Congratulations! You've defused the bomb!\n");
}

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

	if (argc == 1) {
		infile = stdin;
	} else if (argc == 2) {
		infile = fopen(argv[1], "r");
		if (infile == NULL) {
			printf("%s: Error: Couldn't open %s\n");
			exit(8);
		}
	} else {
		printf("Usage: %s [<input_file>]\n");
		exit(8);
	}

	initialize_bomb();
	printf("Welcome to my fiendish little bomb. You have 6 phases with\n");
	printf("which to blow yourself up. Have a nice day!\n");
	line = read_line();
	phase_1(line);
	phase_defused();
	printf("Phase 1 defused. How about the next one?\n");
	line = read_line();
	phase_2(line);
	phase_defused();
	printf("That's number 2.  Keep going!\n");
	line = read_line();
	phase_3(line);
	phase_defused();
	printf("Halfway there!\n");
	line = read_line();
	phase_4(line);
	phase_defused();
	printf("So you got that one.  Try this one.\n");
	line = read_line();
	phase_5(line);
	phase_defused();
	printf("Good work!  On to the next...\n");
	line = read_line();
	phase_6(line);
	phase_defused(); // Secret phase
}

Correct input

input.txt

Public speaking is very easy.
1 2 6 24 120 720
0 q 777
9 austinpowers
? %+-!
4 2 6 3 1 5
1001