Skip to content

REPL introduction guide

Tamas K Lengyel edited this page Apr 3, 2020 · 1 revision

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.

What is REPL?

REPL stands for Read Eval Print Loop. In simple term - it's a python console, what allows you to interact with running drakvuf.

What can I do with REPL?

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.

How is REPL working?

REPL is based on python3 python3/c api and ctypes. To generate bindings ctypesgen is used.

How to start REPL?

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.

How do I create/remove a trap?

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))

Show me a bigger example!

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.