Hello everyone! I will share the note about Bomb Lab here. This lab provides us with an interesting way to learn X86-64 assembly. Here we go!

Introduction: A “binary bomb” is a program provided to students as an object code file. When run, it prompts the user to type in 6 different strings. If any of these is incorrect, the bomb “explodes,’ printing an error message and logging the event on a grading server. Students must “defuse” their own unique bomb by disassembling and reverse engineering the program to determine what the 6 strings should be. The lab teaches students to understand assembly language and also forces them to learn how to use a debugger.

Environment & Tools: We chose Ubuntu as our OS, VScode to show the code, and gdb to debug.

There are many tools that are designed to help you figure out both how programs work, and what is wrong when they don’t work. Here is a list of some of the tools you may find useful in analyzing your bomb, and hints on how to use them.

  • gdb — The GNU debugger, allows you to trace through the program
  • objdump -t — Prints out the program’s symbol table
  • objdump -d — Dumps the assembly code of the program
  • strings — Prints out all printable strings in your bomb

Here are many common commands about gdb:

gdb filenameLoad the file
runstart the program
run 1 2 3start the program with args 1 2 3
killstop the program
quitexit
break sumset break point before function “sum”
break *0x8048c3set break point at *0x8048c3
delete 1delete break point 1
continuecontinue until the next break point
clear sumdelete break point before function “sum”
info registers/functions/stackshow the information of registers/functions/stack
print *(int*)0x123456print the value of 0x123456(by int)
print /x /d /t $raxprint rax in hex/dec/bin

And this is a table of registers:

There are seven phases in this lab.

Before we defuse this bomb, we should do some preparation.

Preparation:

Download: bomblab64.zip

1. Get the assembly code.

objdump -d bomb>asse.asm

objdump -t bomb>sym_table

Then we can see two new files in your folder.

2. Load the executable file.

First, we load gdb, just enter gdb in the terminal.

then we use “file bomb” to load the file.

Then it is time to defuse the bomb. Mr. Evil, I will beat you. Haha.

Outline & Phase 1

There is a “.c” file in this folder. Let’s take a peek at the code.

#include <stdio.h>
#include <stdlib.h>
#include "support.h"
#include "phases.h"

/* 
 * Note to self: Remember to erase this file so my victims will have no
 * idea what is going on, and so they will all blow up in a
 * spectaculary fiendish explosion. -- Dr. Evil 
 */

FILE *infile;

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

    /* Note to self: remember to port this bomb to Windows and put a 
     * fantastic GUI on it. */

    /* When run with no arguments, the bomb reads its input lines 
     * from standard input. */
    if (argc == 1) {  
	infile = stdin;
    } 

    /* When run with one argument <file>, the bomb reads from <file> 
     * until EOF, and then switches to standard input. Thus, as you 
     * defuse each phase, you can add its defusing string to <file> and
     * avoid having to retype it. */
    else if (argc == 2) {
	if (!(infile = fopen(argv[1], "r"))) {
	    printf("%s: Error: Couldn't open %s\n", argv[0], argv[1]);
	    exit(8);
	}
    }

    /* You can't call the bomb with more than 1 command line argument. */
    else {
	printf("Usage: %s [<input_file>]\n", argv[0]);
	exit(8);
    }

    /* Do all sorts of secret stuff that makes the bomb harder to defuse. */
    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");

    /* Hmm...  Six phases must be more secure than one phase! */
    input = read_line();             /* Get input                   */
    phase_1(input);                  /* Run the phase               */
    phase_defused();                 /* Drat!  They figured it out!
				      * Let me know how they did it. */
    printf("Phase 1 defused. How about the next one?\n");

    /* The second phase is harder.  No one will ever figure out
     * how to defuse this... */
    input = read_line();
    phase_2(input);
    phase_defused();
    printf("That's number 2.  Keep going!\n");

    /* I guess this is too easy so far.  Some more complex code will
     * confuse people. */
    input = read_line();
    phase_3(input);
    phase_defused();
    printf("Halfway there!\n");

    /* Oh yeah?  Well, how good is your math?  Try on this saucy problem! */
    input = read_line();
    phase_4(input);
    phase_defused();
    printf("So you got that one.  Try this one.\n");
    
    /* Round and 'round in memory we go, where we stop, the bomb blows! */
    input = read_line();
    phase_5(input);
    phase_defused();
    printf("Good work!  On to the next...\n");

    /* This phase will never be used, since no one will get past the
     * earlier ones.  But just in case, make this one extra hard. */
    input = read_line();
    phase_6(input);
    phase_defused();

    /* Wow, they got it!  But isn't something... missing?  Perhaps
     * something they overlooked?  Mua ha ha ha ha! */
    
    return 0;
}

We can find many important functions in this program and also guess their actions by their names. Every phase has its own key. If we enter the right key, the phase_defused() function will work, or we will fail.

To verify our guess, we also need to go through the assembly code.

Here is the assembly code for the main() function.

0000000000400da0 <main>:
  400da0:	53                   	push   %rbx
  400da1:	83 ff 01             	cmp    $0x1,%edi
  400da4:	75 10                	jne    400db6 <main+0x16>
  400da6:	48 8b 05 9b 29 20 00 	mov    0x20299b(%rip),%rax        # 603748 <stdin@@GLIBC_2.2.5>
  400dad:	48 89 05 b4 29 20 00 	mov    %rax,0x2029b4(%rip)        # 603768 <infile>
  400db4:	eb 63                	jmp    400e19 <main+0x79>
  400db6:	48 89 f3             	mov    %rsi,%rbx
  400db9:	83 ff 02             	cmp    $0x2,%edi
  400dbc:	75 3a                	jne    400df8 <main+0x58>
  400dbe:	48 8b 7e 08          	mov    0x8(%rsi),%rdi
  400dc2:	be b4 22 40 00       	mov    $0x4022b4,%esi
  400dc7:	e8 44 fe ff ff       	callq  400c10 <fopen@plt>
  400dcc:	48 89 05 95 29 20 00 	mov    %rax,0x202995(%rip)        # 603768 <infile>
  400dd3:	48 85 c0             	test   %rax,%rax
  400dd6:	75 41                	jne    400e19 <main+0x79>
  400dd8:	48 8b 4b 08          	mov    0x8(%rbx),%rcx
  400ddc:	48 8b 13             	mov    (%rbx),%rdx
  400ddf:	be b6 22 40 00       	mov    $0x4022b6,%esi
  400de4:	bf 01 00 00 00       	mov    $0x1,%edi
  400de9:	e8 12 fe ff ff       	callq  400c00 <__printf_chk@plt>
  400dee:	bf 08 00 00 00       	mov    $0x8,%edi
  400df3:	e8 28 fe ff ff       	callq  400c20 <exit@plt>
  400df8:	48 8b 16             	mov    (%rsi),%rdx
  400dfb:	be d3 22 40 00       	mov    $0x4022d3,%esi
  400e00:	bf 01 00 00 00       	mov    $0x1,%edi
  400e05:	b8 00 00 00 00       	mov    $0x0,%eax
  400e0a:	e8 f1 fd ff ff       	callq  400c00 <__printf_chk@plt>
  400e0f:	bf 08 00 00 00       	mov    $0x8,%edi
  400e14:	e8 07 fe ff ff       	callq  400c20 <exit@plt>
  400e19:	e8 84 05 00 00       	callq  4013a2 <initialize_bomb>
  400e1e:	bf 38 23 40 00       	mov    $0x402338,%edi
  400e23:	e8 e8 fc ff ff       	callq  400b10 <puts@plt>
  400e28:	bf 78 23 40 00       	mov    $0x402378,%edi
  400e2d:	e8 de fc ff ff       	callq  400b10 <puts@plt>
  400e32:	e8 67 06 00 00       	callq  40149e <read_line>
  400e37:	48 89 c7             	mov    %rax,%rdi
  400e3a:	e8 a1 00 00 00       	callq  400ee0 <phase_1>
  400e3f:	e8 80 07 00 00       	callq  4015c4 <phase_defused>
  400e44:	bf a8 23 40 00       	mov    $0x4023a8,%edi
  400e49:	e8 c2 fc ff ff       	callq  400b10 <puts@plt>
  400e4e:	e8 4b 06 00 00       	callq  40149e <read_line>
  400e53:	48 89 c7             	mov    %rax,%rdi
  400e56:	e8 a1 00 00 00       	callq  400efc <phase_2>
  400e5b:	e8 64 07 00 00       	callq  4015c4 <phase_defused>
  400e60:	bf ed 22 40 00       	mov    $0x4022ed,%edi
  400e65:	e8 a6 fc ff ff       	callq  400b10 <puts@plt>
  400e6a:	e8 2f 06 00 00       	callq  40149e <read_line>
  400e6f:	48 89 c7             	mov    %rax,%rdi
  400e72:	e8 cc 00 00 00       	callq  400f43 <phase_3>
  400e77:	e8 48 07 00 00       	callq  4015c4 <phase_defused>
  400e7c:	bf 0b 23 40 00       	mov    $0x40230b,%edi
  400e81:	e8 8a fc ff ff       	callq  400b10 <puts@plt>
  400e86:	e8 13 06 00 00       	callq  40149e <read_line>
  400e8b:	48 89 c7             	mov    %rax,%rdi
  400e8e:	e8 79 01 00 00       	callq  40100c <phase_4>
  400e93:	e8 2c 07 00 00       	callq  4015c4 <phase_defused>
  400e98:	bf d8 23 40 00       	mov    $0x4023d8,%edi
  400e9d:	e8 6e fc ff ff       	callq  400b10 <puts@plt>
  400ea2:	e8 f7 05 00 00       	callq  40149e <read_line>
  400ea7:	48 89 c7             	mov    %rax,%rdi
  400eaa:	e8 b3 01 00 00       	callq  401062 <phase_5>
  400eaf:	e8 10 07 00 00       	callq  4015c4 <phase_defused>
  400eb4:	bf 1a 23 40 00       	mov    $0x40231a,%edi
  400eb9:	e8 52 fc ff ff       	callq  400b10 <puts@plt>
  400ebe:	e8 db 05 00 00       	callq  40149e <read_line>
  400ec3:	48 89 c7             	mov    %rax,%rdi
  400ec6:	e8 29 02 00 00       	callq  4010f4 <phase_6>
  400ecb:	e8 f4 06 00 00       	callq  4015c4 <phase_defused>
  400ed0:	b8 00 00 00 00       	mov    $0x0,%eax
  400ed5:	5b                   	pop    %rbx
  400ed6:	c3                   	retq   
  400ed7:	90                   	nop
  400ed8:	90                   	nop
  400ed9:	90                   	nop
  400eda:	90                   	nop
  400edb:	90                   	nop
  400edc:	90                   	nop
  400edd:	90                   	nop
  400ede:	90                   	nop
  400edf:	90                   	nop

The functions table:

(gdb) file bomb
Reading symbols from bomb...
(gdb) info functions
All defined functions:

File bomb.c:
36:	int main(int, char **);

Non-debugging symbols:
0x0000000000400ac0  _init
0x0000000000400ae0  getenv@plt
0x0000000000400af0  __errno_location@plt
0x0000000000400b00  strcpy@plt
0x0000000000400b10  puts@plt
0x0000000000400b20  write@plt
0x0000000000400b30  __stack_chk_fail@plt
0x0000000000400b40  alarm@plt
0x0000000000400b50  close@plt
0x0000000000400b60  read@plt
0x0000000000400b70  __libc_start_main@plt
0x0000000000400b80  fgets@plt
0x0000000000400b90  signal@plt
0x0000000000400ba0  gethostbyname@plt
0x0000000000400bb0  __memmove_chk@plt
0x0000000000400bc0  __memcpy_chk@plt
0x0000000000400bd0  strtol@plt
--Type <RET> for more, q to quit, c to continue without paging--
0x0000000000400be0  fflush@plt
0x0000000000400bf0  __isoc99_sscanf@plt
0x0000000000400c00  __printf_chk@plt
0x0000000000400c10  fopen@plt
0x0000000000400c20  exit@plt
0x0000000000400c30  connect@plt
0x0000000000400c40  __fprintf_chk@plt
0x0000000000400c50  sleep@plt
0x0000000000400c60  __ctype_b_loc@plt
0x0000000000400c70  __sprintf_chk@plt
0x0000000000400c80  socket@plt
0x0000000000400c90  _start
0x0000000000400cbc  call_gmon_start
0x0000000000400ce0  deregister_tm_clones
0x0000000000400d10  register_tm_clones
0x0000000000400d50  __do_global_dtors_aux
0x0000000000400d70  frame_dummy
0x0000000000400ee0  phase_1
0x0000000000400efc  phase_2
0x0000000000400f43  phase_3
0x0000000000400fce  func4
0x000000000040100c  phase_4
0x0000000000401062  phase_5
--Type <RET> for more, q to quit, c to continue without paging--
0x00000000004010f4  phase_6
0x0000000000401204  fun7
0x0000000000401242  secret_phase
0x00000000004012a0  sig_handler
0x00000000004012f6  invalid_phase
0x000000000040131b  string_length
0x0000000000401338  strings_not_equal
0x00000000004013a2  initialize_bomb
0x00000000004013ba  initialize_bomb_solve
0x00000000004013bc  blank_line
0x00000000004013f9  skip
0x000000000040143a  explode_bomb
0x000000000040145c  read_six_numbers
0x000000000040149e  read_line
0x00000000004015c4  phase_defused
0x0000000000401660  sigalrm_handler
0x000000000040168e  rio_readlineb
0x00000000004017ac  submitr
0x0000000000401f91  init_timeout
0x0000000000401fb8  init_driver
0x000000000040218d  driver_post
0x0000000000402210  __libc_csu_init
0x00000000004022a0  __libc_csu_fini
--Type <RET> for more, q to quit, c to continue without paging--
0x00000000004022a4  _fini

It seems like we are right.

We can find that the mean() function reads the string and call phase1(). Then we jump to the address of phase_1().

0000000000400ee0 <phase_1>:
  400ee0:	48 83 ec 08          	sub    $0x8,%rsp
  400ee4:	be 00 24 40 00       	mov    $0x402400,%esi
  400ee9:	e8 4a 04 00 00       	callq  401338 <strings_not_equal>
  400eee:	85 c0                	test   %eax,%eax
  400ef0:	74 05                	je     400ef7 <phase_1+0x17>
  400ef2:	e8 43 05 00 00       	callq  40143a <explode_bomb>
  400ef7:	48 83 c4 08          	add    $0x8,%rsp
  400efb:	c3                   	retq   

From this function, we can infer how it works. Before phase_1() works, we have already entered a string. Then this function will call strings_not_equal(). If these two strings are equal, the function will return to the caller function. Otherwise, it will call explode_bomb(). We can know the result of its name :(. So, the question is which two of the strings it compares. The one must be what we just entered, because of phase_1(input). What is the other one? We have already known that strings_not_equal uses two arguments. So, flowing the sequence of registers, %rdi stores the first argument, and %rsi stores the second argument.

  400e32:	e8 67 06 00 00       	callq  40149e <read_line>
  400e37:	48 89 c7             	mov    %rax,%rdi

From these two lines in main(), we can see that the first argument, rdi, is filled by the return value (mostly belongs to read_line()). And back to phase_1(), %esi is filled by the special value, 0x402400. So, we guess the address of the key is 0x402400.

To show the content of the string, we directly use “x /s 0x402400” in gdb. The result is

But we are not sure about that, and we do not want it “boom!!!”. So, we set a breakpoint before the explode_bomb().

Then we run the bomb and enter the key.

Great! We are right! Phase 1 was defused! To finish this practice, we also need to learn more usage of gdb.

Pause the running program which is calling the input function: Press ctrl+c

But this program is still running.

Then we use the command kill to stop it.

We can use “delete” to delete the breakpoint.

Set breaks before strings_not_equal() (to test “continue”) and explode_bomb()(for security) .

Run again. The program paused at breakpoint 2.

Continue.

OK, that’s all. Stop it and quit.

Quite interesting, right? We have already learned the basic way to use gdb and objdump. And we also experienced the process of “bomb defusing”. Phase_1 is easy to solve. It is mostly like a self-study tutorial or the beginning of this assignment. We will continue to do this assignment together step by step.

Bye!

Next: Phase_2

Views: 158

One thought on “Bomb Lab (Beginning and Phase1)”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.