Video: Solving VM-based challenges using Cerbero

How to solve VM-based challenges with the help of Cerbero.

This is the template code:

from Pro.Core import *
from Pro.UI import *
from Pro.ccast import sbyte
import os, struct

REG_COUNT = 16

def regName(id):
    return "R" + str(id)

def disassemble(code, regs):
    return "instr"

STEP_VIEW_ID = 1
DISASM_VIEW_ID = 2
MEMORY_VIEW_ID = 3
REGISTERS_VIEW_ID = 4
STACK_VIEW_ID = 5

# the dump directory should have files with increasing number as name
# e.g.: 0, 1, 2, etc.
DBGDIR = r"path/to/state/dumps"
BPX = -1

# logic to extract the instruction pointer from the dumps
# we use that as text in the Trace table and to go to a break point
def loadStepDescr(ud, steps):
    stepsdescr = []
    bpx_pos = -1
    for step in steps:
        with open(os.path.join(DBGDIR, str(step)), "rb") as f:
            f.seek((REG_COUNT - 1) * 2)
            ip = struct.unpack_from(">H", f.read(2), 0)[0]
            if bpx_pos == -1 and ip == BPX:
                bpx_pos = len(stepsdescr)
            stepsdescr.append("%04X" % (ip,))
    ud["stepsdescr"] = stepsdescr
    if bpx_pos != -1:
        ud["bpxpos"] = bpx_pos

def loadStep(cv, step, ud):
    #with open(os.path.join(DBGDIR, str(step)), "rb") as f:
    #    dump = f.read()
    dump = b"\x00\x01" * REG_COUNT
    regs = struct.unpack_from("<" + ("H" * REG_COUNT), dump, 0)
    ud["regs"] = regs
    # set up regs table
    t = cv.getView(REGISTERS_VIEW_ID)
    labels = NTStringList()
    labels.append("Register")
    labels.append("Value")
    t.setColumnCount(2)
    t.setRowCount(REG_COUNT)
    t.setColumnLabels(labels)
    t.setColumnCWidth(0, 10)
    t.setColumnCWidth(1, 20)
    # set up memory
    h = cv.getView(MEMORY_VIEW_ID)
    curoffs = h.getCurrentOffset()
    cursoroffs = h.getCursorOffset()
    #mem = dump[REG_COUNT * 2:]
    mem = b"dummy memory"
    ud["mem"] = mem
    h.setBytes(mem)
    h.setCursorOffset(cursoroffs)
    h.setCurrentOffset(curoffs)
    # set up stack table
    stack = (0x1000, 0x2000, 0x3000)
    ud["cursp"] = 0x6000
    ud["stack"] = stack
    t = cv.getView(STACK_VIEW_ID)
    labels = NTStringList()
    labels.append("Stack address")
    labels.append("Value")
    t.setColumnCount(2)
    t.setRowCount(len(stack))
    t.setColumnLabels(labels)
    t.setColumnCWidth(0, 10)
    t.setColumnCWidth(1, 20)
    # set up disasm
    t = cv.getView(DISASM_VIEW_ID)
    disasm = disassemble(mem, regs)
    t.setText(disasm)

def tracerCallback(cv, ud, code, view, data):
    if code == pvnInit:
        # get steps
        steps = os.listdir(dbgdir)
        steps = [int(e) for e in steps]
        steps = sorted(steps)
        ud["steps"] = steps
        loadStepDescr(ud, steps)
        # set up steps
        t = cv.getView(STEP_VIEW_ID)
        labels = NTStringList()
        labels.append("Trace")
        t.setColumnCount(1)
        t.setRowCount(len(steps))
        t.setColumnLabels(labels)
        t.setColumnCWidth(0, 10)
        # go to bpx if any
        if "bpxpos" in ud:
            bpxpos = ud["bpxpos"]
            t.setSelectedRow(bpxpos)
        return 1
    elif code == pvnGetTableRow:
        vid = view.id()
        if vid == STEP_VIEW_ID:
            data.setText(0, str(ud["stepsdescr"][data.row]))
        elif vid == REGISTERS_VIEW_ID:
            data.setText(0, regName(data.row))
            v = ud["regs"][data.row]
            data.setText(1, "%d (0x%X)" % (v, v))
            if data.row >= 13:
                data.setBgColor(0, ProColor_Special)
                data.setBgColor(1, ProColor_Special)
        elif vid == STACK_VIEW_ID:
            spaddr = ud["cursp"] + (data.row * 2)
            data.setText(0, "0x%04X" % (spaddr,))
            v = ud["stack"][data.row]
            data.setText(1, "%d (0x%X)" % (v, v))
    elif code == pvnRowSelected:
        vid = view.id()
        if vid == STEP_VIEW_ID:
            loadStep(cv, ud["steps"][data.row], ud)
    return 0

def tracerDlg():
    ctx = proContext()
    v = ctx.createView(ProView.Type_Custom, "Tracer Demo")
    user_data = {}
    v.setup("<ui><hs><table id='1'/><vs><text id='2'/><hex id='3'/></vs><table id='4'/><table id='5'/></hs></ui>", tracerCallback, user_data)
    dlg = ctx.createDialog(v)
    dlg.show()
    
tracerDlg()

Ghidra & Cerbero: released the native interface PoC

… or what happens when two multi-headed monsters meet. 🙂

I just released version 3.2 of Cerbero Suite which contains the anticipated proof-of-concept native interface for Ghidra. To install the necessary extension in Ghidra, open the “util” directory and extract the contents of ghidra.zip. You’ll find a PDF document with the setup instructions.

The interface works on Windows, Linux and OS X. How does it work you might wonder? It works via IPC, specifically via sockets. When I first came up with the idea I was curious about two things: to test the SDK of Cerbero against a new challenge and to see if the responsiveness of the UI would be good enough.

Regarding the responsiveness, I didn’t have an answer to that until I had a working disassembly view. I think it’s very responsive. In fact, I developed and tested the UI on different machines than the one running Ghidra and even in that scenario the UI was fast. 🙂

The PoC comes with the most fundamental views as you can see from the screen-shot. Navigation is complete, comments and bookmarks. Renaming is partially done, unfortunately renaming of variables is not yet supported. That was a feature which I wanted to have even in the PoC, but at a certain point I couldn’t delay any further the release.

Be aware that this is a PoC, I didn’t do extensive testing and there are some very important features which are still missing. Just to name a few: automatic refresh of the disassembly during analysis is missing, manual defining of code/data is missing, so is the capability to filter and sort table items.

Although things are missing, I tried to polish the UI enough to make it useful for some actual work and for a real evaluation on the user side. I didn’t experience any crash and in the worst case scenario you can just close the UI process and spawn a new one. In fact, you can even open multiple UI instances for the same file, it’s not an issue.

The whole project (research/C++ UI/Java extension) represents one month of work on my side. So I feel pretty confident that I can make the integration very smooth in a matter of a few months. The reason why I released this as a PoC is that before investing more time into it, I want to see if there’s actual interest for it from the community. The PoC itself was a nice project for myself, but now it’s up to you to decide if you want to make it mature into a real project.

Cerbero, as you know, is a commercial application, but it can be freely downloaded and used as a trial without any limitation. So trying it out shouldn’t be an issue.

Happy hacking! 🙂