-
Notifications
You must be signed in to change notification settings - Fork 257
REPL introduction guide
This is a small tutorial that aims to briefly explain how REPL works and how it can be used to enchance your debugging experience with drakvuf.
REPL stands for Read Eval Print Loop. In simple term - it's a python console, what allows you to interact with running drakvuf.
REPL allows you to do anything you want. Every plugin included in drakvuf can be rewritten into python using REPL. Possibilities are endless. The most common usage of REPL is debugging.
REPL is based on python3 python3/c api and ctypes. To generate bindings ctypesgen is used.
First of all, you need to install required python dependencies. You can do it in virtualenv (as long as you execute rest of the commands from within). Following commands should do the trick.
python3 -m venv .venv
source .venv/bin/activate
pip install ctypesgen ipython
Next, compile drakvuf with REPL enabled. To do that use add --enable-repl
to your configure
command.
Then invoke repl_start
during any breakpoint you want. You can even use it as a callback. It's that simple!
Alternatively you can run repl
binary, which will inject a trap and put you into REPL instantly. This is very convenient tool to try out REPL.
trap = libdrakvuf.drakvuf_trap_t()
trap.type = libdrakvuf.REGISTER
trap.name = libdrakvuf.ReturnString("fancy name")
trap.reg = 20 # CR3, for now this isn't in py_libdrakvuf
trap.cb = repl_start # we could specify a custom python function with correct signature and it would be called
libdrakvuf.drakvuf_add_trap(drakvuf, trap);
# note, this will leak memory, as we should pass free instead of nullptr
libdrakvuf.drakvuf_remove_trap(drakvuf, trap, cast(None, libdrakvuf.drakvuf_trap_free_t))
Let's try to implement clipboardmon inside REPL. For that we need to setup a trap like this:
drakvuf_trap_t trap =
{
.type = BREAKPOINT,
.cb = &repl_start,
.name = "our cool name",
.breakpoint.lookup_type = LOOKUP_PID,
.breakpoint.pid = 4,
.breakpoint.addr_type = ADDR_RVA,
.breakpoint.module = "ntoskrnl.exe"
};
drakvuf_get_kernel_symbol_rva(drakvuf, "NtDelayExecution", &rva->breakpoint.rva);
The code translates to:
# remove auto generated trap
libdrakvuf.drakvuf_remove_trap(drakvuf, trap_info.contents.trap, cast(None, libdrakvuf.drakvuf_trap_free_t))
# create new trap
trap = libdrakvuf.drakvuf_trap_t()
trap.type = libdrakvuf.BREAKPOINT
trap.cb = repl_start
trap.name = libdrakvuf.ReturnString("our cool name")
trap.breakpoint.lookup_type = libdrakvuf.LOOKUP_PID
trap.breakpoint.pid = 4
trap.breakpoint.addr_type = libdrakvuf.ADDR_RVA
# note, we need to pass rva address by pointer, to do that we need a c_ulong helper variable
rva = c_ulong()
libdrakvuf.drakvuf_get_kernel_symbol_rva(drakvuf, "NtDelayExecution", byref(rva))
trap.breakpoint.rva = rva
libdrakvuf.drakvuf_add_trap(drakvuf, trap)
After this we can call exit()
and when our trap is called a REPL will start.