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 filename | Load the file |
| run | start the program |
| run 1 2 3 | start the program with args 1 2 3 |
| kill | stop the program |
| quit | exit |
| break sum | set break point before function “sum” |
| break *0x8048c3 | set break point at *0x8048c3 |
| delete 1 | delete break point 1 |
| continue | continue until the next break point |
| clear sum | delete break point before function “sum” |
| info registers/functions/stack | show the information of registers/functions/stack |
| print *(int*)0x123456 | print the value of 0x123456(by int) |
| print /x /d /t $rax | print 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!
Views: 158

damn,you’re so juan~