Binary Coverage

Purpose

This script is designed to build the coverage of a binary executed in a REVEN scenario.

How to use

usage: bin_coverage.py [-h] [--host HOST] [-p PORT] binary
positional arguments:
binary                Binary on which to compute coverage
optional arguments:
-h, --help            show this help message and exit
--host HOST           Reven host, as a string (default: "localhost")
-p PORT, --port PORT  Reven port, as an int (default: 13370)

Known limitations

N/A

Supported versions

REVEN 2.2+

Supported perimeter

Any REVEN scenario.

Dependencies

None.

Source

import argparse
import builtins
from collections import defaultdict

import reven2 as reven

# %% [markdown]
# # Binary Coverage
#
# ## Purpose
#
# This script is designed to build the coverage of a binary executed in a REVEN scenario.
#
# ## How to use
#
# ```bash
# usage: bin_coverage.py [-h] [--host HOST] [-p PORT] binary
#
# positional arguments:
#   binary                Binary on which to compute coverage
#
# optional arguments:
#   -h, --help            show this help message and exit
#   --host HOST           Reven host, as a string (default: "localhost")
#   -p PORT, --port PORT  Reven port, as an int (default: 13370)
# ```
#
# ## Known limitations
#
# N/A
#
# ## Supported versions
#
# REVEN 2.2+
#
# ## Supported perimeter
#
# Any REVEN scenario.
#
# ## Dependencies
#
# None.


def find_and_choose_binary(ossi, requested_binary):
    binaries = list(ossi.executed_binaries(requested_binary))
    if len(binaries) == 0:
        raise RuntimeError('Binary "{}" not executed in the trace.'.format(requested_binary))

    if len(binaries) == 1:
        return binaries[0]

    print('Multiple matches for "{}":'.format(requested_binary))
    for (index, binary) in enumerate(binaries):
        print("{}: {}".format(index, binary.path))
    answer = builtins.input("Please choose one binary: ")
    return binaries[int(answer)]


def compute_binary_coverages(trace, binary):
    coverages = {}
    for ctx in trace.search.binary(binary):
        asid = ctx.read(reven.arch.x64.cr3)
        loc = ctx.ossi.location()
        symbol = "unknown" if loc.symbol is None else loc.symbol.name
        asid_coverage = coverages.setdefault(asid, [loc.base_address, defaultdict(int)])[1]
        asid_coverage[symbol] += 1
    return coverages


def binary_coverage(reven_server, binary):
    binary = find_and_choose_binary(reven_server.ossi, binary)
    return compute_binary_coverages(reven_server.trace, binary)


def print_binary_coverages(coverages):
    for (asid, asid_coverage) in coverages.items():
        print("***** Coverage for CR3 = {:#x}: base address = {:#x} *****\n".format(asid, asid_coverage[0]))
        for (symbol, symbol_coverage) in asid_coverage[1].items():
            print("  {}: {}".format(symbol, symbol_coverage))
        print("\n")


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--host", type=str, default="localhost", help='Reven host, as a string (default: "localhost")')
    parser.add_argument("-p", "--port", type=int, default="13370", help="Reven port, as an int (default: 13370)")
    parser.add_argument("binary", type=str, help="Binary on which to compute coverage")
    args = parser.parse_args()

    reven_server = reven.RevenServer(args.host, args.port)
    coverages = binary_coverage(reven_server, args.binary)
    print_binary_coverages(coverages)