CSC373 Lab2 (the Bomblab)
Description [back]
The fiendish Dr. Lancaster has recruited you to the bomb squad. You have volunteered to either single handedly or with one other classmate defuse one of the program bombs that have been discovered.
You will need to obtain your zipped bomb and extract two parts: an executable bomb and the source code, bomb.c. The bomb.c file only contains the main function. The main function calls 6 additional functions: phase_1, phase_2, ... phase_6. The object files for these functions are linked into the executable file bomb, but the C source code is not included in your zip file.
For each phase, the main function reads an input string from standard input and calls the corresonding phase_ function.
The six phases are progressively more difficult.
It is not a good idea to assume that the input string is very short. For some of the phases an assumption that the input string is less than 80 characters is not correct.
You should only work with the bomb provided to you as they are all different. You will submit a text file with the input strings that are successful in defusing your bomb. The set of 6 strings that defuse your bomb will not be the same that will work for some one else's bomb.
The main Linux utilities that can assist you in discovering the magic 6 strings to defuse your bomb are:
- strings
- objdump
- gdb
The debugger, gdb, is the main tool that will allow you to examine your executable in detail and is the key to essentially all the phases. But don't ignore the strings utility. It can provide hints. You may get lucky.
Requirements - What to turn in [back]
Submit a text file named "soln.txt" containing the strings - one string for each phase you solved for your bomb.
You may work on the bomb either individually or with as a team with one other person.
To submit your solution file, change to the directory on the Linux machine containing the lab2 files and your solution file and type:
make handin
Points earned:
Number of Phases Defused Points Earned 1 62 2 70 3 78 4 85 5 92 6 100
Getting Started [back]
Step 1
You may work on this lab alone or in a team with 2 people.
Request your bomb by email indicating whether you will work alone or listing the 2 people in the team. A reply email will provide instructions for obtaining your personalized bomb.
Get the bomb.zip file with your customized executable file to defuse .
Unzip bomb.zip to get:
- bomb.c (source C code for the main function only)
- bomb (executable)
Step 2
Run the bomb program at the prompt just to see what it does.
(It tries to read input, but doesn't prompt you. So after it prints the introductory message, enter some input and see what happens.)
Note if you have determined one or more magic strings and put them in a file, say soln.txt, you can avoid having to retype those strings by executing the bomb like this:
bomb soln.txt
The bomb program is cleverly written so that if a command line argument (like soln.txt) is provided, the bomb program will read strings from that file as it needs input until it reaches end of file. Then it will switch to reading from standard input!
Otherwise, you will have to retype magic strings for already defused phases, when working on the next phase.
Step 3
Use objdump to generate a text file with assembly code of your executable.
objdump -d bomb >bomb.s
The -d option means disassemble; that is, reverse the assembly process and produce assembly listing from the executable (or ojbect file).
The output of objdump is to standard output (the terminal window) by default. The ">bomb.s" redirects the output instead to the file "bomb.s". If bomb.s exists it is overwritten. If it doesn't exist, it is created.
Step 4
Use the strings utility to extract any text strings that might be contained in your executable.
Note that the strings utility is an unsophisticated tool that just tries to find sequences of bytes that look like characters and outputs each one as a "string."
This can result in a number of false "positives"; that is, sequences of bytes which are coincidentally the codes of ascii characters.
The output of strings is also to standard output, so it may be helpful to redirect output to a file:
strings bomb >bomb_strings.txt
Since bomb_strings.txt is a text file, you can then examine bomb_strings.txt with emacs or with the "pager" less:
less bomb_strings.txt
Step 5
Use the debugger, gdb, to analyze your executable.
Using gdb [back]
You can look at the assembly code by using nano (or emacs) to open the bomb.s file you've created with objdump.
But the debugger, gdb, allows you to
- step through execution one assembly instruction at a time
- set break points and continue execution up to the next break point
- step into functions or execute a function as one step.
- finish executing a function and return to its caller
- print addresses and/or contents of addresses
- print contents of registers
- modify memory and/or register contents
- list assembly instructions for the current function
- display where execution is paused (which instruction)
Using gdb [back]Running gdb
To run gdb on your bomb type:
gdb bomb
An introductory gdb message appears and then a gdb prompt.
You can now enter gdb commands. Note that bomb is not yet running, but you use gdb to run bomb under gdb's control.
To control where to pause, you will need to use gdb to set break points.
Without breakpoints running bomb will not pause and gdb will execute bomb the same as running it normally.
Gdb commands [back]
- Starting, Break Points and Stopping
- Stepping one Instruction at a Time
- Examining Data
- Printing a function's machine instructions
Starting and Stopping [back to gdb cmds]
You need to set at least one break point before starting the program running in gdb. Otherwise, it will just execute normally without letting you enter any more gdb commands to examine your program.
gdb Command | Abbreviation | Description |
---|---|---|
break | b location | Set a break point at the beginning of a function, at a line number (only if you have the source code), or at an instruction address |
Examples | ||
b main | Set a break point at the beginning of function main | |
b phase_1 | Set a break point at the beginning of function phase_1 | |
b 74 | Set a break point at line 74 of the current file (Only works if the source code is available and the file was compiled with debugging information. Your main function works, but not any function it calls.) | |
b *0x8048d7d | Set a break point at instruction address 0x8048d7d. Note that you must use the asterisk (*) when setting a break point at an address. | |
run | r optional args | Start the program running from the beginning; suspend the program if a break point is reached and wait for more gdb commands. |
Examples | ||
r | Run the program being debugged (with no additional command line arguments) | |
r soln.txt | Run the program being debugged with command line argument
soln.txt; e.g, as if you had executed bomb soln.txt at the shell prompt. |
|
continue | c | Continue executing until another break point is reached. |
quit | q | Stop execution of the program being debugged. |
Stepping one instruction at a time [back to gdb cmds]
gdb Command | Abbreviation | Description |
---|---|---|
step one instruction | si | Execute the next machine instruction. If it is a function call, step into the function. |
Examples | ||
si | Execute the next machine instruction | |
si 5 | Execute the next 5 machine instructions. | |
step one instruction | ni |
Like the si command, execute the next machine
instruction. However, if that instruction is a function call,
execute the entire function in one step. (step over rather
than into the function). You should always step over the standard C input and output function calls such as printf, scanf, and fgets. |
step | s | Execute the next C statement. If it is a function call, step into the function. If gdb has no line number information, it will continue executing to the end of the function and/or the end of the program. For your program, gdb only has line number information for the main function, not for any of the phase_n functions (n=1,2,3,4,5,6). |
next | n | Like the step command, executes the next C statement. However, if the statement is a function call, execute the entire function in one step. (step over). You should always step over the standard C input and output function calls such as printf, scanf, and fgets. |
Examining Data [back to gdb cmds]
There are two basic gdb commands for examining data. But when dealing with machine code, note that data will be in registers and/or in memory.
Sometimes a value in a register is a memory address.
To examine an ordinary value in a register use the gdb print command.
To examine a value in a register that is a memory address, it is more convenient to use the x command
gdb Command | Abbreviation | Description |
---|---|---|
p location | Print the value at a location | |
Examples | ||
p/x $eax | Print the contents of register %eax as a hexadecimal integer | |
p/x 16 | print the base 10 integer 16 in hexadecimal format (e.g. as 0x10 | |
p/d 0x80408dc | print the hexadecimal 0x80408dc as an integer in base
10 Output would be: p/d 0x80489fb $4 = 134515195 (The value is stored in the next gdb variable, $4; the next gdb varialbe will be $5) |
|
p $ebp + 8 | Evaluate the contents of register %ebp, add 8 and print the result. This is gdb's way of letting you evaluate the address indicated by 8(%ebp). | |
x | x location | Examine the value at a memory location |
Examples | ||
x/d 0x80408dc | print the integer value stored at memory
address 0x80408dc (in base 10) For example, if the value at that address is 6, the output of this x command is 0x80408dc: 6 |
Printing a function's machine instructions [back to gdb cmds]
If you have stepped into one of the phase_n functions for which the source code is not available, and you haven't used objdump to get the assembler code, gdb can also output the assembler code.
The command is
gdb Command | Abbreviation | Description |
---|---|---|
disassemble | disas optional address or range | Output the assembler code for the function that contains an instruction at the given address, or limit the output to the instructions in the given range. If no argument is given, give the code for the function containing the current stopped point of the debugger. |
Examples | ||
disas | Disassemble (give the assembler code) for the current function | |
disas 0x8048d2c | Disassemble (give the assembler code) for the function that contains an instruction at address 0x8048d2c |