In this article, I will briefly go over how I integrated pwntools with radare2. The means by which I have accomplished this are generic and can be extended to integrate pwntools with your debugger of choice (for instance: IDA Pro, pwndbg, Binary Ninja, etc).
Like most techies, I have a constant struggle to get my setup working "just the way i like it". I have been a fan of pwntools for quite a while as it provides nice abstractions for things such as interacting with programs, utilizing memory leaks, and more. When it comes to interacting with programs, the abstraction layer allows you uniformly interact with a program, and not worry whether your program is being run locally, over a network, via ssh, and more. For reversing and writing exploits, it's often times helpful to run the program under a debugger. Pwntools provides an abstraction for that, in the form of the GDB module.
The GDB module of pwntools was created specifically to automate and abstract starting a process under gdb and performing debugging. Note that I said, specifically, gdb, not a generic debugger. This is mostly a historical choice, likely due to GDB being a defacto debugger for Linux executables. So much so, that the use of gdb sneaks in many places (such as QEMU even having default gdb support). A quick aside on gdb:
When discussing gdb, I'd venture most think of the command-line tool that has that name (i.e.: "gdb ./my_program"). Using GDB in that way is fine if what you want to run is local to the same machine you're running the gdb command from. However, what if you wanted to run gdb against an application running on a separate server? Or for that matter, what if you wanted to debug an application/exploit against an emulated architecture that you were not running locally? One commonly used option in this case is to utilize the GDB Server (gdbserver). The GDB Server allows you to run an application under the purview of the GNU debugger on one server, and connect to and control it from another. In this way, you can interact with an application as if you were running it locally, however in the back-end your gdb is transparently sending commands to the gdbserver instead of directly controlling the binary.
This distinction becomes important for pwntools. For example, if we were to run the following command:
from pwn import * gdb.debug("./my_application")
What would actually be happening from pwntool's perspective is that it will start "my_application" up inside gdbserver. From there, it will connect to the gdbserver with a gdb instance. More specifically, the command will look something like this
$ gdb -x /tmp/<pwntools_file> ./my_application
Luckily, many debuggers out there support connecting to the gdbserver as the backend. For instance, IDA Pro and radare2 both support this. With this in mind, I submitted a PR that added an extra step into this functionality. Specifically, pwntools will now check if an executable file named "pwntools-gdb" exists in it's path. If so, it will preferentially utilize that instead of the gdb binary itself. So the above execution instead looks like:
$ pwntools-gdb -x /tmp/<pwntools_file> ./my_application
Why is this a good thing? This now allows for you as a user to create your own handlers for whatever you want to use to debug. The only thing that needs to be supported is that your debugger of choice has to be able to connect to the gdbserver. For my case, I have created a few simple files to manage this. The first, is a script that simply moves or removes ("enables" and "disables") utilization of radare2 as the backend by simply symbolically linking the file name as necessary. The second, more important part, is the script that takes the above pwntools-gdb line, interprets the gdb commands inside the pwntools script file (which contains IP address/port/etc), and runs the application.
This post is already longer than I was hoping it to be, so in short I have uploaded a version of my pwntools-gdb file for radare2. To utilize this, you simply need to name it "pwntools-gdb", chmod u+x, and put it in your PATH somewhere. Also, for the script, any radare2 commands you want to run should start with a hashtag ("#"). This was just a means to shim in commands without getting in the way of the gdb commands which will treat that as a comment. For example:
#!/usr/bin/env python # The following will set a breakpoint at symbol main, then continue execution. from pwn import * gdbscript = """#r2.cmd('db sym.main') #r2.cmd('dc')""" gdb.debug("./my_program",gdbscript=gdbscript)
Here's the shim script for radare2: https://gist.github.com/bannsec/43cf0f1b05ec37eb7e92a2922967bc46
Also, note that in my script I utilize GNU Screen (for which I also added some extra support to pwntools). If you're not a GNU Screen user, you will need to tweak line 60 to start radare2 how you want to. If you're a tmux user who wants to rant about how much better it is, you can send your feedback to >/dev/null. ;-)