Binary
bomb
Reversed source code
bomb.c
// Functions that are not relevant to defusing the bomb are marked "STUB"
static void libc_start_main(void *arg)
{
// STUB
}
static void disarm_handler(void)
{
// STUB
}
// Setting all wires to 0 defuses the bomb
static int wire_yellow = 1;
static int wire_green = 1;
static int wire_blue = 1;
static int wire_red = 1;
/*
* This function gets the amount of time left to display in timer. Since this
* lab is over (it was originally given as a lab in a class at RPI), the timer
* is blank.
*/
static int gettimeleft(int *arg1, int *arg2, int *arg3)
{
// STUB
}
// Print the menu
static void menu(void)
{
// STUB
}
/*
* All of the functions with names ending in "_preflight" except
* green_preflight() use this to read in input.
*/
static char buffer[24];
static void yellow_preflight(void)
{
printf("ENTER_UNLOCK_PASSWORD_1:");
fgets(buffer, 10, stdin);
}
/*
* Password: "84371065"
*
* This one is just a simple check. :)
*/
static void yellow(void)
{
yellow_preflight();
if (strncmp(buffer, "84371065", 10) == 0) {
puts("UNLOCK_PASSWORD_1_ACCEPTED__LOCK_DISENGAGED");
wire_yellow = 0;
return;
}
wire_yellow <<= 0xa;
}
static void green_preflight(char *buf)
{
printf("ENTER_UNLOCK_PASSWORD_2:");
fgets(buf, 0x14, stdin); // UNSAFE
}
/*
* Password: "dcaotdaeAAAAb"
*
* The fgets() in green_preflight() allows buf to overflow into mystery. Since
* it's being bitwise and'ed with 1, the overwritten value needs to be even for
* the mystery == 0 at the end to be true. Dividing by 2 changes the default
* value 1 to 0. Additionally, the strncmp() requires that the first 8
* characters match "dcaotdae". The "AAAA" overwrites the full length of the
* buffer. Since x86 is little-endian, the first byte is least-significant and
* determines if mystery is even. The ASCII value of 'b' is even, which makes
* the bitwise and with 1 set mystery to zero. This function has stack smash
* protection enabled, and the stack guard is directly after mystery. Care must
* be taken to ensure that it is not overwritten. The fgets() inserts a newline
* and null byte at the end of the input, which sets the first 3 bytes of
* mystery to "b\n\0". Since mystery is 4 bytes, the buffer does not overflow
* into the stack guard.
*/
static void green(void) {
char buf[0xc];
int mystery = 1;
green_preflight(buf);
if (strncmp("dcaotdae", buf, 8) == 0) {
// [gh]
puts("UNLOCK_PASSWORD_2_ACCEPTED__LOCK_DISENGAGED");
mystery &= 1;
usleep(0x7a120);
puts("ACTION_OVERRIDDEN_FROM_USER_NOIZEV__LOCK_RE_ENGAGED");
// Oh no I'm doomed!
mystery &= 1;
} else {
// [gc]
wire_green *= 2;
}
// [gg]
if (mystery == 0) {
// [gj]
wire_green /= 2;
}
}
static void blue_preflight(void)
{
printf("ENTER CIRCUIT TRAVERSAL PATH:");
fgets(buffer, 0x10, stdin);
}
struct node {
struct node *next;
};
// sizeof(graph) = 192
// sizeof(graph) / sizeof(node) = 48
struct node graph[48] = {
0x0804c19c, 0x47bbfa96,
0x0804c178, 0x0804c214,
0x50171a6e, 0x0804c1b4,
0x0804c1d8, 0x23daf3f1,
0x0804c1a8, 0x0804c19c,
0x634284d3, 0x0804c1c0,
0x0804c1f0, 0x344c4eb1,
0x0804c1fc, 0x0804c1cc,
0x0c4079ef, 0x0804c214,
0x0804c178, 0x425ebd95,
0x0804c184, 0x0804c1cc,
0x07ace749, 0x0804c1a8,
0x0804c1e4, 0x237a3a88,
0x0804c184, 0x0804c1f0,
0x4b846cb6, 0x0804c184,
0x0804c214, 0x1fba9a98,
0x0804c1c0, 0x0804c19c,
0x3a4ad3ff, 0x0804c1c0,
0x0804c184, 0x16848c16,
0x0804c178, 0x0804c190,
0x499ee4ce, 0x0804c1b4,
0x0804c1c0, 0x261af8fb,
0x0804c184, 0x0804c1cc,
0x770ea82a, 0x0804c1fc
};
static uint32_t solution = 0x40475194;
/*
* Password: "LLRR"
*
* The goal is to make berry == solution by xoring it with successive values
* using L and R commands. I implemented the checker in python and brute
* forced. See solve_blue.py.
*/
static void blue(void)
{
blue_preflight();
struct node *node = graph;
struct node *berry = node + 1;
int letter;
bool end;
for (int i = 0; i <= 0xe; i++) {
// [ge]
end = false;
letter = buffer[i];
if (letter == 'L') {
// [gd]
node = node->next;
} else if (letter == 'R') {
// [gf]
node = (node + 2)->next;
// goto [gl]
} else if (letter == '\n') {
// [gh]
end = true;
} else {
// [gk]
// [gj]
end = true;
puts("boom");
}
// [gl]
if (end) {
break;
}
// [go]
berry ^= node->berry;
}
// [gn]
printf("PROGRAMMING GATE ARRAY...");
fflush(stdout);
sleep(1);
puts("SUCCEEDED");
usleep(0x7a120);
if (berry == solution) {
puts("VOLTAGE_REROUTED_FROM_REMOTE_DETONATION_RECEIVER");
wire_blue--;
} else {
// [gt]
wire_blue++;
}
}
static uint32_t r[3];
/*
* In addition to the usual fgets() into the buffer, this function populates
* and prints the r[3] array with random values. Running this several times,
* you can see that the values are the same each time, which means that an
* earlier function called srand() with a constant seed to make the randomness
* deterministic.
*/
static void red_preflight(void)
{
r[0] = rand() & 0x7fffffff; // 0x6B8B4567
r[1] = rand(); // 0x327B23C6
r[2] = rand(); // 0x643C9869
for (int i = 0; i <= 2; i++) {
// [gf]
printf("CLOCK SYNC %08X\n", r[i]);
usleep(0x7a120);
}
// [gh]
printf("ENTER_CLOCK_RESYNCHRONIZATION_SEQUENCE:");
fgets(buffer, 0x15, stdin);
}
/*
* Password: "KDG3DU32D38EVVXJM64"
*
* Since each character of the buffer is checked individually, this algorithm
* can be modified from a checker to a generator. See solve_red.py for an
* example generator.
*/
static void red(void)
{
red_preflight();
const char *alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
for (int i = 0; i <= 0x12; i++) {
// [ge]
if (buffer[i] != (alphabet[r[2] & 0x1f])) {
wire_red++;
return;
}
// [gd]
r[2] = (r[2] >> 5) | (r[1] << 0x1b);
r[1] = (r[1] >> 5) | (r[0] << 0x1b);
r[0] = r[0] >> 5;
}
wire_red = 0;
return;
}
int main(void)
{
pthread_t thread;
FILE *input;
pthread_create(&thread, NULL, libc_start_main, NULL);
signal(SIGUSR1, disarm_handler);
bool running = true;
for (;;) {
if (running) {
menu();
}
printf("MENU SELECTION: ");
input = stdin;
fgets(buf, 0x14, stdin);
if (strcmp(buf, "1\n") == 0
|| strcasecmp(buf, "yellow\n") == 0) {
yellow();
} else if (strcmp(buf, "2\n") == 0
|| strcasecmp(buf, "green\n") == 0) {
green();
} else if (strcmp(buf, "3\n") == 0
|| strcasecmp(buf, "blue\n") == 0) {
blue();
} else if (strcmp(buf, "4\n") == 0
|| strcasecmp(buf, "red\n") == 0) {
red();
} else if (strcasecmp(buf, "exit\n") == 0
|| strcasecmp(buf, "quit\n") == 0) {
return;
} else if (strcasecmp(buf, "disarm\n") == 0) {
raise(SIGUSR1);
} else if (buf[0] == '\n') {
running = false;
continue;
} else {
wire_green = 2;
}
printf("PRESS ENTER TO RETURN TO MENU");
getchar();
running = true;
}
}
input.txt
1
84371065
2
dcaotdaeAAAAb
3
LLRR
4
KDG3DU32D38EVVXJM64
disarm