Over the last couple of months I’ve spent quite a lot of time in gdb whilst working through the SLAE certification exam assignments. The question I had was how I could become more efficient when working in gdb? I wanted to have easier access to the register values and generally save myself time whilst debugging or analysing shellcode written in nasm assembly. I’ve been running gdb on an Ubuntu 16.04 LTS 32-bit system.
Disabling the Copyright Message on startup
I know this isn’t really a big deal but I wanted to be able to disable the copyright message when gdb started. I know you can use the ‘-q’ argument each time gdb is started but again that just seems like extra effort that isn’t really necessary. The solution I found was to add an command alias within the bash configuration file. The bash configuration file is located in the users’s home directory and called .bashrc.
- Open a terminal session and ensure it is in the home directory by entering
- Use vim to open the bash configuration file:
- Scroll to the ‘Alias definitions’ section. Add the following line:
alias gdb='gdb -q'
- Save the file and exit vim by pressing ‘escape’ and then typing
- For the settings to apply either close the terminal session or use the following command:
What I’ve done is setup an alias so that when gdb is types the actual command that is executed is ‘gdb -q’. Aliases can be used for lots of things, for example if you use multiple versions of python installed and want to set the version which will start when ‘python’ is typed then just add an alias.
The first thing I found was that I was editing the assembly source file a lot and recompiling which meant I was restarting gdb a lot. So I started looking into what I could do to save myself repeating the same tasks over and over. It turns out that gdb has a configuration file called ‘.gdbinit’ that can be to setup how it will run. The file is located in the folder which holds the executable being debugged or in the users home folder. Where this really comes into it’s own is it provides the ability to create user-defined commands. The current user defined commands can be listed by running gdb and typing:
Saving and loading Break Points to a file
I wanted to be able to save the breakpoints I was setting up and this can easily be done be creating a user-defined command. The definition below is in two parts.
- Define the command – ‘define bsave
- Document the command usage – document bsave
# Save breakpoints to a file define bsave save breakpoints .breakpoint end document bsave Saves all current defined breakpoints to the file .breakpoint in the PWD Usage: bsave end
Using the user-define commands
- To run the command the name is simply entered into gdb
- The document about how to use the command is displayed by typing help bsave
Now we can create a couple of other commands
- bload: loads breakpoints from the file .breakpoint
- bsavef: Saves breakpoints to the specified file
- bloadf: Loads breakpoints from the specified files
# Restore breakpoints from a file define bload source .breakpoint end document bload Loads all breakpoints from the file .breakpoint in the PWD Usage: bload end # Save breakpoints to a defined file define bsavef if $argc != 1 help bsave else save breakpoints $arg0 end end document bsavef Saves all current defined breakpoints to the defined file in the PWD Usage: bsavef end # Restore breakpoints from a defined file define bloadf if $argc != 1 help bloadf else source $arg0 end end document bloadf Loads all breakpoints from the defined file in the PWD Usage: bloadf end
Using gdb to debug Assembly
Whilst I was searching for information on how to configure gdb I found the following .gdbinit file
It contains a lot of user-defined commands and other configuration to optimise gdb. Interestingly it doesn’t include user-defined commands to save and load breakpoints to and from a file, so I added the commands above to this file.
I can only thank the author of this file as it is absolutely superb. It includes so much functionality that I’m not going to the whole file as this would take a very long time. Here a couple of examples of
When a program stops at a breakpoint gdb automatically displays the register values, flags and 8 lines of disassembled code:
I found this incredibly useful, not only does it display all the information it’s nicely formatted and flag values will capitalise when they are set, in the screenshot above none of the flag values are set.
It also tells you when a jump will or won’t be taken. Also, it can be seen in the example above the ‘I’ flag is set.
Sometime I’ve been looking at values on the stack or trying to workout why something isn’t working and the information in the screenshot above was now way up the terminal and finding it is going to take a bit of time. No problem just type ‘context’ and it will be displayed.
The vast majority of the configuration included within the .gdbinit file are user-define commands, so as mentioned earlier to find out what it includes just download the file, put it the home folder, start gdb and type help user-defined
List of commands from the version I was using 7.3 with my additional commands from above:
List of commands: 32bits -- Set gdb to work with 32bits binaries 64bits -- Set gdb to work with 64bits binaries argv -- Print program arguments ascii_char -- Print ASCII value of byte at address ADDR assemble -- Assemble instructions using nasm assemble_gas -- Assemble instructions to binary opcodes bhb -- Set hardware assisted breakpoint bload -- Loads all breakpoints from the file .breakpoint in the PWD bloadf -- Loads all breakpoints from the defined file in the PWD bp -- Set breakpoint bpc -- Clear breakpoint bpd -- Disable breakpoint with number NUM bpe -- Enable breakpoint with number NUM bpl -- List all breakpoints bpm -- Set a read/write breakpoint on EXPRESSION bpt -- Set a temporary breakpoint bsave -- Saves all current defined breakpoints to the file .breakpoint in the PWD bsavef -- Saves all current defined breakpoints to the defined file in the PWD cfa -- Change Auxiliary Carry Flag cfc -- Change Carry Flag cfd -- Change Direction Flag cfi -- Change Interrupt Flag cfo -- Change Overflow Flag cfp -- Change Parity Flag cfs -- Change Sign Flag cft -- Change Trap Flag cfz -- Change Zero Flag cls -- Clear screen context -- Print context window context-off -- Disable display of context on every program break context-on -- Enable display of context on every program break contextsize-code -- Set code window size to NUM lines contextsize-data -- Set data dump window size to NUM lines contextsize-stack -- Set stack dump window size to NUM lines datawin -- Display valid address from one register in data window dd -- Display 16 lines of a hex dump of address starting at ADDR ddump -- Display NUM lines of hexdump for address in $data_addr global variable dis -- Disassemble a specified section of memory disablecpuregisters -- Disable display of cpu registers in the context window disabledatawin -- Disable display of data window in the context window disableobjectivec -- Disable display of objective-c information in the context window disablesolib -- Shortcut to disable stop-on-solib-events trick! disablestack -- Disable display of stack information in the context window dump_binfile -- Write a range of memory to a binary file dump_hexfile -- Write a range of memory to a file in Intel ihex (hexdump) format dumpjump -- Display if conditional jump will be taken or not eflags -- Print eflags register enablecpuregisters -- Enable display of cpu registers in the context window enabledatawin -- Enable display of data window in the context window enableobjectivec -- Enable display of objective-c information in the context window enablesolib -- Shortcut to enable stop-on-solib-events trick! enablestack -- Enable display of stack in the context window flags -- Print flags register frame -- Print stack frame func -- Print all function names in target get_insn_type -- Recognize instruction type at address ADDR go -- Step one instruction exactly hex_quad -- Print eight hexadecimal bytes starting at address ADDR hexdump -- Display a 16-byte hex/ASCII dump of memory at address ADDR hook-stop -- !!! FOR INTERNAL USE ONLY - DO NOT CALL !!! init -- Run program and break on _init() int3 -- Patch byte at address ADDR to an INT3 (0xCC) instruction lib -- Print shared libraries linked to target main -- Run program and break on main() n -- Step one instruction nop -- Usage: nop ADDR1 [ADDR2] null -- Usage: null ADDR1 [ADDR2] pret -- Execute until selected stack frame returns (step out of current call) print_insn_type -- Print human-readable mnemonic for the instruction type (usually $INSN_TYPE) reg -- Print CPU registers sig -- Print what debugger does when program gets various signals smallregisters -- Create the 16 and 8 bit cpu registers (gdb doesn't have them by default) sstart -- Run program and break on __libc_start_main() stack -- Print backtrace of the call stack start -- Run program and break on _start() step_to_call -- Single step until a call instruction is found stepo -- Step over calls (interesting to bypass the ones to msgSend) threads -- Print threads in target tip_display -- Tips on automatically displaying values when a program stops tip_patch -- Tips on patching memory and binary files tip_strip -- Tips on dealing with stripped binaries tip_syntax -- Summary of Intel and AT&T syntax differences tips -- Provide a list of tips from users on various topics trace_calls -- Create a runtime trace of the calls made by target trace_run -- Create a runtime trace of target var -- Print all global and static variable names (symbols)
I hope this is useful and might save some time when using gdb.