Home

Awesome

cpp-veh-dbi

C++ Implementation of VEH based windows dynamic binary instrumentation

Environments

Tested environments

Usage

To build library and samples,

.\script\setup
.\script\build

Then executables can be found in the bin directory.

ls bin

To run sample branch tracer for notepad,

.\bin\dllinjector C:\Windows\notepad.exe .\bin\branchtracer.dll

Then console will log the Windows API call.

+00007FF66FF9AC85,00007FFCEF55CD30,KERNEL32.DLL,GetStartupInfoW
+00007FF66FF9AD18,00007FFCF038FC10,ntdll.dll,RtlRetrieveNtUserPfn
+00007FF66FF9AD18,00007FFCF038FC10,ntdll.dll,RtlRetrieveNtUserPfn
+00007FF66FF9AD5A,00007FFCEEE6A6A0,msvcrt.dll,initterm
+00007FF66FF9AE4A,00007FFCEEE513B0,msvcrt.dll,ismbblead
+00007FF66FF9AE4A,00007FFCEEE513B0,msvcrt.dll,ismbblead

Structure

Struct VehDBI has three ways to instrument binary.

  1. Handler : Trigger on specified address.
  2. Tracer : Tracing instruction with debugging event.
  3. BTCallback : Callback on every instruction in text section.

1. Handler

Sample handler which triggered on entrypoint.

struct EntrypointHandler : Handler {
    void Handle(PCONTEXT context) override {
        std::ofstream("CONOUT$") << "trigged on entrypoint" << std::endl;
    }
};

// create dbi
VehDBI dbi;
// handler sample
size_t entrypoint = Utils::GetEntryPointAddress();
dbi.AddHandler(entrypoint, std::make_unique<EntrypointHandler>());

VehDBI::AddHandler get two arguments, address(size_t) and handler(std::unique_ptr<Handler>).

Handler require only one method, which will be invoked in specified address.

// Interface for debug event handler.
struct Handler {
    // Default virtual destructor.
    virtual ~Handler() = default;
    // Handle debug event.
    virtual void Handle(PCONTEXT context) = 0;
};

2. Tracer

Sample tracer which tracing branch instruction, branch_tracer.

Tracer require two methods.

If tracer set software bp, HandleBreakpoint should recover the opcode.

// Interface for code trace handler.
struct Tracer {
    // Default virtual destructor.
    virtual ~Tracer() = default;
    // Handle single step exception.
    virtual void HandleSingleStep(PCONTEXT context, Utils::SoftwareBP& bp) = 0;
    // Handle software breakpoint exception.
    virtual void HandleBreakpoint(PCONTEXT context, Utils::SoftwareBP& bp) = 0;
};

VehDBI::AddTracer get three arguments, tracer start point(size_t), end point(size_t) and tracer(std::unique_ptr<Tracer>).

dbi.AddTracer(0, 0, std::make_unique<BranchTracer>());

3. BTCallback

Indeed, BTCallback is callback for branch tracer, which call BTCallback::run at every instruction.

VehDBI basically run branch tracer on text section. And VehDBI::AddBTCallback add given callback to the default branch tracer. Then added callback will be invoked on every instruction in text section.

Sample BTCallback which log branch instruction, logger.

// btcallback sample
auto logger = std::make_unique<Logger>("CONOUT$");
// tracer sample
dbi.AddTracer(0, 0, std::make_unique<BranchTracer>(std::move(logger)));

Which is same with VehDBI::AddBTCallback, if tracer (start, end) point is (0, 0).

dbi.AddBTCallback(std::make_unique<Logger>("CONOUT$"));

BTCallback require only one method, which will be invoked on every instruction.

// Callback for branch tracer.
struct BTCallback {
    // Default destructor.
    virtual ~BTCallback() = default;
    // Callback.
    virtual void run(BTInfo const& info, PCONTEXT context) = 0;
};