angrdbg - that's not a typo

In many cases, it's difficult to give a symbolic analyzer good state information to start off with. One wishes there were a way to execute a program up to a point and then grab the state to analyze. Wish no more, there are a few options to do that - though they can be difficult to use and buggy :/ angrgdb is one of these options, and there are very few guides/examples I can find, so I will make one here.

I have found exactly 4 examples online. First, there is an older fork of angrgdb:

There are some sample commands there for the ais3_crackme, but they don't work with the current version of the software. Then, there is the page by the original author:

And there is an example of using python as well as using angrgdb directly within gdb. However, the example does not mention which executable it's supposed to run on. Also, there is a second example there in the form of a asciinema, also without any mention of the executable. Finally, there is a nice example here:

Which requires a patch to angrgdb to function, but is really nice.

In order to test whether an angrgdb installation went on correctly, it's nice to have a working example to start out with, so lets start with the last one. The link to the binary is here:

I am going to use a clean Ubuntu 19.10. Note that angrdbg seems incompatible with virtualenv, so I won't be using it here.

I have put the following examples up on my github page and started a pull request. Until that request is accepted, you can get them here:

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install python3-pip
$ pip3 install angr
$ pip3 install angrgdb
$ pip3 install cooldict
$ echo "python import angrgdb.commands" >> ~/.gdbinit

Example 1

For the first test, crackmenoptrace, we need to install the right library:

$ sudo apt install libc++1

and then angrgdb should execute well:

$ gdb ./crackmenoptrace 
GNU gdb (Ubuntu 8.3-0ubuntu1) 8.3
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./crackmenoptrace...
(No debugging symbols found in ./crackmenoptrace)
(gdb) break *0x400C96
Breakpoint 1 at 0x400c96
(gdb) r
Starting program: /home/jan/Downloads/crackmenoptrace 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Please enter the correct password.
>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 1, 0x0000000000400c96 in main ()
(gdb) 
(gdb) angrgdb find 0x400DAC
(gdb) angrgdb avoid 0x400DC8
(gdb) angrgdb sim $rbp-0x50 0x40
WARNING | 2020-01-15 07:29:06,778 | cle.backends.externs | Symbol was allocated without a known size; emulation will fail if it is used non-opaquely: _ZTIi
WARNING | 2020-01-15 07:29:06,780 | cle.loader | For more information about "Symbol was allocated without a known size", see https://docs.angr.io/extending-angr/environment#simdata
WARNING | 2020-01-15 07:29:06,823 | cle.backends.externs | Symbol was allocated without a known size; emulation will fail if it is used non-opaquely: _ZTIi
WARNING | 2020-01-15 07:29:06,824 | cle.loader | For more information about "Symbol was allocated without a known size", see https://docs.angr.io/extending-angr/environment#simdata
WARNING | 2020-01-15 07:29:06,824 | angr.project | Disabling IRSB translation cache because support for self-modifying code is enabled.
(gdb) angrgdb run
 >> to find: 0x400dac
 >> to avoid: 0x400dc8
 >> running the exploration...
WARNING | 2020-01-15 07:29:13,557 | angr.engines.vex.lifter | Self-modifying code is not always correctly optimized by PyVEX. To guarantee correctness, VEX optimizations have been disabled.
 >> results:

0x7fffffffde30      <64>
   ==> '1_hav3_1nf0rmat10n_that_w1ll_lead_t0_th3_arr3st\x1b0f_cspp3rstick6\x00'


 >> do you want to write-back the results in GDB? [Y, n]  >> syncing results with debugger...
(gdb) 

Example 2

There is a python example on https://github.com/andreafioraldi/angrgdb. Using the information in that example, we can solve the crackme directly in angrgdb. There is no mention which binary the example is supposed to be for, but a quick online search for the flag, shows that the executable is one of the standard angr executables. Available here: https://github.com/angr/angr-doc/tree/master/examples/ais3_crackme

Commands to run this example with the current version of angr:

# Break AFTER all that setup and exception catching is done
b *0x004005f9
# Start it up, give it a buffer of "a"s
r  aaaaaaaa
# Tell angr to find the solution and avoid the failures
angrgdb find 0x00400607
angrgdb avoid 0x00400613
# Tell angr to mark our 64 bytes as symbolic
angrgdb sim $rax 100
# Run!
angrgdb run

Like this:

$ gdb ./ais3_crackme 
GNU gdb (Ubuntu 8.3-0ubuntu1) 8.3
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./ais3_crackme...
(No debugging symbols found in ./ais3_crackme)
(gdb) b *0x004005f9
Breakpoint 1 at 0x4005f9
(gdb) r  aaaaaaaa
Starting program: /home/jan/Downloads/ais3_crackme aaaaaaaa

Breakpoint 1, 0x00000000004005f9 in main ()
(gdb) angrgdb sim $rax 100
WARNING | 2020-01-15 23:21:16,672 | angr.project | Disabling IRSB translation cache because support for self-modifying code is enabled.
(gdb) angrgdb find 0x00400607
(gdb) angrgdb avoid 0x00400613
(gdb) angrgdb run
 >> to find: 0x400607
 >> to avoid: 0x400613
 >> running the exploration...
WARNING | 2020-01-15 23:22:14,215 | angr.engines.vex.lifter | Self-modifying code is not always correctly optimized by PyVEX. To guarantee correctness, VEX optimizations have been disabled.
 >> results:

0x7fffffffe3d8      <100>
   ==> 'ais3{I_tak3_g00d_n0t3s}\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'


 >> do you want to write-back the results in GDB? [Y, n]  >> syncing results with debugger...
(gdb) c
Continuing.
Correct! that is the secret key!
[Inferior 1 (process 27979) exited normally]
(gdb) 

Example 3

On https://github.com/andreafioraldi/angrgdb there is another example in the form of an asciinema. The executable for that example comes from [BackdoorCTF 2017]

Commands to run this example:

# Break AFTER all that setup and exception catching is done
b *0x40085e
# Start it up, give it a buffer of "0"s
r 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# Tell angr to find the solution and avoid the failures
angrgdb find 0x4007b6
angrgdb avoid 0x4007cc
# Tell angr to mark 30 bytes as symbolic
angrgdb sim ($rbp-0x30) 30 
# Run!
angrgdb run

Like this:

/Downloads/Backdoor_CTF _no-calm$ gdb ./challenge 
GNU gdb (Ubuntu 8.3-0ubuntu1) 8.3
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./challenge...
(No debugging symbols found in ./challenge)
(gdb) b *0x40085e
Breakpoint 1 at 0x40085e
(gdb) r 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Starting program: /home/jan/Downloads/Backdoor_CTF _no-calm/challenge 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Breakpoint 1, 0x000000000040085e in main ()
(gdb) angrgdb sim ($rbp-0x30) 30 
WARNING | 2020-01-15 23:49:06,569 | angr.project | Disabling IRSB translation cache because support for self-modifying code is enabled.
(gdb) angrgdb find 0x4007b6
(gdb) angrgdb avoid 0x4007cc
(gdb) angrgdb run
 >> to find: 0x4007b6
 >> to avoid: 0x4007cc
 >> running the exploration...
WARNING | 2020-01-15 23:50:58,613 | angr.engines.vex.lifter | Self-modifying code is not always correctly optimized by PyVEX. To guarantee correctness, VEX optimizations have been disabled.
 >> results:

0x7fffffffde10      <30>
   ==> 'CTF{Now_th1s_1s_t0_g3t_ANGRyy}'


 >> do you want to write-back the results in GDB? [Y, n]  >> syncing results with debugger...
(gdb) c
Continuing.
hacked[Inferior 1 (process 6036) exited normally]
(gdb) 

Example 4

I want to add one more example here, to demonstrate how to handle PIE executables, because the addresses can change.

The executable for this example comes from https://crackmes.one/crackme/5c2acb8933c5d46a3882b8d4

It just asks for a serial number to be passed to it:

$ ./SimpleKeyGen 
./SimpleKeyGen [SERIAL]
$ ./SimpleKeyGen 1234
Bad Serial
$

Open the file in Ghidra:

Note the memory addresses. Since the executable does not have a traditional base address, it simply uses 0x100000. When running this program in gdb, the address is different:

$ gdb SimpleKeyGen 
GNU gdb (Ubuntu 8.3-0ubuntu1) 8.3
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from SimpleKeyGen...
(No debugging symbols found in SimpleKeyGen)
(gdb) b main
Breakpoint 1 at 0x122a
(gdb) r 12345
Starting program: /home/jan/Downloads/SimpleKeyGen/SimpleKeyGen 12345

Breakpoint 1, 0x000055555555522a in main ()
(gdb) 

GDB disables address space randomization, so at least we keep the same address every run. In the following, we take advantage of a gdb feature that allows breakpoint addressing relative to function addresses. In Ghidra, when hovering with the mouse over an instruction, we get the required information:

So the "Good Serial" message is printed at main+0x42. The info functions main command in gdb yields the address of the function:

(gdb) info functions main
All functions matching regular expression "main":
[ ... ]
Non-debugging symbols:
0x0000555555555226  main
(gdb) 

0x0000555555555226 + 0x42 = 0x555555555268, so that is our find address. We determine the avoid address similarly and can run angrgdb.

An alternative way of determining addresses is to find the program load base address and rebase Ghidra:

Reading symbols from SimpleKeyGen...
(No debugging symbols found in SimpleKeyGen)
(gdb) info files
Symbols from "/home/jan/Downloads/SimpleKeyGen/SimpleKeyGen".
Local exec file:
  `/home/jan/Downloads/SimpleKeyGen/SimpleKeyGen', file type elf64-x86-64.
 Entry point: 0x1070
 0x00000000000002a8 - 0x00000000000002c4 is .interp
 0x00000000000002c4 - 0x00000000000002e4 is .note.ABI-tag
 0x00000000000002e4 - 0x0000000000000308 is .note.gnu.build-id
 0x0000000000000308 - 0x0000000000000324 is .gnu.hash
 0x0000000000000328 - 0x0000000000000418 is .dynsym
 0x0000000000000418 - 0x00000000000004ad is .dynstr
 0x00000000000004ae - 0x00000000000004c2 is .gnu.version
 0x00000000000004c8 - 0x00000000000004e8 is .gnu.version_r
 0x00000000000004e8 - 0x00000000000005a8 is .rela.dyn
 0x00000000000005a8 - 0x0000000000000608 is .rela.plt
 0x0000000000001000 - 0x000000000000101b is .init
 0x0000000000001020 - 0x0000000000001070 is .plt
 0x0000000000001070 - 0x0000000000001305 is .text
[ ... ]

(gdb) b main
Breakpoint 1 at 0x122a
(gdb) r 12345
Starting program: /home/jan/Downloads/SimpleKeyGen/SimpleKeyGen 12345

Breakpoint 1, 0x000055555555522a in main ()
(gdb) info files
Symbols from "/home/jan/Downloads/SimpleKeyGen/SimpleKeyGen".
Native process:
  Using the running image of child process 28923.
 While running this, GDB does not access memory from...
Local exec file:
 `/home/jan/Downloads/SimpleKeyGen/SimpleKeyGen', file type elf64-x86-64.
 Entry point: 0x555555555070
 0x00005555555542a8 - 0x00005555555542c4 is .interp
 0x00005555555542c4 - 0x00005555555542e4 is .note.ABI-tag
 0x00005555555542e4 - 0x0000555555554308 is .note.gnu.build-id
 0x0000555555554308 - 0x0000555555554324 is .gnu.hash
 0x0000555555554328 - 0x0000555555554418 is .dynsym
 0x0000555555554418 - 0x00005555555544ad is .dynstr
 0x00005555555544ae - 0x00005555555544c2 is .gnu.version
 0x00005555555544c8 - 0x00005555555544e8 is .gnu.version_r
 0x00005555555544e8 - 0x00005555555545a8 is .rela.dyn
 0x00005555555545a8 - 0x0000555555554608 is .rela.plt
 0x0000555555555000 - 0x000055555555501b is .init
 0x0000555555555020 - 0x0000555555555070 is .plt
 0x0000555555555070 - 0x0000555555555305 is .text

Note the address of where .text is loaded before running and after - 0x0000555555555070 - 0x0000000000001070 = 0x0000555555554000, which is the new base address to enter in Ghidra.

You can see how this yields the 'find' address directly.

$ gdb SimpleKeyGen 
GNU gdb (Ubuntu 8.3-0ubuntu1) 8.3
[ ... ]
(No debugging symbols found in SimpleKeyGen)
(gdb) b *main+0x32
Breakpoint 1 at 0x1258
(gdb) r 12345
Starting program: /home/jan/Downloads/SimpleKeyGen/SimpleKeyGen 12345

Breakpoint 1, 0x0000555555555258 in main ()
(gdb) angrgdb sim $rax 0x15
WARNING | 2020-01-20 00:11:54,989 | angr.project | Disabling IRSB translation cache because support for self-modifying code is enabled.
(gdb) angrgdb find 0x555555555268
(gdb) angrgdb avoid 0x55555555527B
(gdb) angrgdb run
 >> to find: 0x555555555268
 >> to avoid: 0x55555555527b
 >> running the exploration...
WARNING | 2020-01-20 00:12:17,694 | angr.engines.vex.lifter | Self-modifying code is not always correctly optimized by PyVEX. To guarantee correctness, VEX optimizations have been disabled.
WARNING | 2020-01-20 00:12:18,013 | angr.state_plugins.unicorn_engine | fetching empty page [0x555556000000, 0x555556000fff]
 >> results:

0x7fffffffe3a2      <21>
   ==> '?@\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x00\x00\x00\x00'


 >> do you want to write-back the results in GDB? [Y, n]  >> syncing results with debugger...
(gdb) 

We have a solution, though the numbers represent all non-printable characters. To restrict the answer to printable characters, we would need to continue using a python script, since we can use more of the angr functionality there. However, there is a bug that keeps us from doing so. I submitted the issue to the author. If it gets fixed, I will revisit this example here.